about summary refs log tree commit diff
path: root/third_party/immer
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2020-07-15T07·20+0100
committerVincent Ambo <mail@tazj.in>2020-07-15T07·23+0100
commit1213b086a1015a662ab7ebd658f784534fd3116a (patch)
treed3bc8f3b7f40b8b60f0ef6fbd649cf765f4fdfb6 /third_party/immer
parent1390827b9ea1e04bc9863e48930bfd16db3b716e (diff)
parent7f19d641647ac4ef313ed88d6b5c140983ce5436 (diff)
merge(3p/immer): Subtree merge at 'ad3e3556d' as 'third_party/immer' r/1299
Change-Id: I9636a41ad44b4218293833fd3e9456d9b07c731b
Diffstat (limited to 'third_party/immer')
-rw-r--r--third_party/immer/.clang-format38
-rw-r--r--third_party/immer/.dir-locals.el7
-rw-r--r--third_party/immer/.github/FUNDING.yml3
-rw-r--r--third_party/immer/.gitignore24
-rw-r--r--third_party/immer/.gitmodules6
-rw-r--r--third_party/immer/.travis.yml84
-rw-r--r--third_party/immer/BUILD11
-rw-r--r--third_party/immer/CMakeLists.txt141
-rw-r--r--third_party/immer/LICENSE23
-rw-r--r--third_party/immer/README.rst225
-rw-r--r--third_party/immer/WORKSPACE0
-rw-r--r--third_party/immer/benchmark/CMakeLists.txt128
-rw-r--r--third_party/immer/benchmark/config.hpp55
-rw-r--r--third_party/immer/benchmark/extra/refcounting.cpp146
-rw-r--r--third_party/immer/benchmark/set/access.hpp175
-rw-r--r--third_party/immer/benchmark/set/access.ipp30
-rw-r--r--third_party/immer/benchmark/set/insert.hpp55
-rw-r--r--third_party/immer/benchmark/set/insert.ipp28
-rw-r--r--third_party/immer/benchmark/set/iter.hpp111
-rw-r--r--third_party/immer/benchmark/set/iter.ipp25
-rw-r--r--third_party/immer/benchmark/set/string-box/access.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-box/generator.ipp46
-rw-r--r--third_party/immer/benchmark/set/string-box/insert.cpp11
-rw-r--r--third_party/immer/benchmark/set/string-box/iter.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-long/access.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-long/generator.ipp44
-rw-r--r--third_party/immer/benchmark/set/string-long/insert.cpp11
-rw-r--r--third_party/immer/benchmark/set/string-long/iter.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-short/access.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-short/generator.ipp44
-rw-r--r--third_party/immer/benchmark/set/string-short/insert.cpp10
-rw-r--r--third_party/immer/benchmark/set/string-short/iter.cpp10
-rw-r--r--third_party/immer/benchmark/set/unsigned/access.cpp10
-rw-r--r--third_party/immer/benchmark/set/unsigned/generator.ipp32
-rw-r--r--third_party/immer/benchmark/set/unsigned/insert.cpp10
-rw-r--r--third_party/immer/benchmark/set/unsigned/iter.cpp10
-rw-r--r--third_party/immer/benchmark/vector/access.hpp261
-rw-r--r--third_party/immer/benchmark/vector/assoc.hpp245
-rw-r--r--third_party/immer/benchmark/vector/branching/access.ipp20
-rw-r--r--third_party/immer/benchmark/vector/branching/assoc.ipp20
-rw-r--r--third_party/immer/benchmark/vector/branching/basic/access.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/basic/assoc.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/basic/concat.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/basic/push.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/concat.ipp20
-rw-r--r--third_party/immer/benchmark/vector/branching/gc/access.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/gc/assoc.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/gc/concat.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/gc/push.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/push.ipp20
-rw-r--r--third_party/immer/benchmark/vector/branching/safe/access.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/safe/assoc.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/safe/concat.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/safe/push.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/unsafe/access.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/unsafe/assoc.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/unsafe/concat.cpp10
-rw-r--r--third_party/immer/benchmark/vector/branching/unsafe/push.cpp10
-rw-r--r--third_party/immer/benchmark/vector/common.hpp212
-rw-r--r--third_party/immer/benchmark/vector/concat.hpp166
-rw-r--r--third_party/immer/benchmark/vector/drop.hpp142
-rw-r--r--third_party/immer/benchmark/vector/misc/access.cpp94
-rw-r--r--third_party/immer/benchmark/vector/misc/assoc.cpp119
-rw-r--r--third_party/immer/benchmark/vector/misc/concat.cpp49
-rw-r--r--third_party/immer/benchmark/vector/misc/drop.cpp63
-rw-r--r--third_party/immer/benchmark/vector/misc/push-front.cpp30
-rw-r--r--third_party/immer/benchmark/vector/misc/push.cpp83
-rw-r--r--third_party/immer/benchmark/vector/misc/take.cpp65
-rw-r--r--third_party/immer/benchmark/vector/paper/access.cpp31
-rw-r--r--third_party/immer/benchmark/vector/paper/assoc-random.cpp40
-rw-r--r--third_party/immer/benchmark/vector/paper/assoc.cpp40
-rw-r--r--third_party/immer/benchmark/vector/paper/concat.cpp22
-rw-r--r--third_party/immer/benchmark/vector/paper/push.cpp28
-rw-r--r--third_party/immer/benchmark/vector/push.hpp111
-rw-r--r--third_party/immer/benchmark/vector/push_front.hpp46
-rw-r--r--third_party/immer/benchmark/vector/take.hpp144
-rw-r--r--third_party/immer/cmake/FindBoehmGC.cmake108
-rw-r--r--third_party/immer/cmake/FindGuile.cmake326
-rw-r--r--third_party/immer/cmake/FindRRB.cmake15
-rw-r--r--third_party/immer/cmake/ImmerUtils.cmake24
-rw-r--r--third_party/immer/codecov.yml2
-rw-r--r--third_party/immer/default.nix14
-rw-r--r--third_party/immer/doc/CMakeLists.txt23
-rw-r--r--third_party/immer/doc/Makefile226
-rw-r--r--third_party/immer/doc/_static/logo-black.svg100
-rw-r--r--third_party/immer/doc/_static/logo-front.svg99
-rw-r--r--third_party/immer/doc/_static/logo.svg99
-rw-r--r--third_party/immer/doc/_static/patreon.svg120
-rw-r--r--third_party/immer/doc/_static/sinusoidal-badge.svg166
-rw-r--r--third_party/immer/doc/algorithms.rst28
-rw-r--r--third_party/immer/doc/conf.py452
-rw-r--r--third_party/immer/doc/containers.rst50
-rw-r--r--third_party/immer/doc/design.rst268
-rw-r--r--third_party/immer/doc/doxygen.config23
l---------third_party/immer/doc/guile.rst1
-rw-r--r--third_party/immer/doc/implementation.rst6
-rw-r--r--third_party/immer/doc/index.rst31
-rw-r--r--third_party/immer/doc/introduction.rst6
-rw-r--r--third_party/immer/doc/memory.rst204
l---------third_party/immer/doc/python.rst1
-rw-r--r--third_party/immer/doc/requirements.txt3
-rwxr-xr-xthird_party/immer/doc/sphinx-html-hack.bash101
-rw-r--r--third_party/immer/doc/transients.rst51
-rw-r--r--third_party/immer/doc/utilities.rst15
-rw-r--r--third_party/immer/example/CMakeLists.txt17
-rw-r--r--third_party/immer/example/array/array.cpp54
-rw-r--r--third_party/immer/example/box/box.cpp25
-rw-r--r--third_party/immer/example/flex-vector/flex-vector.cpp103
-rw-r--r--third_party/immer/example/map/intro.cpp23
-rw-r--r--third_party/immer/example/set/intro.cpp22
-rw-r--r--third_party/immer/example/vector/fizzbuzz.cpp34
-rw-r--r--third_party/immer/example/vector/gc.cpp37
-rw-r--r--third_party/immer/example/vector/intro.cpp22
-rw-r--r--third_party/immer/example/vector/iota-move.cpp25
-rw-r--r--third_party/immer/example/vector/iota-slow.cpp25
-rw-r--r--third_party/immer/example/vector/iota-transient-std.cpp29
-rw-r--r--third_party/immer/example/vector/iota-transient.cpp27
-rw-r--r--third_party/immer/example/vector/move.cpp35
-rw-r--r--third_party/immer/example/vector/vector.cpp53
-rw-r--r--third_party/immer/extra/fuzzer/CMakeLists.txt29
-rw-r--r--third_party/immer/extra/fuzzer/array-gc.cpp103
-rw-r--r--third_party/immer/extra/fuzzer/array.cpp80
-rw-r--r--third_party/immer/extra/fuzzer/flex-vector-gc.cpp162
-rw-r--r--third_party/immer/extra/fuzzer/flex-vector.cpp150
-rw-r--r--third_party/immer/extra/fuzzer/fuzzer_input.hpp62
-rw-r--r--third_party/immer/extra/fuzzer/map-gc.cpp93
-rw-r--r--third_party/immer/extra/fuzzer/map.cpp85
-rw-r--r--third_party/immer/extra/fuzzer/set-gc.cpp78
-rw-r--r--third_party/immer/extra/fuzzer/set.cpp70
-rw-r--r--third_party/immer/extra/fuzzer/vector-gc.cpp104
-rw-r--r--third_party/immer/extra/fuzzer/vector.cpp82
-rw-r--r--third_party/immer/extra/guile/CMakeLists.txt24
-rw-r--r--third_party/immer/extra/guile/README.rst144
-rw-r--r--third_party/immer/extra/guile/benchmark.scm168
-rw-r--r--third_party/immer/extra/guile/example.scm51
-rw-r--r--third_party/immer/extra/guile/immer.scm.in5
-rw-r--r--third_party/immer/extra/guile/scm/detail/convert.hpp58
-rw-r--r--third_party/immer/extra/guile/scm/detail/define.hpp36
-rw-r--r--third_party/immer/extra/guile/scm/detail/finalizer_wrapper.hpp62
-rw-r--r--third_party/immer/extra/guile/scm/detail/function_args.hpp21
-rw-r--r--third_party/immer/extra/guile/scm/detail/invoke.hpp39
-rw-r--r--third_party/immer/extra/guile/scm/detail/pack.hpp52
-rw-r--r--third_party/immer/extra/guile/scm/detail/subr_wrapper.hpp111
-rw-r--r--third_party/immer/extra/guile/scm/detail/util.hpp49
-rw-r--r--third_party/immer/extra/guile/scm/group.hpp88
-rw-r--r--third_party/immer/extra/guile/scm/list.hpp54
-rw-r--r--third_party/immer/extra/guile/scm/scm.hpp14
-rw-r--r--third_party/immer/extra/guile/scm/type.hpp153
-rw-r--r--third_party/immer/extra/guile/scm/val.hpp88
-rw-r--r--third_party/immer/extra/guile/src/immer.cpp153
-rw-r--r--third_party/immer/extra/js/immer.cpp79
-rw-r--r--third_party/immer/extra/js/index.js74
-rw-r--r--third_party/immer/extra/js/index.tpl.html77
-rw-r--r--third_party/immer/extra/js/lib/benchmark.js2811
-rw-r--r--third_party/immer/extra/js/lib/immutable.min.js36
-rw-r--r--third_party/immer/extra/js/lib/lodash.js16607
-rw-r--r--third_party/immer/extra/js/lib/mori.js435
-rw-r--r--third_party/immer/extra/js/lib/platform.js1135
-rw-r--r--third_party/immer/extra/js/makefile29
-rw-r--r--third_party/immer/extra/python/CMakeLists.txt46
-rw-r--r--third_party/immer/extra/python/README.rst42
-rw-r--r--third_party/immer/extra/python/benchmark/test_benchmarks.py45
-rwxr-xr-xthird_party/immer/extra/python/example.py21
-rw-r--r--third_party/immer/extra/python/immer/__init__.py2
m---------third_party/immer/extra/python/lib/pybind110
-rw-r--r--third_party/immer/extra/python/src/immer-boost.cpp80
-rw-r--r--third_party/immer/extra/python/src/immer-pybind.cpp77
-rw-r--r--third_party/immer/extra/python/src/immer-raw.cpp402
-rw-r--r--third_party/immer/immer/algorithm.hpp212
-rw-r--r--third_party/immer/immer/array.hpp364
-rw-r--r--third_party/immer/immer/array_transient.hpp202
-rw-r--r--third_party/immer/immer/atom.hpp254
-rw-r--r--third_party/immer/immer/box.hpp194
-rw-r--r--third_party/immer/immer/config.hpp93
-rw-r--r--third_party/immer/immer/detail/arrays/no_capacity.hpp203
-rw-r--r--third_party/immer/immer/detail/arrays/node.hpp127
-rw-r--r--third_party/immer/immer/detail/arrays/with_capacity.hpp303
-rw-r--r--third_party/immer/immer/detail/combine_standard_layout.hpp235
-rw-r--r--third_party/immer/immer/detail/hamts/bits.hpp108
-rw-r--r--third_party/immer/immer/detail/hamts/champ.hpp473
-rw-r--r--third_party/immer/immer/detail/hamts/champ_iterator.hpp148
-rw-r--r--third_party/immer/immer/detail/hamts/node.hpp717
-rw-r--r--third_party/immer/immer/detail/iterator_facade.hpp212
-rw-r--r--third_party/immer/immer/detail/rbts/bits.hpp33
-rw-r--r--third_party/immer/immer/detail/rbts/node.hpp932
-rw-r--r--third_party/immer/immer/detail/rbts/operations.hpp2461
-rw-r--r--third_party/immer/immer/detail/rbts/position.hpp1977
-rw-r--r--third_party/immer/immer/detail/rbts/rbtree.hpp509
-rw-r--r--third_party/immer/immer/detail/rbts/rbtree_iterator.hpp99
-rw-r--r--third_party/immer/immer/detail/rbts/rrbtree.hpp1396
-rw-r--r--third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp98
-rw-r--r--third_party/immer/immer/detail/rbts/visitor.hpp56
-rw-r--r--third_party/immer/immer/detail/ref_count_base.hpp36
-rw-r--r--third_party/immer/immer/detail/type_traits.hpp223
-rw-r--r--third_party/immer/immer/detail/util.hpp258
-rw-r--r--third_party/immer/immer/experimental/detail/dvektor_impl.hpp498
-rw-r--r--third_party/immer/immer/experimental/dvektor.hpp69
-rw-r--r--third_party/immer/immer/flex_vector.hpp608
-rw-r--r--third_party/immer/immer/flex_vector_transient.hpp251
-rw-r--r--third_party/immer/immer/heap/cpp_heap.hpp41
-rw-r--r--third_party/immer/immer/heap/debug_size_heap.hpp69
-rw-r--r--third_party/immer/immer/heap/free_list_heap.hpp83
-rw-r--r--third_party/immer/immer/heap/free_list_node.hpp24
-rw-r--r--third_party/immer/immer/heap/gc_heap.hpp127
-rw-r--r--third_party/immer/immer/heap/heap_policy.hpp141
-rw-r--r--third_party/immer/immer/heap/identity_heap.hpp34
-rw-r--r--third_party/immer/immer/heap/malloc_heap.hpp44
-rw-r--r--third_party/immer/immer/heap/split_heap.hpp40
-rw-r--r--third_party/immer/immer/heap/tags.hpp16
-rw-r--r--third_party/immer/immer/heap/thread_local_free_list_heap.hpp55
-rw-r--r--third_party/immer/immer/heap/unsafe_free_list_heap.hpp109
-rw-r--r--third_party/immer/immer/heap/with_data.hpp43
-rw-r--r--third_party/immer/immer/map.hpp342
-rw-r--r--third_party/immer/immer/map_transient.hpp41
-rw-r--r--third_party/immer/immer/memory_policy.hpp135
-rw-r--r--third_party/immer/immer/refcount/enable_intrusive_ptr.hpp37
-rw-r--r--third_party/immer/immer/refcount/no_refcount_policy.hpp45
-rw-r--r--third_party/immer/immer/refcount/refcount_policy.hpp101
-rw-r--r--third_party/immer/immer/refcount/unsafe_refcount_policy.hpp40
-rw-r--r--third_party/immer/immer/set.hpp198
-rw-r--r--third_party/immer/immer/set_transient.hpp40
-rw-r--r--third_party/immer/immer/transience/gc_transience_policy.hpp110
-rw-r--r--third_party/immer/immer/transience/no_transience_policy.hpp48
-rw-r--r--third_party/immer/immer/vector.hpp412
-rw-r--r--third_party/immer/immer/vector_transient.hpp203
-rw-r--r--third_party/immer/nix/benchmarks.nix100
-rw-r--r--third_party/immer/nix/docs.nix27
-rw-r--r--third_party/immer/setup.py68
-rw-r--r--third_party/immer/shell.nix65
-rw-r--r--third_party/immer/test/CMakeLists.txt22
-rw-r--r--third_party/immer/test/array/default.cpp12
-rw-r--r--third_party/immer/test/array/gc.cpp22
-rw-r--r--third_party/immer/test/array_transient/default.cpp41
-rw-r--r--third_party/immer/test/array_transient/gc.cpp50
-rw-r--r--third_party/immer/test/atom/default.cpp12
-rw-r--r--third_party/immer/test/atom/gc.cpp20
-rw-r--r--third_party/immer/test/atom/generic.ipp53
-rw-r--r--third_party/immer/test/box/default.cpp12
-rw-r--r--third_party/immer/test/box/gc.cpp20
-rw-r--r--third_party/immer/test/box/generic.ipp58
-rw-r--r--third_party/immer/test/box/recursive.cpp108
-rw-r--r--third_party/immer/test/box/vector-of-boxes-transient.cpp36
-rw-r--r--third_party/immer/test/dada.hpp243
-rw-r--r--third_party/immer/test/detail/type_traits.cpp215
-rw-r--r--third_party/immer/test/experimental/dvektor.cpp198
-rw-r--r--third_party/immer/test/flex_vector/B3-BL0.cpp21
-rw-r--r--third_party/immer/test/flex_vector/B3-BL3.cpp21
-rw-r--r--third_party/immer/test/flex_vector/default.cpp14
-rw-r--r--third_party/immer/test/flex_vector/fuzzed-0.cpp380
-rw-r--r--third_party/immer/test/flex_vector/fuzzed-1.cpp368
-rw-r--r--third_party/immer/test/flex_vector/fuzzed-2.cpp188
-rw-r--r--third_party/immer/test/flex_vector/fuzzed-3.cpp222
-rw-r--r--third_party/immer/test/flex_vector/fuzzed-4.cpp364
-rw-r--r--third_party/immer/test/flex_vector/gc.cpp27
-rw-r--r--third_party/immer/test/flex_vector/generic.ipp599
-rw-r--r--third_party/immer/test/flex_vector/issue-45.cpp35
-rw-r--r--third_party/immer/test/flex_vector/issue-47.cpp5
-rw-r--r--third_party/immer/test/flex_vector/regular-B3-BL3.cpp16
-rw-r--r--third_party/immer/test/flex_vector/regular-default.cpp12
-rw-r--r--third_party/immer/test/flex_vector_transient/B3-BL0.cpp29
-rw-r--r--third_party/immer/test/flex_vector_transient/default.cpp17
-rw-r--r--third_party/immer/test/flex_vector_transient/gc.cpp34
-rw-r--r--third_party/immer/test/flex_vector_transient/generic.ipp398
-rw-r--r--third_party/immer/test/flex_vector_transient/regular-default.cpp15
-rw-r--r--third_party/immer/test/flex_vector_transient/regular-gc.cpp31
-rw-r--r--third_party/immer/test/map/B3.cpp18
-rw-r--r--third_party/immer/test/map/B6.cpp18
-rw-r--r--third_party/immer/test/map/default.cpp12
-rw-r--r--third_party/immer/test/map/gc.cpp25
-rw-r--r--third_party/immer/test/map/generic.ipp312
-rw-r--r--third_party/immer/test/map/issue-56.cpp42
-rw-r--r--third_party/immer/test/memory/heaps.cpp101
-rw-r--r--third_party/immer/test/memory/refcounts.cpp62
-rw-r--r--third_party/immer/test/set/B3.cpp17
-rw-r--r--third_party/immer/test/set/B6.cpp17
-rw-r--r--third_party/immer/test/set/default.cpp12
-rw-r--r--third_party/immer/test/set/gc.cpp24
-rw-r--r--third_party/immer/test/set/generic.ipp459
-rw-r--r--third_party/immer/test/transient_tester.hpp54
-rw-r--r--third_party/immer/test/util.hpp103
-rw-r--r--third_party/immer/test/vector/B3-BL0.cpp15
-rw-r--r--third_party/immer/test/vector/B3-BL2.cpp15
-rw-r--r--third_party/immer/test/vector/B3-BL3.cpp15
-rw-r--r--third_party/immer/test/vector/B3-BL4.cpp15
-rw-r--r--third_party/immer/test/vector/default.cpp12
-rw-r--r--third_party/immer/test/vector/gc.cpp22
-rw-r--r--third_party/immer/test/vector/generic.ipp488
-rw-r--r--third_party/immer/test/vector/issue-16.cpp122
-rw-r--r--third_party/immer/test/vector/issue-46.cpp39
-rw-r--r--third_party/immer/test/vector/issue-74.cpp24
-rw-r--r--third_party/immer/test/vector_transient/B3-BL0.cpp21
-rw-r--r--third_party/immer/test/vector_transient/default.cpp15
-rw-r--r--third_party/immer/test/vector_transient/gc.cpp29
-rw-r--r--third_party/immer/test/vector_transient/generic.ipp267
-rw-r--r--third_party/immer/tools/clojure/README.md4
-rw-r--r--third_party/immer/tools/clojure/project.clj12
-rw-r--r--third_party/immer/tools/clojure/src/immer_benchmark.clj131
-rw-r--r--third_party/immer/tools/docker/icfp17/Dockerfile77
-rw-r--r--third_party/immer/tools/include/catch.hpp9458
-rw-r--r--third_party/immer/tools/include/doctest.h5696
-rw-r--r--third_party/immer/tools/include/nonius.h++4292
-rw-r--r--third_party/immer/tools/include/prettyprint.hpp445
-rwxr-xr-xthird_party/immer/tools/reproduce-paper-results.bash25
-rw-r--r--third_party/immer/tools/scala/README.md6
-rw-r--r--third_party/immer/tools/scala/build.sbt27
-rw-r--r--third_party/immer/tools/scala/src/test/scala/org/immer/benchmarks.scala168
-rw-r--r--third_party/immer/tools/scala/version.sbt1
m---------third_party/immer/tools/sinusoidal-sphinx-theme0
-rw-r--r--third_party/immer/tools/travis/ssh-key.encbin0 -> 1680 bytes
-rw-r--r--third_party/immer/tools/travis/ssh-key.pub1
-rwxr-xr-xthird_party/immer/tools/with-tee.bash5
311 files changed, 74221 insertions, 0 deletions
diff --git a/third_party/immer/.clang-format b/third_party/immer/.clang-format
new file mode 100644
index 0000000000..f7308d2588
--- /dev/null
+++ b/third_party/immer/.clang-format
@@ -0,0 +1,38 @@
+---
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: 'true'
+AlignEscapedNewlines: Right
+AlignTrailingComments: 'true'
+AllowShortFunctionsOnASingleLine: 'true'
+AllowShortBlocksOnASingleLine: 'true'
+AlwaysBreakTemplateDeclarations: 'true'
+AccessModifierOffset: -4
+BinPackArguments: 'false'
+BinPackParameters: 'false'
+BreakBeforeBraces: Mozilla
+BreakBeforeInheritanceComma: 'true'
+BreakBeforeTernaryOperators: 'true'
+BreakConstructorInitializers: BeforeComma
+BreakStringLiterals: 'true'
+ColumnLimit: '80'
+CompactNamespaces: 'false'
+ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
+FixNamespaceComments: 'true'
+IndentCaseLabels: 'false'
+IndentWidth: '4'
+IndentWrappedFunctionNames: 'false'
+KeepEmptyLinesAtTheStartOfBlocks: 'false'
+Language: Cpp
+MaxEmptyLinesToKeep: '1'
+NamespaceIndentation: None
+PointerAlignment: Left
+ReflowComments: 'true'
+SortIncludes: 'true'
+SortUsingDeclarations: 'true'
+SpaceAfterCStyleCast: 'true'
+SpaceAfterTemplateKeyword: 'true'
+SpaceBeforeAssignmentOperators: 'true'
+SpaceBeforeParens: ControlStatements
+TabWidth: '4'
+UseTab: Never
+...
diff --git a/third_party/immer/.dir-locals.el b/third_party/immer/.dir-locals.el
new file mode 100644
index 0000000000..8adc4f1d20
--- /dev/null
+++ b/third_party/immer/.dir-locals.el
@@ -0,0 +1,7 @@
+((nil .
+      ((indent-tabs-mode . nil)
+       (show-trailing-whitespace . t)))
+ (c-mode .
+         ((mode . c++)))
+ (c++-mode .
+          ((eval add-hook 'before-save-hook #'clang-format-buffer nil t))))
diff --git a/third_party/immer/.github/FUNDING.yml b/third_party/immer/.github/FUNDING.yml
new file mode 100644
index 0000000000..defc6ca1fa
--- /dev/null
+++ b/third_party/immer/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+github: arximboldi
+patreon: sinusoidal
+custom: ["paypal.me/sinusoidal", sinusoid.al]
diff --git a/third_party/immer/.gitignore b/third_party/immer/.gitignore
new file mode 100644
index 0000000000..2074141ec2
--- /dev/null
+++ b/third_party/immer/.gitignore
@@ -0,0 +1,24 @@
+bazel-*
+build/
+build-*/
+reports/
+doc/_build
+doc/_doxygen
+bazel-*
+
+tools/travis/ssh-key
+tools/clojure/target/
+tools/scala/target/
+tools/scala/project/target/
+
+extra/js/out
+extra/js/boost
+
+__pycache__
+
+.gdb_history
+
+.cache
+tools/clojure/.lein*
+
+*.pyc
diff --git a/third_party/immer/.gitmodules b/third_party/immer/.gitmodules
new file mode 100644
index 0000000000..53d1686014
--- /dev/null
+++ b/third_party/immer/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "extra/python/lib/pybind11"]
+	path = extra/python/lib/pybind11
+	url = https://github.com/pybind/pybind11.git
+[submodule "tools/sinusoidal-sphinx-theme"]
+	path = tools/sinusoidal-sphinx-theme
+        url = https://github.com/arximboldi/sinusoidal-sphinx-theme.git
diff --git a/third_party/immer/.travis.yml b/third_party/immer/.travis.yml
new file mode 100644
index 0000000000..cdbe09bf92
--- /dev/null
+++ b/third_party/immer/.travis.yml
@@ -0,0 +1,84 @@
+language: nix
+dist: trusty
+
+git:
+  depth: false
+
+matrix:
+  include:
+    - env: DO=check TYPE=Debug TOOLCHAIN=llvm-5 SANITIZE=true
+    - env: DO=check TYPE=Release TOOLCHAIN=llvm-5 BENCHMARKS=true
+    - env: DO=check TYPE=Debug TOOLCHAIN=gnu-6 COVERAGE=true
+    - env: DO=check TYPE=Release TOOLCHAIN=gnu-6 BENCHMARKS=true
+    - env: DO=check TYPE=Debug TOOLCHAIN=gnu-7 STD=17
+    - env: DO=check TYPE=Debug TOOLCHAIN=llvm-9 STD=17 FUZZERS=true
+    - env: DO=build
+    - env: DO=docs
+
+before_install:
+  - |
+    : ${TOOLCHAIN:=gnu-6}
+    : ${TYPE:=Debug}
+    : ${STD:=14}
+    function build-p { [[ "${DO}" == build ]]; }
+    function check-p { [[ "${DO}" == check ]]; }
+    function docs-p  { [[ "${DO}" == docs ]]; }
+    function coverage-p { [[ "${COVERAGE}" == true ]]; }
+    function benchmarks-p { [[ "${BENCHMARKS}" == true ]]; }
+    function deploy-p {
+        [[ "${TRAVIS_PULL_REQUEST}" == "false" && \
+           "${TRAVIS_BRANCH}" == "master" ]]
+    }
+    function upload-p {
+        [[ -n "$encrypted_1c8d51d72e41_key" && \
+           -n "$encrypted_1c8d51d72e41_iv" ]]
+    }
+    function decrypt-ssh-key {
+        openssl aes-256-cbc \
+            -K  $encrypted_1c8d51d72e41_key \
+            -iv $encrypted_1c8d51d72e41_iv \
+            -in tools/travis/ssh-key.enc \
+            -out tools/travis/ssh-key -d
+        chmod 600 tools/travis/ssh-key
+    }
+    function with-nix {
+        nix-shell --argstr toolchain $TOOLCHAIN --run "set -e; $1"
+    }
+
+install:
+  - with-nix "echo == nix environment ok"
+
+before_script:
+  - |
+    with-nix "
+        mkdir build && cd build
+        cmake .. \
+            -DCMAKE_BUILD_TYPE=${TYPE} \
+            -DCHECK_SLOW_TESTS=false \
+            -DCXX_STANDARD=${STD} \
+            -DCHECK_BENCHMARKS=${BENCHMARKS} \
+            -DENABLE_COVERAGE=${COVERAGE} \
+            -DENABLE_SANITIZE=${SANITIZE} \
+            -DCHECK_FUZZERS=${FUZZERS} \
+            -DDISABLE_FREE_LIST=${SANITIZE}
+    "
+
+script:
+  - |
+    set -e
+    ! build-p || nix-build
+    ! check-p || with-nix "cd build && make -j2 check"
+    ! docs-p  || with-nix "cd build && make docs"
+
+after_success:
+  - |
+    ! coverage-p || with-nix "
+        cd build
+        find . -name \"*.gcno\" | xargs gcov -bcprs ${PWD}
+        bash <(curl -s https://codecov.io/bash) -x gcov
+    "
+    if upload-p; then
+        decrypt-ssh-key
+        ! benchmarks-p || with-nix "cd build && make upload-benchmark-reports"
+        ! docs-p || ! deploy-p || with-nix "cd build && make upload-docs"
+    fi
diff --git a/third_party/immer/BUILD b/third_party/immer/BUILD
new file mode 100644
index 0000000000..3d79830b60
--- /dev/null
+++ b/third_party/immer/BUILD
@@ -0,0 +1,11 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+    name = "immer",
+    include_prefix = "immer",
+    strip_include_prefix =
+        "immer",
+    hdrs = glob([
+        "immer/**/*.hpp",
+    ]),
+)
diff --git a/third_party/immer/CMakeLists.txt b/third_party/immer/CMakeLists.txt
new file mode 100644
index 0000000000..dedfdf7401
--- /dev/null
+++ b/third_party/immer/CMakeLists.txt
@@ -0,0 +1,141 @@
+
+cmake_minimum_required(VERSION 3.5.1)
+cmake_policy(SET CMP0048 NEW) # enable project VERSION
+cmake_policy(SET CMP0056 NEW) # honor link flags in try_compile()
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+project(immer VERSION 0.6.2)
+
+if (NOT MSVC)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-extended-offsetof -Wno-c++17-extensions -Wno-c++1z-extensions -Wno-unknown-warning-option")
+endif()
+set(CMAKE_EXPORT_COMPILE_COMMANDS on)
+set(CMAKE_CXX_EXTENSIONS off)
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments")
+endif()
+
+include(GNUInstallDirs)
+include(ImmerUtils)
+
+#  Options
+#  =======
+
+option(ENABLE_SANITIZE "compile with sanitizers enabled")
+option(ENABLE_COVERAGE "compile with test coverage support")
+option(DISABLE_WERROR "enable --werror")
+option(DISABLE_FREE_LIST "disables the free list heap")
+option(DISABLE_THREAD_SAFETY "disables thread safety by default")
+option(CHECK_FUZZERS "Add fuzzers as part of make check")
+
+option(ENABLE_PYTHON "enable building python module" off)
+option(ENABLE_GUILE "enable building guile module" off)
+option(ENABLE_BOOST_COROUTINE "run benchmarks with boost coroutine" off)
+
+option(immer_BUILD_TESTS "Build tests" ON)
+option(immer_BUILD_EXAMPLES "Build examples" ON)
+option(immer_BUILD_DOCS "Build docs" ON)
+option(immer_BUILD_EXTRAS "Build extras" ON)
+
+set(CXX_STANDARD 14 CACHE STRING "c++ standard number")
+
+set(CMAKE_CXX_STANDARD ${CXX_STANDARD})
+set(CMAKE_CXX_STANDARD_REQUIRED on)
+
+if (ENABLE_SANITIZE)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=leak")
+endif()
+if (NOT MSVC AND NOT DISABLE_WERROR)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+endif()
+
+#  Dependencies
+#  ============
+
+if (ENABLE_BOOST_COROUTINE)
+  set(immer_boost_components coroutine)
+endif()
+
+find_package(Threads)
+find_package(BoehmGC)
+find_package(Boost 1.56 COMPONENTS ${immer_boost_components})
+
+find_program(CCACHE ccache)
+if (CCACHE)
+  message(STATUS "Using ccache: ${CCACHE}")
+  set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
+  set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
+else()
+  message(STATUS "Could not find ccache")
+endif()
+
+if (NOT BOEHM_GC_FOUND)
+  set(BOEHM_GC_LIBRARIES "")
+endif()
+
+#  Targets
+#  =======
+
+# the library
+add_library(immer INTERFACE)
+target_include_directories(immer INTERFACE
+  $<BUILD_INTERFACE:${immer_BINARY_DIR}/>
+  $<BUILD_INTERFACE:${immer_SOURCE_DIR}/>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+install(TARGETS immer EXPORT ImmerConfig)
+install(EXPORT ImmerConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Immer")
+install(DIRECTORY immer DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+
+# development target to be used in tests, examples, benchmarks...
+immer_canonicalize_cmake_booleans(
+  DISABLE_FREE_LIST
+  DISABLE_THREAD_SAFETY
+  CHECK_SLOW_TESTS)
+add_library(immer-dev INTERFACE)
+target_include_directories(immer-dev SYSTEM INTERFACE
+  ${Boost_INCLUDE_DIR}
+  ${BOEHM_GC_INCLUDE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/tools/include)
+target_link_libraries(immer-dev INTERFACE
+  immer
+  ${BOEHM_GC_LIBRARIES}
+  ${CMAKE_THREAD_LIBS_INIT})
+target_compile_definitions(immer-dev INTERFACE
+  -DIMMER_CXX_STANDARD=${CXX_STANDARD}
+  -DIMMER_HAS_LIBGC=1
+  -DIMMER_NO_FREE_LIST=${DISABLE_FREE_LIST}
+  -DIMMER_NO_THREAD_SAFETY=${DISABLE_THREAD_SAFETY}
+  -DIMMER_SLOW_TESTS=${CHECK_SLOW_TESTS})
+if (ENABLE_COVERAGE)
+  target_compile_options(immer-dev INTERFACE "--coverage")
+  target_link_libraries(immer-dev INTERFACE "--coverage")
+endif()
+
+#  Testing
+#  =======
+
+if (immer_BUILD_TESTS)
+  enable_testing()
+
+  add_custom_target(check
+    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+    COMMENT "Build and run all the tests and examples.")
+
+  add_subdirectory(test)
+  add_subdirectory(benchmark)
+endif()
+
+if (immer_BUILD_EXAMPLES)
+  add_subdirectory(example)
+endif()
+
+if (immer_BUILD_DOCS)
+  add_subdirectory(doc)
+endif()
+
+if (immer_BUILD_EXTRAS)
+  add_subdirectory(extra/fuzzer)
+  add_subdirectory(extra/python)
+  add_subdirectory(extra/guile)
+endif()
diff --git a/third_party/immer/LICENSE b/third_party/immer/LICENSE
new file mode 100644
index 0000000000..36b7cd93cd
--- /dev/null
+++ b/third_party/immer/LICENSE
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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/third_party/immer/README.rst b/third_party/immer/README.rst
new file mode 100644
index 0000000000..afa2fecbb3
--- /dev/null
+++ b/third_party/immer/README.rst
@@ -0,0 +1,225 @@
+
+.. image:: https://travis-ci.org/arximboldi/immer.svg?branch=master
+   :target: https://travis-ci.org/arximboldi/immer
+   :alt: Travis Badge
+
+.. image:: https://codecov.io/gh/arximboldi/immer/branch/master/graph/badge.svg
+   :target: https://codecov.io/gh/arximboldi/immer
+   :alt: CodeCov Badge
+
+.. image:: https://cdn.rawgit.com/arximboldi/immer/355a113782aedc2ea22463444014809269c2376d/doc/_static/sinusoidal-badge.svg
+   :target: https://sinusoid.al
+   :alt: Sinusoidal Engineering badge
+   :align: right
+
+.. raw:: html
+
+   <img width="100%" src="https://cdn.rawgit.com/arximboldi/immer/3888170d247359cc0905eed548cd46897caef0f4/doc/_static/logo-front.svg" alt="Logotype"/>
+
+.. include:introduction/start
+
+**immer** is a library of persistent_ and immutable_ data structures
+written in C++.  These enable whole new kinds of architectures for
+interactive and concurrent programs of striking simplicity,
+correctness, and performance.
+
+.. _persistent: https://en.wikipedia.org/wiki/Persistent_data_structure
+.. _immutable:  https://en.wikipedia.org/wiki/Immutable_object
+
+* **Documentation** (Contents_)
+* **Code** (GitHub_)
+* **CppCon'17 Talk**: *Postmodern Immutable Data Structures* (YouTube_, Slides_)
+* **ICFP'17 Paper**: *Persistence for the masses* (Preprint_)
+
+.. _contents: https://sinusoid.es/immer/#contents
+.. _github: https://github.com/arximboldi/immer
+.. _youtube: https://www.youtube.com/watch?v=sPhpelUfu8Q
+.. _slides: https://sinusoid.es/talks/immer-cppcon17
+.. _preprint: https://public.sinusoid.es/misc/immer/immer-icfp17.pdf
+
+
+  .. raw:: html
+
+     <a href="https://www.patreon.com/sinusoidal">
+         <img align="right" src="https://cdn.rawgit.com/arximboldi/immer/master/doc/_static/patreon.svg">
+     </a>
+
+  This library has full months of *pro bono* research and development
+  invested in it.  This is just the first step in a long-term vision
+  of making interactive and concurrent C++ programs easier to
+  write. **Put your logo here and help this project's long term
+  sustainability by buying a sponsorship package:** immer@sinusoid.al
+
+.. include:index/end
+
+Example
+-------
+
+.. github does not support the ``literalinclude`` directive.  This
+   example is copy pasted from ``example/vector/intro.cpp``
+
+.. code-block:: c++
+
+   #include <immer/vector.hpp>
+   int main()
+   {
+       const auto v0 = immer::vector<int>{};
+       const auto v1 = v0.push_back(13);
+       assert(v0.size() == 0 && v1.size() == 1 && v1[0] == 13);
+
+       const auto v2 = v1.set(0, 42);
+       assert(v1[0] == 13 && v2[0] == 42);
+   }
+..
+
+  For a **complete example** check `Ewig, a simple didactic
+  text-editor <https://github.com/arximboldi/ewig>`_ built with this
+  library.  You may also wanna check `Lager, a Redux-like library
+  <https://github.com/arximboldi/lager>`_ for writting interactive
+  software in C++ using a value-oriented design.
+
+
+Why?
+----
+
+In the last few years, there has been a growing interest in immutable
+data structures, motivated by the horizontal scaling of our processing
+power and the ubiquity of highly interactive systems.  Languages like
+Clojure_ and Scala_ provide them by default, and implementations
+for JavaScript like Mori_ and Immutable.js_ are widely used,
+specially in combination with modern UI frameworks like React_.
+
+Interactivity
+    Thanks to *persistence* and *structural sharing*, new values can
+    be efficiently compared with old ones.  This enables simpler ways of
+    *reasoning about change* that sit at the core of modern
+    interactive systems programming paradigms like `reactive
+    programming`_.
+
+Concurrency
+    Passing immutable data structures by value does not need to copy
+    any data. In the absence of mutation, data can be safely read
+    from multiple concurrent processes, and enable concurrency
+    patterns like `share by communicating`_ efficiently.
+
+Parallelism
+   Some recent immutable data structures have interesting properties
+   like :math:`O(log(n))` concatenation, which enable new kinds of
+   `parallelization algorithms`_.
+
+.. _clojure: http://clojure.org/reference/data_structures
+.. _scala: http://docs.scala-lang.org/overviews/collections/overview.html
+
+.. _mori: https://swannodette.github.io/mori/
+.. _immutable.js: https://github.com/facebook/immutable-js
+.. _react: https://facebook.github.io/react/
+
+.. _reactive programming: https://en.wikipedia.org/wiki/Reactive_programming
+.. _share by communicating: https://blog.golang.org/share-memory-by-communicating
+.. _parallelization algorithms: http://docs.scala-lang.org/overviews/parallel-collections/overview.html
+
+Features
+--------
+
+Idiomatic
+    This library doesn't pretend that it is written in Haskell.  It
+    leverages features from recent standards to provide an API that is
+    both efficient and natural for a C++ developer.
+
+Performant
+    You use C++ because you need this.  *Immer* implements state of
+    the art data structures with efficient cache utilization and have
+    been proven production ready in other languages.  It also includes
+    our own improvements over that are only possible because of the
+    C++'s ability to abstract over memory layout.  We monitor the
+    performance impact of every change by collecting `benchmark
+    results`_ directly from CI.
+
+.. _benchmark results: https://public.sinusoid.es/misc/immer/reports/
+
+Customizable
+    We leverage templates and `policy-based design`_ to build
+    data-structures that can be adapted to work efficiently for
+    various purposes and architectures, for example, by choosing among
+    various `memory management strategies`.  This turns
+    *immer* into a good foundation to provide immutable data
+    structures to higher level languages with a C runtime, like
+    Python_ or Guile_.
+
+.. _python: https://www.python.org/
+.. _guile: https://www.gnu.org/software/guile/
+.. _policy-based design: https://en.wikipedia.org/wiki/Policy-based_design
+.. _memory management strategies: https://sinusoid.es/immer/memory.html
+
+Dependencies
+------------
+
+This library is written in **C++14** and a compliant compiler is
+necessary.  It is `continuously tested`_ with Clang 3.8 and GCC 6, but
+it might work with other compilers and versions.
+
+No external library is necessary and there are no other requirements.
+
+.. _continuously tested: https://travis-ci.org/arximboldi/immer
+
+Usage
+-----
+
+This is a **header only** library.  You can just copy the ``immer``
+subfolder somewhere in your *include path*.
+
+If you are using the `Nix package manager`_ (we strongly recommend it)
+you can just::
+
+    nix-env -if https://github.com/arximboldi/immer/archive/master.tar.gz
+
+Alternatively, you can use `CMake`_ to install the library in your
+system once you have manually cloned the repository::
+
+    mkdir -p build && cd build
+    cmake .. && sudo make install
+
+.. _nix package manager: https://nixos.org/nix
+.. _cmake: https://cmake.org/
+
+Development
+-----------
+
+In order to develop the library, you will need to compile and run the
+examples, tests and benchmarks.  These require some additional tools.
+The easiest way to install them is by using the `Nix package
+manager`_.  At the root of the repository just type::
+
+    nix-shell
+
+This will download all required dependencies and create an isolated
+environment in which you can use these dependencies, without polluting
+your system.
+
+Then you can proceed to generate a development project using `CMake`_::
+
+    mkdir build && cd build
+    cmake ..
+
+From then on, one may build and run all tests by doing::
+
+    make check
+
+In order to build and run all benchmarks when running ``make check``,
+run ``cmake`` again with the option ``-DCHECK_BENCHMARKS=1``.  The
+results of running the benchmarks will be saved to a folder
+``reports/`` in the project root.
+
+License
+-------
+
+**This software is licensed under the Boost Software License 1.0**.
+
+.. image:: https://upload.wikimedia.org/wikipedia/commons/c/cd/Boost.png
+   :alt: Boost logo
+   :target: http://boost.org/LICENSE_1_0.txt
+   :align: right
+
+The full text of the license is can be accessed `via this link
+<http://boost.org/LICENSE_1_0.txt>`_ and is also included
+in the ``LICENSE`` file of this software package.
diff --git a/third_party/immer/WORKSPACE b/third_party/immer/WORKSPACE
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/immer/WORKSPACE
diff --git a/third_party/immer/benchmark/CMakeLists.txt b/third_party/immer/benchmark/CMakeLists.txt
new file mode 100644
index 0000000000..0f9061aa3c
--- /dev/null
+++ b/third_party/immer/benchmark/CMakeLists.txt
@@ -0,0 +1,128 @@
+
+#  Config
+#  ======
+
+option(CHECK_BENCHMARKS "Run benchmarks on check target" off)
+option(BENCHMARK_DISABLE_GC "Disable gc during a measurement")
+
+set(BENCHMARK_PARAM   "N:1000" CACHE STRING "Benchmark parameters")
+set(BENCHMARK_SAMPLES "20"     CACHE STRING "Benchmark samples")
+
+#  Dependencies
+#  ============
+
+find_package(RRB)
+
+if (NOT RRB_FOUND)
+  message(STATUS "Disabling benchmarks")
+  return()
+endif()
+
+# These are expected to be in the include path, the nix-shell
+# environment installs them:
+#
+#    https://github.com/marcusz/steady
+#    https://github.com/deepsea-inria/chunkedseq.git
+#    https://github.com/rsms/immutable-cpp.git
+
+#  Targets
+#  =======
+
+add_custom_target(benchmarks
+  COMMENT "Build all benchmarks.")
+
+execute_process(
+  COMMAND git log -1 --format=%h
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  OUTPUT_VARIABLE immer_git_commit_hash
+  OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+execute_process(
+  COMMAND git status --porcelain
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  OUTPUT_VARIABLE immer_git_status
+  OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if (NOT immer_git_status STREQUAL "")
+  set(immer_git_commit_hash "${immer_git_commit_hash}+")
+endif()
+
+site_name(immer_hostname)
+
+get_filename_component(immer_compiler_name "${CMAKE_CXX_COMPILER}" NAME)
+
+set(immer_benchmark_report_base_dir "${CMAKE_SOURCE_DIR}/reports")
+set(immer_benchmark_report_dir "${immer_benchmark_report_base_dir}/report_\
+${immer_git_commit_hash}_\
+${immer_hostname}_\
+${immer_compiler_name}_\
+${BENCHMARK_PARAM}_\
+s${BENCHMARK_SAMPLES}")
+
+if(DISABLE_FREE_LIST)
+  set(immer_benchmark_report_dir "${immer_benchmark_report_dir}_nofl")
+endif()
+
+if(DISABLE_THREAD_SAFETY)
+  set(immer_benchmark_report_dir "${immer_benchmark_report_dir}_nots")
+endif()
+
+if(BENCHMARK_DISABLE_GC)
+  set(immer_benchmark_report_dir "${immer_benchmark_report_dir}_nogc")
+endif()
+
+if(CHECK_BENCHMARKS)
+  add_dependencies(check benchmarks)
+endif()
+
+add_custom_target(benchmark-report-dir
+  COMMAND ${CMAKE_COMMAND}
+  -E make_directory ${immer_benchmark_report_dir})
+
+file(GLOB_RECURSE immer_benchmarks "*.cpp")
+foreach(_file IN LISTS immer_benchmarks)
+  immer_target_name_for(_target _output "${_file}")
+  add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
+  set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
+  add_dependencies(benchmarks ${_target})
+  add_dependencies(${_target} benchmark-report-dir)
+  target_compile_options(${_target} PUBLIC -Wno-unused-function)
+  target_compile_definitions(${_target} PUBLIC
+    NONIUS_RUNNER
+    IMMER_BENCHMARK_LIBRRB=1
+    IMMER_BENCHMARK_STEADY=1
+    IMMER_BENCHMARK_EXPERIMENTAL=0
+    IMMER_BENCHMARK_DISABLE_GC=${BENCHMARK_DISABLE_GC}
+    IMMER_BENCHMARK_BOOST_COROUTINE=${ENABLE_BOOST_COROUTINE})
+  target_link_libraries(${_target} PUBLIC
+    immer-dev
+    ${RRB_LIBRARIES})
+  target_include_directories(${_target} SYSTEM PUBLIC
+    ${RRB_INCLUDE_DIR})
+  if(CHECK_BENCHMARKS)
+    add_test("benchmark/${_output}" "${CMAKE_SOURCE_DIR}/tools/with-tee.bash"
+      ${immer_benchmark_report_dir}/${_target}.out
+      "${CMAKE_CURRENT_BINARY_DIR}/${_output}" -v
+      -t ${_target}
+      -r html
+      -s ${BENCHMARK_SAMPLES}
+      -p ${BENCHMARK_PARAM}
+      -o ${immer_benchmark_report_dir}/${_target}.html)
+  endif()
+endforeach()
+
+set(immer_ssh_method
+  ssh -p 5488
+      -o StrictHostKeyChecking=no
+      -i ${CMAKE_SOURCE_DIR}/tools/travis/ssh-key)
+
+add_custom_target(upload-benchmark-reports
+  COMMAND
+  rsync -av -e \"${immer_ssh_method}\"
+        ${immer_benchmark_report_base_dir}
+        raskolnikov@sinusoid.es:public/misc/immer/)
+
+add_custom_target(copy-benchmark-reports
+  COMMAND
+  rsync -av ${immer_benchmark_report_base_dir}
+        ~/public/misc/immer/)
diff --git a/third_party/immer/benchmark/config.hpp b/third_party/immer/benchmark/config.hpp
new file mode 100644
index 0000000000..c32ef5812f
--- /dev/null
+++ b/third_party/immer/benchmark/config.hpp
@@ -0,0 +1,55 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <nonius.h++>
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace {
+
+NONIUS_PARAM(N, std::size_t{1000})
+
+struct gc_disable
+{
+    gc_disable()
+    {
+#if IMMER_BENCHMARK_DISABLE_GC
+        GC_disable();
+#else
+        GC_gcollect();
+#endif
+    }
+    ~gc_disable()
+    {
+#if IMMER_BENCHMARK_DISABLE_GC
+        GC_enable();
+        GC_gcollect();
+#endif
+    }
+    gc_disable(const gc_disable&) = delete;
+    gc_disable(gc_disable&&) = delete;
+};
+
+template <typename Meter, typename Fn>
+void measure(Meter& m, Fn&& fn)
+{
+    gc_disable guard;
+    return m.measure(std::forward<Fn>(fn));
+}
+
+using def_memory    = immer::default_memory_policy;
+using gc_memory     = immer::memory_policy<immer::heap_policy<immer::gc_heap>, immer::no_refcount_policy>;
+using gcf_memory    = immer::memory_policy<immer::heap_policy<immer::gc_heap>, immer::no_refcount_policy, immer::gc_transience_policy, false>;
+using basic_memory  = immer::memory_policy<immer::heap_policy<immer::cpp_heap>, immer::refcount_policy>;
+using safe_memory   = immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap>, immer::refcount_policy>;
+using unsafe_memory = immer::memory_policy<immer::unsafe_free_list_heap_policy<immer::cpp_heap>, immer::unsafe_refcount_policy>;
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/extra/refcounting.cpp b/third_party/immer/benchmark/extra/refcounting.cpp
new file mode 100644
index 0000000000..c7c5183e62
--- /dev/null
+++ b/third_party/immer/benchmark/extra/refcounting.cpp
@@ -0,0 +1,146 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/detail/ref_count_base.hpp>
+
+#include <nonius.h++>
+#include <boost/intrusive_ptr.hpp>
+
+#include <atomic>
+#include <cstdlib>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include <array>
+#include <vector>
+
+NONIUS_PARAM(N, std::size_t{1000})
+
+constexpr auto benchmark_size = 32u;
+
+struct object_t : immer::detail::ref_count_base<object_t>
+{};
+
+auto make_data()
+{
+    auto objs = std::array<std::unique_ptr<object_t>, benchmark_size>();
+    std::generate(objs.begin(), objs.end(), [] {
+        return std::make_unique<object_t>();
+    });
+    auto refs = std::array<object_t*, benchmark_size>();
+    std::transform(objs.begin(), objs.end(), refs.begin(), [](auto& obj) {
+        return obj.get();
+    });
+    return make_pair(std::move(objs),
+                     std::move(refs));
+}
+
+NONIUS_BENCHMARK("intrusive_ptr", [] (nonius::chronometer meter)
+{
+    auto arr     = std::array<boost::intrusive_ptr<object_t>, benchmark_size>{};
+    auto storage = std::vector<
+        nonius::storage_for<
+            std::array<boost::intrusive_ptr<object_t>, benchmark_size>>> (
+                meter.runs());
+    std::generate(arr.begin(), arr.end(), [] {
+        return new object_t{};
+    });
+    meter.measure([&] (int i) {
+        storage[i].construct(arr);
+    });
+})
+
+NONIUS_BENCHMARK("generic", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        std::transform(refs.begin(), refs.end(), r, [] (auto& p) {
+            if (p) p->ref_count.fetch_add(1, std::memory_order_relaxed);
+            return p;
+        });
+        return r;
+    });
+})
+
+NONIUS_BENCHMARK("manual", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        for (auto& p : refs)
+            if (p) p->ref_count.fetch_add(1, std::memory_order_relaxed);
+        std::copy(refs.begin(), refs.end(), r);
+        return r;
+    });
+})
+
+NONIUS_BENCHMARK("manual - unroll", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        auto e = refs.end();
+        for (auto p = refs.begin(); p != e;) {
+            (*p++)->ref_count.fetch_add(1, std::memory_order_relaxed);
+            (*p++)->ref_count.fetch_add(1, std::memory_order_relaxed);
+            (*p++)->ref_count.fetch_add(1, std::memory_order_relaxed);
+            (*p++)->ref_count.fetch_add(1, std::memory_order_relaxed);
+        }
+        std::copy(refs.begin(), refs.end(), r);
+        return r;
+    });
+})
+
+NONIUS_BENCHMARK("manual - nocheck", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        for (auto& p : refs)
+            p->ref_count.fetch_add(1, std::memory_order_relaxed);
+        std::copy(refs.begin(), refs.end(), r);
+        return r;
+    });
+})
+
+NONIUS_BENCHMARK("manual - constant", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        for (auto i = 0u; i < benchmark_size; ++i)
+            refs[i]->ref_count.fetch_add(1, std::memory_order_relaxed);
+        std::copy(refs.begin(), refs.end(), r);
+        return r;
+    });
+})
+
+NONIUS_BENCHMARK("manual - memcopy", [] (nonius::chronometer meter)
+{
+    auto data = make_data();
+    auto& refs = data.second;
+    object_t* r[benchmark_size];
+
+    meter.measure([&] {
+        for (auto& p : refs)
+            if (p) p->ref_count.fetch_add(1, std::memory_order_relaxed);
+        std::memcpy(r, &refs[0], sizeof(object_t*) * benchmark_size);
+        return r;
+    });
+})
diff --git a/third_party/immer/benchmark/set/access.hpp b/third_party/immer/benchmark/set/access.hpp
new file mode 100644
index 0000000000..8605c0ff45
--- /dev/null
+++ b/third_party/immer/benchmark/set/access.hpp
@@ -0,0 +1,175 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/config.hpp"
+
+#include <immer/set.hpp>
+#include <hash_trie.hpp> // Phil Nash
+#include <boost/container/flat_set.hpp>
+#include <set>
+#include <unordered_set>
+
+namespace {
+
+template <typename T=unsigned>
+auto make_generator_ranged(std::size_t runs)
+{
+    assert(runs > 0);
+    auto engine = std::default_random_engine{13};
+    auto dist = std::uniform_int_distribution<T>{0, (T)runs-1};
+    auto r = std::vector<T>(runs);
+    std::generate_n(r.begin(), runs, std::bind(dist, engine));
+    return r;
+}
+
+template <typename Generator, typename Set>
+auto benchmark_access_std()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n  = meter.param<N>();
+        auto g1 = Generator{}(n);
+        auto g2 = make_generator_ranged(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i)
+                c += v.count(g1[g2[i]]);
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_access_hamt()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n);
+        auto g2 = make_generator_ranged(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i) {
+                auto& x = g1[g2[i]];
+                auto leaf = v.find(x).leaf();
+                c += !!(leaf && leaf->find(x));
+            }
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+
+template <typename Generator, typename Set>
+auto benchmark_access()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n);
+        auto g2 = make_generator_ranged(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i)
+                c += v.count(g1[g2[i]]);
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_bad_access_std()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n*2);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i)
+                c += v.count(g1[n+i]);
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_bad_access_hamt()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n*2);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i) {
+                auto& x = g1[n+i];
+                auto leaf = v.find(x).leaf();
+                c += !!(leaf && leaf->find(x));
+            }
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+
+template <typename Generator, typename Set>
+auto benchmark_bad_access()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n*2);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(g1[i]);
+
+        measure(meter, [&] {
+            auto c = 0u;
+            for (auto i = 0u; i < n; ++i)
+                c += v.count(g1[n+i]);
+            volatile auto r = c;
+            return r;
+        });
+    };
+}
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/access.ipp b/third_party/immer/benchmark/set/access.ipp
new file mode 100644
index 0000000000..f026d9cfa7
--- /dev/null
+++ b/third_party/immer/benchmark/set/access.ipp
@@ -0,0 +1,30 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "access.hpp"
+
+#ifndef GENERATOR_T
+#error "you must define a GENERATOR_T"
+#endif
+
+using generator__ = GENERATOR_T;
+using t__ = typename decltype(generator__{}(0))::value_type;
+
+NONIUS_BENCHMARK("std::set", benchmark_access_std<generator__, std::set<t__>>())
+NONIUS_BENCHMARK("std::unordered_set", benchmark_access_std<generator__, std::unordered_set<t__>>())
+NONIUS_BENCHMARK("boost::flat_set", benchmark_access_std<generator__, boost::container::flat_set<t__>>())
+NONIUS_BENCHMARK("hamt::hash_trie", benchmark_access_hamt<generator__, hamt::hash_trie<t__>>())
+NONIUS_BENCHMARK("immer::set/5B", benchmark_access<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/4B", benchmark_access<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,4>>())
+
+NONIUS_BENCHMARK("bad/std::set", benchmark_bad_access_std<generator__, std::set<t__>>())
+NONIUS_BENCHMARK("bad/std::unordered_set", benchmark_bad_access_std<generator__, std::unordered_set<t__>>())
+NONIUS_BENCHMARK("bad/boost::flat_set", benchmark_bad_access_std<generator__, boost::container::flat_set<t__>>())
+NONIUS_BENCHMARK("bad/hamt::hash_trie", benchmark_bad_access_hamt<generator__, hamt::hash_trie<t__>>())
+NONIUS_BENCHMARK("bad/immer::set/5B", benchmark_bad_access<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,5>>())
+NONIUS_BENCHMARK("bad/immer::set/4B", benchmark_bad_access<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,4>>())
diff --git a/third_party/immer/benchmark/set/insert.hpp b/third_party/immer/benchmark/set/insert.hpp
new file mode 100644
index 0000000000..be679b4de3
--- /dev/null
+++ b/third_party/immer/benchmark/set/insert.hpp
@@ -0,0 +1,55 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/config.hpp"
+
+#include <immer/set.hpp>
+#include <hash_trie.hpp> // Phil Nash
+#include <boost/container/flat_set.hpp>
+#include <set>
+#include <unordered_set>
+
+namespace {
+
+template <typename Generator, typename Set>
+auto benchmark_insert_mut_std()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g = Generator{}(n);
+
+        measure(meter, [&] {
+            auto v = Set{};
+            for (auto i = 0u; i < n; ++i)
+                v.insert(g[i]);
+            return v;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_insert()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g = Generator{}(n);
+
+        measure(meter, [&] {
+            auto v = Set{};
+            for (auto i = 0u; i < n; ++i)
+                v = v.insert(g[i]);
+            return v;
+        });
+    };
+}
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/insert.ipp b/third_party/immer/benchmark/set/insert.ipp
new file mode 100644
index 0000000000..6717ccd57e
--- /dev/null
+++ b/third_party/immer/benchmark/set/insert.ipp
@@ -0,0 +1,28 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "insert.hpp"
+
+#ifndef GENERATOR_T
+#error "you must define a GENERATOR_T"
+#endif
+
+using generator__ = GENERATOR_T;
+using t__ = typename decltype(generator__{}(0))::value_type;
+
+NONIUS_BENCHMARK("std::set", benchmark_insert_mut_std<generator__, std::set<t__>>())
+NONIUS_BENCHMARK("std::unordered_set", benchmark_insert_mut_std<generator__, std::unordered_set<t__>>())
+NONIUS_BENCHMARK("boost::flat_set", benchmark_insert_mut_std<generator__, boost::container::flat_set<t__>>())
+NONIUS_BENCHMARK("hamt::hash_trie", benchmark_insert_mut_std<generator__, hamt::hash_trie<t__>>())
+
+NONIUS_BENCHMARK("immer::set/5B", benchmark_insert<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,5>>())
+NONIUS_BENCHMARK("immer::set/4B", benchmark_insert<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,4>>())
+#ifndef DISABLE_GC_BENCHMARKS
+NONIUS_BENCHMARK("immer::set/GC", benchmark_insert<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,gc_memory,5>>())
+#endif
+NONIUS_BENCHMARK("immer::set/UN", benchmark_insert<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,unsafe_memory,5>>())
diff --git a/third_party/immer/benchmark/set/iter.hpp b/third_party/immer/benchmark/set/iter.hpp
new file mode 100644
index 0000000000..91dd486443
--- /dev/null
+++ b/third_party/immer/benchmark/set/iter.hpp
@@ -0,0 +1,111 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/config.hpp"
+
+#include <immer/set.hpp>
+#include <immer/box.hpp>
+#include <immer/algorithm.hpp>
+#include <hash_trie.hpp> // Phil Nash
+#include <boost/container/flat_set.hpp>
+#include <set>
+#include <unordered_set>
+#include <numeric>
+
+namespace {
+
+template <typename T>
+struct iter_step
+{
+    unsigned operator() (unsigned x, const T& y) const
+    {
+        return x + y;
+    }
+};
+
+template <>
+struct iter_step<std::string>
+{
+    unsigned operator() (unsigned x, const std::string& y) const
+    {
+        return x + (unsigned) y.size();
+    }
+};
+
+template <>
+struct iter_step<immer::box<std::string>>
+{
+    unsigned operator() (unsigned x, const immer::box<std::string>& y) const
+    {
+        return x + (unsigned) y->size();
+    }
+};
+
+template <typename Generator, typename Set>
+auto benchmark_access_std_iter()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n  = meter.param<N>();
+        auto g1 = Generator{}(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v.insert(g1[i]);
+
+        using step_t = iter_step<typename decltype(g1)::value_type>;
+        measure(meter, [&] {
+            volatile auto c = std::accumulate(v.begin(), v.end(), 0u, step_t{});
+            return c;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_access_reduce()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(g1[i]);
+
+        using step_t = iter_step<typename decltype(g1)::value_type>;
+        measure(meter, [&] {
+            volatile auto c = immer::accumulate(v, 0u, step_t{});
+            return c;
+        });
+    };
+}
+
+template <typename Generator, typename Set>
+auto benchmark_access_iter()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto g1 = Generator{}(n);
+
+        auto v = Set{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(g1[i]);
+
+        using step_t = iter_step<typename decltype(g1)::value_type>;
+        measure(meter, [&] {
+            volatile auto c = std::accumulate(v.begin(), v.end(), 0u, step_t{});
+            return c;
+        });
+    };
+}
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/iter.ipp b/third_party/immer/benchmark/set/iter.ipp
new file mode 100644
index 0000000000..f8c3cb9695
--- /dev/null
+++ b/third_party/immer/benchmark/set/iter.ipp
@@ -0,0 +1,25 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "iter.hpp"
+
+#ifndef GENERATOR_T
+#error "you must define a GENERATOR_T"
+#endif
+
+using generator__ = GENERATOR_T;
+using t__ = typename decltype(generator__{}(0))::value_type;
+
+NONIUS_BENCHMARK("iter/std::set", benchmark_access_std_iter<generator__, std::set<t__>>())
+NONIUS_BENCHMARK("iter/std::unordered_set", benchmark_access_std_iter<generator__, std::unordered_set<t__>>())
+NONIUS_BENCHMARK("iter/boost::flat_set", benchmark_access_std_iter<generator__, boost::container::flat_set<t__>>())
+NONIUS_BENCHMARK("iter/hamt::hash_trie", benchmark_access_std_iter<generator__, hamt::hash_trie<t__>>())
+NONIUS_BENCHMARK("iter/immer::set/5B", benchmark_access_iter<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,5>>())
+NONIUS_BENCHMARK("iter/immer::set/4B", benchmark_access_iter<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,4>>())
+NONIUS_BENCHMARK("reduce/immer::set/5B", benchmark_access_reduce<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,5>>())
+NONIUS_BENCHMARK("reduce/immer::set/4B", benchmark_access_reduce<generator__, immer::set<t__, std::hash<t__>,std::equal_to<t__>,def_memory,4>>())
diff --git a/third_party/immer/benchmark/set/string-box/access.cpp b/third_party/immer/benchmark/set/string-box/access.cpp
new file mode 100644
index 0000000000..c3deffbb06
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-box/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/set/string-box/generator.ipp b/third_party/immer/benchmark/set/string-box/generator.ipp
new file mode 100644
index 0000000000..9adc82e0bd
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-box/generator.ipp
@@ -0,0 +1,46 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/box.hpp>
+
+#include <random>
+#include <vector>
+#include <cassert>
+#include <functional>
+#include <algorithm>
+
+#define GENERATOR_T generate_unsigned
+
+namespace {
+
+struct GENERATOR_T
+{
+    static constexpr auto char_set   = "_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    static constexpr auto max_length = 64;
+    static constexpr auto min_length = 8;
+
+    auto operator() (std::size_t runs) const
+    {
+        assert(runs > 0);
+        auto engine = std::default_random_engine{42};
+        auto dist = std::uniform_int_distribution<unsigned>{};
+        auto gen = std::bind(dist, engine);
+        auto r = std::vector<immer::box<std::string>>(runs);
+        std::generate_n(r.begin(), runs, [&] {
+            auto len = gen() % (max_length - min_length) + min_length;
+            auto str = std::string(len, ' ');
+            std::generate_n(str.begin(), len, [&] {
+                return char_set[gen() % sizeof(char_set)];
+            });
+            return str;
+        });
+        return r;
+    }
+};
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/string-box/insert.cpp b/third_party/immer/benchmark/set/string-box/insert.cpp
new file mode 100644
index 0000000000..c5762498c2
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-box/insert.cpp
@@ -0,0 +1,11 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define DISABLE_GC_BENCHMARKS
+#include "generator.ipp"
+#include "../insert.ipp"
diff --git a/third_party/immer/benchmark/set/string-box/iter.cpp b/third_party/immer/benchmark/set/string-box/iter.cpp
new file mode 100644
index 0000000000..83e57e7845
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-box/iter.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../iter.ipp"
diff --git a/third_party/immer/benchmark/set/string-long/access.cpp b/third_party/immer/benchmark/set/string-long/access.cpp
new file mode 100644
index 0000000000..c3deffbb06
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-long/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/set/string-long/generator.ipp b/third_party/immer/benchmark/set/string-long/generator.ipp
new file mode 100644
index 0000000000..386364cc96
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-long/generator.ipp
@@ -0,0 +1,44 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <random>
+#include <vector>
+#include <cassert>
+#include <functional>
+#include <algorithm>
+
+#define GENERATOR_T generate_unsigned
+
+namespace {
+
+struct GENERATOR_T
+{
+    static constexpr auto char_set   = "_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    static constexpr auto max_length = 256;
+    static constexpr auto min_length = 32;
+
+    auto operator() (std::size_t runs) const
+    {
+        assert(runs > 0);
+        auto engine = std::default_random_engine{42};
+        auto dist = std::uniform_int_distribution<unsigned>{};
+        auto gen = std::bind(dist, engine);
+        auto r = std::vector<std::string>(runs);
+        std::generate_n(r.begin(), runs, [&] {
+            auto len = gen() % (max_length - min_length) + min_length;
+            auto str = std::string(len, ' ');
+            std::generate_n(str.begin(), len, [&] {
+                return char_set[gen() % sizeof(char_set)];
+            });
+            return str;
+        });
+        return r;
+    }
+};
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/string-long/insert.cpp b/third_party/immer/benchmark/set/string-long/insert.cpp
new file mode 100644
index 0000000000..c5762498c2
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-long/insert.cpp
@@ -0,0 +1,11 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define DISABLE_GC_BENCHMARKS
+#include "generator.ipp"
+#include "../insert.ipp"
diff --git a/third_party/immer/benchmark/set/string-long/iter.cpp b/third_party/immer/benchmark/set/string-long/iter.cpp
new file mode 100644
index 0000000000..83e57e7845
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-long/iter.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../iter.ipp"
diff --git a/third_party/immer/benchmark/set/string-short/access.cpp b/third_party/immer/benchmark/set/string-short/access.cpp
new file mode 100644
index 0000000000..c3deffbb06
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-short/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/set/string-short/generator.ipp b/third_party/immer/benchmark/set/string-short/generator.ipp
new file mode 100644
index 0000000000..bd40d2d639
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-short/generator.ipp
@@ -0,0 +1,44 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <random>
+#include <vector>
+#include <cassert>
+#include <functional>
+#include <algorithm>
+
+#define GENERATOR_T generate_unsigned
+
+namespace {
+
+struct GENERATOR_T
+{
+    static constexpr auto char_set   = "_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    static constexpr auto max_length = 15;
+    static constexpr auto min_length = 4;
+
+    auto operator() (std::size_t runs) const
+    {
+        assert(runs > 0);
+        auto engine = std::default_random_engine{42};
+        auto dist = std::uniform_int_distribution<unsigned>{};
+        auto gen = std::bind(dist, engine);
+        auto r = std::vector<std::string>(runs);
+        std::generate_n(r.begin(), runs, [&] {
+            auto len = gen() % (max_length - min_length) + min_length;
+            auto str = std::string(len, ' ');
+            std::generate_n(str.begin(), len, [&] {
+                return char_set[gen() % sizeof(char_set)];
+            });
+            return str;
+        });
+        return r;
+    }
+};
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/string-short/insert.cpp b/third_party/immer/benchmark/set/string-short/insert.cpp
new file mode 100644
index 0000000000..26dfbce292
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-short/insert.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../insert.ipp"
diff --git a/third_party/immer/benchmark/set/string-short/iter.cpp b/third_party/immer/benchmark/set/string-short/iter.cpp
new file mode 100644
index 0000000000..83e57e7845
--- /dev/null
+++ b/third_party/immer/benchmark/set/string-short/iter.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../iter.ipp"
diff --git a/third_party/immer/benchmark/set/unsigned/access.cpp b/third_party/immer/benchmark/set/unsigned/access.cpp
new file mode 100644
index 0000000000..c3deffbb06
--- /dev/null
+++ b/third_party/immer/benchmark/set/unsigned/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/set/unsigned/generator.ipp b/third_party/immer/benchmark/set/unsigned/generator.ipp
new file mode 100644
index 0000000000..2a2a2a0fb6
--- /dev/null
+++ b/third_party/immer/benchmark/set/unsigned/generator.ipp
@@ -0,0 +1,32 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <random>
+#include <vector>
+#include <cassert>
+#include <functional>
+#include <algorithm>
+
+#define GENERATOR_T generate_unsigned
+
+namespace {
+
+struct GENERATOR_T
+{
+    auto operator() (std::size_t runs) const
+    {
+        assert(runs > 0);
+        auto engine = std::default_random_engine{42};
+        auto dist = std::uniform_int_distribution<unsigned>{};
+        auto r = std::vector<unsigned>(runs);
+        std::generate_n(r.begin(), runs, std::bind(dist, engine));
+        return r;
+    }
+};
+
+} // namespace
diff --git a/third_party/immer/benchmark/set/unsigned/insert.cpp b/third_party/immer/benchmark/set/unsigned/insert.cpp
new file mode 100644
index 0000000000..26dfbce292
--- /dev/null
+++ b/third_party/immer/benchmark/set/unsigned/insert.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../insert.ipp"
diff --git a/third_party/immer/benchmark/set/unsigned/iter.cpp b/third_party/immer/benchmark/set/unsigned/iter.cpp
new file mode 100644
index 0000000000..83e57e7845
--- /dev/null
+++ b/third_party/immer/benchmark/set/unsigned/iter.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "generator.ipp"
+#include "../iter.ipp"
diff --git a/third_party/immer/benchmark/vector/access.hpp b/third_party/immer/benchmark/vector/access.hpp
new file mode 100644
index 0000000000..4dff7a667f
--- /dev/null
+++ b/third_party/immer/benchmark/vector/access.hpp
@@ -0,0 +1,261 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+#include <immer/algorithm.hpp>
+
+#if IMMER_BENCHMARK_BOOST_COROUTINE
+#include <boost/coroutine2/all.hpp>
+#endif
+
+namespace {
+
+template <typename Vektor>
+auto benchmark_access_reduce_chunkedseq()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v.push_back(i);
+        return [=] {
+            auto init = 0u;
+            v.for_each_segment([&] (auto first, auto last) {
+                init = std::accumulate(first, last, init);
+            });
+            return init;
+        };
+    };
+}
+
+template <typename Vektor>
+auto benchmark_access_iter_std()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v.push_back(i);
+        return [=] {
+            auto volatile x = std::accumulate(v.begin(), v.end(), 0u);
+            return x;
+        };
+    };
+}
+
+template <typename Vektor>
+auto benchmark_access_idx_std()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v.push_back(i);
+        return [=] {
+            auto r = 0u;
+            for (auto i = 0u; i < n; ++i)
+                r += v[i];
+            volatile auto rr = r;
+            return rr;
+        };
+    };
+}
+
+template <typename Vektor>
+auto benchmark_access_random_std()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+        auto v = Vektor{};
+        auto g = make_generator(n);
+        for (auto i = 0u; i < n; ++i)
+            v.push_back(i);
+        return [=] {
+            auto r = 0u;
+            for (auto i = 0u; i < n; ++i)
+                r += v[g[i]];
+            volatile auto rr = r;
+            return rr;
+        };
+    };
+}
+
+template <typename Vektor, typename PushFn=push_back_fn>
+auto benchmark_access_iter()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        return [=] {
+            auto volatile x = std::accumulate(v.begin(), v.end(), 0u);
+            return x;
+        };
+    };
+}
+
+#if IMMER_BENCHMARK_BOOST_COROUTINE
+template <typename Vektor, typename PushFn=push_back_fn>
+auto benchmark_access_coro()
+{
+    return [] (nonius::parameters params)
+    {
+        using coro_t = typename boost::coroutines2::coroutine<int>;
+
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        return [=] {
+            auto c = coro_t::pull_type { [&](auto& sink) {
+                v.for_each_chunk([&](auto f, auto l) {
+                    for (; f != l; ++f)
+                        sink(*f);
+                });
+            }};
+            auto volatile x = std::accumulate(begin(c), end(c), 0u);
+            return x;
+        };
+    };
+}
+#endif
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_access_idx()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        return [=] {
+            auto r = 0u;
+            for (auto i = 0u; i < n; ++i)
+                r += v[i];
+            volatile auto rr = r;
+            return rr;
+        };
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_access_reduce()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        return [=] {
+            auto volatile x = immer::accumulate(v, 0u);
+            return x;
+        };
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_access_reduce_range()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        return [=] {
+            auto volatile x = immer::accumulate(v.begin(), v.end(), 0u);
+            return x;
+        };
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_access_random()
+{
+    return [] (nonius::parameters params)
+    {
+        auto n = params.get<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+        auto g = make_generator(n);
+
+        return [=] {
+            auto r = 0u;
+            for (auto i = 0u; i < n; ++i)
+                r += v[g[i]];
+            volatile auto rr = r;
+            return rr;
+        };
+    };
+}
+
+template <typename Fn>
+auto benchmark_access_librrb(Fn maker)
+{
+    return
+        [=] (nonius::parameters params) {
+            auto n = params.get<N>();
+            auto v = maker(n);
+            return
+                [=] {
+                    auto r = 0u;
+                    for (auto i = 0u; i < n; ++i)
+                        r += reinterpret_cast<unsigned long>(rrb_nth(v, i));
+                    volatile auto rr = r;
+                    return rr;
+                };
+        };
+}
+
+template <typename Fn>
+auto benchmark_access_random_librrb(Fn maker)
+{
+    return
+        [=] (nonius::parameters params) {
+            auto n = params.get<N>();
+            auto v = maker(n);
+            auto g = make_generator(n);
+            return
+                [=] {
+                    auto r = 0u;
+                    for (auto i = 0u; i < n; ++i)
+                        r += reinterpret_cast<unsigned long>(rrb_nth(v, g[i]));
+                    volatile auto rr = r;
+                    return rr;
+                };
+        };
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/assoc.hpp b/third_party/immer/benchmark/vector/assoc.hpp
new file mode 100644
index 0000000000..5619d7ee69
--- /dev/null
+++ b/third_party/immer/benchmark/vector/assoc.hpp
@@ -0,0 +1,245 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+template <typename Vektor>
+auto benchmark_assoc_std()
+{
+    return
+        [] (nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+            auto v = Vektor(n, 0);
+            std::iota(v.begin(), v.end(), 0u);
+            auto all = std::vector<Vektor>(meter.runs(), v);
+            meter.measure([&] (int iter) {
+                auto& r = all[iter];
+                for (auto i = 0u; i < n; ++i)
+                    r[i] = n - i;
+                return r;
+            });
+        };
+}
+
+template <typename Vektor>
+auto benchmark_assoc_random_std()
+{
+    return
+        [](nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+            auto g = make_generator(n);
+            auto v = Vektor(n, 0);
+            std::iota(v.begin(), v.end(), 0u);
+            auto all = std::vector<Vektor>(meter.runs(), v);
+            meter.measure([&] (int iter) {
+                auto& r = all[iter];
+                for (auto i = 0u; i < n; ++i)
+                    r[g[i]] = n - i;
+                return r;
+            });
+        };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn,
+          typename SetFn=set_fn>
+auto benchmark_assoc()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = 0u; i < n; ++i)
+                r = SetFn{}(r, i, n - i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_assoc_move()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = 0u; i < n; ++i)
+                r = std::move(r).set(i, n - i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn,
+          typename SetFn=set_fn>
+auto benchmark_assoc_random()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        auto g = make_generator(n);
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = 0u; i < n; ++i)
+                r = SetFn{}(r, g[i], i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_assoc_mut()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v.transient();
+            for (auto i = 0u; i < n; ++i)
+                r.set(i, n - i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_assoc_mut_random()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        auto g = make_generator(n);
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v.transient();
+            for (auto i = 0u; i < n; ++i)
+                r.set(g[i], i);
+            return r;
+        });
+    };
+}
+
+template <typename Fn>
+auto benchmark_assoc_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n);
+            measure(
+                meter, [&] {
+                    auto r = v;
+                    for (auto i = 0u; i < n; ++i)
+                        r = rrb_update(r, i, reinterpret_cast<void*>(n - i));
+                    return r;
+                });
+        };
+}
+
+template <typename Fn>
+auto benchmark_assoc_random_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n);
+            auto g = make_generator(n);
+            measure(
+                meter, [&] {
+                    auto r = v;
+                    for (auto i = 0u; i < n; ++i)
+                        r = rrb_update(r, g[i], reinterpret_cast<void*>(i));
+                    return r;
+                });
+        };
+}
+
+template <typename Fn>
+auto benchmark_assoc_mut_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n);
+            measure(
+                meter, [&] {
+                    auto r = rrb_to_transient(v);
+                    for (auto i = 0u; i < n; ++i)
+                        r = transient_rrb_update(
+                            r, i, reinterpret_cast<void*>(i));
+                    return r;
+                });
+        };
+}
+
+template <typename Fn>
+auto benchmark_assoc_mut_random_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n);
+            auto g = make_generator(n);
+            measure(
+                meter, [&] {
+                    auto r = rrb_to_transient(v);
+                    for (auto i = 0u; i < n; ++i)
+                        r = transient_rrb_update(
+                            r, g[i], reinterpret_cast<void*>(i));
+                    return r;
+                });
+        };
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/branching/access.ipp b/third_party/immer/benchmark/vector/branching/access.ipp
new file mode 100644
index 0000000000..bd7f985d4d
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/access.ipp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/access.hpp"
+#include <immer/flex_vector.hpp>
+
+#ifndef MEMORY_T
+#error "define the MEMORY_T"
+#endif
+
+NONIUS_BENCHMARK("flex/3", benchmark_access_idx<immer::flex_vector<std::size_t,MEMORY_T,3>>())
+NONIUS_BENCHMARK("flex/4", benchmark_access_idx<immer::flex_vector<std::size_t,MEMORY_T,4>>())
+NONIUS_BENCHMARK("flex/5", benchmark_access_idx<immer::flex_vector<std::size_t,MEMORY_T,5>>())
+NONIUS_BENCHMARK("flex/6", benchmark_access_idx<immer::flex_vector<std::size_t,MEMORY_T,6>>())
+NONIUS_BENCHMARK("flex/7", benchmark_access_idx<immer::flex_vector<std::size_t,MEMORY_T,7>>())
diff --git a/third_party/immer/benchmark/vector/branching/assoc.ipp b/third_party/immer/benchmark/vector/branching/assoc.ipp
new file mode 100644
index 0000000000..4aa9984f92
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/assoc.ipp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/assoc.hpp"
+#include <immer/flex_vector.hpp>
+
+#ifndef MEMORY_T
+#error "define the MEMORY_T"
+#endif
+
+NONIUS_BENCHMARK("flex/3", benchmark_assoc<immer::flex_vector<std::size_t,MEMORY_T,3>>())
+NONIUS_BENCHMARK("flex/4", benchmark_assoc<immer::flex_vector<std::size_t,MEMORY_T,4>>())
+NONIUS_BENCHMARK("flex/5", benchmark_assoc<immer::flex_vector<std::size_t,MEMORY_T,5>>())
+NONIUS_BENCHMARK("flex/6", benchmark_assoc<immer::flex_vector<std::size_t,MEMORY_T,6>>())
+NONIUS_BENCHMARK("flex/7", benchmark_assoc<immer::flex_vector<std::size_t,MEMORY_T,7>>())
diff --git a/third_party/immer/benchmark/vector/branching/basic/access.cpp b/third_party/immer/benchmark/vector/branching/basic/access.cpp
new file mode 100644
index 0000000000..23eb25565d
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/basic/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T basic_memory
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/basic/assoc.cpp b/third_party/immer/benchmark/vector/branching/basic/assoc.cpp
new file mode 100644
index 0000000000..210a432aba
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/basic/assoc.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T basic_memory
+#include "../assoc.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/basic/concat.cpp b/third_party/immer/benchmark/vector/branching/basic/concat.cpp
new file mode 100644
index 0000000000..21163bdc0b
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/basic/concat.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T basic_memory
+#include "../concat.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/basic/push.cpp b/third_party/immer/benchmark/vector/branching/basic/push.cpp
new file mode 100644
index 0000000000..6be28f9219
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/basic/push.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T basic_memory
+#include "../push.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/concat.ipp b/third_party/immer/benchmark/vector/branching/concat.ipp
new file mode 100644
index 0000000000..0e21ca0b11
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/concat.ipp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/concat.hpp"
+#include <immer/flex_vector.hpp>
+
+#ifndef MEMORY_T
+#error "define the MEMORY_T"
+#endif
+
+NONIUS_BENCHMARK("flex/3", benchmark_concat<immer::flex_vector<std::size_t,MEMORY_T,3>>())
+NONIUS_BENCHMARK("flex/4", benchmark_concat<immer::flex_vector<std::size_t,MEMORY_T,4>>())
+NONIUS_BENCHMARK("flex/5", benchmark_concat<immer::flex_vector<std::size_t,MEMORY_T,5>>())
+NONIUS_BENCHMARK("flex/6", benchmark_concat<immer::flex_vector<std::size_t,MEMORY_T,6>>())
+NONIUS_BENCHMARK("flex/7", benchmark_concat<immer::flex_vector<std::size_t,MEMORY_T,7>>())
diff --git a/third_party/immer/benchmark/vector/branching/gc/access.cpp b/third_party/immer/benchmark/vector/branching/gc/access.cpp
new file mode 100644
index 0000000000..cec060481c
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/gc/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T gc_memory
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/gc/assoc.cpp b/third_party/immer/benchmark/vector/branching/gc/assoc.cpp
new file mode 100644
index 0000000000..448b2fd2ff
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/gc/assoc.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T gc_memory
+#include "../assoc.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/gc/concat.cpp b/third_party/immer/benchmark/vector/branching/gc/concat.cpp
new file mode 100644
index 0000000000..6c32ca821c
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/gc/concat.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T gc_memory
+#include "../concat.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/gc/push.cpp b/third_party/immer/benchmark/vector/branching/gc/push.cpp
new file mode 100644
index 0000000000..f1d1e33b92
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/gc/push.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T gc_memory
+#include "../push.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/push.ipp b/third_party/immer/benchmark/vector/branching/push.ipp
new file mode 100644
index 0000000000..2f92d65692
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/push.ipp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/push.hpp"
+#include <immer/flex_vector.hpp>
+
+#ifndef MEMORY_T
+#error "define the MEMORY_T"
+#endif
+
+NONIUS_BENCHMARK("flex/3", benchmark_push<immer::flex_vector<std::size_t,MEMORY_T,3>>())
+NONIUS_BENCHMARK("flex/4", benchmark_push<immer::flex_vector<std::size_t,MEMORY_T,4>>())
+NONIUS_BENCHMARK("flex/5", benchmark_push<immer::flex_vector<std::size_t,MEMORY_T,5>>())
+NONIUS_BENCHMARK("flex/6", benchmark_push<immer::flex_vector<std::size_t,MEMORY_T,6>>())
+NONIUS_BENCHMARK("flex/7", benchmark_push<immer::flex_vector<std::size_t,MEMORY_T,7>>())
diff --git a/third_party/immer/benchmark/vector/branching/safe/access.cpp b/third_party/immer/benchmark/vector/branching/safe/access.cpp
new file mode 100644
index 0000000000..027aa1b761
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/safe/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T safe_memory
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/safe/assoc.cpp b/third_party/immer/benchmark/vector/branching/safe/assoc.cpp
new file mode 100644
index 0000000000..c2f5b0cc20
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/safe/assoc.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T safe_memory
+#include "../assoc.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/safe/concat.cpp b/third_party/immer/benchmark/vector/branching/safe/concat.cpp
new file mode 100644
index 0000000000..3c5dbb8a70
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/safe/concat.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T safe_memory
+#include "../concat.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/safe/push.cpp b/third_party/immer/benchmark/vector/branching/safe/push.cpp
new file mode 100644
index 0000000000..19d5bbd254
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/safe/push.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T safe_memory
+#include "../push.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/unsafe/access.cpp b/third_party/immer/benchmark/vector/branching/unsafe/access.cpp
new file mode 100644
index 0000000000..156d84e717
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/unsafe/access.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T unsafe_memory
+#include "../access.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/unsafe/assoc.cpp b/third_party/immer/benchmark/vector/branching/unsafe/assoc.cpp
new file mode 100644
index 0000000000..ff1802c0bc
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/unsafe/assoc.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T unsafe_memory
+#include "../assoc.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/unsafe/concat.cpp b/third_party/immer/benchmark/vector/branching/unsafe/concat.cpp
new file mode 100644
index 0000000000..d6d401bd48
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/unsafe/concat.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T unsafe_memory
+#include "../concat.ipp"
diff --git a/third_party/immer/benchmark/vector/branching/unsafe/push.cpp b/third_party/immer/benchmark/vector/branching/unsafe/push.cpp
new file mode 100644
index 0000000000..09ba8d7a37
--- /dev/null
+++ b/third_party/immer/benchmark/vector/branching/unsafe/push.cpp
@@ -0,0 +1,10 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#define MEMORY_T unsafe_memory
+#include "../push.ipp"
diff --git a/third_party/immer/benchmark/vector/common.hpp b/third_party/immer/benchmark/vector/common.hpp
new file mode 100644
index 0000000000..c96d6d017d
--- /dev/null
+++ b/third_party/immer/benchmark/vector/common.hpp
@@ -0,0 +1,212 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <utility>
+#include <cstddef>
+#include <limits>
+
+#include "benchmark/config.hpp"
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#include <immer/heap/gc_heap.hpp>
+#endif
+
+namespace immer {
+template <typename T, typename MP> class array;
+} // namespace immer
+
+namespace {
+
+auto make_generator(std::size_t runs)
+{
+    assert(runs > 0);
+    auto engine = std::default_random_engine{42};
+    auto dist = std::uniform_int_distribution<std::size_t>{0, runs-1};
+    auto r = std::vector<std::size_t>(runs);
+    std::generate_n(r.begin(), runs, std::bind(dist, engine));
+    return r;
+}
+
+struct push_back_fn
+{
+    template <typename T, typename U>
+    auto operator() (T&& v, U&& x)
+    { return std::forward<T>(v).push_back(std::forward<U>(x)); }
+};
+
+struct push_front_fn
+{
+    template <typename T, typename U>
+    auto operator() (T&& v, U&& x)
+    { return std::forward<T>(v).push_front(std::forward<U>(x)); }
+};
+
+struct set_fn
+{
+    template <typename T, typename I, typename U>
+    decltype(auto) operator() (T&& v, I i, U&& x)
+    { return std::forward<T>(v).set(i, std::forward<U>(x)); }
+};
+
+struct store_fn
+{
+    template <typename T, typename I, typename U>
+    decltype(auto) operator() (T&& v, I i, U&& x)
+    { return std::forward<T>(v).store(i, std::forward<U>(x)); }
+};
+
+template <typename T>
+struct get_limit : std::integral_constant<
+    std::size_t, std::numeric_limits<std::size_t>::max()> {};
+
+template <typename T, typename MP>
+struct get_limit<immer::array<T, MP>> : std::integral_constant<
+    std::size_t, 10000> {};
+
+auto make_librrb_vector(std::size_t n)
+{
+    auto v = rrb_create();
+    for (auto i = 0u; i < n; ++i) {
+        v = rrb_push(v, reinterpret_cast<void*>(i));
+    }
+    return v;
+}
+
+auto make_librrb_vector_f(std::size_t n)
+{
+    auto v = rrb_create();
+    for (auto i = 0u; i < n; ++i) {
+        auto f = rrb_push(rrb_create(),
+                          reinterpret_cast<void*>(i));
+        v = rrb_concat(f, v);
+    }
+    return v;
+}
+
+
+// copied from:
+// https://github.com/ivmai/bdwgc/blob/master/include/gc_allocator.h
+
+template <class GC_tp>
+struct GC_type_traits
+{
+  std::false_type GC_is_ptr_free;
+};
+
+# define GC_DECLARE_PTRFREE(T)                  \
+    template<> struct GC_type_traits<T> {       \
+        std::true_type GC_is_ptr_free;          \
+    }
+
+GC_DECLARE_PTRFREE(char);
+GC_DECLARE_PTRFREE(signed char);
+GC_DECLARE_PTRFREE(unsigned char);
+GC_DECLARE_PTRFREE(signed short);
+GC_DECLARE_PTRFREE(unsigned short);
+GC_DECLARE_PTRFREE(signed int);
+GC_DECLARE_PTRFREE(unsigned int);
+GC_DECLARE_PTRFREE(signed long);
+GC_DECLARE_PTRFREE(unsigned long);
+GC_DECLARE_PTRFREE(float);
+GC_DECLARE_PTRFREE(double);
+GC_DECLARE_PTRFREE(long double);
+
+template <class IsPtrFree>
+inline void* GC_selective_alloc(size_t n, IsPtrFree, bool ignore_off_page)
+{
+    return ignore_off_page
+        ? GC_MALLOC_IGNORE_OFF_PAGE(n)
+        : GC_MALLOC(n);
+}
+
+template <>
+inline void* GC_selective_alloc<std::true_type>(size_t n,
+                                                std::true_type,
+                                                bool ignore_off_page)
+{
+    return ignore_off_page
+        ? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n)
+        : GC_MALLOC_ATOMIC(n);
+}
+
+template <class T>
+class gc_allocator
+{
+public:
+    typedef size_t       size_type;
+    typedef ptrdiff_t    difference_type;
+    typedef T*       pointer;
+    typedef const T* const_pointer;
+    typedef T&       reference;
+    typedef const T& const_reference;
+    typedef T        value_type;
+
+    template <class T1> struct rebind {
+        typedef gc_allocator<T1> other;
+    };
+
+    gc_allocator()  {}
+    gc_allocator(const gc_allocator&) throw() {}
+    template <class T1>
+    explicit gc_allocator(const gc_allocator<T1>&) throw() {}
+    ~gc_allocator() throw() {}
+
+    pointer address(reference GC_x) const { return &GC_x; }
+    const_pointer address(const_reference GC_x) const { return &GC_x; }
+
+    // GC_n is permitted to be 0.  The C++ standard says nothing about what
+    // the return value is when GC_n == 0.
+    T* allocate(size_type GC_n, const void* = 0)
+    {
+        GC_type_traits<T> traits;
+        return static_cast<T *>
+            (GC_selective_alloc(GC_n * sizeof(T),
+                                traits.GC_is_ptr_free, false));
+    }
+
+    // p is not permitted to be a null pointer.
+    void deallocate(pointer p, size_type /* GC_n */)
+    { GC_FREE(p); }
+
+    size_type max_size() const throw()
+    { return size_t(-1) / sizeof(T); }
+
+    void construct(pointer p, const T& __val) { new(p) T(__val); }
+    void destroy(pointer p) { p->~T(); }
+};
+
+template<>
+class gc_allocator<void>
+{
+    typedef size_t      size_type;
+    typedef ptrdiff_t   difference_type;
+    typedef void*       pointer;
+    typedef const void* const_pointer;
+    typedef void        value_type;
+
+    template <class T1> struct rebind {
+        typedef gc_allocator<T1> other;
+    };
+};
+
+template <class T1, class T2>
+inline bool operator==(const gc_allocator<T1>&, const gc_allocator<T2>&)
+{ return true; }
+
+template <class T1, class T2>
+inline bool operator!=(const gc_allocator<T1>&, const gc_allocator<T2>&)
+{ return false; }
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/concat.hpp b/third_party/immer/benchmark/vector/concat.hpp
new file mode 100644
index 0000000000..8656587d5d
--- /dev/null
+++ b/third_party/immer/benchmark/vector/concat.hpp
@@ -0,0 +1,166 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+constexpr auto concat_steps = 10u;
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_concat()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            return v + v;
+        });
+    };
+}
+
+template <typename Fn>
+auto benchmark_concat_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n);
+            measure(meter, [&] {
+                    return rrb_concat(v, v);
+                });
+        };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_concat_incr()
+{
+    return
+        [] (nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+
+            auto v = Vektor{};
+            for (auto i = 0u; i < n / concat_steps; ++i)
+                v = PushFn{}(std::move(v), i);
+
+            measure(meter, [&] {
+                    auto r = Vektor{};
+                    for (auto i = 0u; i < concat_steps; ++i)
+                        r = r + v;
+                    return r;
+                });
+        };
+}
+
+template <typename Vektor>
+auto benchmark_concat_incr_mut()
+{
+    return
+        [] (nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+
+            auto v = Vektor{}.transient();
+            for (auto i = 0u; i < n / concat_steps; ++i)
+                v.push_back(i);
+
+            measure(meter, [&] (int run) {
+                auto r = Vektor{}.transient();
+                for (auto i = 0u; i < concat_steps; ++i)
+                    r.append(v);
+                return r;
+            });
+        };
+}
+
+template <typename Vektor>
+auto benchmark_concat_incr_mut2()
+{
+    return
+        [] (nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+
+            using transient_t = typename Vektor::transient_type;
+            using steps_t = std::vector<transient_t, gc_allocator<transient_t>>;
+            auto vs = std::vector<steps_t, gc_allocator<steps_t>>(meter.runs());
+            for (auto k = 0u; k < vs.size(); ++k) {
+                vs[k].reserve(concat_steps);
+                for (auto j = 0u; j < concat_steps; ++j) {
+                    auto vv = Vektor{}.transient();
+                    for (auto i = 0u; i < n / concat_steps; ++i)
+                        vv.push_back(i);
+                    vs[k].push_back(std::move(vv));
+                }
+            }
+            measure(meter, [&] (int run) {
+                auto& vr = vs[run];
+                auto r = Vektor{}.transient();
+                assert(vr.size() == concat_steps);
+                for (auto i = 0u; i < concat_steps; ++i)
+                    r.append(std::move(vr[i]));
+                return r;
+            });
+        };
+}
+
+template <typename Vektor>
+auto benchmark_concat_incr_chunkedseq()
+{
+    return
+        [] (nonius::chronometer meter)
+        {
+            auto n = meter.param<N>();
+
+            using steps_t = std::vector<Vektor>;
+            auto vs = std::vector<steps_t>(meter.runs());
+            for (auto k = 0u; k < vs.size(); ++k) {
+                for (auto j = 0u; j < concat_steps; ++j) {
+                    auto vv = Vektor{};
+                    for (auto i = 0u; i < n / concat_steps; ++i)
+                        vv.push_back(i);
+                    vs[k].push_back(std::move(vv));
+                }
+            }
+            measure(meter, [&] (int run) {
+                auto& vr = vs[run];
+                auto r = Vektor{};
+                for (auto i = 0u; i < concat_steps; ++i)
+                    r.concat(vr[i]);
+                return r;
+            });
+        };
+}
+
+template <typename Fn>
+auto benchmark_concat_incr_librrb(Fn maker)
+{
+    return
+        [=] (nonius::chronometer meter) {
+            auto n = meter.param<N>();
+            auto v = maker(n / concat_steps);
+            measure(meter, [&] {
+                    auto r = rrb_create();
+                    for (auto i = 0ul; i < concat_steps; ++i)
+                        r = rrb_concat(r, v);
+                    return r;
+                });
+        };
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/drop.hpp b/third_party/immer/benchmark/vector/drop.hpp
new file mode 100644
index 0000000000..0a525f67a5
--- /dev/null
+++ b/third_party/immer/benchmark/vector/drop.hpp
@@ -0,0 +1,142 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_drop()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            for (auto i = 0u; i < n; ++i)
+                (void) v.drop(i);
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_drop_lin()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = 0u; i < n; ++i)
+                r = r.drop(1);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_drop_move()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = 0u; i < n; ++i)
+                r = std::move(r).drop(1);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_drop_mut()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto vv = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            vv = PushFn{}(std::move(vv), i);
+
+        measure(meter, [&] {
+            auto v = vv.transient();
+            for (auto i = 0u; i < n; ++i)
+                (void) v.drop(1);
+        });
+    };
+}
+
+template <typename Fn>
+auto benchmark_drop_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(meter, [&] {
+                for (auto i = 0u; i < n; ++i)
+                    rrb_slice(v, i, n);
+            });
+    };
+}
+
+template <typename Fn>
+auto benchmark_drop_lin_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(meter, [&] {
+                auto r = v;
+                for (auto i = 0u; i < n; ++i)
+                    r = rrb_slice(r, 1, n);
+                return r;
+            });
+    };
+}
+
+template <typename Fn>
+auto benchmark_drop_mut_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(meter, [&] {
+                auto r = rrb_to_transient(v);
+                for (auto i = 0u; i < n; ++i)
+                    r = transient_rrb_slice(r, 1, n);
+                return r;
+            });
+    };
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/misc/access.cpp b/third_party/immer/benchmark/vector/misc/access.cpp
new file mode 100644
index 0000000000..9a00266a87
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/access.cpp
@@ -0,0 +1,94 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/access.hpp"
+
+#include <immer/algorithm.hpp>
+#include <immer/array.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+#include <immer/experimental/dvektor.hpp>
+#endif
+
+#if IMMER_BENCHMARK_STEADY
+#define QUARK_ASSERT_ON 0
+#include <steady/steady_vector.h>
+#endif
+
+#include <vector>
+#include <list>
+#include <numeric>
+
+NONIUS_BENCHMARK("std::vector", benchmark_access_iter_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("std::vector/idx", benchmark_access_idx_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("std::vector/random", benchmark_access_random_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("std::list", benchmark_access_iter_std<std::list<unsigned>>())
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_access_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F", benchmark_access_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("librrb/random", benchmark_access_random_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F/random", benchmark_access_random_librrb(make_librrb_vector_f))
+#endif
+
+NONIUS_BENCHMARK("flex/5B",     benchmark_access_iter<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/F/5B",   benchmark_access_iter<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("vector/4B",   benchmark_access_iter<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B",   benchmark_access_iter<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B",   benchmark_access_iter<immer::vector<unsigned,def_memory,6>>())
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B",  benchmark_access_iter<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B",  benchmark_access_iter<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B",  benchmark_access_iter<immer::dvektor<unsigned,def_memory,6>>())
+#endif
+
+#if IMMER_BENCHMARK_STEADY
+NONIUS_BENCHMARK("steady/idx",      benchmark_access_idx<steady::vector<unsigned>>())
+#endif
+
+NONIUS_BENCHMARK("flex/5B/idx",     benchmark_access_idx<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/F/5B/idx",   benchmark_access_idx<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("vector/4B/idx",   benchmark_access_idx<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B/idx",   benchmark_access_idx<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B/idx",   benchmark_access_idx<immer::vector<unsigned,def_memory,6>>())
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B/idx",  benchmark_access_idx<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B/idx",  benchmark_access_idx<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B/idx",  benchmark_access_idx<immer::dvektor<unsigned,def_memory,6>>())
+#endif
+
+NONIUS_BENCHMARK("flex/5B/reduce_i", benchmark_access_reduce_range<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/5B/reduce_i", benchmark_access_reduce_range<immer::vector<unsigned,def_memory,5>>())
+
+NONIUS_BENCHMARK("flex/5B/reduce",   benchmark_access_reduce<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/F/5B/reduce", benchmark_access_reduce<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("vector/4B/reduce", benchmark_access_reduce<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B/reduce", benchmark_access_reduce<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B/reduce", benchmark_access_reduce<immer::vector<unsigned,def_memory,6>>())
+
+#if IMMER_BENCHMARK_STEADY
+NONIUS_BENCHMARK("steady/random",      benchmark_access_random<steady::vector<unsigned>>())
+#endif
+
+NONIUS_BENCHMARK("flex/5B/random",     benchmark_access_random<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/F/5B/random",   benchmark_access_random<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("vector/4B/random",   benchmark_access_random<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B/random",   benchmark_access_random<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B/random",   benchmark_access_random<immer::vector<unsigned,def_memory,6>>())
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B/random",  benchmark_access_random<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B/random",  benchmark_access_random<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B/random",  benchmark_access_random<immer::dvektor<unsigned,def_memory,6>>())
+#endif
+
+#if IMMER_BENCHMARK_BOOST_COROUTINE
+NONIUS_BENCHMARK("vector/5B/coro", benchmark_access_coro<immer::vector<unsigned,def_memory,5>>())
+#endif
diff --git a/third_party/immer/benchmark/vector/misc/assoc.cpp b/third_party/immer/benchmark/vector/misc/assoc.cpp
new file mode 100644
index 0000000000..2b3d6d76b9
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/assoc.cpp
@@ -0,0 +1,119 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/assoc.hpp"
+
+#include <immer/array.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+#include <immer/experimental/dvektor.hpp>
+#endif
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#include <vector>
+#include <list>
+
+#if IMMER_BENCHMARK_STEADY
+#define QUARK_ASSERT_ON 0
+#include <steady/steady_vector.h>
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+NONIUS_BENCHMARK("std::vector", benchmark_assoc_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("std::vector/random", benchmark_assoc_random_std<std::vector<unsigned>>())
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_assoc_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F", benchmark_assoc_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("librrb/random", benchmark_assoc_random_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("t/librrb", benchmark_assoc_mut_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("t/librrb/F", benchmark_assoc_mut_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("t/librrb/random", benchmark_assoc_mut_random_librrb(make_librrb_vector))
+#endif
+
+#if IMMER_BENCHMARK_STEADY
+NONIUS_BENCHMARK("steady",     benchmark_assoc<steady::vector<unsigned>, push_back_fn, store_fn>())
+#endif
+
+NONIUS_BENCHMARK("flex/5B",    benchmark_assoc<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/F/5B",  benchmark_assoc<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("flex/GC",    benchmark_assoc<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("flex/F/GC",  benchmark_assoc<immer::flex_vector<unsigned,gc_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GCF", benchmark_assoc<immer::flex_vector<unsigned,gcf_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("flex_s/GC",  benchmark_assoc<immer::flex_vector<std::size_t,gc_memory,5>>())
+NONIUS_BENCHMARK("flex_s/F/GC",benchmark_assoc<immer::flex_vector<std::size_t,gc_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("flex_s/F/GCF",benchmark_assoc<immer::flex_vector<std::size_t,gcf_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("vector/4B",  benchmark_assoc<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B",  benchmark_assoc<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B",  benchmark_assoc<immer::vector<unsigned,def_memory,6>>())
+
+NONIUS_BENCHMARK("vector/GC",  benchmark_assoc<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("vector/NO",  benchmark_assoc<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("vector/UN",  benchmark_assoc<immer::vector<unsigned,unsafe_memory,5>>())
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B", benchmark_assoc<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B", benchmark_assoc<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B", benchmark_assoc<immer::dvektor<unsigned,def_memory,6>>())
+
+NONIUS_BENCHMARK("dvektor/GC", benchmark_assoc<immer::dvektor<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("dvektor/NO", benchmark_assoc<immer::dvektor<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("dvektor/UN", benchmark_assoc<immer::dvektor<unsigned,unsafe_memory,5>>())
+#endif
+
+NONIUS_BENCHMARK("array",      benchmark_assoc<immer::array<unsigned>>())
+
+#if IMMER_BENCHMARK_STEADY
+NONIUS_BENCHMARK("steady/random",      benchmark_assoc_random<steady::vector<unsigned>, push_back_fn, store_fn>())
+#endif
+
+NONIUS_BENCHMARK("flex/5B/random",     benchmark_assoc_random<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/4B/random",   benchmark_assoc_random<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B/random",   benchmark_assoc_random<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B/random",   benchmark_assoc_random<immer::vector<unsigned,def_memory,6>>())
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B/random",  benchmark_assoc_random<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B/random",  benchmark_assoc_random<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B/random",  benchmark_assoc_random<immer::dvektor<unsigned,def_memory,6>>())
+#endif
+NONIUS_BENCHMARK("array/random",       benchmark_assoc_random<immer::array<unsigned>>())
+
+NONIUS_BENCHMARK("t/vector/5B",  benchmark_assoc_mut<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("t/vector/GC",  benchmark_assoc_mut<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("t/vector/NO",  benchmark_assoc_mut<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("t/vector/UN",  benchmark_assoc_mut<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("t/flex/F/5B",  benchmark_assoc_mut<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("m/vector/5B",  benchmark_assoc_move<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("m/vector/GC",  benchmark_assoc_move<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("m/vector/NO",  benchmark_assoc_move<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("m/vector/UN",  benchmark_assoc_move<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("m/flex/F/5B",  benchmark_assoc_move<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("t/vector/5B/random",  benchmark_assoc_mut_random<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("t/vector/GC/random",  benchmark_assoc_mut_random<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("t/vector/NO/random",  benchmark_assoc_mut_random<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("t/vector/UN/random",  benchmark_assoc_mut_random<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("t/flex/F/5B/random",  benchmark_assoc_mut_random<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
diff --git a/third_party/immer/benchmark/vector/misc/concat.cpp b/third_party/immer/benchmark/vector/misc/concat.cpp
new file mode 100644
index 0000000000..9bc10c5c23
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/concat.cpp
@@ -0,0 +1,49 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/concat.hpp"
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_concat_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F", benchmark_concat_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("i/librrb", benchmark_concat_incr_librrb(make_librrb_vector))
+#endif
+
+NONIUS_BENCHMARK("flex/4B", benchmark_concat<immer::flex_vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("flex/5B", benchmark_concat<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/6B", benchmark_concat<immer::flex_vector<unsigned,def_memory,6>>())
+NONIUS_BENCHMARK("flex/GC", benchmark_concat<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("flex_s/GC", benchmark_concat<immer::flex_vector<std::size_t,gc_memory,5>>())
+NONIUS_BENCHMARK("flex/NO", benchmark_concat<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("flex/UN", benchmark_concat<immer::flex_vector<unsigned,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("flex/F/5B", benchmark_concat<immer::flex_vector<unsigned,def_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GC", benchmark_concat<immer::flex_vector<unsigned,gc_memory,5>,push_front_fn>())
+NONIUS_BENCHMARK("flex_s/F/GC", benchmark_concat<immer::flex_vector<std::size_t,gc_memory,5>,push_front_fn>())
+
+NONIUS_BENCHMARK("i/flex/GC", benchmark_concat_incr<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("i/flex_s/GC", benchmark_concat_incr<immer::flex_vector<std::size_t,gc_memory,5>>())
+NONIUS_BENCHMARK("i/flex/5B", benchmark_concat_incr<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("i/flex/UN", benchmark_concat_incr<immer::flex_vector<unsigned,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("m/flex/GC", benchmark_concat_incr_mut<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("m/flex_s/GC", benchmark_concat_incr_mut<immer::flex_vector<std::size_t,gc_memory,5>>())
diff --git a/third_party/immer/benchmark/vector/misc/drop.cpp b/third_party/immer/benchmark/vector/misc/drop.cpp
new file mode 100644
index 0000000000..c2ca48390b
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/drop.cpp
@@ -0,0 +1,63 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/drop.hpp"
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_drop_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F", benchmark_drop_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("l/librrb", benchmark_drop_lin_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("l/librrb/F", benchmark_drop_lin_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("t/librrb", benchmark_drop_mut_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("t/librrb/F", benchmark_drop_mut_librrb(make_librrb_vector_f))
+#endif
+
+NONIUS_BENCHMARK("flex/4B", benchmark_drop<immer::flex_vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("flex/5B", benchmark_drop<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/6B", benchmark_drop<immer::flex_vector<unsigned,def_memory,6>>())
+NONIUS_BENCHMARK("flex/GC", benchmark_drop<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("flex/NO", benchmark_drop<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("flex/UN", benchmark_drop<immer::flex_vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("flex_s/GC", benchmark_drop<immer::flex_vector<std::size_t,gc_memory,5>>())
+
+NONIUS_BENCHMARK("flex/F/5B", benchmark_drop<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GC", benchmark_drop<immer::flex_vector<unsigned,gc_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GCF", benchmark_drop<immer::flex_vector<unsigned,gcf_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex_s/F/GC", benchmark_drop<immer::flex_vector<std::size_t,gc_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("l/flex/5B", benchmark_drop_lin<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("l/flex/GC", benchmark_drop_lin<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("l/flex/NO", benchmark_drop_lin<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("l/flex/UN", benchmark_drop_lin<immer::flex_vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("l/flex/F/5B", benchmark_drop_lin<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("m/flex/5B", benchmark_drop_move<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("m/flex/GC", benchmark_drop_move<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("m/flex/NO", benchmark_drop_move<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("m/flex/UN", benchmark_drop_move<immer::flex_vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("m/flex/F/5B", benchmark_drop_move<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("t/flex/5B", benchmark_drop_mut<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("t/flex/GC", benchmark_drop_mut<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("t/flex/NO", benchmark_drop_mut<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("t/flex/UN", benchmark_drop_mut<immer::flex_vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("t/flex/F/5B", benchmark_drop_mut<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
diff --git a/third_party/immer/benchmark/vector/misc/push-front.cpp b/third_party/immer/benchmark/vector/misc/push-front.cpp
new file mode 100644
index 0000000000..48b98c8ee9
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/push-front.cpp
@@ -0,0 +1,30 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/push_front.hpp"
+
+#include <immer/flex_vector.hpp>
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_push_front_librrb)
+#endif
+NONIUS_BENCHMARK("flex/4B", bechmark_push_front<immer::flex_vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("flex/5B", bechmark_push_front<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex/6B", bechmark_push_front<immer::flex_vector<unsigned,def_memory,6>>())
+NONIUS_BENCHMARK("flex/GC", bechmark_push_front<immer::flex_vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("flex_s/GC", bechmark_push_front<immer::flex_vector<std::size_t,gc_memory,5>>())
+NONIUS_BENCHMARK("flex/NO", bechmark_push_front<immer::flex_vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("flex/UN", bechmark_push_front<immer::flex_vector<unsigned,unsafe_memory,5>>())
diff --git a/third_party/immer/benchmark/vector/misc/push.cpp b/third_party/immer/benchmark/vector/misc/push.cpp
new file mode 100644
index 0000000000..98f57600bd
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/push.cpp
@@ -0,0 +1,83 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/push.hpp"
+
+#include <immer/array.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/vector_transient.hpp>
+#include <immer/vector.hpp>
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+#include <immer/experimental/dvektor.hpp>
+#endif
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#include <vector>
+#include <list>
+#include <numeric>
+
+#if IMMER_BENCHMARK_STEADY
+#define QUARK_ASSERT_ON 0
+#include <steady/steady_vector.h>
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_push_librrb)
+NONIUS_BENCHMARK("t/librrb", benchmark_push_mut_librrb)
+#endif
+
+NONIUS_BENCHMARK("std::vector", benchmark_push_mut_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("std::list",   benchmark_push_mut_std<std::list<unsigned>>())
+
+NONIUS_BENCHMARK("m/vector/5B", benchmark_push_move<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("m/vector/GC", benchmark_push_move<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("m/vector/NO", benchmark_push_move<immer::vector<unsigned,basic_memory,5>>())
+
+NONIUS_BENCHMARK("t/vector/5B", benchmark_push_mut<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("t/vector/GC", benchmark_push_mut<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("t/vector/NO", benchmark_push_mut<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("t/vector/UN", benchmark_push_mut<immer::vector<unsigned,unsafe_memory,5>>())
+
+NONIUS_BENCHMARK("flex/5B",    benchmark_push<immer::flex_vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("flex_s/GC",  benchmark_push<immer::flex_vector<std::size_t,gc_memory,5>>())
+
+NONIUS_BENCHMARK("vector/4B",  benchmark_push<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B",  benchmark_push<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B",  benchmark_push<immer::vector<unsigned,def_memory,6>>())
+
+NONIUS_BENCHMARK("vector/GC",  benchmark_push<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("vector/NO",  benchmark_push<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("vector/UN",  benchmark_push<immer::vector<unsigned,unsafe_memory,5>>())
+
+#if IMMER_BENCHMARK_EXPERIMENTAL
+NONIUS_BENCHMARK("dvektor/4B", benchmark_push<immer::dvektor<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("dvektor/5B", benchmark_push<immer::dvektor<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("dvektor/6B", benchmark_push<immer::dvektor<unsigned,def_memory,6>>())
+
+NONIUS_BENCHMARK("dvektor/GC", benchmark_push<immer::dvektor<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("dvektor/NO", benchmark_push<immer::dvektor<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("dvektor/UN", benchmark_push<immer::dvektor<unsigned,unsafe_memory,5>>())
+#endif
+
+NONIUS_BENCHMARK("array",      benchmark_push<immer::array<unsigned>>())
+
+#if IMMER_BENCHMARK_STEADY
+NONIUS_BENCHMARK("steady",     benchmark_push<steady::vector<unsigned>>())
+#endif
diff --git a/third_party/immer/benchmark/vector/misc/take.cpp b/third_party/immer/benchmark/vector/misc/take.cpp
new file mode 100644
index 0000000000..78f0e958db
--- /dev/null
+++ b/third_party/immer/benchmark/vector/misc/take.cpp
@@ -0,0 +1,65 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/take.hpp"
+
+#include <immer/vector.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/vector_transient.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#if IMMER_BENCHMARK_LIBRRB
+extern "C" {
+#define restrict __restrict__
+#include <rrb.h>
+#undef restrict
+}
+#endif
+
+#if IMMER_BENCHMARK_LIBRRB
+NONIUS_BENCHMARK("librrb", benchmark_take_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("librrb/F", benchmark_take_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("l/librrb", benchmark_take_lin_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("l/librrb/F", benchmark_take_lin_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("t/librrb", benchmark_take_mut_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("t/librrb/F", benchmark_take_mut_librrb(make_librrb_vector_f))
+#endif
+
+NONIUS_BENCHMARK("vector/4B", benchmark_take<immer::vector<unsigned,def_memory,4>>())
+NONIUS_BENCHMARK("vector/5B", benchmark_take<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("vector/6B", benchmark_take<immer::vector<unsigned,def_memory,6>>())
+NONIUS_BENCHMARK("vector/GC", benchmark_take<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("vector/NO", benchmark_take<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("vector/UN", benchmark_take<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("vector_s/GC", benchmark_take<immer::vector<std::size_t,gc_memory,5>>())
+
+NONIUS_BENCHMARK("flex/F/5B", benchmark_take<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GC", benchmark_take<immer::flex_vector<unsigned,gc_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex/F/GCF", benchmark_take<immer::flex_vector<unsigned,gcf_memory,5>, push_front_fn>())
+NONIUS_BENCHMARK("flex_s/F/GC", benchmark_take<immer::flex_vector<std::size_t,gc_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("l/vector/5B", benchmark_take_lin<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("l/vector/GC", benchmark_take_lin<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("l/vector/NO", benchmark_take_lin<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("l/vector/UN", benchmark_take_lin<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("l/flex/F/5B", benchmark_take_lin<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("m/vector/5B", benchmark_take_move<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("m/vector/GC", benchmark_take_move<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("m/vector/NO", benchmark_take_move<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("m/vector/UN", benchmark_take_move<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("m/flex/F/5B", benchmark_take_move<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
+
+NONIUS_BENCHMARK("t/vector/5B", benchmark_take_mut<immer::vector<unsigned,def_memory,5>>())
+NONIUS_BENCHMARK("t/vector/GC", benchmark_take_mut<immer::vector<unsigned,gc_memory,5>>())
+NONIUS_BENCHMARK("t/vector/NO", benchmark_take_mut<immer::vector<unsigned,basic_memory,5>>())
+NONIUS_BENCHMARK("t/vector/UN", benchmark_take_mut<immer::vector<unsigned,unsafe_memory,5>>())
+NONIUS_BENCHMARK("t/flex/F/5B", benchmark_take_mut<immer::flex_vector<unsigned,def_memory,5>, push_front_fn>())
diff --git a/third_party/immer/benchmark/vector/paper/access.cpp b/third_party/immer/benchmark/vector/paper/access.cpp
new file mode 100644
index 0000000000..db677a1adb
--- /dev/null
+++ b/third_party/immer/benchmark/vector/paper/access.cpp
@@ -0,0 +1,31 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/access.hpp"
+#include <immer/flex_vector.hpp>
+#include <chunkedseq/chunkedseq.hpp>
+
+NONIUS_BENCHMARK("idx owrs", benchmark_access_idx<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("idx librrb", benchmark_access_librrb(make_librrb_vector))
+NONIUS_BENCHMARK("idx relaxed owrs", benchmark_access_idx<immer::flex_vector<unsigned,def_memory>,push_front_fn>())
+NONIUS_BENCHMARK("idx relaxed librrb", benchmark_access_librrb(make_librrb_vector_f))
+NONIUS_BENCHMARK("idx std::vector", benchmark_access_idx_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("idx chunkedseq32", benchmark_access_idx_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("idx chunkedseq", benchmark_access_idx_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
+
+NONIUS_BENCHMARK("iter orws", benchmark_access_iter<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("iter relaxed orws", benchmark_access_iter<immer::flex_vector<unsigned,def_memory>,push_front_fn>())
+NONIUS_BENCHMARK("iter chunkedseq32", benchmark_access_iter_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("iter chunkedseq", benchmark_access_iter_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
+
+NONIUS_BENCHMARK("reduce owrs", benchmark_access_reduce<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("reduce relaxed owrs", benchmark_access_reduce<immer::flex_vector<unsigned,def_memory>,push_front_fn>())
+NONIUS_BENCHMARK("reduce chunkedseq32", benchmark_access_reduce_chunkedseq<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("reduce chunkedseq", benchmark_access_reduce_chunkedseq<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
+NONIUS_BENCHMARK("reduce std::vector", benchmark_access_iter_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("reduce std::list", benchmark_access_iter_std<std::list<unsigned>>())
diff --git a/third_party/immer/benchmark/vector/paper/assoc-random.cpp b/third_party/immer/benchmark/vector/paper/assoc-random.cpp
new file mode 100644
index 0000000000..becd4dc0c5
--- /dev/null
+++ b/third_party/immer/benchmark/vector/paper/assoc-random.cpp
@@ -0,0 +1,40 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/assoc.hpp"
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <chunkedseq/chunkedseq.hpp>
+
+NONIUS_BENCHMARK("ours/basic",   benchmark_assoc_random<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("ours/safe",    benchmark_assoc_random<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("ours/unsafe",  benchmark_assoc_random<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("ours/gc",      benchmark_assoc_random<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("librrb",       benchmark_assoc_random_librrb(make_librrb_vector))
+
+NONIUS_BENCHMARK("relaxed ours/basic",   benchmark_assoc_random<immer::flex_vector<unsigned,basic_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/safe",    benchmark_assoc_random<immer::flex_vector<unsigned,def_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/unsafe",  benchmark_assoc_random<immer::flex_vector<unsigned,unsafe_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/gc",      benchmark_assoc_random<immer::flex_vector<unsigned,gc_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed librrb",       benchmark_assoc_random_librrb(make_librrb_vector_f))
+
+NONIUS_BENCHMARK("transient ours/basic",   benchmark_assoc_mut_random<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("transient ours/safe",    benchmark_assoc_mut_random<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("transient ours/unsafe",  benchmark_assoc_mut_random<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("transient ours/gc",      benchmark_assoc_mut_random<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("transient librrb",       benchmark_assoc_mut_random_librrb(make_librrb_vector))
+
+NONIUS_BENCHMARK("transient relaxed ours/basic",   benchmark_assoc_mut_random<immer::flex_vector<unsigned,basic_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/safe",    benchmark_assoc_mut_random<immer::flex_vector<unsigned,def_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/unsafe",  benchmark_assoc_mut_random<immer::flex_vector<unsigned,unsafe_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/gc",      benchmark_assoc_mut_random<immer::flex_vector<unsigned,gc_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed librrb",       benchmark_assoc_mut_random_librrb(make_librrb_vector_f))
+
+NONIUS_BENCHMARK("transient std::vector",  benchmark_assoc_random_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("transient chunkedseq32", benchmark_assoc_random_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
+NONIUS_BENCHMARK("transient chunkedseq",   benchmark_assoc_random_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
diff --git a/third_party/immer/benchmark/vector/paper/assoc.cpp b/third_party/immer/benchmark/vector/paper/assoc.cpp
new file mode 100644
index 0000000000..fe682ce749
--- /dev/null
+++ b/third_party/immer/benchmark/vector/paper/assoc.cpp
@@ -0,0 +1,40 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/assoc.hpp"
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <chunkedseq/chunkedseq.hpp>
+
+NONIUS_BENCHMARK("ours/basic",   benchmark_assoc<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("ours/safe",    benchmark_assoc<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("ours/unsafe",  benchmark_assoc<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("ours/gc",      benchmark_assoc<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("librrb",       benchmark_assoc_librrb(make_librrb_vector))
+
+NONIUS_BENCHMARK("relaxed ours/basic",   benchmark_assoc<immer::flex_vector<unsigned,basic_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/safe",    benchmark_assoc<immer::flex_vector<unsigned,def_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/unsafe",  benchmark_assoc<immer::flex_vector<unsigned,unsafe_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed ours/gc",      benchmark_assoc<immer::flex_vector<unsigned,gc_memory>,push_front_fn>())
+NONIUS_BENCHMARK("relaxed librrb",       benchmark_assoc_librrb(make_librrb_vector_f))
+
+NONIUS_BENCHMARK("transient ours/basic",   benchmark_assoc_mut<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("transient ours/safe",    benchmark_assoc_mut<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("transient ours/unsafe",  benchmark_assoc_mut<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("transient ours/gc",      benchmark_assoc_mut<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("transient librrb",       benchmark_assoc_mut_librrb(make_librrb_vector))
+
+NONIUS_BENCHMARK("transient relaxed ours/basic",   benchmark_assoc_mut<immer::flex_vector<unsigned,basic_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/safe",    benchmark_assoc_mut<immer::flex_vector<unsigned,def_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/unsafe",  benchmark_assoc_mut<immer::flex_vector<unsigned,unsafe_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed ours/gc",      benchmark_assoc_mut<immer::flex_vector<unsigned,gc_memory>,push_back_fn>())
+NONIUS_BENCHMARK("transient relaxed librrb",       benchmark_assoc_mut_librrb(make_librrb_vector_f))
+
+NONIUS_BENCHMARK("transient std::vector",  benchmark_assoc_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("transient chunkedseq32", benchmark_assoc_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("transient chunkedseq",   benchmark_assoc_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
diff --git a/third_party/immer/benchmark/vector/paper/concat.cpp b/third_party/immer/benchmark/vector/paper/concat.cpp
new file mode 100644
index 0000000000..af9b3d20ed
--- /dev/null
+++ b/third_party/immer/benchmark/vector/paper/concat.cpp
@@ -0,0 +1,22 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/concat.hpp"
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <chunkedseq/chunkedseq.hpp>
+
+NONIUS_BENCHMARK("ours/basic",   benchmark_concat_incr<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("ours/safe",    benchmark_concat_incr<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("ours/unsafe",  benchmark_concat_incr<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("ours/gc",      benchmark_concat_incr<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("librrb",       benchmark_concat_incr_librrb(make_librrb_vector))
+
+NONIUS_BENCHMARK("transient ours/gc",      benchmark_concat_incr_mut<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("transient chunkedseq32", benchmark_concat_incr_chunkedseq<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("transient chunkedseq",   benchmark_concat_incr_chunkedseq<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
diff --git a/third_party/immer/benchmark/vector/paper/push.cpp b/third_party/immer/benchmark/vector/paper/push.cpp
new file mode 100644
index 0000000000..7c467985bb
--- /dev/null
+++ b/third_party/immer/benchmark/vector/paper/push.cpp
@@ -0,0 +1,28 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "benchmark/vector/push.hpp"
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <chunkedseq/chunkedseq.hpp>
+
+NONIUS_BENCHMARK("ours/basic",   benchmark_push<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("ours/safe",    benchmark_push<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("ours/unsafe",  benchmark_push<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("ours/gc",      benchmark_push<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("librrb",       benchmark_push_librrb)
+
+NONIUS_BENCHMARK("transient ours/basic",   benchmark_push_mut<immer::flex_vector<unsigned,basic_memory>>())
+NONIUS_BENCHMARK("transient ours/safe",    benchmark_push_mut<immer::flex_vector<unsigned,def_memory>>())
+NONIUS_BENCHMARK("transient ours/unsafe",  benchmark_push_mut<immer::flex_vector<unsigned,unsafe_memory>>())
+NONIUS_BENCHMARK("transient ours/gc",      benchmark_push_mut<immer::flex_vector<unsigned,gc_memory>>())
+NONIUS_BENCHMARK("transient librrb",       benchmark_push_mut_librrb)
+NONIUS_BENCHMARK("transient std::vector",  benchmark_push_mut_std<std::vector<unsigned>>())
+NONIUS_BENCHMARK("transient std::list",    benchmark_push_mut_std<std::list<unsigned>>())
+NONIUS_BENCHMARK("transient chunkedseq32", benchmark_push_mut_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned, 32>>())
+NONIUS_BENCHMARK("transient chunkedseq",   benchmark_push_mut_std<pasl::data::chunkedseq::bootstrapped::deque<unsigned>>())
diff --git a/third_party/immer/benchmark/vector/push.hpp b/third_party/immer/benchmark/vector/push.hpp
new file mode 100644
index 0000000000..83f128c8fc
--- /dev/null
+++ b/third_party/immer/benchmark/vector/push.hpp
@@ -0,0 +1,111 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+template <typename Vektor>
+auto benchmark_push_mut_std()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        measure(meter, [&] {
+            auto v = Vektor{};
+            for (auto i = 0u; i < n; ++i)
+                v.push_back(i);
+            return v;
+        });
+    };
+}
+
+template <typename Vektor>
+auto benchmark_push_mut()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        measure(meter, [&] {
+            auto v = Vektor{}.transient();
+            for (auto i = 0u; i < n; ++i)
+                v.push_back(i);
+            return v;
+        });
+    };
+}
+
+template <typename Vektor>
+auto benchmark_push_move()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        measure(meter, [&] {
+            auto v = Vektor{};
+            for (auto i = 0u; i < n; ++i)
+                v = std::move(v).push_back(i);
+            return v;
+        });
+    };
+}
+
+template <typename Vektor>
+auto benchmark_push()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        if (n > get_limit<Vektor>{})
+            nonius::skip();
+
+        measure(meter, [&] {
+            auto v = Vektor{};
+            for (auto i = 0u; i < n; ++i)
+                v = v.push_back(i);
+            return v;
+        });
+    };
+}
+
+auto benchmark_push_librrb(nonius::chronometer meter)
+{
+    auto n = meter.param<N>();
+
+    measure(meter, [&] {
+        auto v = rrb_create();
+        for (auto i = 0u; i < n; ++i)
+            v = rrb_push(v, reinterpret_cast<void*>(i));
+        return v;
+    });
+}
+
+auto benchmark_push_mut_librrb(nonius::chronometer meter)
+{
+    auto n = meter.param<N>();
+
+    measure(meter, [&] {
+        auto v = rrb_to_transient(rrb_create());
+        for (auto i = 0u; i < n; ++i)
+            v = transient_rrb_push(v, reinterpret_cast<void*>(i));
+        return v;
+    });
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/push_front.hpp b/third_party/immer/benchmark/vector/push_front.hpp
new file mode 100644
index 0000000000..25ccbab0f9
--- /dev/null
+++ b/third_party/immer/benchmark/vector/push_front.hpp
@@ -0,0 +1,46 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+template <typename Vektor>
+auto bechmark_push_front()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        measure(meter, [&] {
+            auto v = Vektor{};
+            for (auto i = 0u; i < n; ++i)
+                v = v.push_front(i);
+            return v;
+        });
+    };
+}
+
+auto benchmark_push_front_librrb(nonius::chronometer meter)
+{
+    auto n = meter.param<N>();
+
+    measure(meter, [&] {
+        auto v = rrb_create();
+        for (auto i = 0u; i < n; ++i) {
+            auto f = rrb_push(rrb_create(),
+                              reinterpret_cast<void*>(i));
+            v = rrb_concat(f, v);
+        }
+        return v;
+    });
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/benchmark/vector/take.hpp b/third_party/immer/benchmark/vector/take.hpp
new file mode 100644
index 0000000000..bce5aceb00
--- /dev/null
+++ b/third_party/immer/benchmark/vector/take.hpp
@@ -0,0 +1,144 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "benchmark/vector/common.hpp"
+
+namespace {
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_take()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            for (auto i = 0u; i < n; ++i)
+                (void) v.take(i);
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_take_lin()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = n; i > 0; --i)
+                r = r.take(i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_take_move()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto v = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            v = PushFn{}(std::move(v), i);
+
+        measure(meter, [&] {
+            auto r = v;
+            for (auto i = n; i > 0; --i)
+                r = std::move(r).take(i);
+            return r;
+        });
+    };
+}
+
+template <typename Vektor,
+          typename PushFn=push_back_fn>
+auto benchmark_take_mut()
+{
+    return [] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+
+        auto vv = Vektor{};
+        for (auto i = 0u; i < n; ++i)
+            vv = PushFn{}(std::move(vv), i);
+
+        measure(meter, [&] {
+            auto v = vv.transient();
+            for (auto i = n; i > 0; --i)
+                (void) v.take(i);
+        });
+    };
+}
+
+template <typename Fn>
+auto benchmark_take_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(meter, [&] {
+                for (auto i = 0u; i < n; ++i)
+                    rrb_slice(v, 0, i);
+            });
+    };
+}
+
+template <typename Fn>
+auto benchmark_take_lin_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(
+            meter, [&] {
+                auto r = v;
+                for (auto i = n; i > 0; --i)
+                    r = rrb_slice(r, 0, i);
+                return r;
+            });
+    };
+}
+
+template <typename Fn>
+auto benchmark_take_mut_librrb(Fn make)
+{
+    return [=] (nonius::chronometer meter)
+    {
+        auto n = meter.param<N>();
+        auto v = make(n);
+        measure(
+            meter, [&] {
+                auto r = rrb_to_transient(v);
+                for (auto i = n; i > 0; --i)
+                    r = transient_rrb_slice(r, 0, i);
+                return r;
+            });
+    };
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/cmake/FindBoehmGC.cmake b/third_party/immer/cmake/FindBoehmGC.cmake
new file mode 100644
index 0000000000..bdb7240e6d
--- /dev/null
+++ b/third_party/immer/cmake/FindBoehmGC.cmake
@@ -0,0 +1,108 @@
+# - Try to find Boehm GC
+#   Once done, this will define
+#
+#   BOEHM_GC_FOUND - system has Boehm GC
+#   BOEHM_GC_INCLUDE_DIR - the Boehm GC include directories
+#   BOEHM_GC_LIBRARIES - link these to use Boehm GC
+# 
+#   Copyright (c) 2010-2015  Takashi Kato <ktakashi@ymail.com>
+# 
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+# 
+#   1. Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+# 
+#   2. Redistributions in binary form must reproduce the above copyright
+#      notice, this list of conditions and the following disclaimer in the
+#      documentation and/or other materials provided with the distribution.
+# 
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+#   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+#   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+#   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+#   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+#  $Id: $
+# 
+
+# CMake module to find Boehm GC
+
+# use pkg-config if available
+FIND_PACKAGE(PkgConfig)
+PKG_CHECK_MODULES(PC_BDW_GC QUIET bdw-gc)
+
+# try newer one first in case of gc.h is overwritten.
+FIND_PATH(BOEHM_GC_INCLUDE_DIR gc/gc.h
+  HINTS ${PC_BDW_GC_INCLUDEDIR} ${PC_BDW_GC_INCLUDE_DIRS})
+
+IF (NOT BOEHM_GC_INCLUDE_DIR)
+  FIND_PATH(BOEHM_GC_INCLUDE_DIR gc.h
+    HINTS ${PC_BDW_GC_INCLUDEDIR} ${PC_BDW_GC_INCLUDE_DIRS})
+  IF (BOEHM_GC_INCLUDE_DIR)
+    SET(HAVE_GC_H TRUE)
+  ENDIF()
+ELSE()
+  SET(HAVE_GC_GC_H TRUE)
+ENDIF()
+
+# For FreeBSD we need to use gc-threaded
+IF (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+  # checks if 'gc' supports 'GC_get_parallel' and if it does
+  # then use it
+  INCLUDE(${CMAKE_ROOT}/Modules/CheckCSourceCompiles.cmake)
+  # not sure if this links properly...
+  FIND_LIBRARY(BOEHM_GC_LIBRARIES NAMES gc
+    HINTS ${PC_BDW_GC_LIBDIR} ${PC_BDW_GC_LIBRARY_DIRS})
+  MESSAGE(STATUS "GC library ${BOEHM_GC_LIBRARIES}")
+  SET(CMAKE_REQUIRED_LIBRARIES "gc")
+  SET(CMAKE_REQUIRED_DEFINITIONS "-DGC_THREADS")
+  SET(CMAKE_REQUIRED_INCLUDES "${BOEHM_GC_INCLUDE_DIR}")
+  SET(CMAKE_REQUIRED_FLAGS "-L${PC_BDW_GC_LIBRARY_DIRS}")
+  MESSAGE(STATUS "Boehm GC include dir: ${CMAKE_REQUIRED_INCLUDES}")
+  CHECK_C_SOURCE_RUNS(
+    "#include <gc.h>
+int main() {
+int i= GC_get_parallel();
+return 0;
+}
+" GC_GET_PARALLEL_WORKS)
+  IF (NOT GC_GET_PARALLEL_WORKS)
+    MESSAGE(STATUS "Try gc-threaded")
+
+    # bdw-gc-threaded is the proper name for FreeBSD pkg-config
+    PKG_CHECK_MODULES(PC_BDW_GC_THREADED bdw-gc-threaded)
+    FIND_LIBRARY(BOEHM_GC_THREADED_LIBRARIES NAMES gc-threaded
+      HINTS ${PC_BDW_GC_THREADED_LIBDIR} ${PC_BDW_GC_THREADED_THREADED_DIRS})
+
+    MESSAGE(STATUS "GC threaded library ${BOEHM_GC_THREADED_LIBRARIES}")
+    IF (BOEHM_GC_THREADED_LIBRARIES)
+      # OK just use it
+      SET(BOEHM_GC_LIBRARIES "${BOEHM_GC_THREADED_LIBRARIES}")
+    ENDIF()
+  ENDIF()
+ELSE()
+  FIND_LIBRARY(BOEHM_GC_LIBRARIES NAMES gc
+    HINTS ${PC_BDW_GC_LIBDIR} ${PC_BDW_GC_LIBRARY_DIRS})
+  # OpenSolaris uses bgc as Boehm GC runtime in its package manager.
+  # so try it
+  IF (NOT BOEHM_GC_LIBRARIES)
+    FIND_LIBRARY(BOEHM_GC_LIBRARIES NAMES bgc
+      HINTS ${PC_BDW_GC_LIBDIR} ${PC_BDW_GC_LIBRARY_DIRS})
+  ENDIF()
+ENDIF()
+
+MESSAGE(STATUS "Found GC library: ${BOEHM_GC_LIBRARIES}")
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Boehm_GC DEFAULT_MSG
+                                  BOEHM_GC_LIBRARIES BOEHM_GC_INCLUDE_DIR)
+
+MARK_AS_ADVANCED(BOEHM_GC_LIBRARIES BOEHM_GC_INCLUDE_DIR)
diff --git a/third_party/immer/cmake/FindGuile.cmake b/third_party/immer/cmake/FindGuile.cmake
new file mode 100644
index 0000000000..2b8781c48f
--- /dev/null
+++ b/third_party/immer/cmake/FindGuile.cmake
@@ -0,0 +1,326 @@
+#[[.rst
+#
+# FindGuile
+# ---------
+# Find the development libraries for Guile.
+#
+# Exported Vars
+# ~~~~~~~~~~~~~
+#
+# .. variable:: Guile_FOUND
+#
+#    Set to *true* if Guile was found.
+#
+# .. variable:: Guile_INCLUDE_DIRS
+#
+#    A list of include directories.
+#
+# .. variable:: Guile_LIBRARIES
+#
+#    A list of libraries needed to build you project.
+#
+# .. variable:: Guile_VERSION_STRING
+#
+#    Guile full version.
+#
+# .. variable:: Guile_VERSION_MAJOR
+#
+#    Guile major version.
+#
+# .. variable:: Guile_VERSION_MINOR
+#
+#    Guile minor version.
+#
+# .. variable:: Guile_VERSION_PATCH
+#
+#    Guile patch version.
+#
+# .. variable:: Guile_EXECUTABLE
+#
+#    Guile executable (optional).
+#
+# .. variable:: Guile_CONFIG_EXECUTABLE
+#
+#    Guile configuration executable (optional).
+#
+# .. variable:: Guile_ROOT_DIR
+#
+#    Guile installation root dir (optional).
+#
+# .. variable:: Guile_SITE_DIR
+#
+#    Guile installation module site dir (optional).
+#
+# .. variable:: Guile_EXTENSION_DIR
+#
+#    Guile installation extension dir (optional).
+#
+# Control VARS
+# ~~~~~~~~~~~~
+# :envvar:`Guile_ROOT_DIR`
+#
+#   Use this variable to provide hints to :filename:`find_{*}` commands,
+#   you may pass it to :command:`cmake` or set the environtment variable.
+#
+# .. code-block:: cmake
+#
+#    % cmake . -Bbuild -DGuile_ROOT_DIR=<extra-path>
+#
+#    # or
+#    % export Guile_ROOT_DIR=<extra-path>;
+#    % cmake . -Bbuild
+#
+#    # or
+#    % Guile_ROOT_DIR=<extra-path> cmake . -Bbuild
+#
+#
+#]]
+
+
+#[[.rst
+#
+# Copyright © 2016, Edelcides Gonçalves <eatg75 |0x40| gmail>
+#
+# Permission to use, copy, modify, and/or distribute this software for
+# any purpose with or without fee is hereby granted, provided that the
+# above copyright notice and this permission notice appear in all
+# copies.
+#
+# *THE SOFTWARE IS PROVIDED* **AS IS** *AND ISC DISCLAIMS ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE
+# LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE*.
+#
+# This file is not part of CMake
+#
+#]]
+
+
+include (SelectLibraryConfigurations)
+include (FindPackageHandleStandardArgs)
+
+function (_guile_find_component_include_dir component header)
+  find_path ("${component}_INCLUDE_DIR"
+    ${header}
+    HINTS
+    "${GUile_ROOT_DIR}"
+    ENV Guile_ROOT_DIR
+    PATH_SUFFIXES
+    Guile guile Guile-2.0 guile-2.0 Guile/2.0 guile/2.0 GC
+    gc)
+
+  set ("${component}_INCLUDE_DIR" "${${component}_INCLUDE_DIR}"
+    PARENT_SCOPE)
+endfunction ()
+
+function (_guile_find_component_library component_name component)
+
+  find_library ("${component_name}_LIBRARY_RELEASE"
+    NAMES "${component}" "${component}-2.0"
+    HINTS
+    "${GUile_ROOT_DIR}"
+    ENV Guile_ROOT_DIR
+    PATHS
+    /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
+    /usr/lib64/${CMAKE_LIBRARY_ARCHITECTURE}
+    /usr/lib32/${CMAKE_LIBRARY_ARCHITECTURE})
+
+  if  (${component_name}_LIBRARY_RELEASE)
+    select_library_configurations (${component_name})
+    set ("${component_name}_LIBRARY_RELEASE"
+      "${${component_name}_LIBRARY_RELEASE}" PARENT_SCOPE)
+    set ("${component_name}_LIBRARY"
+      "${${component_name}_LIBRARY}" PARENT_SCOPE)
+    set ("${component_name}_LIBRARIES"
+      "${${component_name}_LIBRARIES}" PARENT_SCOPE)
+  endif ()
+endfunction ()
+
+function (_guile_find_version_2 header_file macro_name)
+  file (STRINGS "${header_file}" _VERSION
+    REGEX "#define[\t ]+${macro_name}[\t ]+[0-9]+")
+
+  if (_VERSION)
+    string (REGEX REPLACE
+      ".*#define[\t ]+${macro_name}[\t ]+([0-9]+).*"
+      "\\1" _VERSION_VALUE "${_VERSION}")
+    if ("${_VERSION}" STREQUAL "${_VERSION_VALUE}")
+      set (_VERSION_FOUND 0 PARENT_SCOPE)
+    else ()
+      set (_VERSION_FOUND 1 PARENT_SCOPE)
+      set (_VERSION "${_VERSION_VALUE}" PARENT_SCOPE)
+    endif ()
+  else ()
+    set (_VERSION_FOUND 0 PARENT_SCOPE)
+  endif ()
+endfunction ()
+
+
+##### Entry Point #####
+
+set (Guile_FOUND)
+set (Guile_INCLUDE_DIRS)
+set (Guile_LIBRARIES)
+set (Guile_VERSION_STRING)
+set (Guile_VERSION_MAJOR)
+set (Guile_VERSION_MINOR)
+set (Guile_VERSION_PATCH)
+set (Guile_EXECUTABLE)
+
+_guile_find_component_include_dir (Guile "libguile.h")
+if (Guile_INCLUDE_DIR)
+  _guile_find_version_2 ("${Guile_INCLUDE_DIR}/libguile/version.h"
+    SCM_MAJOR_VERSION)
+  if (_VERSION_FOUND)
+    set (Guile_VERSION_MAJOR "${_VERSION}")
+  else ()
+    message (FATAL_ERROR "FindGuile: Failed to find Guile_MAJOR_VERSION.")
+  endif ()
+
+  _guile_find_version_2 ("${Guile_INCLUDE_DIR}/libguile/version.h"
+    SCM_MINOR_VERSION)
+  if (_VERSION_FOUND)
+    set (Guile_VERSION_MINOR "${_VERSION}")
+  else ()
+    message (FATAL_ERROR "FindGuile: Failed to find Guile_MINOR_VERSION.")
+  endif ()
+
+  _guile_find_version_2 ("${Guile_INCLUDE_DIR}/libguile/version.h"
+    SCM_MICRO_VERSION)
+  if (_VERSION_FOUND)
+    set (Guile_VERSION_PATCH "${_VERSION}")
+  else ()
+    message (FATAL_ERROR "FindGuile: Failed to find Guile_MICRO_VERSION.")
+  endif ()
+  set (Guile_VERSION_STRING "${Guile_VERSION_MAJOR}.${Guile_VERSION_MINOR}.${Guile_VERSION_PATCH}")
+
+  unset (_VERSION_FOUND)
+  unset (_VERSION)
+endif ()
+
+_guile_find_component_include_dir (Guile_GC "gc.h")
+_guile_find_component_library (Guile guile)
+_guile_find_component_library (Guile_GC gc)
+
+find_program (Guile_EXECUTABLE
+  guile
+  DOC "Guile executable.")
+
+if (Guile_EXECUTABLE)
+  execute_process (COMMAND ${Guile_EXECUTABLE} --version
+    RESULT_VARIABLE _status
+    OUTPUT_VARIABLE _output
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  string (REGEX REPLACE ".*\\(GNU Guile\\)[\t ]+([0-9]+)\\..*" "\\1"
+    _guile_ver_major "${_output}")
+
+  string (REGEX REPLACE ".*\\(GNU Guile\\)[\t ]+[0-9]+\\.([0-9]+).*" "\\1"
+    _guile_ver_minor "${_output}")
+
+  string (REGEX REPLACE ".*\\(GNU Guile\\)[\t ]+[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1"
+    _guile_ver_patch "${_output}")
+
+  set (_version "${_guile_ver_major}.${_guile_ver_minor}.${_guile_ver_patch}")
+
+  if (NOT Guile_FIND_QUIETLY)
+    if (NOT Guile_VERSION_STRING STREQUAL _version)
+      message (WARNING "FindGuile: Versions provided by library differs from the one provided by executable.")
+    endif ()
+
+    if (NOT _status STREQUAL "0")
+      message (WARNING "FindGuile: guile (1) process exited abnormally.")
+    endif ()
+  endif ()
+
+  unset (_version)
+  unset (_status)
+  unset (_version)
+  unset (_guile_ver_major)
+  unset (_guile_ver_minor)
+  unset (_guile_ver_patch)
+endif ()
+
+find_package_handle_standard_args (GC
+  "FindGuile: Failed to find dependency GC."
+  Guile_GC_INCLUDE_DIR
+  Guile_GC_LIBRARY
+  Guile_GC_LIBRARIES
+  Guile_GC_LIBRARY_RELEASE)
+
+find_package_handle_standard_args (Guile
+  REQUIRED_VARS
+  Guile_INCLUDE_DIR
+  Guile_LIBRARY
+  Guile_LIBRARIES
+  Guile_LIBRARY_RELEASE
+  GC_FOUND
+  VERSION_VAR Guile_VERSION_STRING)
+
+if (Guile_FOUND)
+  list (APPEND Guile_INCLUDE_DIRS "${Guile_INCLUDE_DIR}"
+    "${Guile_GC_INCLUDE_DIR}")
+
+  if (NOT TARGET Guile::Library AND NOT TARGET GC::Library)
+    add_library (Guile::GC::Library UNKNOWN IMPORTED)
+    set_property (TARGET Guile::GC::Library APPEND
+      PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+
+    set_target_properties (Guile::GC::Library
+      PROPERTIES
+      INTERFACE_INCLUDE_DIRS
+      "${Guile_GC_INCLUDE_DIR}"
+      IMPORTED_LOCATION
+      "${Guile_GC_LIBRARY}"
+      IMPORTED_LOCATION_RELEASE
+      "${Guile_GC_LIBRARY_RELEASE}")
+
+    add_library (Guile::Library UNKNOWN IMPORTED)
+    set_property (TARGET Guile::Library APPEND
+      PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+    set_property (TARGET Guile::Library APPEND
+      PROPERTY
+      INTERFACE_LINK_LIBRARIES
+      Guile::GC::Library)
+
+    set_target_properties (Guile::Library
+      PROPERTIES
+      INTERFACE_INCLUDE_DIRSr
+      "${Guile_INCLUDE_DIR}"
+      IMPORTED_LOCATION "${Guile_LIBRARY}"
+      IMPORTED_LOCATION_RELEASE
+      "${Guile_LIBRARY_RELEASE}")
+
+    set (Guile_LIBRARIES Guile::Library Guile::GC::Library)
+  endif ()
+endif ()
+
+find_program(Guile_CONFIG_EXECUTABLE
+  NAMES guile-config
+  DOC "Guile configutration binary")
+
+if (Guile_CONFIG_EXECUTABLE)
+  execute_process (COMMAND ${Guile_CONFIG_EXECUTABLE} info prefix
+    OUTPUT_VARIABLE Guile_ROOT_DIR
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  execute_process (COMMAND ${Guile_CONFIG_EXECUTABLE} info sitedir
+    OUTPUT_VARIABLE Guile_SITE_DIR
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  execute_process (COMMAND ${Guile_CONFIG_EXECUTABLE} info extensiondir
+    OUTPUT_VARIABLE Guile_EXTENSION_DIR
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+endif ()
+
+mark_as_advanced (Guile_EXECUTABLE
+  Guile_INCLUDE_DIR
+  Guile_LIBRARY
+  Guile_LIBRARY_RELEASE
+  Guile_GC_INCLUDE_DIR
+  Guile_GC_LIBRARY
+  Guile_GC_LIBRARY_RELEASE)
diff --git a/third_party/immer/cmake/FindRRB.cmake b/third_party/immer/cmake/FindRRB.cmake
new file mode 100644
index 0000000000..86b552f86e
--- /dev/null
+++ b/third_party/immer/cmake/FindRRB.cmake
@@ -0,0 +1,15 @@
+# - Try to find c-rrb
+# Once done, this will define
+#
+#  RRB_FOUND - system has RRB
+#  RRB_INCLUDE_DIR - the RRB include directories
+#  RRB_LIBRARIES - link these to use RRB
+
+find_path(RRB_INCLUDE_DIR rrb.h)
+find_library(RRB_LIBRARIES NAMES rrb librrb)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(
+  RRB DEFAULT_MSG RRB_LIBRARIES RRB_INCLUDE_DIR)
+
+mark_as_advanced(RRB_INCLUDE_DIR RRB_LIBRARIES)
diff --git a/third_party/immer/cmake/ImmerUtils.cmake b/third_party/immer/cmake/ImmerUtils.cmake
new file mode 100644
index 0000000000..2af63ff606
--- /dev/null
+++ b/third_party/immer/cmake/ImmerUtils.cmake
@@ -0,0 +1,24 @@
+
+function(immer_target_name_for out_target out_file file)
+  get_filename_component(_extension ${_file} EXT)
+
+  file(RELATIVE_PATH _relative ${PROJECT_SOURCE_DIR} ${file})
+  string(REPLACE "${_extension}" "" _name ${_relative})
+  string(REGEX REPLACE "/" "-" _name ${_name})
+  set(${out_target} "${_name}" PARENT_SCOPE)
+
+  file(RELATIVE_PATH _relative ${CMAKE_CURRENT_LIST_DIR} ${file})
+  string(REPLACE "${_extension}" "" _name ${_relative})
+  string(REGEX REPLACE "/" "-" _name ${_name})
+  set(${out_file} "${_name}" PARENT_SCOPE)
+endfunction()
+
+function(immer_canonicalize_cmake_booleans)
+  foreach(var ${ARGN})
+    if(${var})
+      set(${var} 1 PARENT_SCOPE)
+    else()
+      set(${var} 0 PARENT_SCOPE)
+    endif()
+  endforeach()
+endfunction()
diff --git a/third_party/immer/codecov.yml b/third_party/immer/codecov.yml
new file mode 100644
index 0000000000..ab21ebea5d
--- /dev/null
+++ b/third_party/immer/codecov.yml
@@ -0,0 +1,2 @@
+ignore:
+  - tools
diff --git a/third_party/immer/default.nix b/third_party/immer/default.nix
new file mode 100644
index 0000000000..2eb7dbb1f8
--- /dev/null
+++ b/third_party/immer/default.nix
@@ -0,0 +1,14 @@
+with import <nixpkgs> {};
+
+stdenv.mkDerivation rec {
+  name = "immer-git";
+  version = "git";
+  src = fetchGit ./.;
+  nativeBuildInputs = [ cmake ];
+  dontBuild = true;
+  meta = with stdenv.lib; {
+    homepage    = "https://github.com/arximboldi/immer";
+    description = "Postmodern immutable data structures for C++";
+    license     = licenses.boost;
+  };
+}
diff --git a/third_party/immer/doc/CMakeLists.txt b/third_party/immer/doc/CMakeLists.txt
new file mode 100644
index 0000000000..0b3d85c6ac
--- /dev/null
+++ b/third_party/immer/doc/CMakeLists.txt
@@ -0,0 +1,23 @@
+
+#  Targets
+#  =======
+
+add_custom_target(doxygen
+  COMMAND doxygen doxygen.config
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/doc")
+
+add_custom_target(docs
+  COMMAND make html
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/doc")
+add_dependencies(docs doxygen)
+
+set(immer_ssh_method
+  ssh -p 5488
+      -o StrictHostKeyChecking=no
+      -i ${CMAKE_SOURCE_DIR}/tools/travis/ssh-key)
+
+add_custom_target(upload-docs
+  COMMAND
+  rsync -av -e \"${immer_ssh_method}\"
+        ${CMAKE_SOURCE_DIR}/doc/_build/html/*
+        raskolnikov@sinusoid.es:public/immer/)
diff --git a/third_party/immer/doc/Makefile b/third_party/immer/doc/Makefile
new file mode 100644
index 0000000000..101addd4ef
--- /dev/null
+++ b/third_party/immer/doc/Makefile
@@ -0,0 +1,226 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  applehelp  to make an Apple Help Book"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  epub3      to make an epub3"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  xml        to make Docutils-native XML files"
+	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+	@echo "  coverage   to run coverage check of the documentation (if enabled)"
+	@echo "  dummy      to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+	rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	bash sphinx-html-hack.bash
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/immer.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/immer.qhc"
+
+.PHONY: applehelp
+applehelp:
+	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+	@echo
+	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+	@echo "N.B. You won't be able to view it unless you put it in" \
+	      "~/Library/Documentation/Help or install it in your application" \
+	      "bundle."
+
+.PHONY: devhelp
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/immer"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/immer"
+	@echo "# devhelp"
+
+.PHONY: epub
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+	$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+	@echo
+	@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through platex and dvipdfmx..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+	@echo "Testing of coverage in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+	@echo
+	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+	$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+	@echo
+	@echo "Build finished. Dummy builder generates no files."
diff --git a/third_party/immer/doc/_static/logo-black.svg b/third_party/immer/doc/_static/logo-black.svg
new file mode 100644
index 0000000000..370e047c4b
--- /dev/null
+++ b/third_party/immer/doc/_static/logo-black.svg
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="437.76035"
+   height="141.89464"
+   viewBox="0 0 115.8241 37.542956"
+   version="1.1"
+   id="svg5447"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="logo-black.svg">
+  <defs
+     id="defs5441" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#000000"
+     bordercolor="#8b8384"
+     borderopacity="1"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="335.15011"
+     inkscape:cy="98.353481"
+     inkscape:document-units="mm"
+     inkscape:current-layer="g4502"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1152"
+     inkscape:window-height="1080"
+     inkscape:window-x="1920"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     units="px" />
+  <metadata
+     id="metadata5444">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(6.8182189,-70.341619)">
+    <g
+       id="g4502"
+       transform="matrix(0.45286986,0,0,0.45286986,18.570763,48.756461)">
+      <g
+         id="g5321"
+         transform="matrix(0.28222223,0,0,0.28222223,263.62161,572.00923)"
+         style="fill:none;stroke:#ffffff">
+        <g
+           style="fill:none;stroke:#ffffff;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
+           transform="translate(303.46093,-4.9296877)"
+           id="g5317">
+          <path
+             id="rect5315"
+             style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+             d="m -1334.1996,-1796.9898 h 179.7406 v 179.7405 h -179.7406 z m -92,0 h 179.7406 v 179.7405 h -179.7406 z m 46,48 h 179.7406 v 179.7405 h -179.7406 z m 0,-94 h 179.7406 v 179.7405 h -179.7406 z m 0,48 h 179.7406 v 179.7405 h -179.7406 z"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:3.62277412px;line-height:0%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Heavy';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.30189785px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="43.067963"
+         y="112.13454"
+         id="text5338"><tspan
+           sodipodi:role="line"
+           id="tspan5336"
+           x="43.067963"
+           y="112.13454"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:53.88218689px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Bold';fill:#ffffff;stroke-width:0.30189785px">immer</tspan></text>
+      <rect
+         ry="0"
+         y="66.38237"
+         x="-37.756119"
+         height="7.9375"
+         width="7.9375"
+         id="rect5425"
+         style="opacity:1;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:3.35253906;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/third_party/immer/doc/_static/logo-front.svg b/third_party/immer/doc/_static/logo-front.svg
new file mode 100644
index 0000000000..b56393b9b1
--- /dev/null
+++ b/third_party/immer/doc/_static/logo-front.svg
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1037.7604"
+   height="441.89462"
+   viewBox="0 0 274.57411 116.91795"
+   version="1.1"
+   id="svg5447"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="logo-front.svg">
+  <defs
+     id="defs5441" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="172.29297"
+     inkscape:cy="385.49612"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="150"
+     fit-margin-left="300"
+     fit-margin-right="300"
+     fit-margin-bottom="150"
+     inkscape:window-width="1440"
+     inkscape:window-height="1080"
+     inkscape:window-x="1920"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     units="px" />
+  <metadata
+     id="metadata5444">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(86.193219,-30.654119)">
+    <g
+       id="g4502"
+       transform="matrix(0.45286986,0,0,0.45286986,18.570763,48.756461)">
+      <g
+         id="g5321"
+         transform="matrix(0.28222223,0,0,0.28222223,263.62161,572.00923)">
+        <g
+           style="stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
+           transform="translate(303.46093,-4.9296877)"
+           id="g5317">
+          <path
+             id="rect5315"
+             style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+             d="m -1334.1996,-1796.9898 h 179.7406 v 179.7405 h -179.7406 z m -92,0 h 179.7406 v 179.7405 h -179.7406 z m 46,48 h 179.7406 v 179.7405 h -179.7406 z m 0,-94 h 179.7406 v 179.7405 h -179.7406 z m 0,48 h 179.7406 v 179.7405 h -179.7406 z"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:3.62277412px;line-height:0%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Heavy';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.30189785px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="43.067963"
+         y="112.13454"
+         id="text5338"><tspan
+           sodipodi:role="line"
+           id="tspan5336"
+           x="43.067963"
+           y="112.13454"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:53.88218689px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Bold';stroke-width:0.30189785px">immer</tspan></text>
+      <rect
+         ry="0"
+         y="66.38237"
+         x="-37.756119"
+         height="7.9375"
+         width="7.9375"
+         id="rect5425"
+         style="opacity:1;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:3.35253906;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/third_party/immer/doc/_static/logo.svg b/third_party/immer/doc/_static/logo.svg
new file mode 100644
index 0000000000..64bff0dde8
--- /dev/null
+++ b/third_party/immer/doc/_static/logo.svg
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="437.76035"
+   height="141.89464"
+   viewBox="0 0 115.8241 37.542956"
+   version="1.1"
+   id="svg5447"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="logo.svg">
+  <defs
+     id="defs5441" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="-127.70703"
+     inkscape:cy="235.49612"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1440"
+     inkscape:window-height="1080"
+     inkscape:window-x="1920"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     units="px" />
+  <metadata
+     id="metadata5444">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(6.8182189,-70.341619)">
+    <g
+       id="g4502"
+       transform="matrix(0.45286986,0,0,0.45286986,18.570763,48.756461)">
+      <g
+         id="g5321"
+         transform="matrix(0.28222223,0,0,0.28222223,263.62161,572.00923)">
+        <g
+           style="stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
+           transform="translate(303.46093,-4.9296877)"
+           id="g5317">
+          <path
+             id="rect5315"
+             style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+             d="m -1334.1996,-1796.9898 h 179.7406 v 179.7405 h -179.7406 z m -92,0 h 179.7406 v 179.7405 h -179.7406 z m 46,48 h 179.7406 v 179.7405 h -179.7406 z m 0,-94 h 179.7406 v 179.7405 h -179.7406 z m 0,48 h 179.7406 v 179.7405 h -179.7406 z"
+             inkscape:connector-curvature="0" />
+        </g>
+      </g>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:3.62277412px;line-height:0%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Heavy';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.30189785px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="43.067963"
+         y="112.13454"
+         id="text5338"><tspan
+           sodipodi:role="line"
+           id="tspan5336"
+           x="43.067963"
+           y="112.13454"
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:53.88218689px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Bold';stroke-width:0.30189785px">immer</tspan></text>
+      <rect
+         ry="0"
+         y="66.38237"
+         x="-37.756119"
+         height="7.9375"
+         width="7.9375"
+         id="rect5425"
+         style="opacity:1;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:3.35253906;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/third_party/immer/doc/_static/patreon.svg b/third_party/immer/doc/_static/patreon.svg
new file mode 100644
index 0000000000..a8cc33975b
--- /dev/null
+++ b/third_party/immer/doc/_static/patreon.svg
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   viewBox="0 0 175.20599 88.309158"
+   xml:space="preserve"
+   sodipodi:docname="patreon.svg"
+   inkscape:version="0.92.2pre0 (973e216, 2017-07-25)"
+   width="175.20599"
+   height="88.309158"><metadata
+     id="metadata53"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>support</dc:title></cc:Work></rdf:RDF></metadata><defs
+     id="defs51" /><sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1267"
+     inkscape:window-height="1054"
+     id="namedview49"
+     showgrid="false"
+     inkscape:zoom="1.672"
+     inkscape:cx="-2.5899259"
+     inkscape:cy="100.34184"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="Layer_1"
+     fit-margin-left="16"
+     fit-margin-right="16"
+     fit-margin-bottom="16"
+     fit-margin-top="16"
+     showborder="true" /><style
+     type="text/css"
+     id="style2">
+	.st0{fill:#E64825;}
+	.st1{fill:#FFFFFF;}
+	.st2{enable-background:new    ;}
+</style><title
+     id="title4">support</title><g
+     id="g4591"
+     transform="matrix(1.1623322,0,0,1.1623322,4.376678,-18.86997)"><rect
+       ry="5.3968496"
+       y="30.000004"
+       x="10"
+       height="48.444977"
+       width="123.20574"
+       id="rect4576"
+       style="opacity:1;vector-effect:none;fill:#f96854;fill-opacity:1;stroke:none;stroke-width:1.95915079;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><g
+       transform="translate(9.401915,29.145936)"
+       id="g4574"><g
+         id="g4557"
+         transform="translate(-120.21531,7.9502949)"><path
+           id="path8"
+           d="m 143.8,12.9 h 0.7 c 6.4,0.2 11.5,5.6 11.3,12 -0.2,6.2 -5.1,11.1 -11.3,11.3 h -5.7 c 0,-3.9 0,-7.8 0,-11.7 0.1,-2.9 2.5,-5.3 5.4,-5.2 2.9,0.1 5.3,2.5 5.2,5.4 -0.1,2.9 -2.5,5.3 -5.4,5.2 -0.8,0 -1.7,-0.2 -2.4,-0.6 0,1.3 0,2.5 0,3.8 1.7,0.5 3.6,0.5 5.3,-0.1 4.7,-1.5 7.2,-6.6 5.6,-11.2 -1.5,-4.7 -6.6,-7.2 -11.2,-5.6 -3.6,1.2 -6,4.5 -6.1,8.3 0,3.9 0,7.8 0,11.7 h -2.8 v -12 c 0.1,-4 2.3,-7.6 5.6,-9.6 1.9,-1.1 3.8,-1.7 5.8,-1.7 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path10"
+           d="m 186.5,13.9 h 1.9 c 0,1.4 0,2.9 0,4.3 h 2.8 c 0,0.6 0,1.3 0,1.9 h -2.8 c 0,3.1 0,6.2 0,9.3 h -1.9 c 0,-3.1 0,-6.2 0,-9.3 h -1.1 c 0,-0.6 0,-1.3 0,-1.9 h 1.1 c 0,-1.4 -0.1,-2.9 0,-4.3 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path12"
+           d="m 177.5,17.8 c 3.3,-0.7 6.5,1.5 7.2,4.8 0,0.1 0,0.2 0,0.2 0.1,0.6 0.1,1.2 0.1,1.8 0,1.6 0,3.2 0,4.8 h -1.9 c 0,-0.4 0,-0.8 0,-1.2 -2.4,2.3 -6.3,2.2 -8.6,-0.2 -2.3,-2.4 -2.2,-6.3 0.2,-8.6 0.8,-0.9 1.9,-1.4 3,-1.6 z m 0.8,1.7 c -2.3,0.2 -4,2.3 -3.8,4.6 0,0.3 0.1,0.5 0.2,0.8 0.2,0.8 0.7,1.5 1.3,2 0.7,0.6 1.6,1 2.6,1 0.8,0 1.6,-0.1 2.3,-0.5 2,-1.2 2.7,-3.7 1.6,-5.8 -1,-1.4 -2.5,-2.2 -4.2,-2.1 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path14"
+           d="m 203.3,17.7 c 2,-0.3 4,0.5 5.3,1.9 l -7.9,6.6 c 1.4,1.9 4,2.3 5.9,0.9 1.3,-0.9 1.9,-2.5 1.7,-4 h 1.9 c 0.1,1.3 -0.2,2.6 -0.9,3.8 -0.5,0.9 -1.2,1.6 -2.1,2.1 -2.9,1.7 -6.6,0.8 -8.4,-2.1 -0.7,-1.1 -1,-2.4 -0.9,-3.6 0.1,-1 0.4,-2 1,-2.8 1.1,-1.5 2.6,-2.5 4.4,-2.8 z m -2.8,3.8 c -0.6,0.9 -0.8,2 -0.6,3 l 5.6,-4.7 c -0.3,-0.1 -0.7,-0.2 -1,-0.2 -1.6,-0.2 -3.1,0.6 -4,1.9 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path16"
+           d="m 216.6,17.7 c 1.3,-0.1 2.6,0.2 3.6,0.9 2.9,1.8 3.7,5.5 1.9,8.4 -1.8,2.9 -5.5,3.7 -8.4,1.9 -2.9,-1.8 -3.7,-5.5 -1.9,-8.4 0.5,-0.8 1.2,-1.5 2,-2 0.9,-0.5 1.8,-0.8 2.8,-0.8 z m 0.1,1.8 c -2.3,0.2 -4.1,2.2 -3.9,4.5 0.2,2.3 2.2,4.1 4.5,3.9 2.3,-0.2 4.1,-2.2 3.9,-4.5 -0.1,-1.2 -0.7,-2.4 -1.7,-3.1 -0.8,-0.6 -1.8,-0.8 -2.8,-0.8 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path18"
+           d="m 226,18.9 c 1.2,-1.1 2.8,-1.5 4.3,-1.1 1.2,0.3 2.2,1 2.9,1.9 0.6,0.8 1,1.8 1,2.8 0,1.5 0,3 0,4.6 v 2.2 h -1.9 c 0,-2.2 0,-4.5 0,-6.7 0,-1.1 -0.6,-2.1 -1.5,-2.6 -0.8,-0.5 -1.8,-0.6 -2.7,-0.3 -1.1,0.4 -1.9,1.5 -2.1,2.7 0,0.5 0,1 0,1.6 0,1.8 0,3.6 0,5.4 h -1.9 c 0,-3.7 0,-7.4 0,-11.1 h 1.9 c 0,0.1 0,0.3 0,0.6 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path20"
+           d="m 161.1,20 c 2.1,-2.6 5.9,-3.1 8.6,-1 2.6,2.1 3.1,5.9 1,8.6 -1.1,1.3 -2.6,2.2 -4.3,2.3 -1,0.1 -2,-0.1 -2.8,-0.6 -0.7,-0.3 -1.3,-0.8 -1.8,-1.3 0,1.9 0,3.8 0,5.7 h -1.9 c 0,-3.3 0,-6.6 0,-9.9 -0.1,-1.4 0.4,-2.7 1.2,-3.8 z m 4,-0.4 c -2.3,0.4 -3.8,2.6 -3.4,4.9 0.4,2.3 2.6,3.8 4.9,3.4 2.3,-0.4 3.8,-2.6 3.4,-4.9 -0.2,-1.2 -1,-2.3 -2,-2.9 -0.9,-0.5 -1.9,-0.7 -2.9,-0.5 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /><path
+           id="path22"
+           d="m 192,18.2 h 1.8 c 0,0.6 0,1.1 0,1.7 1.2,-1.1 2.8,-1.8 4.4,-1.7 0,0.6 0,1.3 0,1.9 -2.3,-0.1 -4.3,1.7 -4.4,4.1 0,0 0,0.1 0,0.1 0,1.7 0,3.4 0,5.1 H 192 c 0.1,-3.8 0,-7.5 0,-11.2 z"
+           class="st1"
+           inkscape:connector-curvature="0"
+           style="fill:#ffffff" /></g><text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.8520174px;line-height:4.5em;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro';text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.73991024"
+         x="114.89897"
+         y="14.870183"
+         id="text4561"><tspan
+           sodipodi:role="line"
+           id="tspan4559"
+           x="114.89897"
+           y="14.870183"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro';fill:#ffffff;stroke-width:0.73991024">support us on</tspan></text>
+</g></g></svg>
\ No newline at end of file
diff --git a/third_party/immer/doc/_static/sinusoidal-badge.svg b/third_party/immer/doc/_static/sinusoidal-badge.svg
new file mode 100644
index 0000000000..0282a2f7a4
--- /dev/null
+++ b/third_party/immer/doc/_static/sinusoidal-badge.svg
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="350"
+   height="20"
+   version="1.1"
+   id="svg28"
+   sodipodi:docname="sinusoidal-badge.svg"
+   inkscape:version="0.92.2pre0 (973e216, 2017-07-25)">
+  <metadata
+     id="metadata34">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs32">
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="Arrow1Lstart"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart">
+      <path
+         inkscape:connector-curvature="0"
+         transform="matrix(0.8,0,0,0.8,10,0)"
+         style="fill:#444444;fill-opacity:1;fill-rule:evenodd;stroke:#444444;stroke-width:1.00000003pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path10186" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1267"
+     inkscape:window-height="1054"
+     id="namedview30"
+     showgrid="false"
+     inkscape:zoom="1"
+     inkscape:cx="89.411561"
+     inkscape:cy="-177.54677"
+     inkscape:window-x="0"
+     inkscape:window-y="26"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="g26" />
+  <linearGradient
+     id="b"
+     x2="0"
+     y2="1">
+    <stop
+       offset="0"
+       stop-color="#bbb"
+       stop-opacity=".1"
+       id="stop2" />
+    <stop
+       offset="1"
+       stop-opacity=".1"
+       id="stop4" />
+  </linearGradient>
+  <mask
+     id="a">
+    <rect
+       width="96"
+       height="20"
+       rx="3"
+       id="rect7"
+       x="0"
+       y="0"
+       style="fill:#ffffff" />
+  </mask>
+  <g
+     font-size="11"
+     id="g26"
+     style="font-size:11px;font-family:'DejaVu Sans', Verdana, Geneva, sans-serif;text-anchor:middle;fill:#000000">
+    <text
+       x="30"
+       y="15"
+       id="text18"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro';fill:#000000;fill-opacity:0.3" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:4.5em;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro';text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
+       x="52.626465"
+       y="14.072"
+       id="text843"><tspan
+         sodipodi:role="line"
+         id="tspan841"
+         x="52.626465"
+         y="14.072"
+         style="font-size:13.33333397px"><tspan
+           style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:13.33333397px;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Light'"
+           id="tspan859">quality libre software by</tspan><tspan
+           style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13.33333397px;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro Bold'"
+           id="tspan4711">  </tspan><tspan
+           style="font-weight:bold;font-size:13.33333397px"
+           id="tspan849">sinusoidal engineering</tspan></tspan></text>
+    <g
+       transform="matrix(4.4383113,0,0,4.4383113,-73.95876,-379.69787)"
+       id="layer1"
+       inkscape:label="Layer 1">
+      <g
+         id="g10518"
+         transform="matrix(0.07290361,0,0,0.07290361,90.22887,85.474692)">
+        <g
+           id="g906"
+           transform="matrix(0.8913912,0,0,0.8913912,6.4029255,3.518587)">
+          <g
+             transform="translate(2.9886077,2.3730299)"
+             style="stroke-width:6;stroke-miterlimit:4;stroke-dasharray:none"
+             id="g10505">
+            <g
+               transform="matrix(0.28222223,0,0,0.28222223,237.85811,-1729.1481)"
+               style="stroke:#444444;stroke-width:32.14861298;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+               id="g6292">
+              <rect
+                 y="6129.2646"
+                 x="-842.80432"
+                 height="206.24994"
+                 width="206.25005"
+                 id="rect6290"
+                 style="opacity:1;fill:none;fill-opacity:1;stroke:#444444;stroke-width:32.14861298;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+            </g>
+            <path
+               id="path6294"
+               title="sin(x/2)"
+               d="m 55.5625,0.66666667 c -0.928371,0 -1.856705,0.13319437 -2.785079,0.39715343 -0.928371,0.2636873 -1.856744,0.6592201 -2.785077,1.1798418 -0.928372,0.5208916 -1.856743,1.1674139 -2.785116,1.9309205 -0.928334,0.7632369 -1.856705,1.6439988 -2.785078,2.6290465 -0.928372,0.9850477 -1.856743,2.0754629 -2.785077,3.2555751 -0.928372,1.180112 -1.856744,2.450461 -2.785115,3.793218 -0.928336,1.342755 -1.856706,2.758458 -2.785077,4.227385 -0.928374,1.469197 -1.856708,2.991617 -2.785079,4.546459 -0.928372,1.555111 -1.856744,3.142642 -2.785115,4.741252 -0.928334,1.598609 -1.856707,3.208295 -2.785078,4.806905 -0.928371,1.598608 -1.856705,3.18614 -2.785079,4.741251 -0.928371,1.555112 -1.856705,3.077263 -2.785077,4.546188 -0.928408,1.469197 -1.856743,2.88463 -2.785079,4.227655 -0.928407,1.342757 -1.856742,2.612837 -2.785115,3.793218 -0.928334,1.180113 -1.856705,2.270258 -2.785077,3.255575 -0.928372,0.985319 -1.856742,1.86554 -2.785113,2.629047 -0.928337,0.763506 -1.8567441,1.409758 -2.785079,1.93092 C 7.2876175,57.819171 6.3592466,58.214164 5.4308755,58.478121 4.5025393,58.742347 3.5742044,58.875 2.6458333,58.875"
+               style="opacity:1;fill:none;fill-opacity:1;stroke:#444444;stroke-width:9.07305336;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+               inkscape:connector-curvature="0" />
+          </g>
+          <circle
+             r="7.2447839"
+             cy="2.9294415"
+             cx="61.518147"
+             id="ellipse10510"
+             style="opacity:1;fill:#f0544c;fill-opacity:1;stroke:#ffffff;stroke-width:5.44383192;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <circle
+             style="opacity:1;fill:#444444;fill-opacity:1;stroke:#ffffff;stroke-width:5.44383192;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+             id="circle898"
+             cx="2.0896502"
+             cy="61.450638"
+             r="7.2447839" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/third_party/immer/doc/algorithms.rst b/third_party/immer/doc/algorithms.rst
new file mode 100644
index 0000000000..f7de519c05
--- /dev/null
+++ b/third_party/immer/doc/algorithms.rst
@@ -0,0 +1,28 @@
+
+Algorithms
+==========
+
+This module provides overloads of standard algorithms that leverage
+the internal structure of the immutable containers to provide faster
+iteration. These are drop-in replacements of the respective STL
+algorithms that can be a few times faster when applied on immutable
+sequences.
+
+For further convenience they can be passed in just a container where
+the standard library algorithms require being passed in two iterators.
+
+.. note::
+
+   They are a similar idea to `structure-aware iterators`_
+   but implemented using higher order functions in order to support
+   structures of any depth.  The downside is that this sometimes
+   causes the compiler to generate bigger executable files.
+
+.. _structure-aware iterators: https://www.youtube.com/watch?v=T3oA3zAMH9M
+
+
+-----
+
+.. doxygengroup:: algorithm
+   :project: immer
+   :content-only:
diff --git a/third_party/immer/doc/conf.py b/third_party/immer/doc/conf.py
new file mode 100644
index 0000000000..3c32cc505b
--- /dev/null
+++ b/third_party/immer/doc/conf.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+#
+# immer documentation build configuration file, created by
+# sphinx-quickstart on Thu Oct 27 18:10:24 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.mathjax',
+    'breathe',
+]
+
+breathe_projects = { "immer": "_doxygen/xml" }
+breathe_default_project = "immer"
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+from recommonmark.parser import CommonMarkParser
+from recommonmark.transform import AutoStructify
+source_parsers = { '.md': CommonMarkParser, }
+source_suffix = ['.rst', '.md']
+
+def setup(app):
+    app.add_config_value('recommonmark_config', {
+        'enable_eval_rst': True,
+    }, True)
+    app.add_transform(AutoStructify)
+
+# The encoding of source files.
+#
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'immer'
+copyright = u'2016, 2017 Juan Pedro Bolivar Puente'
+author = u'Juan Pedro Bolivar Puente'
+
+raw_enabled = True
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'0.0.0'
+# The full version, including alpha/beta/rc tags.
+release = u'0.0.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+
+import sys
+import os.path
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             '../tools/sinusoidal-sphinx-theme'))
+
+import sinusoidal_sphinx_theme
+html_theme_path = sinusoidal_sphinx_theme.html_theme_path()
+html_theme = 'sinusoidal_sphinx_theme'
+extensions.append("sinusoidal_sphinx_theme")
+html_theme_options = {
+    "project_nav_name": "immer",
+    "github_link" : "https://github.com/arximboldi/immer",
+}
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = u'immer v0.0.0'
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#
+html_logo = '_static/logo-black.svg'
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+#
+# html_domain_indices = True
+
+# If false, no index is generated.
+#
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'immerdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+     # The paper size ('letterpaper' or 'a4paper').
+     #
+     # 'papersize': 'letterpaper',
+
+     # The font size ('10pt', '11pt' or '12pt').
+     #
+     # 'pointsize': '10pt',
+
+     # Additional stuff for the LaTeX preamble.
+     #
+     # 'preamble': '',
+
+     # Latex figure (float) alignment
+     #
+     # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'immer.tex', u'immer Documentation',
+     u'Juan Pedro Bolivar Puente', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, 	itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'immer', u'immer Documentation',
+     [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'immer', u'immer Documentation',
+     author, 'immer', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+# The basename for the epub file. It defaults to the project name.
+# epub_basename = project
+
+# The HTML theme for the epub output. Since the default themes are not
+# optimized for small screen space, using the same theme for HTML and epub
+# output is usually not wise. This defaults to 'epub', a theme designed to save
+# visual space.
+#
+# epub_theme = 'epub'
+
+# The language of the text. It defaults to the language option
+# or 'en' if the language is not set.
+#
+# epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+# epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#
+# epub_cover = ()
+
+# A sequence of (type, uri, title) tuples for the guide element of content.opf.
+#
+# epub_guide = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_pre_files = []
+
+# HTML files that should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+# The depth of the table of contents in toc.ncx.
+#
+# epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#
+# epub_tocdup = True
+
+# Choose between 'default' and 'includehidden'.
+#
+# epub_tocscope = 'default'
+
+# Fix unsupported image types using the Pillow.
+#
+# epub_fix_images = False
+
+# Scale large images.
+#
+# epub_max_image_width = 0
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# epub_show_urls = 'inline'
+
+# If false, no index is generated.
+#
+# epub_use_index = True
diff --git a/third_party/immer/doc/containers.rst b/third_party/immer/doc/containers.rst
new file mode 100644
index 0000000000..c6913ba4a5
--- /dev/null
+++ b/third_party/immer/doc/containers.rst
@@ -0,0 +1,50 @@
+.. highlight:: c++
+
+Containers
+==========
+
+This section describes all the containers provided by the library.
+Check the :ref:`design` section for a general discussion of these
+containers and how to use them.
+
+box
+---
+
+.. doxygenclass:: immer::box
+    :members:
+    :undoc-members:
+
+array
+-----
+
+.. doxygenclass:: immer::array
+    :members:
+    :undoc-members:
+
+vector
+------
+
+.. doxygenclass:: immer::vector
+    :members:
+    :undoc-members:
+
+flex_vector
+-----------
+
+.. doxygenclass:: immer::flex_vector
+    :members:
+    :undoc-members:
+
+set
+---
+
+.. doxygenclass:: immer::set
+    :members:
+    :undoc-members:
+
+map
+---
+
+.. doxygenclass:: immer::map
+    :members:
+    :undoc-members:
diff --git a/third_party/immer/doc/design.rst b/third_party/immer/doc/design.rst
new file mode 100644
index 0000000000..27d29e9e61
--- /dev/null
+++ b/third_party/immer/doc/design.rst
@@ -0,0 +1,268 @@
+.. _design:
+
+Design
+======
+
+This is a library of **immutable containers**.
+
+These containers have all their methods marked ``const``.  Instead of
+mutating them *in place*, they provide manipulation functions that
+*return a new transformed value*, leaving the original value
+unaltered.  In the context of data-structures, this property of
+preserving old values is called **persistence**.
+
+Most of these containers use data-structures in which these operations
+can be done *efficiently*.  In particular, not all data is copied when
+a new value is produced.  Instead, the new values may share,
+internally, common data with other objects.  We sometimes refer to
+this property as **structural sharing**.  This behaviour is
+transparent to the user.
+
+Assigment
+---------
+
+We are sorry, we lied. These containers provide *one mutating
+operation*: **assignment** --- i.e. ``operator=``.
+
+There is a good reason: without ``operator=`` everything becomes
+complicated in C++.  For example, one may not contain non-assignable
+types in many standard containers, assignment would also be disabled
+from your custom types holding immutable containers, and so on and so
+forth.
+
+C++ is a multi-paradigm language with an imperative core.  Thus, it is
+built on the foundation that *variables* can be mutated ---
+i.e. assigned to.  We don't want to ride against this tide.  What we
+want to prevent is in-place *object* manipulation.  Because of C++
+semantics, *variable* assignment is defined in terms of *object
+mutation*, so we have to provide *this very particular mutating
+operation*, but nothing else.  Of course, you are free to mark your
+variables ``const`` to completely forbid assignment.
+
+.. warning::
+
+   **Assignment is not thread safe**.  When a *mutable* variable is
+   shared across multiple threads, protect access using some other
+   mechanism.
+
+   For obvious reasons, all other methods, which are ``const``, are
+   thread-safe.  It is safe to share *immutable* state across multiple
+   threads.
+
+To ``const`` or not to ``const``
+--------------------------------
+
+Many C++ programmers, influenced by functional programming, are trying
+to escape the evils of mutability by using ``const`` whenever
+possible.  We also do it ourselves in many of our examples to
+reinforce the property of immutability.
+
+While in general this is a good practice backed up with very good
+intentions, it has one caveat: *it disables moveability*.  It does so
+even when ``std::move()`` is used.  This makes sense, since moving from
+an object may mutate it, and ``const``, my friends, prevents *all*
+mutations.  For example:
+
+.. literalinclude:: ../example/vector/move.cpp
+   :language: c++
+   :start-after: move-bad/start
+   :end-before:  move-bad/end
+
+One may think that the variable ``v`` is moved into the
+``push_back()`` call.  This is not the case, because the variable
+``v`` is marked ``const``.  Of course, one may enable the move by
+removing it, as in:
+
+.. literalinclude:: ../example/vector/move.cpp
+   :language: c++
+   :start-after: move-good/start
+   :end-before:  move-good/end
+
+So, is it bad style then to use ``const`` as much as possible?  I
+wouldn't say so and it is advisable when ``std::move()`` is not used.
+An alternative style is to not use ``const`` but adopt an `AAA-style
+<aaa>`_ (*Almost Always use Auto*).  This way, it is easy to look for
+mutations by looking for lines that contain ``=`` but no ``auto``.
+Remember that when using our immutable containers ``operator=`` is the
+only way to mutate a variable.
+
+.. _aaa: https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/
+
+.. admonition:: Why does ``const`` prevent move semantics?
+
+   For those adventurous into the grainy details C++, here is why.
+   ``std::move()`` does not move anything, it is just *a cast* from
+   normal *l-value* references (``T&``) to *r-value* reference
+   (``T&&``).  This is, you pass it a variable, and it returns a
+   reference to its object disguised as an intermediate result.  In
+   exchange, you promise not to do anything with this variable later
+   [#f1]_. It is the role of the thing that *receives the moved-from
+   value* (in the previous example, ``push_back``) to actually do
+   anything interesting with it --- for example, steal its contents
+   😈.
+
+   So if you pass a ``T&`` to ``std::move()`` you get a ``T&&`` and,
+   unsurprisingly, if you pass a ``const T&`` you get a ``const T&&``.
+   But the receivers of the moved-from value (like constructors or our
+   ``push_back()``) maybe be moved-into because they provide an
+   overload that expects ``T&&`` --- without the ``const``!  Since a
+   ``const T&&`` can not be converted into a ``T&&``, the compiler
+   looks up for you another viable overload, and most often finds a
+   copy constructor or something alike that expects a ``const T&`` or
+   just ``T``, to which a ``const T&&`` can be converted.  The code
+   compiles and works correctly, but it is less efficient than we
+   expected.  Our call to ``std::move()`` was fruitless.
+
+   .. [#f1] For the sake of completeness: it is actually allowed to do stuff
+            with the variable *after another value is assigned to it*.
+
+.. _move-semantics:
+
+Leveraging move semantics
+-------------------------
+
+When using :ref:`reference counting<rc>` (which is the default)
+mutating operations can often be faster when operating on *r-value
+references* (temporaries and moved-from values).  Note that this
+removes *persistence*, since one can not access the moved-from value
+anymore!  However, this may be a good idea when doing a chain of
+operations where the intermediate values are not important to us.
+
+For example, let's say we want to write a function that inserts all
+integers in the range :math:`[first, last)` into an immutable vector.
+From the point of view of the caller of the function, this function is
+a *transaction*.  Whatever intermediate vectors are generated inside
+of it can be discarded since the caller can only see the initial
+vector (the one passed in as argument) and the vector with *all* the
+elements.  We may write such function like this:
+
+.. literalinclude:: ../example/vector/iota-move.cpp
+   :language: c++
+   :start-after: myiota/start
+   :end-before:  myiota/end
+
+The intermediate values are *moved* into the next ``push_back()``
+call.  They are going to be discarded anyways, this little
+``std::move`` just makes the whole thing faster, letting ``push_back``
+mutate part of the internal data structure in place when possible.
+
+If you don't like this syntax, :doc:`transients<transients>` may be
+used to obtain similar performance benefits.
+
+.. admonition:: Assigment guarantees
+
+   From the language point of view, the only requirement on moved from
+   values is that they should still be destructible.  We provide the
+   following two additional guarantees:
+
+   - **It is valid to assign to a moved-from variable**.  The variable
+     gets the assigned value and becomes usable again.  This is the
+     behaviour of standard types.
+
+   - **It is valid to assign a moved-from variable to itself**.  For
+     most standard types this is *undefined behaviour*.  However, for our
+     immutable containers types, expressions of the form ``v =
+     std::move(v)`` are well-defined.
+
+Recursive types
+---------------
+
+Most containers will fail to be instantiated with a type of unknown
+size, this is, an *incomplete type*.  This prevents using them for
+building recursive types.  The following code fails to compile:
+
+.. code-block:: c++
+
+  struct my_type
+  {
+      int data;
+      immer::vector<my_type> children;
+  };
+
+However, we can easily workaround this by using an ``immer::box`` to wrap
+the elements in the vector, as in:
+
+.. code-block:: c++
+
+  struct my_type
+  {
+      int data;
+      immer::vector<immer::box<my_type>> children;
+  };
+
+.. admonition:: Standard containers and incomplete types
+
+  While the first example might seem to compile when using some
+  implementations of ``std::vector`` instead of ``immer::vector``, such
+  use is actually forbidden by the standard:
+
+    **17.6.4.8** *Other functions (...)* 2. the effects are undefined in
+    the following cases: (...) In particular---if an incomplete type (3.9)
+    is used as a template argument when instantiating a template
+    component, unless specifically allowed for that component.
+
+.. _batch-update:
+Efficient batch manipulations
+-----------------------------
+
+Sometimes you may write a function that needs to do multiple changes
+to a container.  Like most code you write with this library, this
+function is *pure*: it takes one container value in, and produces a
+new container value out, no side-effects.
+
+Let's say we want to write a function that inserts all integers in the
+range :math:`[first, last)` into an immutable vector:
+
+.. literalinclude:: ../example/vector/iota-slow.cpp
+   :language: c++
+   :start-after: include:myiota/start
+   :end-before:  include:myiota/end
+
+This function works as expected, but it is slower than necessary.
+On every loop iteration, a new value is produced, just to be
+forgotten in the next iteration.
+
+Instead, we can grab a mutable view on the value, a :ref:`transient`.
+Then, we manipulate it *in-place*.  When we are done with it, we
+extract back an immutable value from it.  The code now looks like
+this:
+
+.. _iota-transient:
+
+.. literalinclude:: ../example/vector/iota-transient.cpp
+   :language: c++
+   :start-after: include:myiota/start
+   :end-before:  include:myiota/end
+
+Both conversions are :math:`O(1)`.  Note that calling ``transient()``
+does not break the immutability of the variable it is called on.  The
+new mutable object will adopt its contents, but when a mutation is
+performed, it will copy the data necessary using *copy on write*.
+Subsequent manipulations may hit parts that have already been copied,
+and these changes are done in-place.  Because of this, it does not
+make sense to use transients to do only one change.
+
+.. tip::
+
+   Note that :ref:`move semantics<move-semantics>` can be used instead to
+   support a similar use-case.  However, transients optimise updates
+   even when reference counting is disabled.
+
+.. _std-compat:
+Standard library compatibility
+------------------------------
+
+While the immutable containers provide an interface that follows a
+functional style, this is incompatible with what the standard library
+algorithms sometimes expect. :ref:`transients` try to provide an
+interface as similar as possible to similar standard library
+containers.  Thus, can also be used to interoperate with standard
+library components.
+
+For example the :ref:`myiota() function above<iota-transient>` may as
+well be written using standard library tools:
+
+.. literalinclude:: ../example/vector/iota-transient-std.cpp
+   :language: c++
+   :start-after: include:myiota/start
+   :end-before:  include:myiota/end
diff --git a/third_party/immer/doc/doxygen.config b/third_party/immer/doc/doxygen.config
new file mode 100644
index 0000000000..b6002b8d8f
--- /dev/null
+++ b/third_party/immer/doc/doxygen.config
@@ -0,0 +1,23 @@
+PROJECT_NAME     = "immer"
+OUTPUT_DIRECTORY = _doxygen
+GENERATE_LATEX   = NO
+GENERATE_MAN     = NO
+GENERATE_RTF     = NO
+GENERATE_HTML    = NO
+GENERATE_XML     = YES
+INPUT            = \
+                 ../immer \
+                 ../immer/heap \
+                 ../immer/refcount \
+                 ../immer/transience
+INCLUDE_PATH     = ..
+QUIET            = YES
+
+JAVADOC_AUTOBRIEF = YES
+ENABLE_PREPROCESSING = YES
+MARKDOWN_SUPPORT = YES
+MACRO_EXPANSION = YES
+
+ALIASES  = "rst=\verbatim embed:rst:leading-asterisk\n"
+ALIASES += "endrst=\n\endverbatim"
+ALIASES += "rst{1}=\rst\1\endrst"
diff --git a/third_party/immer/doc/guile.rst b/third_party/immer/doc/guile.rst
new file mode 120000
index 0000000000..dfda6df878
--- /dev/null
+++ b/third_party/immer/doc/guile.rst
@@ -0,0 +1 @@
+../extra/guile/README.rst
\ No newline at end of file
diff --git a/third_party/immer/doc/implementation.rst b/third_party/immer/doc/implementation.rst
new file mode 100644
index 0000000000..3bfdd9c2b8
--- /dev/null
+++ b/third_party/immer/doc/implementation.rst
@@ -0,0 +1,6 @@
+Implementation overview
+=======================
+
+.. image:: https://upload.wikimedia.org/wikipedia/commons/e/e6/%22Work_in_progress%22%2C_animated.gif
+   :alt: Work in Progress
+   :align: center
diff --git a/third_party/immer/doc/index.rst b/third_party/immer/doc/index.rst
new file mode 100644
index 0000000000..311549c74b
--- /dev/null
+++ b/third_party/immer/doc/index.rst
@@ -0,0 +1,31 @@
+
+.. include:: ../README.rst
+   :end-before: include:index/end
+
+Contents
+--------
+
+.. toctree::
+   :caption: User Manual
+   :maxdepth: 3
+
+   introduction
+   design
+   containers
+   transients
+   algorithms
+   utilities
+   memory
+
+.. toctree::
+   :caption: Experimental
+   :maxdepth: 3
+
+   python
+   guile
+
+----
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/third_party/immer/doc/introduction.rst b/third_party/immer/doc/introduction.rst
new file mode 100644
index 0000000000..2445cf9df8
--- /dev/null
+++ b/third_party/immer/doc/introduction.rst
@@ -0,0 +1,6 @@
+
+Introduction
+============
+
+.. include:: ../README.rst
+   :start-after: include:introduction/start
diff --git a/third_party/immer/doc/memory.rst b/third_party/immer/doc/memory.rst
new file mode 100644
index 0000000000..3138b249f3
--- /dev/null
+++ b/third_party/immer/doc/memory.rst
@@ -0,0 +1,204 @@
+Memory management
+=================
+
+Memory management is specially important for immutable data
+structures.  This is mainly due to:
+
+#. In order to preserve the old value, new memory has to be allocated
+   to store the new data whenever a container is manipulated.  Thus,
+   more allocations are performed *when changing* than with traditional
+   mutable data structures.
+
+#. In order to support *structural sharing* transparently, some kind
+   of garbage collection mechanism is required.  Passing immutable
+   data structures around is, internally, just passing references,
+   thus the system needs to figure out somehow when old values are not
+   referenced anymore and should be deallocated.
+
+Thus, most containers in this library can be customized via policies_
+in order to use different *allocation* and *garbage collection*
+strategies.
+
+.. doxygentypedef:: immer::default_memory_policy
+.. doxygentypedef:: immer::default_heap_policy
+.. doxygentypedef:: immer::default_refcount_policy
+
+.. _policies: https://en.wikipedia.org/wiki/Policy-based_design
+
+.. _memory policy:
+
+Memory policy
+-------------
+
+.. doxygenstruct:: immer::memory_policy
+    :members:
+    :undoc-members:
+
+.. _gc:
+
+Example: tracing garbage collection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is note worthy that all aspects of a
+:cpp:class:`immer::memory_policy` are not completely orthogonal.
+
+Let's say you want to use a `tracing garbage collector`_. Actually, we
+already provide :cpp:class:`a heap <immer::gc_heap>` that interfaces
+with the `Boehm's conservative garbage collector`_.  Chunks of memory
+allocated with this heap do not need to be deallocated, instead, after
+a certain number of allocations, the heap itself will scan the stack
+and all allocated memory to find references to other blocks of memory.
+The memory that is not referenced anymore is automatically *freed*.
+Thus, no reference counting mechanism is needed, and it makes no sense
+to use this heap with anything else than the
+:cpp:class:`immer::no_refcount_policy`.  Also, a big object can be
+separated in parts that contain pointers and parts that do not, which
+make scanning references faster.  So it makes most sense to use
+``prefer_fewer_bigger_objects = false``.
+
+.. note:: There are few considerations to note when using
+          :cpp:class:`gc_heap` with containers.  Please make sure to
+          read :cpp:class:`its documentation section <immer::gc_heap>`
+          before using it.
+
+.. literalinclude:: ../example/vector/gc.cpp
+   :language: c++
+   :start-after: example/start
+   :end-before:  example/end
+
+
+.. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc
+.. _tracing garbage collector: https://en.wikipedia.org/wiki/Tracing_garbage_collection
+
+Heaps
+-----
+
+A **heap policy** is a `metafunction class`_ that given the sizes of
+the objects that we want to allocate, it *returns* a heap that can
+allocate objects of those sizes.
+
+.. _metafunction class: http://www.boost.org/doc/libs/1_62_0/libs/mpl/doc/refmanual/metafunction-class.html
+
+A **heap** is a type with a methods ``void* allocate(std::size_t)``
+and ``void deallocate(void*)`` that return and release raw memory.
+For a canonical model of this concept check the
+:cpp:class:`immer::cpp_heap`.
+
+.. note:: Currently, *heaps* can only have **global state**.  Having
+          internal state poses conceptual problems for immutable data
+          structures: should a `const` method of a container modify
+          its internal allocator state?  Should every immutable
+          container object have its own internal state, or new objects
+          made from another one just keep a reference to the allocator
+          of the parent?
+
+          On the other hand, having some **scoped state** does make
+          sense for some use-cases of immutable data structures.  For
+          example, we might want to support variations of `region
+          based allocation`_.  This interface might evolve to evolve
+          to support some kind of non-global state to accommodate
+          these use cases.
+
+.. _region based allocation: https://en.wikipedia.org/wiki/Region-based_memory_management
+
+.. admonition:: Why not use the standard allocator interface?
+
+   The standard allocator API was not designed to support different
+   allocation strategies, but to abstract over the underlying memory
+   model instead.  In C++11 the situation improves, but the new API is
+   *stateful*, posing various challenges as described in the previous
+   note.  So far it was easier to provide our own allocation
+   interface.  In the future, we will provide adaptors so
+   standard-compatible allocators can also be used with ``immer``.
+
+Heap policies
+~~~~~~~~~~~~~
+
+.. doxygenstruct:: immer::heap_policy
+   :members:
+   :undoc-members:
+
+.. doxygenstruct:: immer::free_list_heap_policy
+
+Standard heap
+~~~~~~~~~~~~~
+
+.. doxygenstruct:: immer::cpp_heap
+   :members:
+
+Malloc heap
+~~~~~~~~~~~
+
+.. doxygenstruct:: immer::malloc_heap
+   :members:
+
+Garbage collected heap
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. doxygenclass:: immer::gc_heap
+
+Heap adaptors
+~~~~~~~~~~~~~
+
+Inspired by `Andrei Alexandrescu's talk on allocators <allocation
+vexation>`_ and `Emery Berger's heap layers <heap layers>`_ we provide
+allocator adaptors that can be combined using C++ mixins.  These
+enable building more complex allocator out of simpler strategies, or
+provide application specific optimizations on top of general
+allocators.
+
+.. _allocation vexation: https://www.youtube.com/watch?v=LIb3L4vKZ7U
+.. _heap layers: https://github.com/emeryberger/Heap-Layers
+
+.. doxygenstruct:: immer::with_data
+
+.. doxygenstruct:: immer::free_list_heap
+
+.. doxygenstruct:: immer::thread_local_free_list_heap
+
+.. doxygenstruct:: immer::unsafe_free_list_heap
+
+.. doxygenstruct:: immer::identity_heap
+
+.. doxygenstruct:: immer::debug_size_heap
+
+.. doxygenstruct:: immer::split_heap
+
+.. _rc:
+
+Reference counting
+------------------
+
+`Reference counting`_ is the most commonly used garbage collection
+strategy for C++.  It can be implemented non-intrusively, in a way
+orthogonal to the allocation strategy. It is deterministic, playing
+well with RAII_.
+
+A `memory policy`_ can provide a reference counting strategy that
+containers can use to track their contents.
+
+.. _reference counting: https://en.wikipedia.org/wiki/Reference_counting
+.. _raii: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
+
+.. doxygenstruct:: immer::refcount_policy
+
+.. doxygenstruct:: immer::unsafe_refcount_policy
+
+.. doxygenstruct:: immer::no_refcount_policy
+
+Transience
+----------
+
+In order to support `transients`, it is needed to provide a mechanism
+to track the ownership of the data allocated inside the container.
+This concept is encapsulated in *transience policies*.
+
+Note that when :ref:`reference counting <rc>` is available, no such mechanism is
+needed.  However, when :ref:`tracing garbage collection<gc>` is used instead,
+a special policy has to be provided.  Otherwise, the transient API is
+still available, but it will perform poorly, since it won't be able to
+mutate any data in place.
+
+.. doxygenstruct:: immer::no_transience_policy
+
+.. doxygenstruct:: immer::gc_transience_policy
diff --git a/third_party/immer/doc/python.rst b/third_party/immer/doc/python.rst
new file mode 120000
index 0000000000..4266420dfb
--- /dev/null
+++ b/third_party/immer/doc/python.rst
@@ -0,0 +1 @@
+../extra/python/README.rst
\ No newline at end of file
diff --git a/third_party/immer/doc/requirements.txt b/third_party/immer/doc/requirements.txt
new file mode 100644
index 0000000000..4d0744feec
--- /dev/null
+++ b/third_party/immer/doc/requirements.txt
@@ -0,0 +1,3 @@
+git+https://github.com/arximboldi/sphinx.git
+git+https://github.com/arximboldi/breathe.git
+recommonmark
diff --git a/third_party/immer/doc/sphinx-html-hack.bash b/third_party/immer/doc/sphinx-html-hack.bash
new file mode 100755
index 0000000000..1dccb9265d
--- /dev/null
+++ b/third_party/immer/doc/sphinx-html-hack.bash
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+location=`dirname $0`
+
+echo "Running $0 at $location"
+
+# Fixes issues described here among others
+# https://github.com/michaeljones/breathe/issues/284
+
+fix-missing-class-name()
+{
+    src='<span id="\([^"]*\)"></span>\(.*\)<em class="property">class </em>'
+    dst='<span id=""></span>\2<em class="property">class</em><tt class="descname">\1</tt>'
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-missing-struct-name()
+{
+    src='<span id="\([^"]*\)"></span>\(.*\)<em class="property">struct </em>'
+    dst='<span id=""></span>\2<em class="property">struct</em><tt class="descname">\1</tt>'
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-double-using-keyword()
+{
+    src='<em class="property">using</em><em class="property">using </em>'
+    dst='<em class="property">using </em>'
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-do-not-repeat-type-in-member-using-declaration()
+{
+    src='<em class="property">using </em><code class="descname">\(\([^:]*::\)*\)\([^ ]*\) = \([^<]*\)</code>'
+    dst='<em class="property">using </em><code class="descname">\3 = \4</code>'
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-do-not-repeat-type-in-member-using-declaration
+
+fix-remove-double-class-name()
+{
+    # src='<code class="descclassname">\([^&]*\)&lt;\([^&]*\)&gt;::</code>'
+    # dst='<code class="descclassname">\1::</code>'
+    src='<code class="descclassname">\([^<]*\)</code>'
+    dst=''
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-remove-straneous-typedefs()
+{
+    src='typedef '
+    dst=''
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-remove-straneous-typedefs-2()
+{
+    src='= typedef '
+    dst='= '
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-remove-straneous-typedefs-2
+
+fix-remove-straneous-using-declarations()
+{
+    src='<em class="property">using </em>template&lt;&gt;<br />'
+    dst=''
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+
+fix-remove-straneous-template-in-using-declarations-1()
+{
+    src='\(<dl class="type">\n<dt[^>]*>\)\ntemplate&lt;&gt;<br />'
+    dst='\1'
+    pre=':a;N;$!ba;'
+    sed -i "$pre;s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-remove-straneous-template-in-using-declarations-1
+
+fix-remove-straneous-template-in-using-declarations-2()
+{
+    src='></span>template&lt;&gt;<br /><span '
+    dst='></span><span '
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-remove-straneous-template-in-using-declarations-2
+
+fix-remove-countainer-css-class-in-member-definitions-causing-overflow()
+{
+    src='breathe-sectiondef\([[:alnum:] _-]*\)container'
+    dst='breathe-sectiondef'
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-remove-countainer-css-class-in-member-definitions-causing-overflow
+
+fix-remove-inherits-from()
+{
+    src='<p>Inherits from [^/]*</p>'
+    dst=''
+    sed -i "s@$src@$dst@g" $location/_build/html/*.html
+}
+fix-remove-inherits-from
diff --git a/third_party/immer/doc/transients.rst b/third_party/immer/doc/transients.rst
new file mode 100644
index 0000000000..9b385d4db5
--- /dev/null
+++ b/third_party/immer/doc/transients.rst
@@ -0,0 +1,51 @@
+.. _transient:
+
+Transients
+==========
+
+*Transients* is a concept borrowed `from Clojure
+<clojure-transients>`_, with some twists to turn make more idiomatic
+in C++.  Essentially, they are a mutable interface built on top of the
+same data structures the implements the immutable containers under the
+hood.
+
+These can be useful for :ref:`performing efficient batch
+updates<batch-update>` or :ref:`interfacing with standard
+algorithms<std-compat>`.
+
+.. _clojure-transients: https://clojure.org/reference/transients
+
+array_transient
+---------------
+
+.. doxygenclass:: immer::array_transient
+    :members:
+    :undoc-members:
+
+vector_transient
+----------------
+
+.. doxygenclass:: immer::vector_transient
+    :members:
+    :undoc-members:
+
+flex_vector_transient
+---------------------
+
+.. doxygenclass:: immer::flex_vector_transient
+    :members:
+    :undoc-members:
+
+set_transient
+-------------
+
+.. doxygenclass:: immer::set_transient
+    :members:
+    :undoc-members:
+
+map_transient
+-------------
+
+.. doxygenclass:: immer::map_transient
+    :members:
+    :undoc-members:
diff --git a/third_party/immer/doc/utilities.rst b/third_party/immer/doc/utilities.rst
new file mode 100644
index 0000000000..eaeea475c2
--- /dev/null
+++ b/third_party/immer/doc/utilities.rst
@@ -0,0 +1,15 @@
+.. highlight:: c++
+
+Utilities
+=========
+
+The library provides also some types that, while they are not exactly
+immutable data-structures, they are very useful when using immutable
+data-structures and value-oriented design in practice.
+
+atom
+----
+
+.. doxygenclass:: immer::atom
+    :members:
+    :undoc-members:
diff --git a/third_party/immer/example/CMakeLists.txt b/third_party/immer/example/CMakeLists.txt
new file mode 100644
index 0000000000..2aa0f55427
--- /dev/null
+++ b/third_party/immer/example/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+#  Targets
+#  =======
+
+add_custom_target(examples
+  COMMENT "Build all examples.")
+add_dependencies(check examples)
+
+file(GLOB_RECURSE immer_examples "*.cpp")
+foreach(_file IN LISTS immer_examples)
+  immer_target_name_for(_target _output "${_file}")
+  add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
+  add_dependencies(examples ${_target})
+  set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
+  target_link_libraries(${_target} PUBLIC immer-dev)
+  add_test("example/${_output}" ${_output})
+endforeach()
diff --git a/third_party/immer/example/array/array.cpp b/third_party/immer/example/array/array.cpp
new file mode 100644
index 0000000000..43972ad61a
--- /dev/null
+++ b/third_party/immer/example/array/array.cpp
@@ -0,0 +1,54 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/array.hpp>
+
+#include <cassert>
+
+int main()
+{
+    {
+        // include:push-back/start
+        auto v1 = immer::array<int>{1};
+        auto v2 = v1.push_back(8);
+
+        assert((v1 == immer::array<int>{1}));
+        assert((v2 == immer::array<int>{1, 8}));
+        // include:push-back/end
+    }
+
+    {
+        // include:set/start
+        auto v1 = immer::array<int>{1, 2, 3};
+        auto v2 = v1.set(0, 5);
+
+        assert((v1 == immer::array<int>{1, 2, 3}));
+        assert((v2 == immer::array<int>{5, 2, 3}));
+        // include:set/end
+    }
+
+    {
+        // include:update/start
+        auto v1 = immer::array<int>{1, 2, 3, 4};
+        auto v2 = v1.update(2, [&](auto l) { return ++l; });
+
+        assert((v1 == immer::array<int>{1, 2, 3, 4}));
+        assert((v2 == immer::array<int>{1, 2, 4, 4}));
+        // include:update/end
+    }
+
+    {
+        // include:take/start
+        auto v1 = immer::array<int>{1, 2, 3, 4, 5, 6};
+        auto v2 = v1.take(3);
+
+        assert((v1 == immer::array<int>{1, 2, 3, 4, 5, 6}));
+        assert((v2 == immer::array<int>{1, 2, 3}));
+        // include:take/end
+    }
+}
diff --git a/third_party/immer/example/box/box.cpp b/third_party/immer/example/box/box.cpp
new file mode 100644
index 0000000000..8f045e8876
--- /dev/null
+++ b/third_party/immer/example/box/box.cpp
@@ -0,0 +1,25 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/box.hpp>
+
+#include <cassert>
+#include <string>
+
+int main()
+{
+    {
+        // include:update/start
+        auto v1 = immer::box<std::string>{"hello"};
+        auto v2 = v1.update([&](auto l) { return l + ", world!"; });
+
+        assert((v1 == immer::box<std::string>{"hello"}));
+        assert((v2 == immer::box<std::string>{"hello, world!"}));
+        // include:update/end
+    }
+}
diff --git a/third_party/immer/example/flex-vector/flex-vector.cpp b/third_party/immer/example/flex-vector/flex-vector.cpp
new file mode 100644
index 0000000000..7c8a779339
--- /dev/null
+++ b/third_party/immer/example/flex-vector/flex-vector.cpp
@@ -0,0 +1,103 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+
+#include <cassert>
+
+int main()
+{
+    {
+        // include:push-back/start
+        auto v1 = immer::flex_vector<int>{1};
+        auto v2 = v1.push_back(8);
+
+        assert((v1 == immer::flex_vector<int>{1}));
+        assert((v2 == immer::flex_vector<int>{1, 8}));
+        // include:push-back/end
+    }
+    {
+        // include:push-front/start
+        auto v1 = immer::flex_vector<int>{1};
+        auto v2 = v1.push_front(8);
+
+        assert((v1 == immer::flex_vector<int>{1}));
+        assert((v2 == immer::flex_vector<int>{8, 1}));
+        // include:push-front/end
+    }
+
+    {
+        // include:set/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3};
+        auto v2 = v1.set(0, 5);
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3}));
+        assert((v2 == immer::flex_vector<int>{5, 2, 3}));
+        // include:set/end
+    }
+
+    {
+        // include:update/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3, 4};
+        auto v2 = v1.update(2, [&](auto l) { return ++l; });
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3, 4}));
+        assert((v2 == immer::flex_vector<int>{1, 2, 4, 4}));
+        // include:update/end
+    }
+
+    {
+        // include:take/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3, 4, 5, 6};
+        auto v2 = v1.take(3);
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3, 4, 5, 6}));
+        assert((v2 == immer::flex_vector<int>{1, 2, 3}));
+        // include:take/end
+    }
+
+    {
+        // include:drop/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3, 4, 5, 6};
+        auto v2 = v1.drop(3);
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3, 4, 5, 6}));
+        assert((v2 == immer::flex_vector<int>{4, 5, 6}));
+        // include:drop/end
+    }
+
+    {
+        // include:insert/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3};
+        auto v2 = v1.insert(0, 0);
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3}));
+        assert((v2 == immer::flex_vector<int>{0, 1, 2, 3}));
+        // include:insert/end
+    }
+
+    {
+        // include:erase/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3, 4, 5};
+        auto v2 = v1.erase(2);
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3, 4, 5}));
+        assert((v2 == immer::flex_vector<int>{1, 2, 4, 5}));
+        // include:erase/end
+    }
+
+    {
+        // include:concat/start
+        auto v1 = immer::flex_vector<int>{1, 2, 3};
+        auto v2 = v1 + v1;
+
+        assert((v1 == immer::flex_vector<int>{1, 2, 3}));
+        assert((v2 == immer::flex_vector<int>{1, 2, 3, 1, 2, 3}));
+        // include:concat/end
+    }
+}
diff --git a/third_party/immer/example/map/intro.cpp b/third_party/immer/example/map/intro.cpp
new file mode 100644
index 0000000000..94222f99b3
--- /dev/null
+++ b/third_party/immer/example/map/intro.cpp
@@ -0,0 +1,23 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <string>
+// include:intro/start
+#include <immer/map.hpp>
+int main()
+{
+    const auto v0 = immer::map<std::string, int>{};
+    const auto v1 = v0.set("hello", 42);
+    assert(v0["hello"] == 0);
+    assert(v1["hello"] == 42);
+
+    const auto v2 = v1.erase("hello");
+    assert(*v1.find("hello") == 42);
+    assert(!v2.find("hello"));
+}
+// include:intro/end
diff --git a/third_party/immer/example/set/intro.cpp b/third_party/immer/example/set/intro.cpp
new file mode 100644
index 0000000000..be932ce654
--- /dev/null
+++ b/third_party/immer/example/set/intro.cpp
@@ -0,0 +1,22 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// include:intro/start
+#include <immer/set.hpp>
+int main()
+{
+    const auto v0 = immer::set<int>{};
+    const auto v1 = v0.insert(42);
+    assert(v0.count(42) == 0);
+    assert(v1.count(42) == 1);
+
+    const auto v2 = v1.erase(42);
+    assert(v1.count(42) == 1);
+    assert(v2.count(42) == 0);
+}
+// include:intro/end
diff --git a/third_party/immer/example/vector/fizzbuzz.cpp b/third_party/immer/example/vector/fizzbuzz.cpp
new file mode 100644
index 0000000000..c7765a6200
--- /dev/null
+++ b/third_party/immer/example/vector/fizzbuzz.cpp
@@ -0,0 +1,34 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+#include <iostream>
+#include <string>
+
+// include:fizzbuzz/start
+immer::vector<std::string>
+fizzbuzz(immer::vector<std::string> v, int first, int last)
+{
+    for (auto i = first; i < last; ++i)
+        v = std::move(v).push_back(
+            i % 15 == 0 ? "FizzBuzz"
+                        : i % 5 == 0 ? "Bizz"
+                                     : i % 3 == 0 ? "Fizz" :
+                                                  /* else */ std::to_string(i));
+    return v;
+}
+// include:fizzbuzz/end
+
+int main()
+{
+    auto v = fizzbuzz({}, 0, 100);
+    std::copy(v.begin(),
+              v.end(),
+              std::ostream_iterator<std::string>{std::cout, "\n"});
+}
diff --git a/third_party/immer/example/vector/gc.cpp b/third_party/immer/example/vector/gc.cpp
new file mode 100644
index 0000000000..2bc4d5116f
--- /dev/null
+++ b/third_party/immer/example/vector/gc.cpp
@@ -0,0 +1,37 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// include:example/start
+#include <immer/heap/gc_heap.hpp>
+#include <immer/heap/heap_policy.hpp>
+#include <immer/memory_policy.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+
+#include <iostream>
+
+// declare a memory policy for using a tracing garbage collector
+using gc_policy = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+// alias the vector type so we are not concerned about memory policies
+// in the places where we actually use it
+template <typename T>
+using my_vector = immer::vector<T, gc_policy>;
+
+int main()
+{
+    auto v =
+        my_vector<const char*>().push_back("hello, ").push_back("world!\n");
+
+    for (auto s : v)
+        std::cout << s;
+}
+// include:example/end
diff --git a/third_party/immer/example/vector/intro.cpp b/third_party/immer/example/vector/intro.cpp
new file mode 100644
index 0000000000..ca832e6065
--- /dev/null
+++ b/third_party/immer/example/vector/intro.cpp
@@ -0,0 +1,22 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// include:intro/start
+#include <immer/vector.hpp>
+int main()
+{
+    const auto v0 = immer::vector<int>{};
+    const auto v1 = v0.push_back(13);
+    assert((v0 == immer::vector<int>{}));
+    assert((v1 == immer::vector<int>{13}));
+
+    const auto v2 = v1.set(0, 42);
+    assert(v1[0] == 13);
+    assert(v2[0] == 42);
+}
+// include:intro/end
diff --git a/third_party/immer/example/vector/iota-move.cpp b/third_party/immer/example/vector/iota-move.cpp
new file mode 100644
index 0000000000..3d03ba5307
--- /dev/null
+++ b/third_party/immer/example/vector/iota-move.cpp
@@ -0,0 +1,25 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <iostream>
+
+// include:myiota/start
+immer::vector<int> myiota(immer::vector<int> v, int first, int last)
+{
+    for (auto i = first; i < last; ++i)
+        v = std::move(v).push_back(i);
+    return v;
+}
+// include:myiota/end
+
+int main()
+{
+    auto v = myiota({}, 0, 100);
+    std::copy(v.begin(), v.end(), std::ostream_iterator<int>{std::cout, "\n"});
+}
diff --git a/third_party/immer/example/vector/iota-slow.cpp b/third_party/immer/example/vector/iota-slow.cpp
new file mode 100644
index 0000000000..a311b7a7ff
--- /dev/null
+++ b/third_party/immer/example/vector/iota-slow.cpp
@@ -0,0 +1,25 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <iostream>
+
+// include:myiota/start
+immer::vector<int> myiota(immer::vector<int> v, int first, int last)
+{
+    for (auto i = first; i < last; ++i)
+        v = v.push_back(i);
+    return v;
+}
+// include:myiota/end
+
+int main()
+{
+    auto v = myiota({}, 0, 100);
+    std::copy(v.begin(), v.end(), std::ostream_iterator<int>{std::cout, "\n"});
+}
diff --git a/third_party/immer/example/vector/iota-transient-std.cpp b/third_party/immer/example/vector/iota-transient-std.cpp
new file mode 100644
index 0000000000..451f44a103
--- /dev/null
+++ b/third_party/immer/example/vector/iota-transient-std.cpp
@@ -0,0 +1,29 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <algorithm>
+#include <iostream>
+
+// include:myiota/start
+immer::vector<int> myiota(immer::vector<int> v, int first, int last)
+{
+    auto t = v.transient();
+    std::generate_n(
+        std::back_inserter(t), last - first, [&] { return first++; });
+    return t.persistent();
+}
+// include:myiota/end
+
+int main()
+{
+    auto v = myiota({}, 0, 100);
+    std::copy(v.begin(), v.end(), std::ostream_iterator<int>{std::cout, "\n"});
+}
diff --git a/third_party/immer/example/vector/iota-transient.cpp b/third_party/immer/example/vector/iota-transient.cpp
new file mode 100644
index 0000000000..d49b14e579
--- /dev/null
+++ b/third_party/immer/example/vector/iota-transient.cpp
@@ -0,0 +1,27 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+#include <iostream>
+
+// include:myiota/start
+immer::vector<int> myiota(immer::vector<int> v, int first, int last)
+{
+    auto t = v.transient();
+    for (auto i = first; i < last; ++i)
+        t.push_back(i);
+    return t.persistent();
+}
+// include:myiota/end
+
+int main()
+{
+    auto v = myiota({}, 0, 100);
+    std::copy(v.begin(), v.end(), std::ostream_iterator<int>{std::cout, "\n"});
+}
diff --git a/third_party/immer/example/vector/move.cpp b/third_party/immer/example/vector/move.cpp
new file mode 100644
index 0000000000..7eb815b62a
--- /dev/null
+++ b/third_party/immer/example/vector/move.cpp
@@ -0,0 +1,35 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <cassert>
+#include <immer/vector.hpp>
+
+// include:move-bad/start
+immer::vector<int> do_stuff(const immer::vector<int> v)
+{
+    return std::move(v).push_back(42);
+}
+// include:move-bad/end
+
+// include:move-good/start
+immer::vector<int> do_stuff_better(immer::vector<int> v)
+{
+    return std::move(v).push_back(42);
+}
+// include:move-good/end
+
+int main()
+{
+    auto v  = immer::vector<int>{};
+    auto v1 = do_stuff(v);
+    auto v2 = do_stuff_better(v);
+    assert(v1.size() == 1);
+    assert(v2.size() == 1);
+    assert(v1[0] == 42);
+    assert(v2[0] == 42);
+}
diff --git a/third_party/immer/example/vector/vector.cpp b/third_party/immer/example/vector/vector.cpp
new file mode 100644
index 0000000000..c59c2d1d17
--- /dev/null
+++ b/third_party/immer/example/vector/vector.cpp
@@ -0,0 +1,53 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <cassert>
+#include <immer/vector.hpp>
+
+int main()
+{
+    {
+        // include:push-back/start
+        auto v1 = immer::vector<int>{1};
+        auto v2 = v1.push_back(8);
+
+        assert((v1 == immer::vector<int>{1}));
+        assert((v2 == immer::vector<int>{1, 8}));
+        // include:push-back/end
+    }
+
+    {
+        // include:set/start
+        auto v1 = immer::vector<int>{1, 2, 3};
+        auto v2 = v1.set(0, 5);
+
+        assert((v1 == immer::vector<int>{1, 2, 3}));
+        assert((v2 == immer::vector<int>{5, 2, 3}));
+        // include:set/end
+    }
+
+    {
+        // include:update/start
+        auto v1 = immer::vector<int>{1, 2, 3, 4};
+        auto v2 = v1.update(2, [&](auto l) { return ++l; });
+
+        assert((v1 == immer::vector<int>{1, 2, 3, 4}));
+        assert((v2 == immer::vector<int>{1, 2, 4, 4}));
+        // include:update/end
+    }
+
+    {
+        // include:take/start
+        auto v1 = immer::vector<int>{1, 2, 3, 4, 5, 6};
+        auto v2 = v1.take(3);
+
+        assert((v1 == immer::vector<int>{1, 2, 3, 4, 5, 6}));
+        assert((v2 == immer::vector<int>{1, 2, 3}));
+        // include:take/end
+    }
+}
diff --git a/third_party/immer/extra/fuzzer/CMakeLists.txt b/third_party/immer/extra/fuzzer/CMakeLists.txt
new file mode 100644
index 0000000000..777289f8a1
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/CMakeLists.txt
@@ -0,0 +1,29 @@
+
+add_custom_target(fuzzers
+  COMMENT "Build all fuzzers.")
+
+if (CHECK_FUZZERS)
+  add_dependencies(tests fuzzers)
+endif()
+
+# LIB_FUZZING_ENGINE is set by the Google OSS-Fuzz
+# infrastructure. Otherwise we use Clang's LibFuzzer
+if (DEFINED ENV{LIB_FUZZING_ENGINE})
+  set(immer_fuzzing_engine $ENV{LIB_FUZZING_ENGINE})
+else()
+  set(immer_fuzzing_engine "-fsanitize=fuzzer")
+endif()
+
+file(GLOB_RECURSE immer_fuzzers "*.cpp")
+foreach(_file IN LISTS immer_fuzzers)
+  immer_target_name_for(_target _output "${_file}")
+  add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
+  set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
+  target_compile_options(${_target} PUBLIC ${immer_fuzzing_engine})
+  target_link_libraries(${_target} PUBLIC ${immer_fuzzing_engine}
+    immer-dev)
+  add_dependencies(fuzzers ${_target})
+  if (CHECK_FUZZERS)
+    add_test("fuzzer/${_output}" ${_output} -max_total_time=1)
+  endif()
+endforeach()
diff --git a/third_party/immer/extra/fuzzer/array-gc.cpp b/third_party/immer/extra/fuzzer/array-gc.cpp
new file mode 100644
index 0000000000..b937212252
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/array-gc.cpp
@@ -0,0 +1,103 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/array.hpp>
+#include <immer/array_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <array>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using array_t     = immer::array<int, gc_memory>;
+    using transient_t = typename array_t::transient_type;
+    using size_t      = std::uint8_t;
+
+    auto vs = std::array<array_t, var_count>{};
+    auto ts = std::array<transient_t, var_count>{};
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_transient,
+            op_persistent,
+            op_push_back,
+            op_update,
+            op_take,
+            op_push_back_mut,
+            op_update_mut,
+            op_take_mut,
+        };
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_transient: {
+            auto src = read<char>(in, is_valid_var);
+            ts[dst]  = vs[src].transient();
+            break;
+        }
+        case op_persistent: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = ts[src].persistent();
+            break;
+        }
+        case op_push_back: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = vs[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_index(vs[src]));
+            vs[dst]  = vs[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            vs[dst]  = vs[src].take(idx);
+            break;
+        }
+        case op_push_back_mut: {
+            ts[dst].push_back(13);
+            break;
+        }
+        case op_update_mut: {
+            auto idx = read<size_t>(in, is_valid_index(ts[dst]));
+            ts[dst].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_mut: {
+            auto idx = read<size_t>(in, is_valid_size(ts[dst]));
+            ts[dst].take(idx);
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/array.cpp b/third_party/immer/extra/fuzzer/array.cpp
new file mode 100644
index 0000000000..094d97b7f4
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/array.cpp
@@ -0,0 +1,80 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/array.hpp>
+
+#include <array>
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using array_t = immer::array<int, immer::default_memory_policy>;
+    using size_t  = std::uint8_t;
+
+    auto vars = std::array<array_t, var_count>{};
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_push_back_move,
+            op_update_move,
+            op_take_move,
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back: {
+            vars[dst] = vars[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto idx  = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_push_back_move: {
+            vars[dst] = std::move(vars[src]).push_back(12);
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_move: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = std::move(vars[src]).take(idx);
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/flex-vector-gc.cpp b/third_party/immer/extra/fuzzer/flex-vector-gc.cpp
new file mode 100644
index 0000000000..00c3028986
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/flex-vector-gc.cpp
@@ -0,0 +1,162 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <array>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+    constexpr auto bits      = 2;
+
+    using vector_t    = immer::flex_vector<int, gc_memory, bits, bits>;
+    using transient_t = typename vector_t::transient_type;
+    using size_t      = std::uint8_t;
+
+    auto vs = std::array<vector_t, var_count>{};
+    auto ts = std::array<transient_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_var_neq = [](auto other) {
+        return [=](auto idx) {
+            return idx >= 0 && idx < var_count && idx != other;
+        };
+    };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        auto max        = std::numeric_limits<size_type>::max() >> (bits * 4);
+        return v1.size() < max && v2.size() < max;
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_transient,
+            op_persistent,
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_mut,
+            op_update_mut,
+            op_take_mut,
+            op_drop_mut,
+            op_prepend_mut,
+            op_prepend_mut_move,
+            op_append_mut,
+            op_append_mut_move,
+        };
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_transient: {
+            auto src = read<char>(in, is_valid_var);
+            ts[dst]  = vs[src].transient();
+            break;
+        }
+        case op_persistent: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = ts[src].persistent();
+            break;
+        }
+        case op_push_back: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = vs[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_index(vs[src]));
+            vs[dst]  = vs[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            vs[dst]  = vs[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            vs[dst]  = vs[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src  = read<char>(in, is_valid_var);
+            auto src2 = read<char>(in, is_valid_var);
+            if (can_concat(vs[src], vs[src2]))
+                vs[dst] = vs[src] + vs[src2];
+            break;
+        }
+        case op_push_back_mut: {
+            ts[dst].push_back(13);
+            break;
+        }
+        case op_update_mut: {
+            auto idx = read<size_t>(in, is_valid_index(ts[dst]));
+            ts[dst].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_mut: {
+            auto idx = read<size_t>(in, is_valid_size(ts[dst]));
+            ts[dst].take(idx);
+            break;
+        }
+        case op_prepend_mut: {
+            auto src = read<char>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src]))
+                ts[dst].prepend(ts[src]);
+            break;
+        }
+        case op_prepend_mut_move: {
+            auto src = read<char>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                ts[dst].prepend(std::move(ts[src]));
+                ts[src] = {};
+            }
+            break;
+        }
+        case op_append_mut: {
+            auto src = read<char>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src]))
+                ts[dst].append(ts[src]);
+            break;
+        }
+        case op_append_mut_move: {
+            auto src = read<char>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                ts[dst].append(std::move(ts[src]));
+                ts[src] = {};
+            }
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/flex-vector.cpp b/third_party/immer/extra/fuzzer/flex-vector.cpp
new file mode 100644
index 0000000000..14395de1d5
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/flex-vector.cpp
@@ -0,0 +1,150 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/box.hpp>
+#include <immer/flex_vector.hpp>
+
+#include <array>
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 8;
+    constexpr auto bits      = 2;
+
+    using vector_t =
+        immer::flex_vector<int, immer::default_memory_policy, bits, bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_var_neq = [](auto other) {
+        return [=](auto idx) {
+            return idx >= 0 && idx < var_count && idx != other;
+        };
+    };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        auto max        = std::numeric_limits<size_type>::max() >> (bits * 4);
+        return v1.size() < max && v2.size() < max;
+    };
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_move,
+            op_update_move,
+            op_take_move,
+            op_drop_move,
+            op_concat_move_l,
+            op_concat_move_r,
+            op_concat_move_lr,
+            op_insert,
+            op_erase,
+            op_compare,
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back: {
+            vars[dst] = vars[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto idx  = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = vars[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src2 = read<char>(in, is_valid_var);
+            if (can_concat(vars[src], vars[src2]))
+                vars[dst] = vars[src] + vars[src2];
+            break;
+        }
+        case op_push_back_move: {
+            vars[dst] = std::move(vars[src]).push_back(21);
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_move: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = std::move(vars[src]).take(idx);
+            break;
+        }
+        case op_drop_move: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = std::move(vars[src]).drop(idx);
+            break;
+        }
+        case op_concat_move_l: {
+            auto src2 = read<char>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2]))
+                vars[dst] = std::move(vars[src]) + vars[src2];
+            break;
+        }
+        case op_concat_move_r: {
+            auto src2 = read<char>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2]))
+                vars[dst] = vars[src] + std::move(vars[src2]);
+            break;
+        }
+        case op_concat_move_lr: {
+            auto src2 = read<char>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2]))
+                vars[dst] = std::move(vars[src]) + std::move(vars[src2]);
+        }
+        case op_compare: {
+            using std::swap;
+            if (vars[src] == vars[dst])
+                swap(vars[src], vars[dst]);
+            break;
+        }
+        case op_erase: {
+            auto idx  = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] = vars[src].erase(idx);
+            break;
+        }
+        case op_insert: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = vars[src].insert(idx, immer::box<int>{42});
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/fuzzer_input.hpp b/third_party/immer/extra/fuzzer/fuzzer_input.hpp
new file mode 100644
index 0000000000..485b7dc72b
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/fuzzer_input.hpp
@@ -0,0 +1,62 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstdint>
+#include <stdexcept>
+
+struct no_more_input : std::exception
+{};
+
+struct fuzzer_input
+{
+    const std::uint8_t* data_;
+    std::size_t size_;
+
+    const std::uint8_t* next(std::size_t size)
+    {
+        if (size_ < size)
+            throw no_more_input{};
+        auto r = data_;
+        data_ += size;
+        size_ -= size;
+        return r;
+    }
+
+    const std::uint8_t* next(std::size_t size, std::size_t align)
+    {
+        auto rem = size % align;
+        if (rem)
+            next(align - rem);
+        return next(size);
+    }
+
+    template <typename Fn>
+    int run(Fn step)
+    {
+        try {
+            while (step(*this))
+                continue;
+        } catch (const no_more_input&) {};
+        return 0;
+    }
+};
+
+template <typename T>
+const T& read(fuzzer_input& fz)
+{
+    return *reinterpret_cast<const T*>(fz.next(sizeof(T), alignof(T)));
+}
+
+template <typename T, typename Cond>
+T read(fuzzer_input& fz, Cond cond)
+{
+    auto x = read<T>(fz);
+    return cond(x) ? x : read<T>(fz, cond);
+}
diff --git a/third_party/immer/extra/fuzzer/map-gc.cpp b/third_party/immer/extra/fuzzer/map-gc.cpp
new file mode 100644
index 0000000000..835f37a058
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/map-gc.cpp
@@ -0,0 +1,93 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/map.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <array>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using map_t =
+        immer::map<char, int, std::hash<char>, std::equal_to<char>, gc_memory>;
+
+    auto vars = std::array<map_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_set,
+            op_erase,
+            op_set_move,
+            op_erase_move,
+            op_iterate,
+            op_find,
+            op_update
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_set: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].set(value, 42);
+            break;
+        }
+        case op_erase: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_set_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).set(value, 42);
+            break;
+        }
+        case op_erase_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).erase(value);
+            break;
+        }
+        case op_iterate: {
+            auto srcv = vars[src];
+            for (const auto& v : srcv) {
+                vars[dst] = vars[dst].set(v.first, v.second);
+            }
+            break;
+        }
+        case op_find: {
+            auto value = read<size_t>(in);
+            auto res   = vars[src].find(value);
+            if (res != nullptr) {
+                vars[dst] = vars[dst].set(*res, 42);
+            }
+            break;
+        }
+        case op_update: {
+            auto key  = read<size_t>(in);
+            vars[dst] = vars[src].update(key, [](int x) { return x + 1; });
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/map.cpp b/third_party/immer/extra/fuzzer/map.cpp
new file mode 100644
index 0000000000..eb650679e9
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/map.cpp
@@ -0,0 +1,85 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/map.hpp>
+
+#include <array>
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using map_t = immer::map<char, int>;
+
+    auto vars = std::array<map_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_set,
+            op_erase,
+            op_set_move,
+            op_erase_move,
+            op_iterate,
+            op_find,
+            op_update
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_set: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].set(value, 42);
+            break;
+        }
+        case op_erase: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_set_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).set(value, 42);
+            break;
+        }
+        case op_erase_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).erase(value);
+            break;
+        }
+        case op_iterate: {
+            auto srcv = vars[src];
+            for (const auto& v : srcv) {
+                vars[dst] = vars[dst].set(v.first, v.second);
+            }
+            break;
+        }
+        case op_find: {
+            auto value = read<size_t>(in);
+            auto res   = vars[src].find(value);
+            if (res != nullptr) {
+                vars[dst] = vars[dst].set(*res, 42);
+            }
+            break;
+        }
+        case op_update: {
+            auto key  = read<size_t>(in);
+            vars[dst] = vars[src].update(key, [](int x) { return x + 1; });
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/set-gc.cpp b/third_party/immer/extra/fuzzer/set-gc.cpp
new file mode 100644
index 0000000000..3b88c08f3c
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/set-gc.cpp
@@ -0,0 +1,78 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/set.hpp>
+
+#include <array>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using set_t =
+        immer::set<int, std::hash<char>, std::equal_to<char>, gc_memory>;
+
+    auto vars = std::array<set_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_insert,
+            op_erase,
+            op_insert_move,
+            op_erase_move,
+            op_iterate
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_insert: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].insert(value);
+            break;
+        }
+        case op_erase: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_insert_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).insert(value);
+            break;
+        }
+        case op_erase_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_iterate: {
+            auto srcv = vars[src];
+            for (const auto& v : srcv) {
+                vars[dst] = vars[dst].insert(v);
+            }
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/set.cpp b/third_party/immer/extra/fuzzer/set.cpp
new file mode 100644
index 0000000000..b25313d18c
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/set.cpp
@@ -0,0 +1,70 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/set.hpp>
+
+#include <array>
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+
+    using set_t = immer::set<int>;
+
+    auto vars = std::array<set_t, var_count>{};
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < var_count; };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_insert,
+            op_erase,
+            op_insert_move,
+            op_erase_move,
+            op_iterate
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_insert: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].insert(value);
+            break;
+        }
+        case op_erase: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_insert_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = std::move(vars[src]).insert(value);
+            break;
+        }
+        case op_erase_move: {
+            auto value = read<size_t>(in);
+            vars[dst]  = vars[src].erase(value);
+            break;
+        }
+        case op_iterate: {
+            auto srcv = vars[src];
+            for (const auto& v : srcv) {
+                vars[dst] = vars[dst].insert(v);
+            }
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/vector-gc.cpp b/third_party/immer/extra/fuzzer/vector-gc.cpp
new file mode 100644
index 0000000000..1faa729c5a
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/vector-gc.cpp
@@ -0,0 +1,104 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <array>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+    constexpr auto bits      = 2;
+
+    using vector_t    = immer::vector<int, gc_memory, bits, bits>;
+    using transient_t = typename vector_t::transient_type;
+    using size_t      = std::uint8_t;
+
+    auto vs = std::array<vector_t, var_count>{};
+    auto ts = std::array<transient_t, var_count>{};
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_transient,
+            op_persistent,
+            op_push_back,
+            op_update,
+            op_take,
+            op_push_back_mut,
+            op_update_mut,
+            op_take_mut,
+        };
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_transient: {
+            auto src = read<char>(in, is_valid_var);
+            ts[dst]  = vs[src].transient();
+            break;
+        }
+        case op_persistent: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = ts[src].persistent();
+            break;
+        }
+        case op_push_back: {
+            auto src = read<char>(in, is_valid_var);
+            vs[dst]  = vs[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_index(vs[src]));
+            vs[dst]  = vs[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto src = read<char>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            vs[dst]  = vs[src].take(idx);
+            break;
+        }
+        case op_push_back_mut: {
+            ts[dst].push_back(13);
+            break;
+        }
+        case op_update_mut: {
+            auto idx = read<size_t>(in, is_valid_index(ts[dst]));
+            ts[dst].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_mut: {
+            auto idx = read<size_t>(in, is_valid_size(ts[dst]));
+            ts[dst].take(idx);
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/fuzzer/vector.cpp b/third_party/immer/extra/fuzzer/vector.cpp
new file mode 100644
index 0000000000..b04cbcee87
--- /dev/null
+++ b/third_party/immer/extra/fuzzer/vector.cpp
@@ -0,0 +1,82 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "fuzzer_input.hpp"
+
+#include <immer/vector.hpp>
+
+#include <array>
+
+extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data,
+                                      std::size_t size)
+{
+    constexpr auto var_count = 4;
+    constexpr auto bits      = 2;
+
+    using vector_t =
+        immer::vector<int, immer::default_memory_policy, bits, bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, var_count>{};
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < var_count; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_push_back_move,
+            op_update_move,
+            op_take_move,
+        };
+        auto src = read<char>(in, is_valid_var);
+        auto dst = read<char>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back: {
+            vars[dst] = vars[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto idx  = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_push_back_move: {
+            vars[dst] = std::move(vars[src]).push_back(12);
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_move: {
+            auto idx  = read<size_t>(in, is_valid_size(vars[src]));
+            vars[dst] = std::move(vars[src]).take(idx);
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
diff --git a/third_party/immer/extra/guile/CMakeLists.txt b/third_party/immer/extra/guile/CMakeLists.txt
new file mode 100644
index 0000000000..99338060d6
--- /dev/null
+++ b/third_party/immer/extra/guile/CMakeLists.txt
@@ -0,0 +1,24 @@
+
+find_package(PkgConfig)
+
+pkg_check_modules(Guile guile-2.2)
+
+if (NOT Guile_FOUND)
+  message(STATUS "Disabling Guile modules")
+  return()
+endif()
+
+set(GUILE_EXTENSION_DIR ${CMAKE_CURRENT_BINARY_DIR})
+configure_file(immer.scm.in immer.scm)
+
+add_library(guile-immer SHARED EXCLUDE_FROM_ALL
+  src/immer.cpp)
+target_include_directories(guile-immer PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CALLABLE_TRAITS_INCLUDE_DIR}
+  ${Guile_INCLUDE_DIRS})
+target_link_libraries(guile-immer PUBLIC
+  immer
+  ${Guile_LIBRARIES})
+
+add_custom_target(guile DEPENDS guile-immer)
diff --git a/third_party/immer/extra/guile/README.rst b/third_party/immer/extra/guile/README.rst
new file mode 100644
index 0000000000..09cf8be644
--- /dev/null
+++ b/third_party/immer/extra/guile/README.rst
@@ -0,0 +1,144 @@
+
+Guile bindings
+==============
+
+This library includes experimental bindings bring efficient immutable
+vectors for the `GNU Guile`_ Scheme implementation.  The interface is
+somewhat **incomplete**, but you can already do something interesting
+things like:
+
+.. literalinclude:: ../extra/guile/example.scm
+   :language: scheme
+   :start-after: intro/start
+   :end-before:  intro/end
+..
+
+    **Do you want to help** making these bindings complete and production
+    ready?  Drop a line at `immer@sinusoid.al
+    <mailto:immer@sinusoid.al>`_ or `open an issue on Github
+    <https://github.com/arximboldi/immer>`_
+
+.. _GNU Guile: https://www.gnu.org/software/guile/
+
+Installation
+------------
+
+.. highlight:: sh
+
+To install the software, you need `GNU Guile 2.2
+<https://www.gnu.org/software/guile/download/>`_.  Then you have to
+`clone the repository <https://github.com/arximboldi/immer>`_ and
+inside the repository do something like::
+
+    mkdir build; cd build
+    cmake .. -DCMAKE_BUILD_TYPE=Release \
+             -DGUILE_EXTENSION_DIR="<somewhere...>"
+    make guile
+    cp extra/guile/libguile-immer.so "<...the GUILE_EXTENSION_DIR>"
+    cp extra/guile/immer.scm "<somewhere in your GUILE_LOAD_PATH>"
+
+Benchmarks
+----------
+
+The library includes some quick and dirty benchmarks that show how
+these vectors perform compared to *mutable vectors*, *lists*, and
+*v-lists*.  Once you have installed the library, you may run them by
+executing the following in the project root::
+
+    guile extra/guile/benchmark.scm
+
+This is the output I get when running those:
+
+.. code-block:: scheme
+   :name: benchmark-output
+
+   (define bench-size 1000000)
+   (define bench-samples 10)
+   ;;;; benchmarking creation...
+   ; evaluating:
+         (apply ivector (iota bench-size))
+   ; average time: 0.0608697784 seconds
+   ; evaluating:
+         (apply ivector-u32 (iota bench-size))
+   ; average time: 0.0567354933 seconds
+   ; evaluating:
+         (iota bench-size)
+   ; average time: 0.032995402 seconds
+   ; evaluating:
+         (apply vector (iota bench-size))
+   ; average time: 0.0513594425 seconds
+   ; evaluating:
+         (apply u32vector (iota bench-size))
+   ; average time: 0.0939185315 seconds
+   ; evaluating:
+         (list->vlist (iota bench-size))
+   ; average time: 0.2369570977 seconds
+   ;;;; benchmarking iteration...
+   (define bench-ivector (apply ivector (iota bench-size)))
+   (define bench-ivector-u32 (apply ivector-u32 (iota bench-size)))
+   (define bench-list (iota bench-size))
+   (define bench-vector (apply vector (iota bench-size)))
+   (define bench-u32vector (apply u32vector (iota bench-size)))
+   (define bench-vlist (list->vlist (iota bench-size)))
+   ; evaluating:
+         (ivector-fold + 0 bench-ivector)
+   ; average time: 0.035750341 seconds
+   ; evaluating:
+         (ivector-u32-fold + 0 bench-ivector-u32)
+   ; average time: 0.0363843682 seconds
+   ; evaluating:
+         (fold + 0 bench-list)
+   ; average time: 0.0271881423 seconds
+   ; evaluating:
+         (vector-fold + 0 bench-vector)
+   ; average time: 0.0405022349 seconds
+   ; evaluating:
+         (vlist-fold + 0 bench-vlist)
+   ; average time: 0.0424709098 seconds
+   ;;;; benchmarking iteration by index...
+   ; evaluating:
+         (let iter ((i 0) (acc 0))
+           (if (< i (ivector-length bench-ivector))
+             (iter (+ i 1) (+ acc (ivector-ref bench-ivector i)))
+             acc))
+   ; average time: 0.2195658936 seconds
+   ; evaluating:
+         (let iter ((i 0) (acc 0))
+           (if (< i (ivector-u32-length bench-ivector-u32))
+             (iter (+ i 1) (+ acc (ivector-u32-ref bench-ivector-u32 i)))
+             acc))
+   ; average time: 0.2205486326 seconds
+   ; evaluating:
+         (let iter ((i 0) (acc 0))
+           (if (< i (vector-length bench-vector))
+             (iter (+ i 1) (+ acc (vector-ref bench-vector i)))
+             acc))
+   ; average time: 0.0097157637 seconds
+   ; evaluating:
+         (let iter ((i 0) (acc 0))
+           (if (< i (u32vector-length bench-u32vector))
+             (iter (+ i 1) (+ acc (u32vector-ref bench-u32vector i)))
+             acc))
+   ; average time: 0.0733736008 seconds
+   ; evaluating:
+         (let iter ((i 0) (acc 0))
+           (if (< i (vlist-length bench-vlist))
+             (iter (+ i 1) (+ acc (vlist-ref bench-vlist i)))
+             acc))
+   ; average time: 0.3220357243 seconds
+   ;;;; benchmarking concatenation...
+   ; evaluating:
+         (ivector-append bench-ivector bench-ivector)
+   ; average time: 1.63022e-5 seconds
+   ; evaluating:
+         (ivector-u32-append bench-ivector-u32 bench-ivector-u32)
+   ; average time: 1.63754e-5 seconds
+   ; evaluating:
+         (append bench-list bench-list)
+   ; average time: 0.0135592963 seconds
+   ; evaluating:
+         (vector-append bench-vector bench-vector)
+   ; average time: 0.0044506586 seconds
+   ; evaluating:
+         (vlist-append bench-vlist bench-vlist)
+   ; average time: 0.3227312512 seconds
diff --git a/third_party/immer/extra/guile/benchmark.scm b/third_party/immer/extra/guile/benchmark.scm
new file mode 100644
index 0000000000..463a8eb17d
--- /dev/null
+++ b/third_party/immer/extra/guile/benchmark.scm
@@ -0,0 +1,168 @@
+;;
+;; immer: immutable data structures for C++
+;; Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+;;
+;; This software is distributed under the Boost Software License, Version 1.0.
+;; See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+;;
+
+(use-modules (immer)
+             (fector) ; https://wingolog.org/pub/fector.scm
+             (srfi srfi-1)
+             (srfi srfi-43)
+             (ice-9 vlist)
+             (ice-9 pretty-print)
+             (rnrs bytevectors))
+
+(define-syntax display-eval
+  (syntax-rules ()
+    ((_ expr)
+     (begin (pretty-print 'expr
+                          #:max-expr-width 72)
+            expr))))
+
+(display-eval (define bench-size 1000000))
+(display-eval (define bench-samples 10))
+
+(define (average . ns)
+  (/ (apply + ns) (length ns)))
+
+(define (generate-n n fn)
+  (unfold (lambda (x) (= x n))
+          (lambda (x) (fn))
+          (lambda (x) (+ x 1))
+          0))
+
+(define-syntax benchmark
+  (syntax-rules ()
+    ((_ expr)
+     (begin
+       (display "; evaluating:    ") (newline)
+       (pretty-print 'expr
+                     #:max-expr-width 72
+                     #:per-line-prefix "      ")
+       (let* ((sample (lambda ()
+                        (gc)
+                        (let* ((t0 (get-internal-real-time))
+                               (r  expr)
+                               (t1 (get-internal-real-time)))
+                          (/ (- t1 t0) internal-time-units-per-second))))
+              (samples (generate-n bench-samples sample))
+              (result (apply average samples)))
+         (display "; average time: ")
+         (display (exact->inexact result))
+         (display " seconds")
+         (newline))))))
+
+(display ";;;; benchmarking creation...") (newline)
+
+(display-eval
+ (define (fector . args)
+   (persistent-fector (fold (lambda (v fv) (fector-push! fv v))
+                            (transient-fector)
+                            args))))
+
+(benchmark (apply ivector (iota bench-size)))
+(benchmark (apply ivector-u32 (iota bench-size)))
+(benchmark (iota bench-size))
+(benchmark (apply vector (iota bench-size)))
+(benchmark (apply u32vector (iota bench-size)))
+(benchmark (list->vlist (iota bench-size)))
+(benchmark (apply fector (iota bench-size)))
+
+(display ";;;; benchmarking iteration...") (newline)
+
+(display-eval (define bench-ivector (apply ivector (iota bench-size))))
+(display-eval (define bench-ivector-u32 (apply ivector-u32 (iota bench-size))))
+(display-eval (define bench-list (iota bench-size)))
+(display-eval (define bench-vector (apply vector (iota bench-size))))
+(display-eval (define bench-u32vector (apply u32vector (iota bench-size))))
+(display-eval (define bench-vlist (list->vlist (iota bench-size))))
+(display-eval (define bench-fector (apply fector (iota bench-size))))
+(display-eval (define bench-bytevector-u32
+                (uint-list->bytevector (iota bench-size)
+                                       (native-endianness)
+                                       4)))
+
+(benchmark (ivector-fold + 0 bench-ivector))
+(benchmark (ivector-u32-fold + 0 bench-ivector-u32))
+(benchmark (fold + 0 bench-list))
+(benchmark (vector-fold + 0 bench-vector))
+(benchmark (vlist-fold + 0 bench-vlist))
+(benchmark (fector-fold + bench-fector 0))
+
+(display ";;;; benchmarking iteration by index...") (newline)
+
+(display-eval
+ (define-syntax iteration-by-index
+   (syntax-rules ()
+     ((_ *length *ref *vector *step)
+      (let ((len (*length *vector)))
+        (let iter ((i 0) (acc 0))
+          (if (< i len)
+              (iter (+ i *step)
+                    (+ acc (*ref *vector i)))
+              acc)))))))
+
+(display-eval
+ (define-syntax iteration-by-index-truncate
+   (syntax-rules ()
+     ((_ *length *ref *vector *step)
+      (let ((len (*length *vector)))
+        (let iter ((i 0) (acc 0))
+          (if (< i len)
+              (iter (+ i *step)
+                    (logand #xffffffffFFFFFFFF
+                            (+ acc (*ref *vector i))))
+              acc)))))))
+
+(benchmark (iteration-by-index ivector-length
+                               ivector-ref
+                               bench-ivector 1))
+(benchmark (iteration-by-index ivector-u32-length
+                               ivector-u32-ref
+                               bench-ivector-u32 1))
+(benchmark (iteration-by-index vector-length
+                               vector-ref
+                               bench-vector 1))
+(benchmark (iteration-by-index u32vector-length
+                               u32vector-ref
+                               bench-u32vector 1))
+(benchmark (iteration-by-index vlist-length
+                               vlist-ref
+                               bench-vlist 1))
+(benchmark (iteration-by-index fector-length
+                               fector-ref
+                               bench-fector 1))
+(benchmark (iteration-by-index bytevector-length
+                               bytevector-u32-native-ref
+                               bench-bytevector-u32 4))
+
+(benchmark (iteration-by-index-truncate ivector-length
+                                        ivector-ref
+                                        bench-ivector 1))
+(benchmark (iteration-by-index-truncate ivector-u32-length
+                                        ivector-u32-ref
+                                        bench-ivector-u32 1))
+(benchmark (iteration-by-index-truncate vector-length
+                                        vector-ref
+                                        bench-vector 1))
+(benchmark (iteration-by-index-truncate u32vector-length
+                                        u32vector-ref
+                                        bench-u32vector 1))
+(benchmark (iteration-by-index-truncate vlist-length
+                                        vlist-ref
+                                        bench-vlist 1))
+(benchmark (iteration-by-index-truncate fector-length
+                                        fector-ref
+                                        bench-fector 1))
+(benchmark (iteration-by-index-truncate bytevector-length
+                                        bytevector-u32-native-ref
+                                        bench-bytevector-u32 4))
+
+(display ";;;; benchmarking concatenation...") (newline)
+(benchmark (ivector-append bench-ivector bench-ivector))
+(benchmark (ivector-u32-append bench-ivector-u32 bench-ivector-u32))
+(benchmark (append bench-list bench-list))
+(benchmark (vector-append bench-vector bench-vector))
+(benchmark (vlist-append bench-vlist bench-vlist))
diff --git a/third_party/immer/extra/guile/example.scm b/third_party/immer/extra/guile/example.scm
new file mode 100644
index 0000000000..6649508ced
--- /dev/null
+++ b/third_party/immer/extra/guile/example.scm
@@ -0,0 +1,51 @@
+;;
+;; immer: immutable data structures for C++
+;; Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+;;
+;; This software is distributed under the Boost Software License, Version 1.0.
+;; See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+;;
+
+;; include:intro/start
+(use-modules (immer)
+             (rnrs base))
+
+(let ((v1 (ivector 1 "hola" 3 'que #:tal)))
+  (assert (eq? (ivector-ref v1 3) 'que))
+
+  (let* ((v2 (ivector-set v1 3 'what))
+         (v2 (ivector-update v2 2 (lambda (x) (+ 1 x)))))
+    (assert (eq? (ivector-ref v1 2) 3))
+    (assert (eq? (ivector-ref v1 3) 'que))
+    (assert (eq? (ivector-ref v2 2) 4))
+    (assert (eq? (ivector-ref v2 3) 'what))
+
+    (let ((v3 (ivector-push v2 "hehe")))
+      (assert (eq? (ivector-length v3) 6))
+      (assert (eq? (ivector-ref v3 (- (ivector-length v3) 1)) "hehe")))))
+
+(let ((v (apply ivector (iota 10))))
+  (assert (eq? (ivector-length v) 10))
+  (assert (eq? (ivector-length (ivector-drop v 3)) 7))
+  (assert (eq? (ivector-length (ivector-take v 3)) 3))
+  (assert (eq? (ivector-length (ivector-append v v)) 20)))
+
+(let ((v1 (make-ivector 3))
+      (v2 (make-ivector 3 ":)")))
+  (assert (eq? (ivector-ref v1 2)
+               (vector-ref (make-vector 3) 2)))
+  (assert (eq? (ivector-ref v2 2) ":)")))
+;; include:intro/end
+
+;; Experiments
+
+(let ((d (dummy)))
+  (dummy-foo d)
+  (dummy-bar d 42))
+(gc)
+
+(func1)
+(func2)
+(func3 (dummy) 12)
+(foo-func1)
+(gc)
diff --git a/third_party/immer/extra/guile/immer.scm.in b/third_party/immer/extra/guile/immer.scm.in
new file mode 100644
index 0000000000..8624aabd5b
--- /dev/null
+++ b/third_party/immer/extra/guile/immer.scm.in
@@ -0,0 +1,5 @@
+(define-module (immer))
+
+;; The extension automatically exports the names via 'scm_c_export'
+(load-extension "@GUILE_EXTENSION_DIR@/libguile-immer"
+                "init_immer")
diff --git a/third_party/immer/extra/guile/scm/detail/convert.hpp b/third_party/immer/extra/guile/scm/detail/convert.hpp
new file mode 100644
index 0000000000..4c87ff1856
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/convert.hpp
@@ -0,0 +1,58 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/util.hpp>
+
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+
+#include <libguile.h>
+
+namespace scm {
+namespace detail {
+
+template <typename T, typename Enable=void>
+struct convert;
+
+template <typename T>
+auto to_scm(T&& v)
+    -> SCM_DECLTYPE_RETURN(
+        convert<std::decay_t<T>>::to_scm(std::forward<T>(v)));
+
+template <typename T>
+auto to_cpp(SCM v)
+    -> SCM_DECLTYPE_RETURN(
+        convert<std::decay_t<T>>::to_cpp(v));
+
+} // namespace detail
+} // namespace scm
+
+#define SCM_DECLARE_NUMERIC_TYPE(cpp_name__, scm_name__)                \
+    namespace scm {                                                     \
+    namespace detail {                                                  \
+    template <>                                                         \
+    struct convert<cpp_name__> {                                        \
+        static cpp_name__ to_cpp(SCM v) { return scm_to_ ## scm_name__(v); } \
+        static SCM to_scm(cpp_name__ v) { return scm_from_ ## scm_name__(v); } \
+    };                                                                  \
+    }} /* namespace scm::detail */                                      \
+    /**/
+
+SCM_DECLARE_NUMERIC_TYPE(float,         double);
+SCM_DECLARE_NUMERIC_TYPE(double,        double);
+SCM_DECLARE_NUMERIC_TYPE(std::int8_t,   int8);
+SCM_DECLARE_NUMERIC_TYPE(std::int16_t,  int16);
+SCM_DECLARE_NUMERIC_TYPE(std::int32_t,  int32);
+SCM_DECLARE_NUMERIC_TYPE(std::int64_t,  int64);
+SCM_DECLARE_NUMERIC_TYPE(std::uint8_t,  uint8);
+SCM_DECLARE_NUMERIC_TYPE(std::uint16_t, uint16);
+SCM_DECLARE_NUMERIC_TYPE(std::uint32_t, uint32);
+SCM_DECLARE_NUMERIC_TYPE(std::uint64_t, uint64);
diff --git a/third_party/immer/extra/guile/scm/detail/define.hpp b/third_party/immer/extra/guile/scm/detail/define.hpp
new file mode 100644
index 0000000000..08b6e76338
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/define.hpp
@@ -0,0 +1,36 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#ifndef SCM_AUTO_EXPORT
+#define SCM_AUTO_EXPORT 1
+#endif
+
+#include <scm/detail/subr_wrapper.hpp>
+#include <scm/list.hpp>
+
+namespace scm {
+namespace detail {
+
+template <typename Tag, typename Fn>
+static void define_impl(const std::string& name, Fn fn)
+{
+    using args_t = function_args_t<Fn>;
+    constexpr auto args_size = pack_size_v<args_t>;
+    constexpr auto has_rest  = std::is_same<pack_last_t<args_t>, scm::args>{};
+    constexpr auto arg_count = args_size - has_rest;
+    auto subr = (scm_t_subr) +subr_wrapper_aux<Tag>(fn, args_t{});
+    scm_c_define_gsubr(name.c_str(), arg_count, 0, has_rest, subr);
+#if SCM_AUTO_EXPORT
+    scm_c_export(name.c_str());
+#endif
+}
+
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/finalizer_wrapper.hpp b/third_party/immer/extra/guile/scm/detail/finalizer_wrapper.hpp
new file mode 100644
index 0000000000..258249eb2c
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/finalizer_wrapper.hpp
@@ -0,0 +1,62 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/invoke.hpp>
+#include <scm/detail/function_args.hpp>
+#include <scm/detail/convert.hpp>
+
+namespace scm {
+namespace detail {
+// this anonymous namespace should help avoiding registration clashes
+// among translation units.
+namespace {
+
+template <typename Tag, typename Fn>
+auto finalizer_wrapper_impl(Fn fn, pack<>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] { invoke(fn_); };
+}
+template <typename Tag, typename Fn, typename T1>
+auto finalizer_wrapper_impl(Fn fn, pack<T1>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1) { invoke(fn_, to_cpp<T1>(a1)); };
+}
+template <typename Tag, typename Fn, typename T1, typename T2>
+auto finalizer_wrapper_impl(Fn fn, pack<T1, T2>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2) {
+        invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2));
+    };
+}
+template <typename Tag, typename Fn, typename T1, typename T2, typename T3>
+auto finalizer_wrapper_impl(Fn fn, pack<T1, T2, T3>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2, SCM a3) {
+        invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2), to_cpp<T3>(a3));
+    };
+}
+
+template <typename Tag, typename Fn>
+auto finalizer_wrapper(Fn fn)
+{
+    return finalizer_wrapper_impl<Tag>(fn, function_args_t<Fn>{});
+}
+
+} // anonymous namespace
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/function_args.hpp b/third_party/immer/extra/guile/scm/detail/function_args.hpp
new file mode 100644
index 0000000000..809e3eb197
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/function_args.hpp
@@ -0,0 +1,21 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/pack.hpp>
+#include <boost/callable_traits/args.hpp>
+
+namespace scm {
+namespace detail {
+
+template <typename Fn>
+using function_args_t = boost::callable_traits::args_t<Fn, pack>;
+
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/invoke.hpp b/third_party/immer/extra/guile/scm/detail/invoke.hpp
new file mode 100644
index 0000000000..d9f2b37cce
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/invoke.hpp
@@ -0,0 +1,39 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+// Adapted from the official std::invoke proposal:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4169.html
+
+#include <type_traits>
+#include <functional>
+
+namespace scm {
+namespace detail {
+
+template <typename Functor, typename... Args>
+std::enable_if_t<
+    std::is_member_pointer<std::decay_t<Functor>>::value,
+    std::result_of_t<Functor&&(Args&&...)>>
+invoke(Functor&& f, Args&&... args)
+{
+    return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Functor, typename... Args>
+std::enable_if_t<
+    !std::is_member_pointer<std::decay_t<Functor>>::value,
+    std::result_of_t<Functor&&(Args&&...)>>
+invoke(Functor&& f, Args&&... args)
+{
+    return std::forward<Functor>(f)(std::forward<Args>(args)...);
+}
+
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/pack.hpp b/third_party/immer/extra/guile/scm/detail/pack.hpp
new file mode 100644
index 0000000000..9a1813570b
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/pack.hpp
@@ -0,0 +1,52 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+namespace scm {
+namespace detail {
+
+struct none_t;
+
+template <typename... Ts>
+struct pack {};
+
+template <typename Pack>
+struct pack_size;
+
+template <typename... Ts>
+struct pack_size<pack<Ts...>>
+{
+    static constexpr auto value = sizeof...(Ts);
+};
+
+template <typename Pack>
+constexpr auto pack_size_v = pack_size<Pack>::value;
+
+template <typename Pack>
+struct pack_last
+{
+    using type = none_t;
+};
+
+template <typename T, typename ...Ts>
+struct pack_last<pack<T, Ts...>>
+    : pack_last<pack<Ts...>>
+{};
+
+template <typename T>
+struct pack_last<pack<T>>
+{
+    using type = T;
+};
+
+template <typename Pack>
+using pack_last_t = typename pack_last<Pack>::type;
+
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/subr_wrapper.hpp b/third_party/immer/extra/guile/scm/detail/subr_wrapper.hpp
new file mode 100644
index 0000000000..fc11ff1c51
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/subr_wrapper.hpp
@@ -0,0 +1,111 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/invoke.hpp>
+#include <scm/detail/function_args.hpp>
+#include <scm/detail/convert.hpp>
+
+namespace scm {
+namespace detail {
+
+// this anonymous namespace should help avoiding registration clashes
+// among translation units.
+namespace {
+
+template <typename Tag, typename R, typename Fn>
+auto subr_wrapper_impl(Fn fn, pack<R>, pack<>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] () -> SCM { return to_scm(invoke(fn_)); };
+}
+template <typename Tag, typename Fn, typename R, typename T1>
+auto subr_wrapper_impl(Fn fn, pack<R>, pack<T1>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1) -> SCM {
+        return to_scm(invoke(fn_, to_cpp<T1>(a1)));
+    };
+}
+template <typename Tag, typename Fn, typename R, typename T1, typename T2>
+auto subr_wrapper_impl(Fn fn, pack<R>, pack<T1, T2>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2) -> SCM {
+        return to_scm(invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2)));
+    };
+}
+template <typename Tag, typename Fn, typename R, typename T1, typename T2,
+          typename T3>
+auto subr_wrapper_impl(Fn fn, pack<R>, pack<T1, T2, T3>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2, SCM a3) -> SCM {
+        return to_scm(invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2),
+                             to_cpp<T3>(a3)));
+    };
+}
+
+template <typename Tag, typename Fn>
+auto subr_wrapper_impl(Fn fn, pack<void>, pack<>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] () -> SCM { invoke(fn_); return SCM_UNSPECIFIED; };
+}
+template <typename Tag, typename Fn, typename T1>
+auto subr_wrapper_impl(Fn fn, pack<void>, pack<T1>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1) -> SCM {
+        invoke(fn_, to_cpp<T1>(a1)); return SCM_UNSPECIFIED;
+    };
+}
+template <typename Tag, typename Fn, typename T1, typename T2>
+auto subr_wrapper_impl(Fn fn, pack<void>, pack<T1, T2>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2) -> SCM {
+        invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2));
+        return SCM_UNSPECIFIED;
+    };
+}
+template <typename Tag, typename Fn, typename T1, typename T2, typename T3>
+auto subr_wrapper_impl(Fn fn, pack<void>, pack<T1, T2, T3>)
+{
+    check_call_once<Tag, Fn>();
+    static const Fn fn_ = fn;
+    return [] (SCM a1, SCM a2, SCM a3) -> SCM {
+        invoke(fn_, to_cpp<T1>(a1), to_cpp<T2>(a2), to_cpp<T3>(a3));
+        return SCM_UNSPECIFIED;
+    };
+}
+
+template <typename Tag, typename Fn, typename... Args>
+auto subr_wrapper_aux(Fn fn, pack<Args...>)
+{
+    return subr_wrapper_impl<Tag>(
+        fn, pack<std::result_of_t<Fn(Args...)>>{}, pack<Args...>{});
+}
+
+template <typename Tag, typename Fn>
+auto subr_wrapper(Fn fn)
+{
+    return subr_wrapper_aux<Tag>(fn, function_args_t<Fn>{});
+}
+
+} // anonymous namespace
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/detail/util.hpp b/third_party/immer/extra/guile/scm/detail/util.hpp
new file mode 100644
index 0000000000..fdc323722e
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/detail/util.hpp
@@ -0,0 +1,49 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <libguile.h>
+
+namespace scm {
+namespace detail {
+
+#define SCM_DECLTYPE_RETURN(...)                \
+    decltype(__VA_ARGS__)                       \
+    { return __VA_ARGS__; }                     \
+    /**/
+
+template <typename... Ts>
+constexpr bool is_valid_v = true;
+
+template <typename... Ts>
+using is_valid_t = void;
+
+template <typename... Ts>
+void check_call_once()
+{
+    static bool called = false;
+    if (called) scm_misc_error (nullptr, "Double defined binding. \
+This may be caused because there are multiple C++ binding groups in the same \
+translation unit.  You may solve this by using different type tags for each \
+binding group.", SCM_EOL);
+    called = true;
+}
+
+struct move_sequence
+{
+    move_sequence() = default;
+    move_sequence(const move_sequence&) = delete;
+    move_sequence(move_sequence&& other)
+    { other.moved_from_ = true; };
+
+    bool moved_from_ = false;
+};
+
+} // namespace detail
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/group.hpp b/third_party/immer/extra/guile/scm/group.hpp
new file mode 100644
index 0000000000..69cd385820
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/group.hpp
@@ -0,0 +1,88 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/define.hpp>
+#include <string>
+
+namespace scm {
+namespace detail {
+
+template <typename Tag, int Seq=0>
+struct definer
+{
+    using this_t = definer;
+    using next_t = definer<Tag, Seq + 1>;
+
+    std::string group_name_ = {};
+
+    definer() = default;
+    definer(definer&&) = default;
+
+    template <int Seq2,
+              typename Enable=std::enable_if_t<Seq2 + 1 == Seq>>
+    definer(definer<Tag, Seq2>)
+    {}
+
+    template <typename Fn>
+    next_t define(std::string name, Fn fn) &&
+    {
+        define_impl<this_t>(name, fn);
+        return { std::move(*this) };
+    }
+
+    template <typename Fn>
+    next_t maker(Fn fn) &&
+    {
+        define_impl<this_t>("make", fn);
+        return { std::move(*this) };
+    }
+};
+
+template <typename Tag, int Seq=0>
+struct group_definer
+{
+    using this_t = group_definer;
+    using next_t = group_definer<Tag, Seq + 1>;
+
+    std::string group_name_ = {};
+
+    group_definer(std::string name)
+        : group_name_{std::move(name)} {}
+
+    group_definer(group_definer&&) = default;
+
+    template <int Seq2,
+              typename Enable=std::enable_if_t<Seq2 + 1 == Seq>>
+    group_definer(group_definer<Tag, Seq2>)
+    {}
+
+    template <typename Fn>
+    next_t define(std::string name, Fn fn) &&
+    {
+        define_impl<this_t>(group_name_ + "-" + name, fn);
+        return { std::move(*this) };
+    }
+};
+
+} // namespace detail
+
+template <typename Tag=void>
+detail::definer<Tag> group()
+{
+    return {};
+}
+
+template <typename Tag=void>
+detail::group_definer<Tag> group(std::string name)
+{
+    return { std::move(name) };
+}
+
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/list.hpp b/third_party/immer/extra/guile/scm/list.hpp
new file mode 100644
index 0000000000..dc162c2002
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/list.hpp
@@ -0,0 +1,54 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/val.hpp>
+#include <iostream>
+
+namespace scm {
+
+struct list : detail::wrapper
+{
+    using base_t = detail::wrapper;
+    using base_t::base_t;
+
+    using iterator = list;
+    using value_type = val;
+
+    list() : base_t{SCM_EOL} {};
+    list end() const { return {}; }
+    list begin() const { return *this; }
+
+    explicit operator bool() { return handle_ != SCM_EOL; }
+
+    val operator* () const { return val{scm_car(handle_)}; }
+
+    list& operator++ ()
+    {
+        handle_ = scm_cdr(handle_);
+        return *this;
+    }
+
+    list operator++ (int)
+    {
+        auto result = *this;
+        result.handle_ = scm_cdr(handle_);
+        return result;
+    }
+};
+
+struct args : list
+{
+    using list::list;
+};
+
+} // namespace scm
+
+SCM_DECLARE_WRAPPER_TYPE(scm::list);
+SCM_DECLARE_WRAPPER_TYPE(scm::args);
diff --git a/third_party/immer/extra/guile/scm/scm.hpp b/third_party/immer/extra/guile/scm/scm.hpp
new file mode 100644
index 0000000000..f4e4989a44
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/scm.hpp
@@ -0,0 +1,14 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/val.hpp>
+#include <scm/list.hpp>
+#include <scm/group.hpp>
+#include <scm/type.hpp>
diff --git a/third_party/immer/extra/guile/scm/type.hpp b/third_party/immer/extra/guile/scm/type.hpp
new file mode 100644
index 0000000000..da53ed46ef
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/type.hpp
@@ -0,0 +1,153 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/finalizer_wrapper.hpp>
+#include <scm/detail/define.hpp>
+#include <string>
+
+namespace scm {
+namespace detail {
+
+template <typename T>
+struct foreign_type_storage
+{
+    static SCM data;
+};
+
+template <typename T>
+SCM foreign_type_storage<T>::data = SCM_UNSPECIFIED;
+
+template <typename T>
+struct convert_foreign_type
+{
+    using storage_t = foreign_type_storage<T>;
+    static T& to_cpp(SCM v)
+    {
+        assert(storage_t::data != SCM_UNSPECIFIED &&
+               "can not convert to undefined type");
+        scm_assert_foreign_object_type(storage_t::data, v);
+        return *(T*)scm_foreign_object_ref(v, 0);
+    }
+
+    template <typename U>
+    static SCM to_scm(U&& v)
+    {
+        assert(storage_t::data != SCM_UNSPECIFIED &&
+               "can not convert from undefined type");
+        return scm_make_foreign_object_1(
+            storage_t::data,
+            new (scm_gc_malloc(sizeof(T), "scmpp")) T(
+                std::forward<U>(v)));
+    }
+};
+
+// Assume that every other type is foreign
+template <typename T>
+struct convert<T,
+               std::enable_if_t<!std::is_fundamental<T>::value &&
+                                // only value types are supported at
+                                // the moment but the story might
+                                // change later...
+                                !std::is_pointer<T>::value>>
+    : convert_foreign_type<T>
+{
+};
+
+template <typename Tag, typename T, int Seq=0>
+struct type_definer : move_sequence
+{
+    using this_t = type_definer;
+    using next_t = type_definer<Tag, T, Seq + 1>;
+
+    std::string type_name_ = {};
+    scm_t_struct_finalize finalizer_ = nullptr;
+
+    type_definer(type_definer&&) = default;
+
+    type_definer(std::string type_name)
+        : type_name_(std::move(type_name))
+    {}
+
+    ~type_definer()
+    {
+        if (!moved_from_) {
+            using storage_t = detail::foreign_type_storage<T>;
+            assert(storage_t::data == SCM_UNSPECIFIED);
+            storage_t::data = scm_make_foreign_object_type(
+                scm_from_utf8_symbol(("<" + type_name_ + ">").c_str()),
+                scm_list_1(scm_from_utf8_symbol("data")),
+                finalizer_);
+        }
+    }
+
+    template <int Seq2, typename Enable=std::enable_if_t<Seq2 + 1 == Seq>>
+    type_definer(type_definer<Tag, T, Seq2> r)
+        : move_sequence{std::move(r)}
+        , type_name_{std::move(r.type_name_)}
+        , finalizer_{std::move(r.finalizer_)}
+    {}
+
+    next_t constructor() &&
+    {
+        define_impl<this_t>(type_name_, [] { return T{}; });
+        return { std::move(*this) };
+    }
+
+    template <typename Fn>
+    next_t constructor(Fn fn) &&
+    {
+        define_impl<this_t>(type_name_, fn);
+        return { std::move(*this) };
+    }
+
+    next_t finalizer() &&
+    {
+        finalizer_ = (scm_t_struct_finalize) +finalizer_wrapper<Tag>(
+            [] (T& x) { x.~T(); });
+        return { std::move(*this) };
+    }
+
+    template <typename Fn>
+    next_t finalizer(Fn fn) &&
+    {
+        finalizer_ = (scm_t_struct_finalize) +finalizer_wrapper<Tag>(fn);
+        return { std::move(*this) };
+    }
+
+    next_t maker() &&
+    {
+        define_impl<this_t>("make-" + type_name_, [] { return T{}; });
+        return { std::move(*this) };
+    }
+
+    template <typename Fn>
+    next_t maker(Fn fn) &&
+    {
+        define_impl<this_t>("make-" + type_name_, fn);
+        return { std::move(*this) };
+    }
+
+    template <typename Fn>
+    next_t define(std::string name, Fn fn) &&
+    {
+        define_impl<this_t>(type_name_ + "-" + name, fn);
+        return { std::move(*this) };
+    }
+};
+
+} // namespace detail
+
+template <typename Tag, typename T=Tag>
+detail::type_definer<Tag, T> type(std::string type_name)
+{
+    return { type_name };
+}
+
+} // namespace scm
diff --git a/third_party/immer/extra/guile/scm/val.hpp b/third_party/immer/extra/guile/scm/val.hpp
new file mode 100644
index 0000000000..63d7189262
--- /dev/null
+++ b/third_party/immer/extra/guile/scm/val.hpp
@@ -0,0 +1,88 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <scm/detail/convert.hpp>
+
+namespace scm {
+namespace detail {
+
+template <typename T>
+struct convert_wrapper_type
+{
+    static T to_cpp(SCM v) { return T{v}; }
+    static SCM to_scm(T v) { return v.get(); }
+};
+
+struct wrapper
+{
+    wrapper() = default;
+    wrapper(SCM hdl) : handle_{hdl} {}
+    SCM get() const { return handle_; }
+    operator SCM () const { return handle_; }
+
+    bool operator==(wrapper other) { return handle_ == other.handle_; }
+    bool operator!=(wrapper other) { return handle_ != other.handle_; }
+
+protected:
+    SCM handle_ = SCM_UNSPECIFIED;
+};
+
+} // namespace detail
+
+struct val : detail::wrapper
+{
+    using base_t = detail::wrapper;
+    using base_t::base_t;
+
+    template <typename T,
+              typename = std::enable_if_t<
+                  (!std::is_same<std::decay_t<T>, val>{} &&
+                   !std::is_same<std::decay_t<T>, SCM>{})>>
+    val(T&& x)
+        : base_t(detail::to_scm(std::forward<T>(x)))
+    {}
+
+    template <typename T,
+              typename = std::enable_if_t<
+                  std::is_same<T, decltype(detail::to_cpp<T>(SCM{}))>{}>>
+    operator T() const { return detail::to_cpp<T>(handle_); }
+
+    template <typename T,
+              typename = std::enable_if_t<
+                  std::is_same<T&, decltype(detail::to_cpp<T>(SCM{}))>{}>>
+    operator T& () const { return detail::to_cpp<T>(handle_); }
+
+    template <typename T,
+              typename = std::enable_if_t<
+                  std::is_same<const T&, decltype(detail::to_cpp<T>(SCM{}))>{}>>
+    operator const T& () const { return detail::to_cpp<T>(handle_); }
+
+    val operator() () const
+    { return val{scm_call_0(get())}; }
+    val operator() (val a0) const
+    { return val{scm_call_1(get(), a0)}; }
+    val operator() (val a0, val a1) const
+    { return val{scm_call_2(get(), a0, a1)}; }
+    val operator() (val a0, val a1, val a3) const
+    { return val{scm_call_3(get(), a0, a1, a3)}; }
+};
+
+} // namespace scm
+
+#define SCM_DECLARE_WRAPPER_TYPE(cpp_name__)                            \
+    namespace scm {                                                     \
+    namespace detail {                                                  \
+    template <>                                                         \
+    struct convert<cpp_name__>                                          \
+        : convert_wrapper_type<cpp_name__> {};                          \
+    }} /* namespace scm::detail */                                      \
+    /**/
+
+SCM_DECLARE_WRAPPER_TYPE(val);
diff --git a/third_party/immer/extra/guile/src/immer.cpp b/third_party/immer/extra/guile/src/immer.cpp
new file mode 100644
index 0000000000..7933447a8d
--- /dev/null
+++ b/third_party/immer/extra/guile/src/immer.cpp
@@ -0,0 +1,153 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/algorithm.hpp>
+#include <scm/scm.hpp>
+#include <iostream>
+
+namespace {
+
+struct guile_heap
+{
+    static void* allocate(std::size_t size)
+    { return scm_gc_malloc(size, "immer"); }
+
+    static void* allocate(std::size_t size, immer::norefs_tag)
+    { return scm_gc_malloc_pointerless(size, "immer"); }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t size, void* obj, Tags...)
+    { scm_gc_free(obj, size, "immer"); }
+};
+
+using guile_memory = immer::memory_policy<
+    immer::heap_policy<guile_heap>,
+    immer::no_refcount_policy,
+    immer::gc_transience_policy,
+    false>;
+
+template <typename T>
+using guile_ivector = immer::flex_vector<T, guile_memory>;
+
+struct dummy
+{
+    SCM port_ = scm_current_warning_port();
+
+    dummy(dummy&&)
+    { scm_puts("~~ dummy move constructor\n", port_); }
+
+    dummy()
+    { scm_puts("~~ dummy default constructor\n", port_); }
+
+    ~dummy()
+    { scm_puts("~~ dummy finalized\n", port_); }
+
+    void foo()
+    { scm_puts("~~ dummy foo\n", port_); }
+
+    int bar(int x)
+    {
+        auto res = x + 42;
+        scm_puts("~~ dummy bar: ", port_);
+        scm_display(scm::val{res}, port_);
+        scm_newline(port_);
+        return res;
+    }
+};
+
+template <int I>
+void func()
+{
+    auto port = scm_current_warning_port();
+    scm_puts("~~ func", port);
+    scm_display(scm_from_int(I), port);
+    scm_newline(port);
+}
+
+template <typename T = scm::val>
+void init_ivector(std::string type_name = "")
+{
+    using namespace std::string_literals;
+
+    using self_t = guile_ivector<T>;
+    using size_t = typename self_t::size_type;
+
+    auto name = "ivector"s + (type_name.empty() ? ""s : "-" + type_name);
+
+    scm::type<self_t>(name)
+        .constructor([] (scm::args rest) {
+            return self_t(rest.begin(), rest.end());
+        })
+        .maker([] (size_t n, scm::args rest) {
+            return self_t(n, rest ? *rest : scm::val{});
+        })
+        .define("ref", &self_t::operator[])
+        .define("length", &self_t::size)
+        .define("set", [] (const self_t& v, size_t i, scm::val x) {
+            return v.set(i, x);
+        })
+        .define("update", [] (const self_t& v, size_t i, scm::val fn) {
+            return v.update(i, fn);
+        })
+        .define("push", [] (const self_t& v, scm::val x) {
+            return v.push_back(x);
+        })
+        .define("take", [] (const self_t& v, size_t s) {
+            return v.take(s);
+        })
+        .define("drop", [] (const self_t& v, size_t s) {
+            return v.drop(s);
+        })
+        .define("append", [] (self_t v, scm::args rest) {
+            for (auto x : rest)
+                v = v + x;
+            return v;
+        })
+        .define("fold", [] (scm::val fn, scm::val first, const self_t& v) {
+            return immer::accumulate(v, first, fn);
+        })
+        ;
+}
+
+} // anonymous namespace
+
+struct bar_tag_t {};
+
+extern "C"
+void init_immer()
+{
+    scm::type<dummy>("dummy")
+        .constructor()
+        .finalizer()
+        .define("foo", &dummy::foo)
+        .define("bar", &dummy::bar);
+
+    scm::group()
+        .define("func1", func<1>);
+
+    scm::group<bar_tag_t>()
+        .define("func2", func<2>)
+        .define("func3", &dummy::bar);
+
+    scm::group("foo")
+        .define("func1", func<1>);
+
+    init_ivector();
+    init_ivector<std::uint8_t>("u8");
+    init_ivector<std::uint16_t>("u16");
+    init_ivector<std::uint32_t>("u32");
+    init_ivector<std::uint64_t>("u64");
+    init_ivector<std::int8_t>("s8");
+    init_ivector<std::int16_t>("s16");
+    init_ivector<std::int32_t>("s32");
+    init_ivector<std::int64_t>("s64");
+    init_ivector<float>("f32");
+    init_ivector<double>("f64");
+}
diff --git a/third_party/immer/extra/js/immer.cpp b/third_party/immer/extra/js/immer.cpp
new file mode 100644
index 0000000000..d0d77c8a2c
--- /dev/null
+++ b/third_party/immer/extra/js/immer.cpp
@@ -0,0 +1,79 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+#include <emscripten/bind.h>
+
+namespace {
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<immer::malloc_heap>,
+    immer::unsafe_refcount_policy>;
+
+template <typename T>
+using js_vector_t = immer::vector<T, memory_t>;
+
+template <typename VectorT>
+VectorT range(typename VectorT::value_type first,
+              typename VectorT::value_type last)
+{
+    auto v = VectorT{};
+    for (; first != last; ++first)
+        v = std::move(v).push_back(first);
+    return v;
+}
+
+template <typename VectorT>
+VectorT range_slow(typename VectorT::value_type first,
+                   typename VectorT::value_type last)
+{
+    auto v = VectorT{};
+    for (; first != last; ++first)
+        v = v.push_back(first);
+    return v;
+}
+
+template <typename VectorT>
+VectorT push_back(VectorT& v, typename VectorT::value_type x)
+{ return v.push_back(x); }
+
+template <typename VectorT>
+VectorT set(VectorT& v, std::size_t i, typename VectorT::value_type x)
+{ return v.set(i, x); }
+
+template <typename T>
+void bind_vector(const char* name)
+{
+    using emscripten::class_;
+
+    using vector_t = js_vector_t<T>;
+
+    class_<vector_t>(name)
+        .constructor()
+        .function("push",  &push_back<vector_t>)
+        .function("set",   &set<vector_t>)
+        .function("get",   &vector_t::operator[])
+        .property("size",  &vector_t::size);
+}
+
+} // anonymous namespace
+
+EMSCRIPTEN_BINDINGS(immer)
+{
+    using emscripten::function;
+
+    bind_vector<emscripten::val>("Vector");
+    bind_vector<int>("VectorInt");
+    bind_vector<double>("VectorNumber");
+
+    function("range_int", &range<js_vector_t<int>>);
+    function("rangeSlow_int", &range_slow<js_vector_t<int>>);
+    function("range_double", &range<js_vector_t<double>>);
+    function("rangeSlow_double", &range_slow<js_vector_t<double>>);
+}
diff --git a/third_party/immer/extra/js/index.js b/third_party/immer/extra/js/index.js
new file mode 100644
index 0000000000..6da994164f
--- /dev/null
+++ b/third_party/immer/extra/js/index.js
@@ -0,0 +1,74 @@
+/*
+ * immer: immutable data structures for C++
+ * Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+ *
+ * This software is distributed under the Boost Software License, Version 1.0.
+ * See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+ */
+
+var immer = Module
+
+var N = 1000
+
+var suite = new Benchmark.Suite('push')
+    .add('Immutable.List', function(){
+        var v = new Immutable.List
+        for (var x = 0; x < N; ++x)
+            v = v.push(x)
+    })
+    .add('mori.vector', function(){
+        var v = mori.vector()
+        for (var x = 0; x < N; ++x)
+            v = mori.conj.f2(v, x)
+    })
+    .add('mori.vector-Transient', function(){
+        var v = mori.mutable.thaw(mori.vector())
+        for (var x = 0; x < N; ++x)
+            v = mori.mutable.conj.f2(v, x)
+        return mori.mutable.freeze(v)
+    })
+    .add('immer.Vector', function(){
+        var v = new immer.Vector
+        for (var x = 0; x < N; ++x) {
+            var v_ = v
+            v = v.push(x)
+            v_.delete()
+        }
+    })
+    .add('immer.VectorInt', function(){
+        var v = new immer.VectorInt
+        for (var x = 0; x < N; ++x) {
+            var v_ = v
+            v = v.push(x)
+            v_.delete()
+        }
+        v.delete()
+    })
+    .add('immer.VectorNumber', function(){
+        var v = new immer.VectorNumber
+        for (var x = 0; x < N; ++x) {
+            var v_ = v
+            v = v.push(x)
+            v_.delete()
+        }
+        v.delete()
+    })
+    .add('immer.VectorInt-Native', function(){
+        immer.rangeSlow_int(0, N).delete()
+    })
+    .add('immer.VectorInt-NativeTransient', function(){
+        immer.range_int(0, N).delete()
+    })
+    .add('immer.VectorDouble-Native', function(){
+        immer.rangeSlow_double(0, N).delete()
+    })
+    .add('immer.VectorDouble-NativeTransient', function(){
+        immer.range_double(0, N).delete()
+    })
+    .on('cycle', function(event) {
+        console.log(String(event.target));
+    })
+    .on('complete', function() {
+        console.log('Fastest is ' + this.filter('fastest').map('name'));
+    })
+    .run({ 'async': true })
diff --git a/third_party/immer/extra/js/index.tpl.html b/third_party/immer/extra/js/index.tpl.html
new file mode 100644
index 0000000000..fdbd40a86a
--- /dev/null
+++ b/third_party/immer/extra/js/index.tpl.html
@@ -0,0 +1,77 @@
+<!--
+    immer: immutable data structures for C++
+    Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+
+    This software is distributed under the Boost Software License, Version 1.0.
+    See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+-->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <script src="../lib/lodash.js"></script>
+    <script src="../lib/platform.js"></script>
+    <script src="../lib/benchmark.js"></script>
+    <script src="../lib/immutable.min.js"></script>
+    <script src="../lib/mori.js"></script>
+    <script>
+    </script>
+  </head>
+  <body>
+    <script>
+     var IMMER_WASM_HAS_MEM = false;
+     var IMMER_JS_FILE = '%IMMER_JS%'
+     var Module = {};
+
+     if (IMMER_JS_FILE.endsWith('wasm.js'))
+         loadWasm()
+     else if (IMMER_JS_FILE.endsWith('asmjs.js'))
+         loadAsmjs()
+     else
+         throw Error('Wrong format')
+
+     function loadAsync(u, c) {
+         var d = document, t = 'script',
+             o = d.createElement(t),
+             s = d.getElementsByTagName(t)[0];
+         o.src = u;
+         if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
+         s.parentNode.insertBefore(o, s);
+     }
+
+     function loadAsmjs() {
+         loadAsync(IMMER_JS_FILE, function () {
+             loadAsync('../index.js')
+         });
+     }
+
+     function loadWasm() {
+         var xhr = new XMLHttpRequest();
+         var basename = IMMER_JS_FILE.substring(0, IMMER_JS_FILE.length-3)
+         xhr.open('GET', basename + '.wasm', true);
+         xhr.responseType = 'arraybuffer';
+         xhr.onload = function() {
+             Module.wasmBinary = xhr.response;
+             if (IMMER_WASM_HAS_MEM) {
+                 (function() {
+                     var memoryInitializer = IMMER_JS_FILE + '.mem';
+                     if (typeof Module['locateFile'] === 'function') {
+                         memoryInitializer = Module['locateFile'](memoryInitializer);
+                     } else if (Module['memoryInitializerPrefixURL']) {
+                         memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
+                     }
+                     var xhr = Module['memoryInitializerRequest'] = new XMLHttpRequest();
+                     xhr.open('GET', memoryInitializer, true);
+                     xhr.responseType = 'arraybuffer';
+                     xhr.send(null);
+                 })();
+             }
+             loadAsync(IMMER_JS_FILE, function () {
+                 loadAsync('../index.js')
+             });
+         };
+         xhr.send(null);
+     }
+    </script>
+  </body>
+</html>
diff --git a/third_party/immer/extra/js/lib/benchmark.js b/third_party/immer/extra/js/lib/benchmark.js
new file mode 100644
index 0000000000..11bc2babaf
--- /dev/null
+++ b/third_party/immer/extra/js/lib/benchmark.js
@@ -0,0 +1,2811 @@
+/*!
+ * Benchmark.js <https://benchmarkjs.com/>
+ * Copyright 2010-2016 Mathias Bynens <https://mths.be/>
+ * Based on JSLitmus.js, copyright Robert Kieffer <http://broofa.com/>
+ * Modified by John-David Dalton <http://allyoucanleet.com/>
+ * Available under MIT license <https://mths.be/mit>
+ */
+;(function() {
+  'use strict';
+
+  /** Used as a safe reference for `undefined` in pre ES5 environments. */
+  var undefined;
+
+  /** Used to determine if values are of the language type Object. */
+  var objectTypes = {
+    'function': true,
+    'object': true
+  };
+
+  /** Used as a reference to the global object. */
+  var root = (objectTypes[typeof window] && window) || this;
+
+  /** Detect free variable `define`. */
+  var freeDefine = typeof define == 'function' && typeof define.amd == 'object' && define.amd && define;
+
+  /** Detect free variable `exports`. */
+  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+  /** Detect free variable `module`. */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */
+  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  /** Detect free variable `require`. */
+  var freeRequire = typeof require == 'function' && require;
+
+  /** Used to assign each benchmark an incremented id. */
+  var counter = 0;
+
+  /** Detect the popular CommonJS extension `module.exports`. */
+  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+  /** Used to detect primitive types. */
+  var rePrimitive = /^(?:boolean|number|string|undefined)$/;
+
+  /** Used to make every compiled test unique. */
+  var uidCounter = 0;
+
+  /** Used to assign default `context` object properties. */
+  var contextProps = [
+    'Array', 'Date', 'Function', 'Math', 'Object', 'RegExp', 'String', '_',
+    'clearTimeout', 'chrome', 'chromium', 'document', 'navigator', 'phantom',
+    'platform', 'process', 'runtime', 'setTimeout'
+  ];
+
+  /** Used to avoid hz of Infinity. */
+  var divisors = {
+    '1': 4096,
+    '2': 512,
+    '3': 64,
+    '4': 8,
+    '5': 0
+  };
+
+  /**
+   * T-Distribution two-tailed critical values for 95% confidence.
+   * For more info see http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm.
+   */
+  var tTable = {
+    '1':  12.706, '2':  4.303, '3':  3.182, '4':  2.776, '5':  2.571, '6':  2.447,
+    '7':  2.365,  '8':  2.306, '9':  2.262, '10': 2.228, '11': 2.201, '12': 2.179,
+    '13': 2.16,   '14': 2.145, '15': 2.131, '16': 2.12,  '17': 2.11,  '18': 2.101,
+    '19': 2.093,  '20': 2.086, '21': 2.08,  '22': 2.074, '23': 2.069, '24': 2.064,
+    '25': 2.06,   '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,
+    'infinity': 1.96
+  };
+
+  /**
+   * Critical Mann-Whitney U-values for 95% confidence.
+   * For more info see http://www.saburchill.com/IBbiology/stats/003.html.
+   */
+  var uTable = {
+    '5':  [0, 1, 2],
+    '6':  [1, 2, 3, 5],
+    '7':  [1, 3, 5, 6, 8],
+    '8':  [2, 4, 6, 8, 10, 13],
+    '9':  [2, 4, 7, 10, 12, 15, 17],
+    '10': [3, 5, 8, 11, 14, 17, 20, 23],
+    '11': [3, 6, 9, 13, 16, 19, 23, 26, 30],
+    '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37],
+    '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45],
+    '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55],
+    '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64],
+    '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75],
+    '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87],
+    '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99],
+    '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113],
+    '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119, 127],
+    '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126, 134, 142],
+    '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 133, 141, 150, 158],
+    '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 140, 149, 157, 166, 175],
+    '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138, 147, 156, 165, 174, 183, 192],
+    '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145, 154, 163, 173, 182, 192, 201, 211],
+    '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151, 161, 171, 181, 191, 200, 210, 220, 230],
+    '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158, 168, 178, 188, 199, 209, 219, 230, 240, 250],
+    '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272],
+    '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294],
+    '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317]
+  };
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Create a new `Benchmark` function using the given `context` object.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Object} [context=root] The context object.
+   * @returns {Function} Returns a new `Benchmark` function.
+   */
+  function runInContext(context) {
+    // Exit early if unable to acquire lodash.
+    var _ = context && context._ || require('lodash') || root._;
+    if (!_) {
+      Benchmark.runInContext = runInContext;
+      return Benchmark;
+    }
+    // Avoid issues with some ES3 environments that attempt to use values, named
+    // after built-in constructors like `Object`, for the creation of literals.
+    // ES5 clears this up by stating that literals must use built-in constructors.
+    // See http://es5.github.io/#x11.1.5.
+    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+    /** Native constructor references. */
+    var Array = context.Array,
+        Date = context.Date,
+        Function = context.Function,
+        Math = context.Math,
+        Object = context.Object,
+        RegExp = context.RegExp,
+        String = context.String;
+
+    /** Used for `Array` and `Object` method references. */
+    var arrayRef = [],
+        objectProto = Object.prototype;
+
+    /** Native method shortcuts. */
+    var abs = Math.abs,
+        clearTimeout = context.clearTimeout,
+        floor = Math.floor,
+        log = Math.log,
+        max = Math.max,
+        min = Math.min,
+        pow = Math.pow,
+        push = arrayRef.push,
+        setTimeout = context.setTimeout,
+        shift = arrayRef.shift,
+        slice = arrayRef.slice,
+        sqrt = Math.sqrt,
+        toString = objectProto.toString,
+        unshift = arrayRef.unshift;
+
+    /** Used to avoid inclusion in Browserified bundles. */
+    var req = require;
+
+    /** Detect DOM document object. */
+    var doc = isHostType(context, 'document') && context.document;
+
+    /** Used to access Wade Simmons' Node.js `microtime` module. */
+    var microtimeObject = req('microtime');
+
+    /** Used to access Node.js's high resolution timer. */
+    var processObject = isHostType(context, 'process') && context.process;
+
+    /** Used to prevent a `removeChild` memory leak in IE < 9. */
+    var trash = doc && doc.createElement('div');
+
+    /** Used to integrity check compiled tests. */
+    var uid = 'uid' + _.now();
+
+    /** Used to avoid infinite recursion when methods call each other. */
+    var calledBy = {};
+
+    /**
+     * An object used to flag environments/features.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @type Object
+     */
+    var support = {};
+
+    (function() {
+
+      /**
+       * Detect if running in a browser environment.
+       *
+       * @memberOf Benchmark.support
+       * @type boolean
+       */
+      support.browser = doc && isHostType(context, 'navigator') && !isHostType(context, 'phantom');
+
+      /**
+       * Detect if the Timers API exists.
+       *
+       * @memberOf Benchmark.support
+       * @type boolean
+       */
+      support.timeout = isHostType(context, 'setTimeout') && isHostType(context, 'clearTimeout');
+
+      /**
+       * Detect if function decompilation is support.
+       *
+       * @name decompilation
+       * @memberOf Benchmark.support
+       * @type boolean
+       */
+      try {
+        // Safari 2.x removes commas in object literals from `Function#toString` results.
+        // See http://webk.it/11609 for more details.
+        // Firefox 3.6 and Opera 9.25 strip grouping parentheses from `Function#toString` results.
+        // See http://bugzil.la/559438 for more details.
+        support.decompilation = Function(
+          ('return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')')
+          // Avoid issues with code added by Istanbul.
+          .replace(/__cov__[^;]+;/g, '')
+        )()(0).x === '1';
+      } catch(e) {
+        support.decompilation = false;
+      }
+    }());
+
+    /**
+     * Timer object used by `clock()` and `Deferred#resolve`.
+     *
+     * @private
+     * @type Object
+     */
+    var timer = {
+
+      /**
+       * The timer namespace object or constructor.
+       *
+       * @private
+       * @memberOf timer
+       * @type {Function|Object}
+       */
+      'ns': Date,
+
+      /**
+       * Starts the deferred timer.
+       *
+       * @private
+       * @memberOf timer
+       * @param {Object} deferred The deferred instance.
+       */
+      'start': null, // Lazy defined in `clock()`.
+
+      /**
+       * Stops the deferred timer.
+       *
+       * @private
+       * @memberOf timer
+       * @param {Object} deferred The deferred instance.
+       */
+      'stop': null // Lazy defined in `clock()`.
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The Benchmark constructor.
+     *
+     * Note: The Benchmark constructor exposes a handful of lodash methods to
+     * make working with arrays, collections, and objects easier. The lodash
+     * methods are:
+     * [`each/forEach`](https://lodash.com/docs#forEach), [`forOwn`](https://lodash.com/docs#forOwn),
+     * [`has`](https://lodash.com/docs#has), [`indexOf`](https://lodash.com/docs#indexOf),
+     * [`map`](https://lodash.com/docs#map), and [`reduce`](https://lodash.com/docs#reduce)
+     *
+     * @constructor
+     * @param {string} name A name to identify the benchmark.
+     * @param {Function|string} fn The test to benchmark.
+     * @param {Object} [options={}] Options object.
+     * @example
+     *
+     * // basic usage (the `new` operator is optional)
+     * var bench = new Benchmark(fn);
+     *
+     * // or using a name first
+     * var bench = new Benchmark('foo', fn);
+     *
+     * // or with options
+     * var bench = new Benchmark('foo', fn, {
+     *
+     *   // displayed by `Benchmark#toString` if `name` is not available
+     *   'id': 'xyz',
+     *
+     *   // called when the benchmark starts running
+     *   'onStart': onStart,
+     *
+     *   // called after each run cycle
+     *   'onCycle': onCycle,
+     *
+     *   // called when aborted
+     *   'onAbort': onAbort,
+     *
+     *   // called when a test errors
+     *   'onError': onError,
+     *
+     *   // called when reset
+     *   'onReset': onReset,
+     *
+     *   // called when the benchmark completes running
+     *   'onComplete': onComplete,
+     *
+     *   // compiled/called before the test loop
+     *   'setup': setup,
+     *
+     *   // compiled/called after the test loop
+     *   'teardown': teardown
+     * });
+     *
+     * // or name and options
+     * var bench = new Benchmark('foo', {
+     *
+     *   // a flag to indicate the benchmark is deferred
+     *   'defer': true,
+     *
+     *   // benchmark test function
+     *   'fn': function(deferred) {
+     *     // call `Deferred#resolve` when the deferred test is finished
+     *     deferred.resolve();
+     *   }
+     * });
+     *
+     * // or options only
+     * var bench = new Benchmark({
+     *
+     *   // benchmark name
+     *   'name': 'foo',
+     *
+     *   // benchmark test as a string
+     *   'fn': '[1,2,3,4].sort()'
+     * });
+     *
+     * // a test's `this` binding is set to the benchmark instance
+     * var bench = new Benchmark('foo', function() {
+     *   'My name is '.concat(this.name); // "My name is foo"
+     * });
+     */
+    function Benchmark(name, fn, options) {
+      var bench = this;
+
+      // Allow instance creation without the `new` operator.
+      if (!(bench instanceof Benchmark)) {
+        return new Benchmark(name, fn, options);
+      }
+      // Juggle arguments.
+      if (_.isPlainObject(name)) {
+        // 1 argument (options).
+        options = name;
+      }
+      else if (_.isFunction(name)) {
+        // 2 arguments (fn, options).
+        options = fn;
+        fn = name;
+      }
+      else if (_.isPlainObject(fn)) {
+        // 2 arguments (name, options).
+        options = fn;
+        fn = null;
+        bench.name = name;
+      }
+      else {
+        // 3 arguments (name, fn [, options]).
+        bench.name = name;
+      }
+      setOptions(bench, options);
+
+      bench.id || (bench.id = ++counter);
+      bench.fn == null && (bench.fn = fn);
+
+      bench.stats = cloneDeep(bench.stats);
+      bench.times = cloneDeep(bench.times);
+    }
+
+    /**
+     * The Deferred constructor.
+     *
+     * @constructor
+     * @memberOf Benchmark
+     * @param {Object} clone The cloned benchmark instance.
+     */
+    function Deferred(clone) {
+      var deferred = this;
+      if (!(deferred instanceof Deferred)) {
+        return new Deferred(clone);
+      }
+      deferred.benchmark = clone;
+      clock(deferred);
+    }
+
+    /**
+     * The Event constructor.
+     *
+     * @constructor
+     * @memberOf Benchmark
+     * @param {Object|string} type The event type.
+     */
+    function Event(type) {
+      var event = this;
+      if (type instanceof Event) {
+        return type;
+      }
+      return (event instanceof Event)
+        ? _.assign(event, { 'timeStamp': _.now() }, typeof type == 'string' ? { 'type': type } : type)
+        : new Event(type);
+    }
+
+    /**
+     * The Suite constructor.
+     *
+     * Note: Each Suite instance has a handful of wrapped lodash methods to
+     * make working with Suites easier. The wrapped lodash methods are:
+     * [`each/forEach`](https://lodash.com/docs#forEach), [`indexOf`](https://lodash.com/docs#indexOf),
+     * [`map`](https://lodash.com/docs#map), and [`reduce`](https://lodash.com/docs#reduce)
+     *
+     * @constructor
+     * @memberOf Benchmark
+     * @param {string} name A name to identify the suite.
+     * @param {Object} [options={}] Options object.
+     * @example
+     *
+     * // basic usage (the `new` operator is optional)
+     * var suite = new Benchmark.Suite;
+     *
+     * // or using a name first
+     * var suite = new Benchmark.Suite('foo');
+     *
+     * // or with options
+     * var suite = new Benchmark.Suite('foo', {
+     *
+     *   // called when the suite starts running
+     *   'onStart': onStart,
+     *
+     *   // called between running benchmarks
+     *   'onCycle': onCycle,
+     *
+     *   // called when aborted
+     *   'onAbort': onAbort,
+     *
+     *   // called when a test errors
+     *   'onError': onError,
+     *
+     *   // called when reset
+     *   'onReset': onReset,
+     *
+     *   // called when the suite completes running
+     *   'onComplete': onComplete
+     * });
+     */
+    function Suite(name, options) {
+      var suite = this;
+
+      // Allow instance creation without the `new` operator.
+      if (!(suite instanceof Suite)) {
+        return new Suite(name, options);
+      }
+      // Juggle arguments.
+      if (_.isPlainObject(name)) {
+        // 1 argument (options).
+        options = name;
+      } else {
+        // 2 arguments (name [, options]).
+        suite.name = name;
+      }
+      setOptions(suite, options);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * A specialized version of `_.cloneDeep` which only clones arrays and plain
+     * objects assigning all other values by reference.
+     *
+     * @private
+     * @param {*} value The value to clone.
+     * @returns {*} The cloned value.
+     */
+    var cloneDeep = _.partial(_.cloneDeepWith, _, function(value) {
+      // Only clone primitives, arrays, and plain objects.
+      return (_.isObject(value) && !_.isArray(value) && !_.isPlainObject(value))
+        ? value
+        : undefined;
+    });
+
+    /**
+     * Creates a function from the given arguments string and body.
+     *
+     * @private
+     * @param {string} args The comma separated function arguments.
+     * @param {string} body The function body.
+     * @returns {Function} The new function.
+     */
+    function createFunction() {
+      // Lazy define.
+      createFunction = function(args, body) {
+        var result,
+            anchor = freeDefine ? freeDefine.amd : Benchmark,
+            prop = uid + 'createFunction';
+
+        runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');
+        result = anchor[prop];
+        delete anchor[prop];
+        return result;
+      };
+      // Fix JaegerMonkey bug.
+      // For more information see http://bugzil.la/639720.
+      createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || _.noop)() == uid ? createFunction : Function;
+      return createFunction.apply(null, arguments);
+    }
+
+    /**
+     * Delay the execution of a function based on the benchmark's `delay` property.
+     *
+     * @private
+     * @param {Object} bench The benchmark instance.
+     * @param {Object} fn The function to execute.
+     */
+    function delay(bench, fn) {
+      bench._timerId = _.delay(fn, bench.delay * 1e3);
+    }
+
+    /**
+     * Destroys the given element.
+     *
+     * @private
+     * @param {Element} element The element to destroy.
+     */
+    function destroyElement(element) {
+      trash.appendChild(element);
+      trash.innerHTML = '';
+    }
+
+    /**
+     * Gets the name of the first argument from a function's source.
+     *
+     * @private
+     * @param {Function} fn The function.
+     * @returns {string} The argument name.
+     */
+    function getFirstArgument(fn) {
+      return (!_.has(fn, 'toString') &&
+        (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || '';
+    }
+
+    /**
+     * Computes the arithmetic mean of a sample.
+     *
+     * @private
+     * @param {Array} sample The sample.
+     * @returns {number} The mean.
+     */
+    function getMean(sample) {
+      return (_.reduce(sample, function(sum, x) {
+        return sum + x;
+      }) / sample.length) || 0;
+    }
+
+    /**
+     * Gets the source code of a function.
+     *
+     * @private
+     * @param {Function} fn The function.
+     * @returns {string} The function's source code.
+     */
+    function getSource(fn) {
+      var result = '';
+      if (isStringable(fn)) {
+        result = String(fn);
+      } else if (support.decompilation) {
+        // Escape the `{` for Firefox 1.
+        result = _.result(/^[^{]+\{([\s\S]*)\}\s*$/.exec(fn), 1);
+      }
+      // Trim string.
+      result = (result || '').replace(/^\s+|\s+$/g, '');
+
+      // Detect strings containing only the "use strict" directive.
+      return /^(?:\/\*+[\w\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result)
+        ? ''
+        : result;
+    }
+
+    /**
+     * Checks if an object is of the specified class.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @param {string} name The name of the class.
+     * @returns {boolean} Returns `true` if the value is of the specified class, else `false`.
+     */
+    function isClassOf(value, name) {
+      return value != null && toString.call(value) == '[object ' + name + ']';
+    }
+
+    /**
+     * Host objects can return type values that are different from their actual
+     * data type. The objects we are concerned with usually return non-primitive
+     * types of "object", "function", or "unknown".
+     *
+     * @private
+     * @param {*} object The owner of the property.
+     * @param {string} property The property to check.
+     * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.
+     */
+    function isHostType(object, property) {
+      if (object == null) {
+        return false;
+      }
+      var type = typeof object[property];
+      return !rePrimitive.test(type) && (type != 'object' || !!object[property]);
+    }
+
+    /**
+     * Checks if a value can be safely coerced to a string.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the value can be coerced, else `false`.
+     */
+    function isStringable(value) {
+      return _.isString(value) || (_.has(value, 'toString') && _.isFunction(value.toString));
+    }
+
+    /**
+     * A wrapper around `require` to suppress `module missing` errors.
+     *
+     * @private
+     * @param {string} id The module id.
+     * @returns {*} The exported module or `null`.
+     */
+    function require(id) {
+      try {
+        var result = freeExports && freeRequire(id);
+      } catch(e) {}
+      return result || null;
+    }
+
+    /**
+     * Runs a snippet of JavaScript via script injection.
+     *
+     * @private
+     * @param {string} code The code to run.
+     */
+    function runScript(code) {
+      var anchor = freeDefine ? define.amd : Benchmark,
+          script = doc.createElement('script'),
+          sibling = doc.getElementsByTagName('script')[0],
+          parent = sibling.parentNode,
+          prop = uid + 'runScript',
+          prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';
+
+      // Firefox 2.0.0.2 cannot use script injection as intended because it executes
+      // asynchronously, but that's OK because script injection is only used to avoid
+      // the previously commented JaegerMonkey bug.
+      try {
+        // Remove the inserted script *before* running the code to avoid differences
+        // in the expected script element count/order of the document.
+        script.appendChild(doc.createTextNode(prefix + code));
+        anchor[prop] = function() { destroyElement(script); };
+      } catch(e) {
+        parent = parent.cloneNode(false);
+        sibling = null;
+        script.text = code;
+      }
+      parent.insertBefore(script, sibling);
+      delete anchor[prop];
+    }
+
+    /**
+     * A helper function for setting options/event handlers.
+     *
+     * @private
+     * @param {Object} object The benchmark or suite instance.
+     * @param {Object} [options={}] Options object.
+     */
+    function setOptions(object, options) {
+      options = object.options = _.assign({}, cloneDeep(object.constructor.options), cloneDeep(options));
+
+      _.forOwn(options, function(value, key) {
+        if (value != null) {
+          // Add event listeners.
+          if (/^on[A-Z]/.test(key)) {
+            _.each(key.split(' '), function(key) {
+              object.on(key.slice(2).toLowerCase(), value);
+            });
+          } else if (!_.has(object, key)) {
+            object[key] = cloneDeep(value);
+          }
+        }
+      });
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Handles cycling/completing the deferred benchmark.
+     *
+     * @memberOf Benchmark.Deferred
+     */
+    function resolve() {
+      var deferred = this,
+          clone = deferred.benchmark,
+          bench = clone._original;
+
+      if (bench.aborted) {
+        // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete.
+        deferred.teardown();
+        clone.running = false;
+        cycle(deferred);
+      }
+      else if (++deferred.cycles < clone.count) {
+        clone.compiled.call(deferred, context, timer);
+      }
+      else {
+        timer.stop(deferred);
+        deferred.teardown();
+        delay(clone, function() { cycle(deferred); });
+      }
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * A generic `Array#filter` like method.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @param {Array} array The array to iterate over.
+     * @param {Function|string} callback The function/alias called per iteration.
+     * @returns {Array} A new array of values that passed callback filter.
+     * @example
+     *
+     * // get odd numbers
+     * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
+     *   return n % 2;
+     * }); // -> [1, 3, 5];
+     *
+     * // get fastest benchmarks
+     * Benchmark.filter(benches, 'fastest');
+     *
+     * // get slowest benchmarks
+     * Benchmark.filter(benches, 'slowest');
+     *
+     * // get benchmarks that completed without erroring
+     * Benchmark.filter(benches, 'successful');
+     */
+    function filter(array, callback) {
+      if (callback === 'successful') {
+        // Callback to exclude those that are errored, unrun, or have hz of Infinity.
+        callback = function(bench) {
+          return bench.cycles && _.isFinite(bench.hz) && !bench.error;
+        };
+      }
+      else if (callback === 'fastest' || callback === 'slowest') {
+        // Get successful, sort by period + margin of error, and filter fastest/slowest.
+        var result = filter(array, 'successful').sort(function(a, b) {
+          a = a.stats; b = b.stats;
+          return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback === 'fastest' ? 1 : -1);
+        });
+
+        return _.filter(result, function(bench) {
+          return result[0].compare(bench) == 0;
+        });
+      }
+      return _.filter(array, callback);
+    }
+
+    /**
+     * Converts a number to a more readable comma-separated string representation.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @param {number} number The number to convert.
+     * @returns {string} The more readable string representation.
+     */
+    function formatNumber(number) {
+      number = String(number).split('.');
+      return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') +
+        (number[1] ? '.' + number[1] : '');
+    }
+
+    /**
+     * Invokes a method on all items in an array.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @param {Array} benches Array of benchmarks to iterate over.
+     * @param {Object|string} name The name of the method to invoke OR options object.
+     * @param {...*} [args] Arguments to invoke the method with.
+     * @returns {Array} A new array of values returned from each method invoked.
+     * @example
+     *
+     * // invoke `reset` on all benchmarks
+     * Benchmark.invoke(benches, 'reset');
+     *
+     * // invoke `emit` with arguments
+     * Benchmark.invoke(benches, 'emit', 'complete', listener);
+     *
+     * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
+     * Benchmark.invoke(benches, {
+     *
+     *   // invoke the `run` method
+     *   'name': 'run',
+     *
+     *   // pass a single argument
+     *   'args': true,
+     *
+     *   // treat as queue, removing benchmarks from front of `benches` until empty
+     *   'queued': true,
+     *
+     *   // called before any benchmarks have been invoked.
+     *   'onStart': onStart,
+     *
+     *   // called between invoking benchmarks
+     *   'onCycle': onCycle,
+     *
+     *   // called after all benchmarks have been invoked.
+     *   'onComplete': onComplete
+     * });
+     */
+    function invoke(benches, name) {
+      var args,
+          bench,
+          queued,
+          index = -1,
+          eventProps = { 'currentTarget': benches },
+          options = { 'onStart': _.noop, 'onCycle': _.noop, 'onComplete': _.noop },
+          result = _.toArray(benches);
+
+      /**
+       * Invokes the method of the current object and if synchronous, fetches the next.
+       */
+      function execute() {
+        var listeners,
+            async = isAsync(bench);
+
+        if (async) {
+          // Use `getNext` as the first listener.
+          bench.on('complete', getNext);
+          listeners = bench.events.complete;
+          listeners.splice(0, 0, listeners.pop());
+        }
+        // Execute method.
+        result[index] = _.isFunction(bench && bench[name]) ? bench[name].apply(bench, args) : undefined;
+        // If synchronous return `true` until finished.
+        return !async && getNext();
+      }
+
+      /**
+       * Fetches the next bench or executes `onComplete` callback.
+       */
+      function getNext(event) {
+        var cycleEvent,
+            last = bench,
+            async = isAsync(last);
+
+        if (async) {
+          last.off('complete', getNext);
+          last.emit('complete');
+        }
+        // Emit "cycle" event.
+        eventProps.type = 'cycle';
+        eventProps.target = last;
+        cycleEvent = Event(eventProps);
+        options.onCycle.call(benches, cycleEvent);
+
+        // Choose next benchmark if not exiting early.
+        if (!cycleEvent.aborted && raiseIndex() !== false) {
+          bench = queued ? benches[0] : result[index];
+          if (isAsync(bench)) {
+            delay(bench, execute);
+          }
+          else if (async) {
+            // Resume execution if previously asynchronous but now synchronous.
+            while (execute()) {}
+          }
+          else {
+            // Continue synchronous execution.
+            return true;
+          }
+        } else {
+          // Emit "complete" event.
+          eventProps.type = 'complete';
+          options.onComplete.call(benches, Event(eventProps));
+        }
+        // When used as a listener `event.aborted = true` will cancel the rest of
+        // the "complete" listeners because they were already called above and when
+        // used as part of `getNext` the `return false` will exit the execution while-loop.
+        if (event) {
+          event.aborted = true;
+        } else {
+          return false;
+        }
+      }
+
+      /**
+       * Checks if invoking `Benchmark#run` with asynchronous cycles.
+       */
+      function isAsync(object) {
+        // Avoid using `instanceof` here because of IE memory leak issues with host objects.
+        var async = args[0] && args[0].async;
+        return name == 'run' && (object instanceof Benchmark) &&
+          ((async == null ? object.options.async : async) && support.timeout || object.defer);
+      }
+
+      /**
+       * Raises `index` to the next defined index or returns `false`.
+       */
+      function raiseIndex() {
+        index++;
+
+        // If queued remove the previous bench.
+        if (queued && index > 0) {
+          shift.call(benches);
+        }
+        // If we reached the last index then return `false`.
+        return (queued ? benches.length : index < result.length)
+          ? index
+          : (index = false);
+      }
+      // Juggle arguments.
+      if (_.isString(name)) {
+        // 2 arguments (array, name).
+        args = slice.call(arguments, 2);
+      } else {
+        // 2 arguments (array, options).
+        options = _.assign(options, name);
+        name = options.name;
+        args = _.isArray(args = 'args' in options ? options.args : []) ? args : [args];
+        queued = options.queued;
+      }
+      // Start iterating over the array.
+      if (raiseIndex() !== false) {
+        // Emit "start" event.
+        bench = result[index];
+        eventProps.type = 'start';
+        eventProps.target = bench;
+        options.onStart.call(benches, Event(eventProps));
+
+        // End early if the suite was aborted in an "onStart" listener.
+        if (name == 'run' && (benches instanceof Suite) && benches.aborted) {
+          // Emit "cycle" event.
+          eventProps.type = 'cycle';
+          options.onCycle.call(benches, Event(eventProps));
+          // Emit "complete" event.
+          eventProps.type = 'complete';
+          options.onComplete.call(benches, Event(eventProps));
+        }
+        // Start method execution.
+        else {
+          if (isAsync(bench)) {
+            delay(bench, execute);
+          } else {
+            while (execute()) {}
+          }
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates a string of joined array values or object key-value pairs.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @param {Array|Object} object The object to operate on.
+     * @param {string} [separator1=','] The separator used between key-value pairs.
+     * @param {string} [separator2=': '] The separator used between keys and values.
+     * @returns {string} The joined result.
+     */
+    function join(object, separator1, separator2) {
+      var result = [],
+          length = (object = Object(object)).length,
+          arrayLike = length === length >>> 0;
+
+      separator2 || (separator2 = ': ');
+      _.each(object, function(value, key) {
+        result.push(arrayLike ? value : key + separator2 + value);
+      });
+      return result.join(separator1 || ',');
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Aborts all benchmarks in the suite.
+     *
+     * @name abort
+     * @memberOf Benchmark.Suite
+     * @returns {Object} The suite instance.
+     */
+    function abortSuite() {
+      var event,
+          suite = this,
+          resetting = calledBy.resetSuite;
+
+      if (suite.running) {
+        event = Event('abort');
+        suite.emit(event);
+        if (!event.cancelled || resetting) {
+          // Avoid infinite recursion.
+          calledBy.abortSuite = true;
+          suite.reset();
+          delete calledBy.abortSuite;
+
+          if (!resetting) {
+            suite.aborted = true;
+            invoke(suite, 'abort');
+          }
+        }
+      }
+      return suite;
+    }
+
+    /**
+     * Adds a test to the benchmark suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {string} name A name to identify the benchmark.
+     * @param {Function|string} fn The test to benchmark.
+     * @param {Object} [options={}] Options object.
+     * @returns {Object} The suite instance.
+     * @example
+     *
+     * // basic usage
+     * suite.add(fn);
+     *
+     * // or using a name first
+     * suite.add('foo', fn);
+     *
+     * // or with options
+     * suite.add('foo', fn, {
+     *   'onCycle': onCycle,
+     *   'onComplete': onComplete
+     * });
+     *
+     * // or name and options
+     * suite.add('foo', {
+     *   'fn': fn,
+     *   'onCycle': onCycle,
+     *   'onComplete': onComplete
+     * });
+     *
+     * // or options only
+     * suite.add({
+     *   'name': 'foo',
+     *   'fn': fn,
+     *   'onCycle': onCycle,
+     *   'onComplete': onComplete
+     * });
+     */
+    function add(name, fn, options) {
+      var suite = this,
+          bench = new Benchmark(name, fn, options),
+          event = Event({ 'type': 'add', 'target': bench });
+
+      if (suite.emit(event), !event.cancelled) {
+        suite.push(bench);
+      }
+      return suite;
+    }
+
+    /**
+     * Creates a new suite with cloned benchmarks.
+     *
+     * @name clone
+     * @memberOf Benchmark.Suite
+     * @param {Object} options Options object to overwrite cloned options.
+     * @returns {Object} The new suite instance.
+     */
+    function cloneSuite(options) {
+      var suite = this,
+          result = new suite.constructor(_.assign({}, suite.options, options));
+
+      // Copy own properties.
+      _.forOwn(suite, function(value, key) {
+        if (!_.has(result, key)) {
+          result[key] = value && _.isFunction(value.clone)
+            ? value.clone()
+            : cloneDeep(value);
+        }
+      });
+      return result;
+    }
+
+    /**
+     * An `Array#filter` like method.
+     *
+     * @name filter
+     * @memberOf Benchmark.Suite
+     * @param {Function|string} callback The function/alias called per iteration.
+     * @returns {Object} A new suite of benchmarks that passed callback filter.
+     */
+    function filterSuite(callback) {
+      var suite = this,
+          result = new suite.constructor(suite.options);
+
+      result.push.apply(result, filter(suite, callback));
+      return result;
+    }
+
+    /**
+     * Resets all benchmarks in the suite.
+     *
+     * @name reset
+     * @memberOf Benchmark.Suite
+     * @returns {Object} The suite instance.
+     */
+    function resetSuite() {
+      var event,
+          suite = this,
+          aborting = calledBy.abortSuite;
+
+      if (suite.running && !aborting) {
+        // No worries, `resetSuite()` is called within `abortSuite()`.
+        calledBy.resetSuite = true;
+        suite.abort();
+        delete calledBy.resetSuite;
+      }
+      // Reset if the state has changed.
+      else if ((suite.aborted || suite.running) &&
+          (suite.emit(event = Event('reset')), !event.cancelled)) {
+        suite.aborted = suite.running = false;
+        if (!aborting) {
+          invoke(suite, 'reset');
+        }
+      }
+      return suite;
+    }
+
+    /**
+     * Runs the suite.
+     *
+     * @name run
+     * @memberOf Benchmark.Suite
+     * @param {Object} [options={}] Options object.
+     * @returns {Object} The suite instance.
+     * @example
+     *
+     * // basic usage
+     * suite.run();
+     *
+     * // or with options
+     * suite.run({ 'async': true, 'queued': true });
+     */
+    function runSuite(options) {
+      var suite = this;
+
+      suite.reset();
+      suite.running = true;
+      options || (options = {});
+
+      invoke(suite, {
+        'name': 'run',
+        'args': options,
+        'queued': options.queued,
+        'onStart': function(event) {
+          suite.emit(event);
+        },
+        'onCycle': function(event) {
+          var bench = event.target;
+          if (bench.error) {
+            suite.emit({ 'type': 'error', 'target': bench });
+          }
+          suite.emit(event);
+          event.aborted = suite.aborted;
+        },
+        'onComplete': function(event) {
+          suite.running = false;
+          suite.emit(event);
+        }
+      });
+      return suite;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Executes all registered listeners of the specified event type.
+     *
+     * @memberOf Benchmark, Benchmark.Suite
+     * @param {Object|string} type The event type or object.
+     * @param {...*} [args] Arguments to invoke the listener with.
+     * @returns {*} Returns the return value of the last listener executed.
+     */
+    function emit(type) {
+      var listeners,
+          object = this,
+          event = Event(type),
+          events = object.events,
+          args = (arguments[0] = event, arguments);
+
+      event.currentTarget || (event.currentTarget = object);
+      event.target || (event.target = object);
+      delete event.result;
+
+      if (events && (listeners = _.has(events, event.type) && events[event.type])) {
+        _.each(listeners.slice(), function(listener) {
+          if ((event.result = listener.apply(object, args)) === false) {
+            event.cancelled = true;
+          }
+          return !event.aborted;
+        });
+      }
+      return event.result;
+    }
+
+    /**
+     * Returns an array of event listeners for a given type that can be manipulated
+     * to add or remove listeners.
+     *
+     * @memberOf Benchmark, Benchmark.Suite
+     * @param {string} type The event type.
+     * @returns {Array} The listeners array.
+     */
+    function listeners(type) {
+      var object = this,
+          events = object.events || (object.events = {});
+
+      return _.has(events, type) ? events[type] : (events[type] = []);
+    }
+
+    /**
+     * Unregisters a listener for the specified event type(s),
+     * or unregisters all listeners for the specified event type(s),
+     * or unregisters all listeners for all event types.
+     *
+     * @memberOf Benchmark, Benchmark.Suite
+     * @param {string} [type] The event type.
+     * @param {Function} [listener] The function to unregister.
+     * @returns {Object} The current instance.
+     * @example
+     *
+     * // unregister a listener for an event type
+     * bench.off('cycle', listener);
+     *
+     * // unregister a listener for multiple event types
+     * bench.off('start cycle', listener);
+     *
+     * // unregister all listeners for an event type
+     * bench.off('cycle');
+     *
+     * // unregister all listeners for multiple event types
+     * bench.off('start cycle complete');
+     *
+     * // unregister all listeners for all event types
+     * bench.off();
+     */
+    function off(type, listener) {
+      var object = this,
+          events = object.events;
+
+      if (!events) {
+        return object;
+      }
+      _.each(type ? type.split(' ') : events, function(listeners, type) {
+        var index;
+        if (typeof listeners == 'string') {
+          type = listeners;
+          listeners = _.has(events, type) && events[type];
+        }
+        if (listeners) {
+          if (listener) {
+            index = _.indexOf(listeners, listener);
+            if (index > -1) {
+              listeners.splice(index, 1);
+            }
+          } else {
+            listeners.length = 0;
+          }
+        }
+      });
+      return object;
+    }
+
+    /**
+     * Registers a listener for the specified event type(s).
+     *
+     * @memberOf Benchmark, Benchmark.Suite
+     * @param {string} type The event type.
+     * @param {Function} listener The function to register.
+     * @returns {Object} The current instance.
+     * @example
+     *
+     * // register a listener for an event type
+     * bench.on('cycle', listener);
+     *
+     * // register a listener for multiple event types
+     * bench.on('start cycle', listener);
+     */
+    function on(type, listener) {
+      var object = this,
+          events = object.events || (object.events = {});
+
+      _.each(type.split(' '), function(type) {
+        (_.has(events, type)
+          ? events[type]
+          : (events[type] = [])
+        ).push(listener);
+      });
+      return object;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Aborts the benchmark without recording times.
+     *
+     * @memberOf Benchmark
+     * @returns {Object} The benchmark instance.
+     */
+    function abort() {
+      var event,
+          bench = this,
+          resetting = calledBy.reset;
+
+      if (bench.running) {
+        event = Event('abort');
+        bench.emit(event);
+        if (!event.cancelled || resetting) {
+          // Avoid infinite recursion.
+          calledBy.abort = true;
+          bench.reset();
+          delete calledBy.abort;
+
+          if (support.timeout) {
+            clearTimeout(bench._timerId);
+            delete bench._timerId;
+          }
+          if (!resetting) {
+            bench.aborted = true;
+            bench.running = false;
+          }
+        }
+      }
+      return bench;
+    }
+
+    /**
+     * Creates a new benchmark using the same test and options.
+     *
+     * @memberOf Benchmark
+     * @param {Object} options Options object to overwrite cloned options.
+     * @returns {Object} The new benchmark instance.
+     * @example
+     *
+     * var bizarro = bench.clone({
+     *   'name': 'doppelganger'
+     * });
+     */
+    function clone(options) {
+      var bench = this,
+          result = new bench.constructor(_.assign({}, bench, options));
+
+      // Correct the `options` object.
+      result.options = _.assign({}, cloneDeep(bench.options), cloneDeep(options));
+
+      // Copy own custom properties.
+      _.forOwn(bench, function(value, key) {
+        if (!_.has(result, key)) {
+          result[key] = cloneDeep(value);
+        }
+      });
+
+      return result;
+    }
+
+    /**
+     * Determines if a benchmark is faster than another.
+     *
+     * @memberOf Benchmark
+     * @param {Object} other The benchmark to compare.
+     * @returns {number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+     */
+    function compare(other) {
+      var bench = this;
+
+      // Exit early if comparing the same benchmark.
+      if (bench == other) {
+        return 0;
+      }
+      var critical,
+          zStat,
+          sample1 = bench.stats.sample,
+          sample2 = other.stats.sample,
+          size1 = sample1.length,
+          size2 = sample2.length,
+          maxSize = max(size1, size2),
+          minSize = min(size1, size2),
+          u1 = getU(sample1, sample2),
+          u2 = getU(sample2, sample1),
+          u = min(u1, u2);
+
+      function getScore(xA, sampleB) {
+        return _.reduce(sampleB, function(total, xB) {
+          return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);
+        }, 0);
+      }
+
+      function getU(sampleA, sampleB) {
+        return _.reduce(sampleA, function(total, xA) {
+          return total + getScore(xA, sampleB);
+        }, 0);
+      }
+
+      function getZ(u) {
+        return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);
+      }
+      // Reject the null hypothesis the two samples come from the
+      // same population (i.e. have the same median) if...
+      if (size1 + size2 > 30) {
+        // ...the z-stat is greater than 1.96 or less than -1.96
+        // http://www.statisticslectures.com/topics/mannwhitneyu/
+        zStat = getZ(u);
+        return abs(zStat) > 1.96 ? (u == u1 ? 1 : -1) : 0;
+      }
+      // ...the U value is less than or equal the critical U value.
+      critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];
+      return u <= critical ? (u == u1 ? 1 : -1) : 0;
+    }
+
+    /**
+     * Reset properties and abort if running.
+     *
+     * @memberOf Benchmark
+     * @returns {Object} The benchmark instance.
+     */
+    function reset() {
+      var bench = this;
+      if (bench.running && !calledBy.abort) {
+        // No worries, `reset()` is called within `abort()`.
+        calledBy.reset = true;
+        bench.abort();
+        delete calledBy.reset;
+        return bench;
+      }
+      var event,
+          index = 0,
+          changes = [],
+          queue = [];
+
+      // A non-recursive solution to check if properties have changed.
+      // For more information see http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4.
+      var data = {
+        'destination': bench,
+        'source': _.assign({}, cloneDeep(bench.constructor.prototype), cloneDeep(bench.options))
+      };
+
+      do {
+        _.forOwn(data.source, function(value, key) {
+          var changed,
+              destination = data.destination,
+              currValue = destination[key];
+
+          // Skip pseudo private properties like `_timerId` which could be a
+          // Java object in environments like RingoJS.
+          if (key.charAt(0) == '_') {
+            return;
+          }
+          if (value && typeof value == 'object') {
+            if (_.isArray(value)) {
+              // Check if an array value has changed to a non-array value.
+              if (!_.isArray(currValue)) {
+                changed = currValue = [];
+              }
+              // Check if an array has changed its length.
+              if (currValue.length != value.length) {
+                changed = currValue = currValue.slice(0, value.length);
+                currValue.length = value.length;
+              }
+            }
+            // Check if an object has changed to a non-object value.
+            else if (!currValue || typeof currValue != 'object') {
+              changed = currValue = {};
+            }
+            // Register a changed object.
+            if (changed) {
+              changes.push({ 'destination': destination, 'key': key, 'value': currValue });
+            }
+            queue.push({ 'destination': currValue, 'source': value });
+          }
+          // Register a changed primitive.
+          else if (value !== currValue && !(value == null || _.isFunction(value))) {
+            changes.push({ 'destination': destination, 'key': key, 'value': value });
+          }
+        });
+      }
+      while ((data = queue[index++]));
+
+      // If changed emit the `reset` event and if it isn't cancelled reset the benchmark.
+      if (changes.length && (bench.emit(event = Event('reset')), !event.cancelled)) {
+        _.each(changes, function(data) {
+          data.destination[data.key] = data.value;
+        });
+      }
+      return bench;
+    }
+
+    /**
+     * Displays relevant benchmark information when coerced to a string.
+     *
+     * @name toString
+     * @memberOf Benchmark
+     * @returns {string} A string representation of the benchmark instance.
+     */
+    function toStringBench() {
+      var bench = this,
+          error = bench.error,
+          hz = bench.hz,
+          id = bench.id,
+          stats = bench.stats,
+          size = stats.sample.length,
+          pm = '\xb1',
+          result = bench.name || (_.isNaN(id) ? id : '<Test #' + id + '>');
+
+      if (error) {
+        result += ': ' + join(error);
+      } else {
+        result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
+          stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
+      }
+      return result;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Clocks the time taken to execute a test per cycle (secs).
+     *
+     * @private
+     * @param {Object} bench The benchmark instance.
+     * @returns {number} The time taken.
+     */
+    function clock() {
+      var options = Benchmark.options,
+          templateData = {},
+          timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];
+
+      // Lazy define for hi-res timers.
+      clock = function(clone) {
+        var deferred;
+
+        if (clone instanceof Deferred) {
+          deferred = clone;
+          clone = deferred.benchmark;
+        }
+        var bench = clone._original,
+            stringable = isStringable(bench.fn),
+            count = bench.count = clone.count,
+            decompilable = stringable || (support.decompilation && (clone.setup !== _.noop || clone.teardown !== _.noop)),
+            id = bench.id,
+            name = bench.name || (typeof id == 'number' ? '<Test #' + id + '>' : id),
+            result = 0;
+
+        // Init `minTime` if needed.
+        clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);
+
+        // Compile in setup/teardown functions and the test loop.
+        // Create a new compiled test, instead of using the cached `bench.compiled`,
+        // to avoid potential engine optimizations enabled over the life of the test.
+        var funcBody = deferred
+          ? 'var d#=this,${fnArg}=d#,m#=d#.benchmark._original,f#=m#.fn,su#=m#.setup,td#=m#.teardown;' +
+            // When `deferred.cycles` is `0` then...
+            'if(!d#.cycles){' +
+            // set `deferred.fn`,
+            'd#.fn=function(){var ${fnArg}=d#;if(typeof f#=="function"){try{${fn}\n}catch(e#){f#(d#)}}else{${fn}\n}};' +
+            // set `deferred.teardown`,
+            'd#.teardown=function(){d#.cycles=0;if(typeof td#=="function"){try{${teardown}\n}catch(e#){td#()}}else{${teardown}\n}};' +
+            // execute the benchmark's `setup`,
+            'if(typeof su#=="function"){try{${setup}\n}catch(e#){su#()}}else{${setup}\n};' +
+            // start timer,
+            't#.start(d#);' +
+            // and then execute `deferred.fn` and return a dummy object.
+            '}d#.fn();return{uid:"${uid}"}'
+
+          : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count,n#=t#.ns;${setup}\n${begin};' +
+            'while(i#--){${fn}\n}${end};${teardown}\nreturn{elapsed:r#,uid:"${uid}"}';
+
+        var compiled = bench.compiled = clone.compiled = createCompiled(bench, decompilable, deferred, funcBody),
+            isEmpty = !(templateData.fn || stringable);
+
+        try {
+          if (isEmpty) {
+            // Firefox may remove dead code from `Function#toString` results.
+            // For more information see http://bugzil.la/536085.
+            throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.');
+          }
+          else if (!deferred) {
+            // Pretest to determine if compiled code exits early, usually by a
+            // rogue `return` statement, by checking for a return object with the uid.
+            bench.count = 1;
+            compiled = decompilable && (compiled.call(bench, context, timer) || {}).uid == templateData.uid && compiled;
+            bench.count = count;
+          }
+        } catch(e) {
+          compiled = null;
+          clone.error = e || new Error(String(e));
+          bench.count = count;
+        }
+        // Fallback when a test exits early or errors during pretest.
+        if (!compiled && !deferred && !isEmpty) {
+          funcBody = (
+            stringable || (decompilable && !clone.error)
+              ? 'function f#(){${fn}\n}var r#,s#,m#=this,i#=m#.count'
+              : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count'
+            ) +
+            ',n#=t#.ns;${setup}\n${begin};m#.f#=f#;while(i#--){m#.f#()}${end};' +
+            'delete m#.f#;${teardown}\nreturn{elapsed:r#}';
+
+          compiled = createCompiled(bench, decompilable, deferred, funcBody);
+
+          try {
+            // Pretest one more time to check for errors.
+            bench.count = 1;
+            compiled.call(bench, context, timer);
+            bench.count = count;
+            delete clone.error;
+          }
+          catch(e) {
+            bench.count = count;
+            if (!clone.error) {
+              clone.error = e || new Error(String(e));
+            }
+          }
+        }
+        // If no errors run the full test loop.
+        if (!clone.error) {
+          compiled = bench.compiled = clone.compiled = createCompiled(bench, decompilable, deferred, funcBody);
+          result = compiled.call(deferred || bench, context, timer).elapsed;
+        }
+        return result;
+      };
+
+      /*----------------------------------------------------------------------*/
+
+      /**
+       * Creates a compiled function from the given function `body`.
+       */
+      function createCompiled(bench, decompilable, deferred, body) {
+        var fn = bench.fn,
+            fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '';
+
+        templateData.uid = uid + uidCounter++;
+
+        _.assign(templateData, {
+          'setup': decompilable ? getSource(bench.setup) : interpolate('m#.setup()'),
+          'fn': decompilable ? getSource(fn) : interpolate('m#.fn(' + fnArg + ')'),
+          'fnArg': fnArg,
+          'teardown': decompilable ? getSource(bench.teardown) : interpolate('m#.teardown()')
+        });
+
+        // Use API of chosen timer.
+        if (timer.unit == 'ns') {
+          _.assign(templateData, {
+            'begin': interpolate('s#=n#()'),
+            'end': interpolate('r#=n#(s#);r#=r#[0]+(r#[1]/1e9)')
+          });
+        }
+        else if (timer.unit == 'us') {
+          if (timer.ns.stop) {
+            _.assign(templateData, {
+              'begin': interpolate('s#=n#.start()'),
+              'end': interpolate('r#=n#.microseconds()/1e6')
+            });
+          } else {
+            _.assign(templateData, {
+              'begin': interpolate('s#=n#()'),
+              'end': interpolate('r#=(n#()-s#)/1e6')
+            });
+          }
+        }
+        else if (timer.ns.now) {
+          _.assign(templateData, {
+            'begin': interpolate('s#=n#.now()'),
+            'end': interpolate('r#=(n#.now()-s#)/1e3')
+          });
+        }
+        else {
+          _.assign(templateData, {
+            'begin': interpolate('s#=new n#().getTime()'),
+            'end': interpolate('r#=(new n#().getTime()-s#)/1e3')
+          });
+        }
+        // Define `timer` methods.
+        timer.start = createFunction(
+          interpolate('o#'),
+          interpolate('var n#=this.ns,${begin};o#.elapsed=0;o#.timeStamp=s#')
+        );
+
+        timer.stop = createFunction(
+          interpolate('o#'),
+          interpolate('var n#=this.ns,s#=o#.timeStamp,${end};o#.elapsed=r#')
+        );
+
+        // Create compiled test.
+        return createFunction(
+          interpolate('window,t#'),
+          'var global = window, clearTimeout = global.clearTimeout, setTimeout = global.setTimeout;\n' +
+          interpolate(body)
+        );
+      }
+
+      /**
+       * Gets the current timer's minimum resolution (secs).
+       */
+      function getRes(unit) {
+        var measured,
+            begin,
+            count = 30,
+            divisor = 1e3,
+            ns = timer.ns,
+            sample = [];
+
+        // Get average smallest measurable time.
+        while (count--) {
+          if (unit == 'us') {
+            divisor = 1e6;
+            if (ns.stop) {
+              ns.start();
+              while (!(measured = ns.microseconds())) {}
+            } else {
+              begin = ns();
+              while (!(measured = ns() - begin)) {}
+            }
+          }
+          else if (unit == 'ns') {
+            divisor = 1e9;
+            begin = (begin = ns())[0] + (begin[1] / divisor);
+            while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) {}
+            divisor = 1;
+          }
+          else if (ns.now) {
+            begin = ns.now();
+            while (!(measured = ns.now() - begin)) {}
+          }
+          else {
+            begin = new ns().getTime();
+            while (!(measured = new ns().getTime() - begin)) {}
+          }
+          // Check for broken timers.
+          if (measured > 0) {
+            sample.push(measured);
+          } else {
+            sample.push(Infinity);
+            break;
+          }
+        }
+        // Convert to seconds.
+        return getMean(sample) / divisor;
+      }
+
+      /**
+       * Interpolates a given template string.
+       */
+      function interpolate(string) {
+        // Replaces all occurrences of `#` with a unique number and template tokens with content.
+        return _.template(string.replace(/\#/g, /\d+/.exec(templateData.uid)))(templateData);
+      }
+
+      /*----------------------------------------------------------------------*/
+
+      // Detect Chrome's microsecond timer:
+      // enable benchmarking via the --enable-benchmarking command
+      // line switch in at least Chrome 7 to use chrome.Interval
+      try {
+        if ((timer.ns = new (context.chrome || context.chromium).Interval)) {
+          timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+        }
+      } catch(e) {}
+
+      // Detect Node.js's nanosecond resolution timer available in Node.js >= 0.8.
+      if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {
+        timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+      }
+      // Detect Wade Simmons' Node.js `microtime` module.
+      if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {
+        timers.push({ 'ns': timer.ns,  'res': getRes('us'), 'unit': 'us' });
+      }
+      // Pick timer with highest resolution.
+      timer = _.minBy(timers, 'res');
+
+      // Error if there are no working timers.
+      if (timer.res == Infinity) {
+        throw new Error('Benchmark.js was unable to find a working timer.');
+      }
+      // Resolve time span required to achieve a percent uncertainty of at most 1%.
+      // For more information see http://spiff.rit.edu/classes/phys273/uncert/uncert.html.
+      options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));
+      return clock.apply(null, arguments);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Computes stats on benchmark results.
+     *
+     * @private
+     * @param {Object} bench The benchmark instance.
+     * @param {Object} options The options object.
+     */
+    function compute(bench, options) {
+      options || (options = {});
+
+      var async = options.async,
+          elapsed = 0,
+          initCount = bench.initCount,
+          minSamples = bench.minSamples,
+          queue = [],
+          sample = bench.stats.sample;
+
+      /**
+       * Adds a clone to the queue.
+       */
+      function enqueue() {
+        queue.push(bench.clone({
+          '_original': bench,
+          'events': {
+            'abort': [update],
+            'cycle': [update],
+            'error': [update],
+            'start': [update]
+          }
+        }));
+      }
+
+      /**
+       * Updates the clone/original benchmarks to keep their data in sync.
+       */
+      function update(event) {
+        var clone = this,
+            type = event.type;
+
+        if (bench.running) {
+          if (type == 'start') {
+            // Note: `clone.minTime` prop is inited in `clock()`.
+            clone.count = bench.initCount;
+          }
+          else {
+            if (type == 'error') {
+              bench.error = clone.error;
+            }
+            if (type == 'abort') {
+              bench.abort();
+              bench.emit('cycle');
+            } else {
+              event.currentTarget = event.target = bench;
+              bench.emit(event);
+            }
+          }
+        } else if (bench.aborted) {
+          // Clear abort listeners to avoid triggering bench's abort/cycle again.
+          clone.events.abort.length = 0;
+          clone.abort();
+        }
+      }
+
+      /**
+       * Determines if more clones should be queued or if cycling should stop.
+       */
+      function evaluate(event) {
+        var critical,
+            df,
+            mean,
+            moe,
+            rme,
+            sd,
+            sem,
+            variance,
+            clone = event.target,
+            done = bench.aborted,
+            now = _.now(),
+            size = sample.push(clone.times.period),
+            maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,
+            times = bench.times,
+            varOf = function(sum, x) { return sum + pow(x - mean, 2); };
+
+        // Exit early for aborted or unclockable tests.
+        if (done || clone.hz == Infinity) {
+          maxedOut = !(size = sample.length = queue.length = 0);
+        }
+
+        if (!done) {
+          // Compute the sample mean (estimate of the population mean).
+          mean = getMean(sample);
+          // Compute the sample variance (estimate of the population variance).
+          variance = _.reduce(sample, varOf, 0) / (size - 1) || 0;
+          // Compute the sample standard deviation (estimate of the population standard deviation).
+          sd = sqrt(variance);
+          // Compute the standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean).
+          sem = sd / sqrt(size);
+          // Compute the degrees of freedom.
+          df = size - 1;
+          // Compute the critical value.
+          critical = tTable[Math.round(df) || 1] || tTable.infinity;
+          // Compute the margin of error.
+          moe = sem * critical;
+          // Compute the relative margin of error.
+          rme = (moe / mean) * 100 || 0;
+
+          _.assign(bench.stats, {
+            'deviation': sd,
+            'mean': mean,
+            'moe': moe,
+            'rme': rme,
+            'sem': sem,
+            'variance': variance
+          });
+
+          // Abort the cycle loop when the minimum sample size has been collected
+          // and the elapsed time exceeds the maximum time allowed per benchmark.
+          // We don't count cycle delays toward the max time because delays may be
+          // increased by browsers that clamp timeouts for inactive tabs. For more
+          // information see https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs.
+          if (maxedOut) {
+            // Reset the `initCount` in case the benchmark is rerun.
+            bench.initCount = initCount;
+            bench.running = false;
+            done = true;
+            times.elapsed = (now - times.timeStamp) / 1e3;
+          }
+          if (bench.hz != Infinity) {
+            bench.hz = 1 / mean;
+            times.cycle = mean * bench.count;
+            times.period = mean;
+          }
+        }
+        // If time permits, increase sample size to reduce the margin of error.
+        if (queue.length < 2 && !maxedOut) {
+          enqueue();
+        }
+        // Abort the `invoke` cycle when done.
+        event.aborted = done;
+      }
+
+      // Init queue and begin.
+      enqueue();
+      invoke(queue, {
+        'name': 'run',
+        'args': { 'async': async },
+        'queued': true,
+        'onCycle': evaluate,
+        'onComplete': function() { bench.emit('complete'); }
+      });
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Cycles a benchmark until a run `count` can be established.
+     *
+     * @private
+     * @param {Object} clone The cloned benchmark instance.
+     * @param {Object} options The options object.
+     */
+    function cycle(clone, options) {
+      options || (options = {});
+
+      var deferred;
+      if (clone instanceof Deferred) {
+        deferred = clone;
+        clone = clone.benchmark;
+      }
+      var clocked,
+          cycles,
+          divisor,
+          event,
+          minTime,
+          period,
+          async = options.async,
+          bench = clone._original,
+          count = clone.count,
+          times = clone.times;
+
+      // Continue, if not aborted between cycles.
+      if (clone.running) {
+        // `minTime` is set to `Benchmark.options.minTime` in `clock()`.
+        cycles = ++clone.cycles;
+        clocked = deferred ? deferred.elapsed : clock(clone);
+        minTime = clone.minTime;
+
+        if (cycles > bench.cycles) {
+          bench.cycles = cycles;
+        }
+        if (clone.error) {
+          event = Event('error');
+          event.message = clone.error;
+          clone.emit(event);
+          if (!event.cancelled) {
+            clone.abort();
+          }
+        }
+      }
+      // Continue, if not errored.
+      if (clone.running) {
+        // Compute the time taken to complete last test cycle.
+        bench.times.cycle = times.cycle = clocked;
+        // Compute the seconds per operation.
+        period = bench.times.period = times.period = clocked / count;
+        // Compute the ops per second.
+        bench.hz = clone.hz = 1 / period;
+        // Avoid working our way up to this next time.
+        bench.initCount = clone.initCount = count;
+        // Do we need to do another cycle?
+        clone.running = clocked < minTime;
+
+        if (clone.running) {
+          // Tests may clock at `0` when `initCount` is a small number,
+          // to avoid that we set its count to something a bit higher.
+          if (!clocked && (divisor = divisors[clone.cycles]) != null) {
+            count = floor(4e6 / divisor);
+          }
+          // Calculate how many more iterations it will take to achieve the `minTime`.
+          if (count <= clone.count) {
+            count += Math.ceil((minTime - clocked) / period);
+          }
+          clone.running = count != Infinity;
+        }
+      }
+      // Should we exit early?
+      event = Event('cycle');
+      clone.emit(event);
+      if (event.aborted) {
+        clone.abort();
+      }
+      // Figure out what to do next.
+      if (clone.running) {
+        // Start a new cycle.
+        clone.count = count;
+        if (deferred) {
+          clone.compiled.call(deferred, context, timer);
+        } else if (async) {
+          delay(clone, function() { cycle(clone, options); });
+        } else {
+          cycle(clone);
+        }
+      }
+      else {
+        // Fix TraceMonkey bug associated with clock fallbacks.
+        // For more information see http://bugzil.la/509069.
+        if (support.browser) {
+          runScript(uid + '=1;delete ' + uid);
+        }
+        // We're done.
+        clone.emit('complete');
+      }
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Runs the benchmark.
+     *
+     * @memberOf Benchmark
+     * @param {Object} [options={}] Options object.
+     * @returns {Object} The benchmark instance.
+     * @example
+     *
+     * // basic usage
+     * bench.run();
+     *
+     * // or with options
+     * bench.run({ 'async': true });
+     */
+    function run(options) {
+      var bench = this,
+          event = Event('start');
+
+      // Set `running` to `false` so `reset()` won't call `abort()`.
+      bench.running = false;
+      bench.reset();
+      bench.running = true;
+
+      bench.count = bench.initCount;
+      bench.times.timeStamp = _.now();
+      bench.emit(event);
+
+      if (!event.cancelled) {
+        options = { 'async': ((options = options && options.async) == null ? bench.async : options) && support.timeout };
+
+        // For clones created within `compute()`.
+        if (bench._original) {
+          if (bench.defer) {
+            Deferred(bench);
+          } else {
+            cycle(bench, options);
+          }
+        }
+        // For original benchmarks.
+        else {
+          compute(bench, options);
+        }
+      }
+      return bench;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // Firefox 1 erroneously defines variable and argument names of functions on
+    // the function itself as non-configurable properties with `undefined` values.
+    // The bugginess continues as the `Benchmark` constructor has an argument
+    // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,
+    // making it non-writable in the process, unless it is the first property
+    // assigned by for-in loop of `_.assign()`.
+    _.assign(Benchmark, {
+
+      /**
+       * The default options copied by benchmark instances.
+       *
+       * @static
+       * @memberOf Benchmark
+       * @type Object
+       */
+      'options': {
+
+        /**
+         * A flag to indicate that benchmark cycles will execute asynchronously
+         * by default.
+         *
+         * @memberOf Benchmark.options
+         * @type boolean
+         */
+        'async': false,
+
+        /**
+         * A flag to indicate that the benchmark clock is deferred.
+         *
+         * @memberOf Benchmark.options
+         * @type boolean
+         */
+        'defer': false,
+
+        /**
+         * The delay between test cycles (secs).
+         * @memberOf Benchmark.options
+         * @type number
+         */
+        'delay': 0.005,
+
+        /**
+         * Displayed by `Benchmark#toString` when a `name` is not available
+         * (auto-generated if absent).
+         *
+         * @memberOf Benchmark.options
+         * @type string
+         */
+        'id': undefined,
+
+        /**
+         * The default number of times to execute a test on a benchmark's first cycle.
+         *
+         * @memberOf Benchmark.options
+         * @type number
+         */
+        'initCount': 1,
+
+        /**
+         * The maximum time a benchmark is allowed to run before finishing (secs).
+         *
+         * Note: Cycle delays aren't counted toward the maximum time.
+         *
+         * @memberOf Benchmark.options
+         * @type number
+         */
+        'maxTime': 5,
+
+        /**
+         * The minimum sample size required to perform statistical analysis.
+         *
+         * @memberOf Benchmark.options
+         * @type number
+         */
+        'minSamples': 5,
+
+        /**
+         * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
+         *
+         * @memberOf Benchmark.options
+         * @type number
+         */
+        'minTime': 0,
+
+        /**
+         * The name of the benchmark.
+         *
+         * @memberOf Benchmark.options
+         * @type string
+         */
+        'name': undefined,
+
+        /**
+         * An event listener called when the benchmark is aborted.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onAbort': undefined,
+
+        /**
+         * An event listener called when the benchmark completes running.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onComplete': undefined,
+
+        /**
+         * An event listener called after each run cycle.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onCycle': undefined,
+
+        /**
+         * An event listener called when a test errors.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onError': undefined,
+
+        /**
+         * An event listener called when the benchmark is reset.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onReset': undefined,
+
+        /**
+         * An event listener called when the benchmark starts running.
+         *
+         * @memberOf Benchmark.options
+         * @type Function
+         */
+        'onStart': undefined
+      },
+
+      /**
+       * Platform object with properties describing things like browser name,
+       * version, and operating system. See [`platform.js`](https://mths.be/platform).
+       *
+       * @static
+       * @memberOf Benchmark
+       * @type Object
+       */
+      'platform': context.platform || require('platform') || ({
+        'description': context.navigator && context.navigator.userAgent || null,
+        'layout': null,
+        'product': null,
+        'name': null,
+        'manufacturer': null,
+        'os': null,
+        'prerelease': null,
+        'version': null,
+        'toString': function() {
+          return this.description || '';
+        }
+      }),
+
+      /**
+       * The semantic version number.
+       *
+       * @static
+       * @memberOf Benchmark
+       * @type string
+       */
+      'version': '2.1.1'
+    });
+
+    _.assign(Benchmark, {
+      'filter': filter,
+      'formatNumber': formatNumber,
+      'invoke': invoke,
+      'join': join,
+      'runInContext': runInContext,
+      'support': support
+    });
+
+    // Add lodash methods to Benchmark.
+    _.each(['each', 'forEach', 'forOwn', 'has', 'indexOf', 'map', 'reduce'], function(methodName) {
+      Benchmark[methodName] = _[methodName];
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    _.assign(Benchmark.prototype, {
+
+      /**
+       * The number of times a test was executed.
+       *
+       * @memberOf Benchmark
+       * @type number
+       */
+      'count': 0,
+
+      /**
+       * The number of cycles performed while benchmarking.
+       *
+       * @memberOf Benchmark
+       * @type number
+       */
+      'cycles': 0,
+
+      /**
+       * The number of executions per second.
+       *
+       * @memberOf Benchmark
+       * @type number
+       */
+      'hz': 0,
+
+      /**
+       * The compiled test function.
+       *
+       * @memberOf Benchmark
+       * @type {Function|string}
+       */
+      'compiled': undefined,
+
+      /**
+       * The error object if the test failed.
+       *
+       * @memberOf Benchmark
+       * @type Object
+       */
+      'error': undefined,
+
+      /**
+       * The test to benchmark.
+       *
+       * @memberOf Benchmark
+       * @type {Function|string}
+       */
+      'fn': undefined,
+
+      /**
+       * A flag to indicate if the benchmark is aborted.
+       *
+       * @memberOf Benchmark
+       * @type boolean
+       */
+      'aborted': false,
+
+      /**
+       * A flag to indicate if the benchmark is running.
+       *
+       * @memberOf Benchmark
+       * @type boolean
+       */
+      'running': false,
+
+      /**
+       * Compiled into the test and executed immediately **before** the test loop.
+       *
+       * @memberOf Benchmark
+       * @type {Function|string}
+       * @example
+       *
+       * // basic usage
+       * var bench = Benchmark({
+       *   'setup': function() {
+       *     var c = this.count,
+       *         element = document.getElementById('container');
+       *     while (c--) {
+       *       element.appendChild(document.createElement('div'));
+       *     }
+       *   },
+       *   'fn': function() {
+       *     element.removeChild(element.lastChild);
+       *   }
+       * });
+       *
+       * // compiles to something like:
+       * var c = this.count,
+       *     element = document.getElementById('container');
+       * while (c--) {
+       *   element.appendChild(document.createElement('div'));
+       * }
+       * var start = new Date;
+       * while (count--) {
+       *   element.removeChild(element.lastChild);
+       * }
+       * var end = new Date - start;
+       *
+       * // or using strings
+       * var bench = Benchmark({
+       *   'setup': '\
+       *     var a = 0;\n\
+       *     (function() {\n\
+       *       (function() {\n\
+       *         (function() {',
+       *   'fn': 'a += 1;',
+       *   'teardown': '\
+       *          }())\n\
+       *        }())\n\
+       *      }())'
+       * });
+       *
+       * // compiles to something like:
+       * var a = 0;
+       * (function() {
+       *   (function() {
+       *     (function() {
+       *       var start = new Date;
+       *       while (count--) {
+       *         a += 1;
+       *       }
+       *       var end = new Date - start;
+       *     }())
+       *   }())
+       * }())
+       */
+      'setup': _.noop,
+
+      /**
+       * Compiled into the test and executed immediately **after** the test loop.
+       *
+       * @memberOf Benchmark
+       * @type {Function|string}
+       */
+      'teardown': _.noop,
+
+      /**
+       * An object of stats including mean, margin or error, and standard deviation.
+       *
+       * @memberOf Benchmark
+       * @type Object
+       */
+      'stats': {
+
+        /**
+         * The margin of error.
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'moe': 0,
+
+        /**
+         * The relative margin of error (expressed as a percentage of the mean).
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'rme': 0,
+
+        /**
+         * The standard error of the mean.
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'sem': 0,
+
+        /**
+         * The sample standard deviation.
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'deviation': 0,
+
+        /**
+         * The sample arithmetic mean (secs).
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'mean': 0,
+
+        /**
+         * The array of sampled periods.
+         *
+         * @memberOf Benchmark#stats
+         * @type Array
+         */
+        'sample': [],
+
+        /**
+         * The sample variance.
+         *
+         * @memberOf Benchmark#stats
+         * @type number
+         */
+        'variance': 0
+      },
+
+      /**
+       * An object of timing data including cycle, elapsed, period, start, and stop.
+       *
+       * @memberOf Benchmark
+       * @type Object
+       */
+      'times': {
+
+        /**
+         * The time taken to complete the last cycle (secs).
+         *
+         * @memberOf Benchmark#times
+         * @type number
+         */
+        'cycle': 0,
+
+        /**
+         * The time taken to complete the benchmark (secs).
+         *
+         * @memberOf Benchmark#times
+         * @type number
+         */
+        'elapsed': 0,
+
+        /**
+         * The time taken to execute the test once (secs).
+         *
+         * @memberOf Benchmark#times
+         * @type number
+         */
+        'period': 0,
+
+        /**
+         * A timestamp of when the benchmark started (ms).
+         *
+         * @memberOf Benchmark#times
+         * @type number
+         */
+        'timeStamp': 0
+      }
+    });
+
+    _.assign(Benchmark.prototype, {
+      'abort': abort,
+      'clone': clone,
+      'compare': compare,
+      'emit': emit,
+      'listeners': listeners,
+      'off': off,
+      'on': on,
+      'reset': reset,
+      'run': run,
+      'toString': toStringBench
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    _.assign(Deferred.prototype, {
+
+      /**
+       * The deferred benchmark instance.
+       *
+       * @memberOf Benchmark.Deferred
+       * @type Object
+       */
+      'benchmark': null,
+
+      /**
+       * The number of deferred cycles performed while benchmarking.
+       *
+       * @memberOf Benchmark.Deferred
+       * @type number
+       */
+      'cycles': 0,
+
+      /**
+       * The time taken to complete the deferred benchmark (secs).
+       *
+       * @memberOf Benchmark.Deferred
+       * @type number
+       */
+      'elapsed': 0,
+
+      /**
+       * A timestamp of when the deferred benchmark started (ms).
+       *
+       * @memberOf Benchmark.Deferred
+       * @type number
+       */
+      'timeStamp': 0
+    });
+
+    _.assign(Deferred.prototype, {
+      'resolve': resolve
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    _.assign(Event.prototype, {
+
+      /**
+       * A flag to indicate if the emitters listener iteration is aborted.
+       *
+       * @memberOf Benchmark.Event
+       * @type boolean
+       */
+      'aborted': false,
+
+      /**
+       * A flag to indicate if the default action is cancelled.
+       *
+       * @memberOf Benchmark.Event
+       * @type boolean
+       */
+      'cancelled': false,
+
+      /**
+       * The object whose listeners are currently being processed.
+       *
+       * @memberOf Benchmark.Event
+       * @type Object
+       */
+      'currentTarget': undefined,
+
+      /**
+       * The return value of the last executed listener.
+       *
+       * @memberOf Benchmark.Event
+       * @type Mixed
+       */
+      'result': undefined,
+
+      /**
+       * The object to which the event was originally emitted.
+       *
+       * @memberOf Benchmark.Event
+       * @type Object
+       */
+      'target': undefined,
+
+      /**
+       * A timestamp of when the event was created (ms).
+       *
+       * @memberOf Benchmark.Event
+       * @type number
+       */
+      'timeStamp': 0,
+
+      /**
+       * The event type.
+       *
+       * @memberOf Benchmark.Event
+       * @type string
+       */
+      'type': ''
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The default options copied by suite instances.
+     *
+     * @static
+     * @memberOf Benchmark.Suite
+     * @type Object
+     */
+    Suite.options = {
+
+      /**
+       * The name of the suite.
+       *
+       * @memberOf Benchmark.Suite.options
+       * @type string
+       */
+      'name': undefined
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    _.assign(Suite.prototype, {
+
+      /**
+       * The number of benchmarks in the suite.
+       *
+       * @memberOf Benchmark.Suite
+       * @type number
+       */
+      'length': 0,
+
+      /**
+       * A flag to indicate if the suite is aborted.
+       *
+       * @memberOf Benchmark.Suite
+       * @type boolean
+       */
+      'aborted': false,
+
+      /**
+       * A flag to indicate if the suite is running.
+       *
+       * @memberOf Benchmark.Suite
+       * @type boolean
+       */
+      'running': false
+    });
+
+    _.assign(Suite.prototype, {
+      'abort': abortSuite,
+      'add': add,
+      'clone': cloneSuite,
+      'emit': emit,
+      'filter': filterSuite,
+      'join': arrayRef.join,
+      'listeners': listeners,
+      'off': off,
+      'on': on,
+      'pop': arrayRef.pop,
+      'push': push,
+      'reset': resetSuite,
+      'run': runSuite,
+      'reverse': arrayRef.reverse,
+      'shift': shift,
+      'slice': slice,
+      'sort': arrayRef.sort,
+      'splice': arrayRef.splice,
+      'unshift': unshift
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    // Expose Deferred, Event, and Suite.
+    _.assign(Benchmark, {
+      'Deferred': Deferred,
+      'Event': Event,
+      'Suite': Suite
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    // Add lodash methods as Suite methods.
+    _.each(['each', 'forEach', 'indexOf', 'map', 'reduce'], function(methodName) {
+      var func = _[methodName];
+      Suite.prototype[methodName] = function() {
+        var args = [this];
+        push.apply(args, arguments);
+        return func.apply(_, args);
+      };
+    });
+
+    // Avoid array-like object bugs with `Array#shift` and `Array#splice`
+    // in Firefox < 10 and IE < 9.
+    _.each(['pop', 'shift', 'splice'], function(methodName) {
+      var func = arrayRef[methodName];
+
+      Suite.prototype[methodName] = function() {
+        var value = this,
+            result = func.apply(value, arguments);
+
+        if (value.length === 0) {
+          delete value[0];
+        }
+        return result;
+      };
+    });
+
+    // Avoid buggy `Array#unshift` in IE < 8 which doesn't return the new
+    // length of the array.
+    Suite.prototype.unshift = function() {
+      var value = this;
+      unshift.apply(value, arguments);
+      return value.length;
+    };
+
+    return Benchmark;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // Export Benchmark.
+  // Some AMD build optimizers, like r.js, check for condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // Define as an anonymous module so, through path mapping, it can be aliased.
+    define(['lodash', 'platform'], function(_, platform) {
+      return runInContext({
+        '_': _,
+        'platform': platform
+      });
+    });
+  }
+  else {
+    var Benchmark = runInContext();
+
+    // Check for `exports` after `define` in case a build optimizer adds an `exports` object.
+    if (freeExports && freeModule) {
+      // Export for Node.js.
+      if (moduleExports) {
+        (freeModule.exports = Benchmark).Benchmark = Benchmark;
+      }
+      // Export for CommonJS support.
+      freeExports.Benchmark = Benchmark;
+    }
+    else {
+      // Export to the global object.
+      root.Benchmark = Benchmark;
+    }
+  }
+}.call(this));
diff --git a/third_party/immer/extra/js/lib/immutable.min.js b/third_party/immer/extra/js/lib/immutable.min.js
new file mode 100644
index 0000000000..18ffb77bb1
--- /dev/null
+++ b/third_party/immer/extra/js/lib/immutable.min.js
@@ -0,0 +1,36 @@
+/**
+ *  Copyright (c) 2014-2015, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Immutable=e()}(this,function(){"use strict";function t(t,e){e&&(t.prototype=Object.create(e.prototype)),t.prototype.constructor=t}function e(t){return o(t)?t:O(t)}function r(t){return u(t)?t:x(t)}function n(t){return s(t)?t:k(t)}function i(t){return o(t)&&!a(t)?t:A(t)}function o(t){return!(!t||!t[ar])}function u(t){return!(!t||!t[hr])}function s(t){return!(!t||!t[fr])}function a(t){return u(t)||s(t)}function h(t){return!(!t||!t[cr])}function f(t){return t.value=!1,t}function c(t){t&&(t.value=!0)}function _(){}function p(t,e){e=e||0;for(var r=Math.max(0,t.length-e),n=Array(r),i=0;r>i;i++)n[i]=t[i+e];return n}function v(t){return void 0===t.size&&(t.size=t.__iterate(y)),t.size}function l(t,e){if("number"!=typeof e){var r=e>>>0;if(""+r!==e||4294967295===r)return NaN;e=r}return 0>e?v(t)+e:e}function y(){return!0}function d(t,e,r){return(0===t||void 0!==r&&-r>=t)&&(void 0===e||void 0!==r&&e>=r)}function m(t,e){return w(t,e,0)}function g(t,e){return w(t,e,e)}function w(t,e,r){return void 0===t?r:0>t?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function S(t){this.next=t}function z(t,e,r,n){var i=0===t?e:1===t?r:[e,r];return n?n.value=i:n={value:i,done:!1},n}function I(){return{value:void 0,done:!0}}function b(t){return!!M(t)}function q(t){return t&&"function"==typeof t.next}function D(t){var e=M(t);return e&&e.call(t)}function M(t){var e=t&&(zr&&t[zr]||t[Ir]);return"function"==typeof e?e:void 0}function E(t){return t&&"number"==typeof t.length}function O(t){return null===t||void 0===t?T():o(t)?t.toSeq():C(t)}function x(t){return null===t||void 0===t?T().toKeyedSeq():o(t)?u(t)?t.toSeq():t.fromEntrySeq():B(t)}function k(t){return null===t||void 0===t?T():o(t)?u(t)?t.entrySeq():t.toIndexedSeq():W(t)}function A(t){return(null===t||void 0===t?T():o(t)?u(t)?t.entrySeq():t:W(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function R(t){var e=Object.keys(t);this._object=t,this._keys=e,
+this.size=e.length}function U(t){this._iterable=t,this.size=t.length||t.size}function K(t){this._iterator=t,this._iteratorCache=[]}function L(t){return!(!t||!t[qr])}function T(){return Dr||(Dr=new j([]))}function B(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():q(t)?new K(t).fromEntrySeq():b(t)?new U(t).fromEntrySeq():"object"==typeof t?new R(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function W(t){var e=J(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function C(t){var e=J(t)||"object"==typeof t&&new R(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function J(t){return E(t)?new j(t):q(t)?new K(t):b(t)?new U(t):void 0}function N(t,e,r,n){var i=t._cache;if(i){for(var o=i.length-1,u=0;o>=u;u++){var s=i[r?o-u:u];if(e(s[1],n?s[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,r)}function P(t,e,r,n){var i=t._cache;if(i){var o=i.length-1,u=0;return new S(function(){var t=i[r?o-u:u];return u++>o?I():z(e,n?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,r)}function H(t,e){return e?V(e,t,"",{"":t}):Y(t)}function V(t,e,r,n){return Array.isArray(e)?t.call(n,r,k(e).map(function(r,n){return V(t,r,n,e)})):Q(e)?t.call(n,r,x(e).map(function(r,n){return V(t,r,n,e)})):e}function Y(t){return Array.isArray(t)?k(t).map(Y).toList():Q(t)?x(t).map(Y).toMap():t}function Q(t){return t&&(t.constructor===Object||void 0===t.constructor)}function X(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return"function"==typeof t.equals&&"function"==typeof e.equals&&t.equals(e)?!0:!1}function F(t,e){if(t===e)return!0;if(!o(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||u(t)!==u(e)||s(t)!==s(e)||h(t)!==h(e))return!1;if(0===t.size&&0===e.size)return!0;
+var r=!a(t);if(h(t)){var n=t.entries();return e.every(function(t,e){var i=n.next().value;return i&&X(i[1],t)&&(r||X(i[0],e))})&&n.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var f=t;t=e,e=f}var c=!0,_=e.__iterate(function(e,n){return(r?t.has(e):i?X(e,t.get(n,yr)):X(t.get(n,yr),e))?void 0:(c=!1,!1)});return c&&t.size===_}function G(t,e){if(!(this instanceof G))return new G(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(Mr)return Mr;Mr=this}}function Z(t,e){if(!t)throw Error(e)}function $(t,e,r){if(!(this instanceof $))return new $(t,e,r);if(Z(0!==r,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),r=void 0===r?1:Math.abs(r),t>e&&(r=-r),this._start=t,this._end=e,this._step=r,this.size=Math.max(0,Math.ceil((e-t)/r-1)+1),0===this.size){if(Er)return Er;Er=this}}function tt(){throw TypeError("Abstract")}function et(){}function rt(){}function nt(){}function it(t){return t>>>1&1073741824|3221225471&t}function ot(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){if(t!==t||t===1/0)return 0;var r=0|t;for(r!==t&&(r^=4294967295*t);t>4294967295;)t/=4294967295,r^=t;return it(r)}if("string"===e)return t.length>Kr?ut(t):st(t);if("function"==typeof t.hashCode)return t.hashCode();if("object"===e)return at(t);if("function"==typeof t.toString)return st(""+t);throw Error("Value type "+e+" cannot be hashed.")}function ut(t){var e=Br[t];return void 0===e&&(e=st(t),Tr===Lr&&(Tr=0,Br={}),Tr++,Br[t]=e),e}function st(t){for(var e=0,r=0;t.length>r;r++)e=31*e+t.charCodeAt(r)|0;return it(e)}function at(t){var e;if(jr&&(e=Or.get(t),void 0!==e))return e;if(e=t[Ur],void 0!==e)return e;if(!Ar){if(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Ur],void 0!==e)return e;if(e=ht(t),void 0!==e)return e}if(e=++Rr,1073741824&Rr&&(Rr=0),jr)Or.set(t,e);else{if(void 0!==kr&&kr(t)===!1)throw Error("Non-extensible objects are not allowed as keys.");
+if(Ar)Object.defineProperty(t,Ur,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Ur]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Ur]=e}}return e}function ht(t){if(t&&t.nodeType>0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ft(t){Z(t!==1/0,"Cannot perform this action with an infinite size.")}function ct(t){return null===t||void 0===t?zt():_t(t)&&!h(t)?t:zt().withMutations(function(e){var n=r(t);ft(n.size),n.forEach(function(t,r){return e.set(r,t)})})}function _t(t){return!(!t||!t[Wr])}function pt(t,e){this.ownerID=t,this.entries=e}function vt(t,e,r){this.ownerID=t,this.bitmap=e,this.nodes=r}function lt(t,e,r){this.ownerID=t,this.count=e,this.nodes=r}function yt(t,e,r){this.ownerID=t,this.keyHash=e,this.entries=r}function dt(t,e,r){this.ownerID=t,this.keyHash=e,this.entry=r}function mt(t,e,r){this._type=e,this._reverse=r,this._stack=t._root&&wt(t._root)}function gt(t,e){return z(t,e[0],e[1])}function wt(t,e){return{node:t,index:0,__prev:e}}function St(t,e,r,n){var i=Object.create(Cr);return i.size=t,i._root=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function zt(){return Jr||(Jr=St(0))}function It(t,e,r){var n,i;if(t._root){var o=f(dr),u=f(mr);if(n=bt(t._root,t.__ownerID,0,void 0,e,r,o,u),!u.value)return t;i=t.size+(o.value?r===yr?-1:1:0)}else{if(r===yr)return t;i=1,n=new pt(t.__ownerID,[[e,r]])}return t.__ownerID?(t.size=i,t._root=n,t.__hash=void 0,t.__altered=!0,t):n?St(i,n):zt()}function bt(t,e,r,n,i,o,u,s){return t?t.update(e,r,n,i,o,u,s):o===yr?t:(c(s),c(u),new dt(e,n,[i,o]))}function qt(t){return t.constructor===dt||t.constructor===yt}function Dt(t,e,r,n,i){if(t.keyHash===n)return new yt(e,n,[t.entry,i]);var o,u=(0===r?t.keyHash:t.keyHash>>>r)&lr,s=(0===r?n:n>>>r)&lr,a=u===s?[Dt(t,e,r+pr,n,i)]:(o=new dt(e,n,i),
+s>u?[t,o]:[o,t]);return new vt(e,1<<u|1<<s,a)}function Mt(t,e,r,n){t||(t=new _);for(var i=new dt(t,ot(r),[r,n]),o=0;e.length>o;o++){var u=e[o];i=i.update(t,0,void 0,u[0],u[1])}return i}function Et(t,e,r,n){for(var i=0,o=0,u=Array(r),s=0,a=1,h=e.length;h>s;s++,a<<=1){var f=e[s];void 0!==f&&s!==n&&(i|=a,u[o++]=f)}return new vt(t,i,u)}function Ot(t,e,r,n,i){for(var o=0,u=Array(vr),s=0;0!==r;s++,r>>>=1)u[s]=1&r?e[o++]:void 0;return u[n]=i,new lt(t,o+1,u)}function xt(t,e,n){for(var i=[],u=0;n.length>u;u++){var s=n[u],a=r(s);o(s)||(a=a.map(function(t){return H(t)})),i.push(a)}return jt(t,e,i)}function kt(t,e,r){return t&&t.mergeDeep&&o(e)?t.mergeDeep(e):X(t,e)?t:e}function At(t){return function(e,r,n){if(e&&e.mergeDeepWith&&o(r))return e.mergeDeepWith(t,r);var i=t(e,r,n);return X(e,i)?e:i}}function jt(t,e,r){return r=r.filter(function(t){return 0!==t.size}),0===r.length?t:0!==t.size||t.__ownerID||1!==r.length?t.withMutations(function(t){for(var n=e?function(r,n){t.update(n,yr,function(t){return t===yr?r:e(t,r,n)})}:function(e,r){t.set(r,e)},i=0;r.length>i;i++)r[i].forEach(n)}):t.constructor(r[0])}function Rt(t,e,r,n){var i=t===yr,o=e.next();if(o.done){var u=i?r:t,s=n(u);return s===u?t:s}Z(i||t&&t.set,"invalid keyPath");var a=o.value,h=i?yr:t.get(a,yr),f=Rt(h,e,r,n);return f===h?t:f===yr?t.remove(a):(i?zt():t).set(a,f)}function Ut(t){return t-=t>>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function Kt(t,e,r,n){var i=n?t:p(t);return i[e]=r,i}function Lt(t,e,r,n){var i=t.length+1;if(n&&e+1===i)return t[e]=r,t;for(var o=Array(i),u=0,s=0;i>s;s++)s===e?(o[s]=r,u=-1):o[s]=t[s+u];return o}function Tt(t,e,r){var n=t.length-1;if(r&&e===n)return t.pop(),t;for(var i=Array(n),o=0,u=0;n>u;u++)u===e&&(o=1),i[u]=t[u+o];return i}function Bt(t){var e=Pt();if(null===t||void 0===t)return e;if(Wt(t))return t;var r=n(t),i=r.size;return 0===i?e:(ft(i),i>0&&vr>i?Nt(0,i,pr,null,new Ct(r.toArray())):e.withMutations(function(t){t.setSize(i),r.forEach(function(e,r){return t.set(r,e)})}))}function Wt(t){
+return!(!t||!t[Vr])}function Ct(t,e){this.array=t,this.ownerID=e}function Jt(t,e){function r(t,e,r){return 0===e?n(t,r):i(t,e,r)}function n(t,r){var n=r===s?a&&a.array:t&&t.array,i=r>o?0:o-r,h=u-r;return h>vr&&(h=vr),function(){if(i===h)return Xr;var t=e?--h:i++;return n&&n[t]}}function i(t,n,i){var s,a=t&&t.array,h=i>o?0:o-i>>n,f=(u-i>>n)+1;return f>vr&&(f=vr),function(){for(;;){if(s){var t=s();if(t!==Xr)return t;s=null}if(h===f)return Xr;var o=e?--f:h++;s=r(a&&a[o],n-pr,i+(o<<n))}}}var o=t._origin,u=t._capacity,s=Gt(u),a=t._tail;return r(t._root,t._level,0)}function Nt(t,e,r,n,i,o,u){var s=Object.create(Yr);return s.size=e-t,s._origin=t,s._capacity=e,s._level=r,s._root=n,s._tail=i,s.__ownerID=o,s.__hash=u,s.__altered=!1,s}function Pt(){return Qr||(Qr=Nt(0,0,pr))}function Ht(t,e,r){if(e=l(t,e),e!==e)return t;if(e>=t.size||0>e)return t.withMutations(function(t){0>e?Xt(t,e).set(0,r):Xt(t,0,e+1).set(e,r)});e+=t._origin;var n=t._tail,i=t._root,o=f(mr);return e>=Gt(t._capacity)?n=Vt(n,t.__ownerID,0,e,r,o):i=Vt(i,t.__ownerID,t._level,e,r,o),o.value?t.__ownerID?(t._root=i,t._tail=n,t.__hash=void 0,t.__altered=!0,t):Nt(t._origin,t._capacity,t._level,i,n):t}function Vt(t,e,r,n,i,o){var u=n>>>r&lr,s=t&&t.array.length>u;if(!s&&void 0===i)return t;var a;if(r>0){var h=t&&t.array[u],f=Vt(h,e,r-pr,n,i,o);return f===h?t:(a=Yt(t,e),a.array[u]=f,a)}return s&&t.array[u]===i?t:(c(o),a=Yt(t,e),void 0===i&&u===a.array.length-1?a.array.pop():a.array[u]=i,a)}function Yt(t,e){return e&&t&&e===t.ownerID?t:new Ct(t?t.array.slice():[],e)}function Qt(t,e){if(e>=Gt(t._capacity))return t._tail;if(1<<t._level+pr>e){for(var r=t._root,n=t._level;r&&n>0;)r=r.array[e>>>n&lr],n-=pr;return r}}function Xt(t,e,r){void 0!==e&&(e=0|e),void 0!==r&&(r=0|r);var n=t.__ownerID||new _,i=t._origin,o=t._capacity,u=i+e,s=void 0===r?o:0>r?o+r:i+r;if(u===i&&s===o)return t;if(u>=s)return t.clear();for(var a=t._level,h=t._root,f=0;0>u+f;)h=new Ct(h&&h.array.length?[void 0,h]:[],n),a+=pr,f+=1<<a;f&&(u+=f,i+=f,s+=f,o+=f);for(var c=Gt(o),p=Gt(s);p>=1<<a+pr;)h=new Ct(h&&h.array.length?[h]:[],n),
+a+=pr;var v=t._tail,l=c>p?Qt(t,s-1):p>c?new Ct([],n):v;if(v&&p>c&&o>u&&v.array.length){h=Yt(h,n);for(var y=h,d=a;d>pr;d-=pr){var m=c>>>d&lr;y=y.array[m]=Yt(y.array[m],n)}y.array[c>>>pr&lr]=v}if(o>s&&(l=l&&l.removeAfter(n,0,s)),u>=p)u-=p,s-=p,a=pr,h=null,l=l&&l.removeBefore(n,0,u);else if(u>i||c>p){for(f=0;h;){var g=u>>>a&lr;if(g!==p>>>a&lr)break;g&&(f+=(1<<a)*g),a-=pr,h=h.array[g]}h&&u>i&&(h=h.removeBefore(n,a,u-f)),h&&c>p&&(h=h.removeAfter(n,a,p-f)),f&&(u-=f,s-=f)}return t.__ownerID?(t.size=s-u,t._origin=u,t._capacity=s,t._level=a,t._root=h,t._tail=l,t.__hash=void 0,t.__altered=!0,t):Nt(u,s,a,h,l)}function Ft(t,e,r){for(var i=[],u=0,s=0;r.length>s;s++){var a=r[s],h=n(a);h.size>u&&(u=h.size),o(a)||(h=h.map(function(t){return H(t)})),i.push(h)}return u>t.size&&(t=t.setSize(u)),jt(t,e,i)}function Gt(t){return vr>t?0:t-1>>>pr<<pr}function Zt(t){return null===t||void 0===t?ee():$t(t)?t:ee().withMutations(function(e){var n=r(t);ft(n.size),n.forEach(function(t,r){return e.set(r,t)})})}function $t(t){return _t(t)&&h(t)}function te(t,e,r,n){var i=Object.create(Zt.prototype);return i.size=t?t.size:0,i._map=t,i._list=e,i.__ownerID=r,i.__hash=n,i}function ee(){return Fr||(Fr=te(zt(),Pt()))}function re(t,e,r){var n,i,o=t._map,u=t._list,s=o.get(e),a=void 0!==s;if(r===yr){if(!a)return t;u.size>=vr&&u.size>=2*o.size?(i=u.filter(function(t,e){return void 0!==t&&s!==e}),n=i.toKeyedSeq().map(function(t){return t[0]}).flip().toMap(),t.__ownerID&&(n.__ownerID=i.__ownerID=t.__ownerID)):(n=o.remove(e),i=s===u.size-1?u.pop():u.set(s,void 0))}else if(a){if(r===u.get(s)[1])return t;n=o,i=u.set(s,[e,r])}else n=o.set(e,u.size),i=u.set(u.size,[e,r]);return t.__ownerID?(t.size=n.size,t._map=n,t._list=i,t.__hash=void 0,t):te(n,i)}function ne(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ie(t){this._iter=t,this.size=t.size}function oe(t){this._iter=t,this.size=t.size}function ue(t){this._iter=t,this.size=t.size}function se(t){var e=Ee(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);
+return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Oe,e.__iterateUncached=function(e,r){var n=this;return t.__iterate(function(t,r){return e(r,t,n)!==!1},r)},e.__iteratorUncached=function(e,r){if(e===Sr){var n=t.__iterator(e,r);return new S(function(){var t=n.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===wr?gr:wr,r)},e}function ae(t,e,r){var n=Ee(t);return n.size=t.size,n.has=function(e){return t.has(e)},n.get=function(n,i){var o=t.get(n,yr);return o===yr?i:e.call(r,o,n,t)},n.__iterateUncached=function(n,i){var o=this;return t.__iterate(function(t,i,u){return n(e.call(r,t,i,u),i,o)!==!1},i)},n.__iteratorUncached=function(n,i){var o=t.__iterator(Sr,i);return new S(function(){var i=o.next();if(i.done)return i;var u=i.value,s=u[0];return z(n,s,e.call(r,u[1],s,t),i)})},n}function he(t,e){var r=Ee(t);return r._iter=t,r.size=t.size,r.reverse=function(){return t},t.flip&&(r.flip=function(){var e=se(t);return e.reverse=function(){return t.flip()},e}),r.get=function(r,n){return t.get(e?r:-1-r,n)},r.has=function(r){return t.has(e?r:-1-r)},r.includes=function(e){return t.includes(e)},r.cacheResult=Oe,r.__iterate=function(e,r){var n=this;return t.__iterate(function(t,r){return e(t,r,n)},!r)},r.__iterator=function(e,r){return t.__iterator(e,!r)},r}function fe(t,e,r,n){var i=Ee(t);return n&&(i.has=function(n){var i=t.get(n,yr);return i!==yr&&!!e.call(r,i,n,t)},i.get=function(n,i){var o=t.get(n,yr);return o!==yr&&e.call(r,o,n,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,s=0;return t.__iterate(function(t,o,a){return e.call(r,t,o,a)?(s++,i(t,n?o:s-1,u)):void 0},o),s},i.__iteratorUncached=function(i,o){var u=t.__iterator(Sr,o),s=0;return new S(function(){for(;;){var o=u.next();if(o.done)return o;var a=o.value,h=a[0],f=a[1];if(e.call(r,f,h,t))return z(i,n?h:s++,f,o)}})},i}function ce(t,e,r){var n=ct().asMutable();return t.__iterate(function(i,o){n.update(e.call(r,i,o,t),0,function(t){
+return t+1})}),n.asImmutable()}function _e(t,e,r){var n=u(t),i=(h(t)?Zt():ct()).asMutable();t.__iterate(function(o,u){i.update(e.call(r,o,u,t),function(t){return t=t||[],t.push(n?[u,o]:o),t})});var o=Me(t);return i.map(function(e){return be(t,o(e))})}function pe(t,e,r,n){var i=t.size;if(void 0!==e&&(e=0|e),void 0!==r&&(r=r===1/0?i:0|r),d(e,r,i))return t;var o=m(e,i),u=g(r,i);if(o!==o||u!==u)return pe(t.toSeq().cacheResult(),e,r,n);var s,a=u-o;a===a&&(s=0>a?0:a);var h=Ee(t);return h.size=0===s?s:t.size&&s||void 0,!n&&L(t)&&s>=0&&(h.get=function(e,r){return e=l(this,e),e>=0&&s>e?t.get(e+o,r):r}),h.__iterateUncached=function(e,r){var i=this;if(0===s)return 0;if(r)return this.cacheResult().__iterate(e,r);var u=0,a=!0,h=0;return t.__iterate(function(t,r){return a&&(a=u++<o)?void 0:(h++,e(t,n?r:h-1,i)!==!1&&h!==s)}),h},h.__iteratorUncached=function(e,r){if(0!==s&&r)return this.cacheResult().__iterator(e,r);var i=0!==s&&t.__iterator(e,r),u=0,a=0;return new S(function(){for(;u++<o;)i.next();if(++a>s)return I();var t=i.next();return n||e===wr?t:e===gr?z(e,a-1,void 0,t):z(e,a-1,t.value[1],t)})},h}function ve(t,e,r){var n=Ee(t);return n.__iterateUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterate(n,i);var u=0;return t.__iterate(function(t,i,s){return e.call(r,t,i,s)&&++u&&n(t,i,o)}),u},n.__iteratorUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterator(n,i);var u=t.__iterator(Sr,i),s=!0;return new S(function(){if(!s)return I();var t=u.next();if(t.done)return t;var i=t.value,a=i[0],h=i[1];return e.call(r,h,a,o)?n===Sr?t:z(n,a,h,t):(s=!1,I())})},n}function le(t,e,r,n){var i=Ee(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,a=0;return t.__iterate(function(t,o,h){return s&&(s=e.call(r,t,o,h))?void 0:(a++,i(t,n?o:a-1,u))}),a},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var s=t.__iterator(Sr,o),a=!0,h=0;return new S(function(){var t,o,f;do{if(t=s.next(),t.done)return n||i===wr?t:i===gr?z(i,h++,void 0,t):z(i,h++,t.value[1],t);
+var c=t.value;o=c[0],f=c[1],a&&(a=e.call(r,f,o,u))}while(a);return i===Sr?t:z(i,o,f,t)})},i}function ye(t,e){var n=u(t),i=[t].concat(e).map(function(t){return o(t)?n&&(t=r(t)):t=n?B(t):W(Array.isArray(t)?t:[t]),t}).filter(function(t){return 0!==t.size});if(0===i.length)return t;if(1===i.length){var a=i[0];if(a===t||n&&u(a)||s(t)&&s(a))return a}var h=new j(i);return n?h=h.toKeyedSeq():s(t)||(h=h.toSetSeq()),h=h.flatten(!0),h.size=i.reduce(function(t,e){if(void 0!==t){var r=e.size;if(void 0!==r)return t+r}},0),h}function de(t,e,r){var n=Ee(t);return n.__iterateUncached=function(n,i){function u(t,h){var f=this;t.__iterate(function(t,i){return(!e||e>h)&&o(t)?u(t,h+1):n(t,r?i:s++,f)===!1&&(a=!0),!a},i)}var s=0,a=!1;return u(t,0),s},n.__iteratorUncached=function(n,i){var u=t.__iterator(n,i),s=[],a=0;return new S(function(){for(;u;){var t=u.next();if(t.done===!1){var h=t.value;if(n===Sr&&(h=h[1]),e&&!(e>s.length)||!o(h))return r?t:z(n,a++,h,t);s.push(u),u=h.__iterator(n,i)}else u=s.pop()}return I()})},n}function me(t,e,r){var n=Me(t);return t.toSeq().map(function(i,o){return n(e.call(r,i,o,t))}).flatten(!0)}function ge(t,e){var r=Ee(t);return r.size=t.size&&2*t.size-1,r.__iterateUncached=function(r,n){var i=this,o=0;return t.__iterate(function(t,n){return(!o||r(e,o++,i)!==!1)&&r(t,o++,i)!==!1},n),o},r.__iteratorUncached=function(r,n){var i,o=t.__iterator(wr,n),u=0;return new S(function(){return(!i||u%2)&&(i=o.next(),i.done)?i:u%2?z(r,u++,e):z(r,u++,i.value,i)})},r}function we(t,e,r){e||(e=xe);var n=u(t),i=0,o=t.toSeq().map(function(e,n){return[n,e,i++,r?r(e,n,t):e]}).toArray();return o.sort(function(t,r){return e(t[3],r[3])||t[2]-r[2]}).forEach(n?function(t,e){o[e].length=2}:function(t,e){o[e]=t[1]}),n?x(o):s(t)?k(o):A(o)}function Se(t,e,r){if(e||(e=xe),r){var n=t.toSeq().map(function(e,n){return[e,r(e,n,t)]}).reduce(function(t,r){return ze(e,t[1],r[1])?r:t});return n&&n[0]}return t.reduce(function(t,r){return ze(e,t,r)?r:t})}function ze(t,e,r){var n=t(r,e);return 0===n&&r!==e&&(void 0===r||null===r||r!==r)||n>0}function Ie(t,r,n){
+var i=Ee(t);return i.size=new j(n).map(function(t){return t.size}).min(),i.__iterate=function(t,e){for(var r,n=this.__iterator(wr,e),i=0;!(r=n.next()).done&&t(r.value,i++,this)!==!1;);return i},i.__iteratorUncached=function(t,i){var o=n.map(function(t){return t=e(t),D(i?t.reverse():t)}),u=0,s=!1;return new S(function(){var e;return s||(e=o.map(function(t){return t.next()}),s=e.some(function(t){return t.done})),s?I():z(t,u++,r.apply(null,e.map(function(t){return t.value})))})},i}function be(t,e){return L(t)?e:t.constructor(e)}function qe(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function De(t){return ft(t.size),v(t)}function Me(t){return u(t)?r:s(t)?n:i}function Ee(t){return Object.create((u(t)?x:s(t)?k:A).prototype)}function Oe(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):O.prototype.cacheResult.call(this)}function xe(t,e){return t>e?1:e>t?-1:0}function ke(t){var r=D(t);if(!r){if(!E(t))throw new TypeError("Expected iterable or array-like: "+t);r=D(e(t))}return r}function Ae(t,e){var r,n=function(o){if(o instanceof n)return o;if(!(this instanceof n))return new n(o);if(!r){r=!0;var u=Object.keys(t);Ue(i,u),i.size=u.length,i._name=e,i._keys=u,i._defaultValues=t}this._map=ct(o)},i=n.prototype=Object.create(Gr);return i.constructor=n,n}function je(t,e,r){var n=Object.create(Object.getPrototypeOf(t));return n._map=e,n.__ownerID=r,n}function Re(t){return t._name||t.constructor.name||"Record"}function Ue(t,e){try{e.forEach(Ke.bind(void 0,t))}catch(r){}}function Ke(t,e){Object.defineProperty(t,e,{get:function(){return this.get(e)},set:function(t){Z(this.__ownerID,"Cannot set on an immutable record."),this.set(e,t)}})}function Le(t){return null===t||void 0===t?Ce():Te(t)&&!h(t)?t:Ce().withMutations(function(e){var r=i(t);ft(r.size),r.forEach(function(t){return e.add(t)})})}function Te(t){return!(!t||!t[Zr])}function Be(t,e){return t.__ownerID?(t.size=e.size,t._map=e,t):e===t._map?t:0===e.size?t.__empty():t.__make(e)}function We(t,e){var r=Object.create($r);
+return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function Ce(){return tn||(tn=We(zt()))}function Je(t){return null===t||void 0===t?He():Ne(t)?t:He().withMutations(function(e){var r=i(t);ft(r.size),r.forEach(function(t){return e.add(t)})})}function Ne(t){return Te(t)&&h(t)}function Pe(t,e){var r=Object.create(en);return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function He(){return rn||(rn=Pe(ee()))}function Ve(t){return null===t||void 0===t?Xe():Ye(t)?t:Xe().unshiftAll(t)}function Ye(t){return!(!t||!t[nn])}function Qe(t,e,r,n){var i=Object.create(on);return i.size=t,i._head=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function Xe(){return un||(un=Qe(0))}function Fe(t,e){var r=function(r){t.prototype[r]=e[r]};return Object.keys(e).forEach(r),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(e).forEach(r),t}function Ge(t,e){return e}function Ze(t,e){return[e,t]}function $e(t){return function(){return!t.apply(this,arguments)}}function tr(t){return function(){return-t.apply(this,arguments)}}function er(t){return"string"==typeof t?JSON.stringify(t):t+""}function rr(){return p(arguments)}function nr(t,e){return e>t?1:t>e?-1:0}function ir(t){if(t.size===1/0)return 0;var e=h(t),r=u(t),n=e?1:0,i=t.__iterate(r?e?function(t,e){n=31*n+ur(ot(t),ot(e))|0}:function(t,e){n=n+ur(ot(t),ot(e))|0}:e?function(t){n=31*n+ot(t)|0}:function(t){n=n+ot(t)|0});return or(i,n)}function or(t,e){return e=xr(e,3432918353),e=xr(e<<15|e>>>-15,461845907),e=xr(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=xr(e^e>>>16,2246822507),e=xr(e^e>>>13,3266489909),e=it(e^e>>>16)}function ur(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var sr=Array.prototype.slice;t(r,e),t(n,e),t(i,e),e.isIterable=o,e.isKeyed=u,e.isIndexed=s,e.isAssociative=a,e.isOrdered=h,e.Keyed=r,e.Indexed=n,e.Set=i;var ar="@@__IMMUTABLE_ITERABLE__@@",hr="@@__IMMUTABLE_KEYED__@@",fr="@@__IMMUTABLE_INDEXED__@@",cr="@@__IMMUTABLE_ORDERED__@@",_r="delete",pr=5,vr=1<<pr,lr=vr-1,yr={},dr={value:!1},mr={value:!1},gr=0,wr=1,Sr=2,zr="function"==typeof Symbol&&Symbol.iterator,Ir="@@iterator",br=zr||Ir;
+S.prototype.toString=function(){return"[Iterator]"},S.KEYS=gr,S.VALUES=wr,S.ENTRIES=Sr,S.prototype.inspect=S.prototype.toSource=function(){return""+this},S.prototype[br]=function(){return this},t(O,e),O.of=function(){return O(arguments)},O.prototype.toSeq=function(){return this},O.prototype.toString=function(){return this.__toString("Seq {","}")},O.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},O.prototype.__iterate=function(t,e){return N(this,t,e,!0)},O.prototype.__iterator=function(t,e){return P(this,t,e,!0)},t(x,O),x.prototype.toKeyedSeq=function(){return this},t(k,O),k.of=function(){return k(arguments)},k.prototype.toIndexedSeq=function(){return this},k.prototype.toString=function(){return this.__toString("Seq [","]")},k.prototype.__iterate=function(t,e){return N(this,t,e,!1)},k.prototype.__iterator=function(t,e){return P(this,t,e,!1)},t(A,O),A.of=function(){return A(arguments)},A.prototype.toSetSeq=function(){return this},O.isSeq=L,O.Keyed=x,O.Set=A,O.Indexed=k;var qr="@@__IMMUTABLE_SEQ__@@";O.prototype[qr]=!0,t(j,k),j.prototype.get=function(t,e){return this.has(t)?this._array[l(this,t)]:e},j.prototype.__iterate=function(t,e){for(var r=this._array,n=r.length-1,i=0;n>=i;i++)if(t(r[e?n-i:i],i,this)===!1)return i+1;return i},j.prototype.__iterator=function(t,e){var r=this._array,n=r.length-1,i=0;return new S(function(){return i>n?I():z(t,i,r[e?n-i++:i++])})},t(R,x),R.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},R.prototype.has=function(t){return this._object.hasOwnProperty(t)},R.prototype.__iterate=function(t,e){for(var r=this._object,n=this._keys,i=n.length-1,o=0;i>=o;o++){var u=n[e?i-o:o];if(t(r[u],u,this)===!1)return o+1}return o},R.prototype.__iterator=function(t,e){var r=this._object,n=this._keys,i=n.length-1,o=0;return new S(function(){var u=n[e?i-o:o];return o++>i?I():z(t,u,r[u])})},R.prototype[cr]=!0,t(U,k),U.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e);
+var r=this._iterable,n=D(r),i=0;if(q(n))for(var o;!(o=n.next()).done&&t(o.value,i++,this)!==!1;);return i},U.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var r=this._iterable,n=D(r);if(!q(n))return new S(I);var i=0;return new S(function(){var e=n.next();return e.done?e:z(t,i++,e.value)})},t(K,k),K.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,n=this._iteratorCache,i=0;n.length>i;)if(t(n[i],i++,this)===!1)return i;for(var o;!(o=r.next()).done;){var u=o.value;if(n[i]=u,t(u,i++,this)===!1)break}return i},K.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var r=this._iterator,n=this._iteratorCache,i=0;return new S(function(){if(i>=n.length){var e=r.next();if(e.done)return e;n[i]=e.value}return z(t,i,n[i++])})};var Dr;t(G,k),G.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},G.prototype.get=function(t,e){return this.has(t)?this._value:e},G.prototype.includes=function(t){return X(this._value,t)},G.prototype.slice=function(t,e){var r=this.size;return d(t,e,r)?this:new G(this._value,g(e,r)-m(t,r))},G.prototype.reverse=function(){return this},G.prototype.indexOf=function(t){return X(this._value,t)?0:-1},G.prototype.lastIndexOf=function(t){return X(this._value,t)?this.size:-1},G.prototype.__iterate=function(t,e){for(var r=0;this.size>r;r++)if(t(this._value,r,this)===!1)return r+1;return r},G.prototype.__iterator=function(t,e){var r=this,n=0;return new S(function(){return r.size>n?z(t,n++,r._value):I()})},G.prototype.equals=function(t){return t instanceof G?X(this._value,t._value):F(t)};var Mr;t($,k),$.prototype.toString=function(){return 0===this.size?"Range []":"Range [ "+this._start+"..."+this._end+(1!==this._step?" by "+this._step:"")+" ]"},$.prototype.get=function(t,e){return this.has(t)?this._start+l(this,t)*this._step:e},$.prototype.includes=function(t){var e=(t-this._start)/this._step;return e>=0&&this.size>e&&e===Math.floor(e);
+},$.prototype.slice=function(t,e){return d(t,e,this.size)?this:(t=m(t,this.size),e=g(e,this.size),t>=e?new $(0,0):new $(this.get(t,this._end),this.get(e,this._end),this._step))},$.prototype.indexOf=function(t){var e=t-this._start;if(e%this._step===0){var r=e/this._step;if(r>=0&&this.size>r)return r}return-1},$.prototype.lastIndexOf=function(t){return this.indexOf(t)},$.prototype.__iterate=function(t,e){for(var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;r>=o;o++){if(t(i,o,this)===!1)return o+1;i+=e?-n:n}return o},$.prototype.__iterator=function(t,e){var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;return new S(function(){var u=i;return i+=e?-n:n,o>r?I():z(t,o++,u)})},$.prototype.equals=function(t){return t instanceof $?this._start===t._start&&this._end===t._end&&this._step===t._step:F(this,t)};var Er;t(tt,e),t(et,tt),t(rt,tt),t(nt,tt),tt.Keyed=et,tt.Indexed=rt,tt.Set=nt;var Or,xr="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(t,e){t=0|t,e=0|e;var r=65535&t,n=65535&e;return r*n+((t>>>16)*n+r*(e>>>16)<<16>>>0)|0},kr=Object.isExtensible,Ar=function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}}(),jr="function"==typeof WeakMap;jr&&(Or=new WeakMap);var Rr=0,Ur="__immutablehash__";"function"==typeof Symbol&&(Ur=Symbol(Ur));var Kr=16,Lr=255,Tr=0,Br={};t(ct,et),ct.of=function(){var t=sr.call(arguments,0);return zt().withMutations(function(e){for(var r=0;t.length>r;r+=2){if(r+1>=t.length)throw Error("Missing value for key: "+t[r]);e.set(t[r],t[r+1])}})},ct.prototype.toString=function(){return this.__toString("Map {","}")},ct.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},ct.prototype.set=function(t,e){return It(this,t,e)},ct.prototype.setIn=function(t,e){return this.updateIn(t,yr,function(){return e})},ct.prototype.remove=function(t){return It(this,t,yr)},ct.prototype.deleteIn=function(t){return this.updateIn(t,function(){return yr})},ct.prototype.update=function(t,e,r){return 1===arguments.length?t(this):this.updateIn([t],e,r);
+},ct.prototype.updateIn=function(t,e,r){r||(r=e,e=void 0);var n=Rt(this,ke(t),e,r);return n===yr?void 0:n},ct.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):zt()},ct.prototype.merge=function(){return xt(this,void 0,arguments)},ct.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return xt(this,t,e)},ct.prototype.mergeIn=function(t){var e=sr.call(arguments,1);return this.updateIn(t,zt(),function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]})},ct.prototype.mergeDeep=function(){return xt(this,kt,arguments)},ct.prototype.mergeDeepWith=function(t){var e=sr.call(arguments,1);return xt(this,At(t),e)},ct.prototype.mergeDeepIn=function(t){var e=sr.call(arguments,1);return this.updateIn(t,zt(),function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]})},ct.prototype.sort=function(t){return Zt(we(this,t))},ct.prototype.sortBy=function(t,e){return Zt(we(this,e,t))},ct.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},ct.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},ct.prototype.asImmutable=function(){return this.__ensureOwner()},ct.prototype.wasAltered=function(){return this.__altered},ct.prototype.__iterator=function(t,e){return new mt(this,t,e)},ct.prototype.__iterate=function(t,e){var r=this,n=0;return this._root&&this._root.iterate(function(e){return n++,t(e[1],e[0],r)},e),n},ct.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?St(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},ct.isMap=_t;var Wr="@@__IMMUTABLE_MAP__@@",Cr=ct.prototype;Cr[Wr]=!0,Cr[_r]=Cr.remove,Cr.removeIn=Cr.deleteIn,pt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},pt.prototype.update=function(t,e,r,n,i,o,u){for(var s=i===yr,a=this.entries,h=0,f=a.length;f>h&&!X(n,a[h][0]);h++);
+var _=f>h;if(_?a[h][1]===i:s)return this;if(c(u),(s||!_)&&c(o),!s||1!==a.length){if(!_&&!s&&a.length>=Nr)return Mt(t,a,n,i);var v=t&&t===this.ownerID,l=v?a:p(a);return _?s?h===f-1?l.pop():l[h]=l.pop():l[h]=[n,i]:l.push([n,i]),v?(this.entries=l,this):new pt(t,l)}},vt.prototype.get=function(t,e,r,n){void 0===e&&(e=ot(r));var i=1<<((0===t?e:e>>>t)&lr),o=this.bitmap;return 0===(o&i)?n:this.nodes[Ut(o&i-1)].get(t+pr,e,r,n)},vt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=(0===e?r:r>>>e)&lr,a=1<<s,h=this.bitmap,f=0!==(h&a);if(!f&&i===yr)return this;var c=Ut(h&a-1),_=this.nodes,p=f?_[c]:void 0,v=bt(p,t,e+pr,r,n,i,o,u);if(v===p)return this;if(!f&&v&&_.length>=Pr)return Ot(t,_,h,s,v);if(f&&!v&&2===_.length&&qt(_[1^c]))return _[1^c];if(f&&v&&1===_.length&&qt(v))return v;var l=t&&t===this.ownerID,y=f?v?h:h^a:h|a,d=f?v?Kt(_,c,v,l):Tt(_,c,l):Lt(_,c,v,l);return l?(this.bitmap=y,this.nodes=d,this):new vt(t,y,d)},lt.prototype.get=function(t,e,r,n){void 0===e&&(e=ot(r));var i=(0===t?e:e>>>t)&lr,o=this.nodes[i];return o?o.get(t+pr,e,r,n):n},lt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=(0===e?r:r>>>e)&lr,a=i===yr,h=this.nodes,f=h[s];if(a&&!f)return this;var c=bt(f,t,e+pr,r,n,i,o,u);if(c===f)return this;var _=this.count;if(f){if(!c&&(_--,Hr>_))return Et(t,h,_,s)}else _++;var p=t&&t===this.ownerID,v=Kt(h,s,c,p);return p?(this.count=_,this.nodes=v,this):new lt(t,_,v)},yt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},yt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=i===yr;if(r!==this.keyHash)return s?this:(c(u),c(o),Dt(this,t,e,r,[n,i]));for(var a=this.entries,h=0,f=a.length;f>h&&!X(n,a[h][0]);h++);var _=f>h;if(_?a[h][1]===i:s)return this;if(c(u),(s||!_)&&c(o),s&&2===f)return new dt(t,this.keyHash,a[1^h]);var v=t&&t===this.ownerID,l=v?a:p(a);return _?s?h===f-1?l.pop():l[h]=l.pop():l[h]=[n,i]:l.push([n,i]),v?(this.entries=l,this):new yt(t,this.keyHash,l)},dt.prototype.get=function(t,e,r,n){return X(r,this.entry[0])?this.entry[1]:n;
+},dt.prototype.update=function(t,e,r,n,i,o,u){var s=i===yr,a=X(n,this.entry[0]);return(a?i===this.entry[1]:s)?this:(c(u),s?void c(o):a?t&&t===this.ownerID?(this.entry[1]=i,this):new dt(t,this.keyHash,[n,i]):(c(o),Dt(this,t,e,ot(n),[n,i])))},pt.prototype.iterate=yt.prototype.iterate=function(t,e){for(var r=this.entries,n=0,i=r.length-1;i>=n;n++)if(t(r[e?i-n:n])===!1)return!1},vt.prototype.iterate=lt.prototype.iterate=function(t,e){for(var r=this.nodes,n=0,i=r.length-1;i>=n;n++){var o=r[e?i-n:n];if(o&&o.iterate(t,e)===!1)return!1}},dt.prototype.iterate=function(t,e){return t(this.entry)},t(mt,S),mt.prototype.next=function(){for(var t=this._type,e=this._stack;e;){var r,n=e.node,i=e.index++;if(n.entry){if(0===i)return gt(t,n.entry)}else if(n.entries){if(r=n.entries.length-1,r>=i)return gt(t,n.entries[this._reverse?r-i:i])}else if(r=n.nodes.length-1,r>=i){var o=n.nodes[this._reverse?r-i:i];if(o){if(o.entry)return gt(t,o.entry);e=this._stack=wt(o,e)}continue}e=this._stack=this._stack.__prev}return I()};var Jr,Nr=vr/4,Pr=vr/2,Hr=vr/4;t(Bt,rt),Bt.of=function(){return this(arguments)},Bt.prototype.toString=function(){return this.__toString("List [","]")},Bt.prototype.get=function(t,e){if(t=l(this,t),t>=0&&this.size>t){t+=this._origin;var r=Qt(this,t);return r&&r.array[t&lr]}return e},Bt.prototype.set=function(t,e){return Ht(this,t,e)},Bt.prototype.remove=function(t){return this.has(t)?0===t?this.shift():t===this.size-1?this.pop():this.splice(t,1):this},Bt.prototype.insert=function(t,e){return this.splice(t,0,e)},Bt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=pr,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):Pt()},Bt.prototype.push=function(){var t=arguments,e=this.size;return this.withMutations(function(r){Xt(r,0,e+t.length);for(var n=0;t.length>n;n++)r.set(e+n,t[n])})},Bt.prototype.pop=function(){return Xt(this,0,-1)},Bt.prototype.unshift=function(){var t=arguments;return this.withMutations(function(e){Xt(e,-t.length);for(var r=0;t.length>r;r++)e.set(r,t[r]);
+})},Bt.prototype.shift=function(){return Xt(this,1)},Bt.prototype.merge=function(){return Ft(this,void 0,arguments)},Bt.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return Ft(this,t,e)},Bt.prototype.mergeDeep=function(){return Ft(this,kt,arguments)},Bt.prototype.mergeDeepWith=function(t){var e=sr.call(arguments,1);return Ft(this,At(t),e)},Bt.prototype.setSize=function(t){return Xt(this,0,t)},Bt.prototype.slice=function(t,e){var r=this.size;return d(t,e,r)?this:Xt(this,m(t,r),g(e,r))},Bt.prototype.__iterator=function(t,e){var r=0,n=Jt(this,e);return new S(function(){var e=n();return e===Xr?I():z(t,r++,e)})},Bt.prototype.__iterate=function(t,e){for(var r,n=0,i=Jt(this,e);(r=i())!==Xr&&t(r,n++,this)!==!1;);return n},Bt.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Nt(this._origin,this._capacity,this._level,this._root,this._tail,t,this.__hash):(this.__ownerID=t,this)},Bt.isList=Wt;var Vr="@@__IMMUTABLE_LIST__@@",Yr=Bt.prototype;Yr[Vr]=!0,Yr[_r]=Yr.remove,Yr.setIn=Cr.setIn,Yr.deleteIn=Yr.removeIn=Cr.removeIn,Yr.update=Cr.update,Yr.updateIn=Cr.updateIn,Yr.mergeIn=Cr.mergeIn,Yr.mergeDeepIn=Cr.mergeDeepIn,Yr.withMutations=Cr.withMutations,Yr.asMutable=Cr.asMutable,Yr.asImmutable=Cr.asImmutable,Yr.wasAltered=Cr.wasAltered,Ct.prototype.removeBefore=function(t,e,r){if(r===e?1<<e:0===this.array.length)return this;var n=r>>>e&lr;if(n>=this.array.length)return new Ct([],t);var i,o=0===n;if(e>0){var u=this.array[n];if(i=u&&u.removeBefore(t,e-pr,r),i===u&&o)return this}if(o&&!i)return this;var s=Yt(this,t);if(!o)for(var a=0;n>a;a++)s.array[a]=void 0;return i&&(s.array[n]=i),s},Ct.prototype.removeAfter=function(t,e,r){if(r===(e?1<<e:0)||0===this.array.length)return this;var n=r-1>>>e&lr;if(n>=this.array.length)return this;var i;if(e>0){var o=this.array[n];if(i=o&&o.removeAfter(t,e-pr,r),i===o&&n===this.array.length-1)return this}var u=Yt(this,t);return u.array.splice(n+1),i&&(u.array[n]=i),u};var Qr,Xr={};t(Zt,ct),Zt.of=function(){return this(arguments)},Zt.prototype.toString=function(){return this.__toString("OrderedMap {","}");
+},Zt.prototype.get=function(t,e){var r=this._map.get(t);return void 0!==r?this._list.get(r)[1]:e},Zt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):ee()},Zt.prototype.set=function(t,e){return re(this,t,e)},Zt.prototype.remove=function(t){return re(this,t,yr)},Zt.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Zt.prototype.__iterate=function(t,e){var r=this;return this._list.__iterate(function(e){return e&&t(e[1],e[0],r)},e)},Zt.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Zt.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),r=this._list.__ensureOwner(t);return t?te(e,r,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=r,this)},Zt.isOrderedMap=$t,Zt.prototype[cr]=!0,Zt.prototype[_r]=Zt.prototype.remove;var Fr;t(ne,x),ne.prototype.get=function(t,e){return this._iter.get(t,e)},ne.prototype.has=function(t){return this._iter.has(t)},ne.prototype.valueSeq=function(){return this._iter.valueSeq()},ne.prototype.reverse=function(){var t=this,e=he(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},ne.prototype.map=function(t,e){var r=this,n=ae(this,t,e);return this._useKeys||(n.valueSeq=function(){return r._iter.toSeq().map(t,e)}),n},ne.prototype.__iterate=function(t,e){var r,n=this;return this._iter.__iterate(this._useKeys?function(e,r){return t(e,r,n)}:(r=e?De(this):0,function(i){return t(i,e?--r:r++,n)}),e)},ne.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var r=this._iter.__iterator(wr,e),n=e?De(this):0;return new S(function(){var i=r.next();return i.done?i:z(t,e?--n:n++,i.value,i)})},ne.prototype[cr]=!0,t(ie,k),ie.prototype.includes=function(t){return this._iter.includes(t)},ie.prototype.__iterate=function(t,e){var r=this,n=0;return this._iter.__iterate(function(e){return t(e,n++,r)},e)},ie.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e),n=0;
+return new S(function(){var e=r.next();return e.done?e:z(t,n++,e.value,e)})},t(oe,A),oe.prototype.has=function(t){return this._iter.includes(t)},oe.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){return t(e,e,r)},e)},oe.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e);return new S(function(){var e=r.next();return e.done?e:z(t,e.value,e.value,e)})},t(ue,x),ue.prototype.entrySeq=function(){return this._iter.toSeq()},ue.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){if(e){qe(e);var n=o(e);return t(n?e.get(1):e[1],n?e.get(0):e[0],r)}},e)},ue.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e);return new S(function(){for(;;){var e=r.next();if(e.done)return e;var n=e.value;if(n){qe(n);var i=o(n);return z(t,i?n.get(0):n[0],i?n.get(1):n[1],e)}}})},ie.prototype.cacheResult=ne.prototype.cacheResult=oe.prototype.cacheResult=ue.prototype.cacheResult=Oe,t(Ae,et),Ae.prototype.toString=function(){return this.__toString(Re(this)+" {","}")},Ae.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Ae.prototype.get=function(t,e){if(!this.has(t))return e;var r=this._defaultValues[t];return this._map?this._map.get(t,r):r},Ae.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=je(this,zt()))},Ae.prototype.set=function(t,e){if(!this.has(t))throw Error('Cannot set unknown key "'+t+'" on '+Re(this));if(this._map&&!this._map.has(t)){var r=this._defaultValues[t];if(e===r)return this}var n=this._map&&this._map.set(t,e);return this.__ownerID||n===this._map?this:je(this,n)},Ae.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:je(this,e)},Ae.prototype.wasAltered=function(){return this._map.wasAltered()},Ae.prototype.__iterator=function(t,e){var n=this;return r(this._defaultValues).map(function(t,e){return n.get(e)}).__iterator(t,e)},Ae.prototype.__iterate=function(t,e){
+var n=this;return r(this._defaultValues).map(function(t,e){return n.get(e)}).__iterate(t,e)},Ae.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?je(this,e,t):(this.__ownerID=t,this._map=e,this)};var Gr=Ae.prototype;Gr[_r]=Gr.remove,Gr.deleteIn=Gr.removeIn=Cr.removeIn,Gr.merge=Cr.merge,Gr.mergeWith=Cr.mergeWith,Gr.mergeIn=Cr.mergeIn,Gr.mergeDeep=Cr.mergeDeep,Gr.mergeDeepWith=Cr.mergeDeepWith,Gr.mergeDeepIn=Cr.mergeDeepIn,Gr.setIn=Cr.setIn,Gr.update=Cr.update,Gr.updateIn=Cr.updateIn,Gr.withMutations=Cr.withMutations,Gr.asMutable=Cr.asMutable,Gr.asImmutable=Cr.asImmutable,t(Le,nt),Le.of=function(){return this(arguments)},Le.fromKeys=function(t){return this(r(t).keySeq())},Le.prototype.toString=function(){return this.__toString("Set {","}")},Le.prototype.has=function(t){return this._map.has(t)},Le.prototype.add=function(t){return Be(this,this._map.set(t,!0))},Le.prototype.remove=function(t){return Be(this,this._map.remove(t))},Le.prototype.clear=function(){return Be(this,this._map.clear())},Le.prototype.union=function(){var t=sr.call(arguments,0);return t=t.filter(function(t){return 0!==t.size}),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations(function(e){for(var r=0;t.length>r;r++)i(t[r]).forEach(function(t){return e.add(t)})}):this.constructor(t[0])},Le.prototype.intersect=function(){var t=sr.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return i(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.every(function(t){return t.includes(e)})||r.remove(e)})})},Le.prototype.subtract=function(){var t=sr.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return i(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.some(function(t){return t.includes(e)})&&r.remove(e)})})},Le.prototype.merge=function(){return this.union.apply(this,arguments)},Le.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return this.union.apply(this,e)},
+Le.prototype.sort=function(t){return Je(we(this,t))},Le.prototype.sortBy=function(t,e){return Je(we(this,e,t))},Le.prototype.wasAltered=function(){return this._map.wasAltered()},Le.prototype.__iterate=function(t,e){var r=this;return this._map.__iterate(function(e,n){return t(n,n,r)},e)},Le.prototype.__iterator=function(t,e){return this._map.map(function(t,e){return e}).__iterator(t,e)},Le.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t);return t?this.__make(e,t):(this.__ownerID=t,this._map=e,this)},Le.isSet=Te;var Zr="@@__IMMUTABLE_SET__@@",$r=Le.prototype;$r[Zr]=!0,$r[_r]=$r.remove,$r.mergeDeep=$r.merge,$r.mergeDeepWith=$r.mergeWith,$r.withMutations=Cr.withMutations,$r.asMutable=Cr.asMutable,$r.asImmutable=Cr.asImmutable,$r.__empty=Ce,$r.__make=We;var tn;t(Je,Le),Je.of=function(){return this(arguments)},Je.fromKeys=function(t){return this(r(t).keySeq())},Je.prototype.toString=function(){return this.__toString("OrderedSet {","}")},Je.isOrderedSet=Ne;var en=Je.prototype;en[cr]=!0,en.__empty=He,en.__make=Pe;var rn;t(Ve,rt),Ve.of=function(){return this(arguments)},Ve.prototype.toString=function(){return this.__toString("Stack [","]")},Ve.prototype.get=function(t,e){var r=this._head;for(t=l(this,t);r&&t--;)r=r.next;return r?r.value:e},Ve.prototype.peek=function(){return this._head&&this._head.value},Ve.prototype.push=function(){if(0===arguments.length)return this;for(var t=this.size+arguments.length,e=this._head,r=arguments.length-1;r>=0;r--)e={value:arguments[r],next:e};return this.__ownerID?(this.size=t,this._head=e,this.__hash=void 0,this.__altered=!0,this):Qe(t,e)},Ve.prototype.pushAll=function(t){if(t=n(t),0===t.size)return this;ft(t.size);var e=this.size,r=this._head;return t.reverse().forEach(function(t){e++,r={value:t,next:r}}),this.__ownerID?(this.size=e,this._head=r,this.__hash=void 0,this.__altered=!0,this):Qe(e,r)},Ve.prototype.pop=function(){return this.slice(1)},Ve.prototype.unshift=function(){return this.push.apply(this,arguments)},Ve.prototype.unshiftAll=function(t){
+return this.pushAll(t)},Ve.prototype.shift=function(){return this.pop.apply(this,arguments)},Ve.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Xe()},Ve.prototype.slice=function(t,e){if(d(t,e,this.size))return this;var r=m(t,this.size),n=g(e,this.size);if(n!==this.size)return rt.prototype.slice.call(this,t,e);for(var i=this.size-r,o=this._head;r--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):Qe(i,o)},Ve.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Qe(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Ve.prototype.__iterate=function(t,e){if(e)return this.reverse().__iterate(t);for(var r=0,n=this._head;n&&t(n.value,r++,this)!==!1;)n=n.next;return r},Ve.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var r=0,n=this._head;return new S(function(){if(n){var e=n.value;return n=n.next,z(t,r++,e)}return I()})},Ve.isStack=Ye;var nn="@@__IMMUTABLE_STACK__@@",on=Ve.prototype;on[nn]=!0,on.withMutations=Cr.withMutations,on.asMutable=Cr.asMutable,on.asImmutable=Cr.asImmutable,on.wasAltered=Cr.wasAltered;var un;e.Iterator=S,Fe(e,{toArray:function(){ft(this.size);var t=Array(this.size||0);return this.valueSeq().__iterate(function(e,r){t[r]=e}),t},toIndexedSeq:function(){return new ie(this)},toJS:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJS?t.toJS():t}).__toJS()},toJSON:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t}).__toJS()},toKeyedSeq:function(){return new ne(this,!0)},toMap:function(){return ct(this.toKeyedSeq())},toObject:function(){ft(this.size);var t={};return this.__iterate(function(e,r){t[r]=e}),t},toOrderedMap:function(){return Zt(this.toKeyedSeq())},toOrderedSet:function(){return Je(u(this)?this.valueSeq():this)},toSet:function(){return Le(u(this)?this.valueSeq():this)},toSetSeq:function(){return new oe(this);
+},toSeq:function(){return s(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Ve(u(this)?this.valueSeq():this)},toList:function(){return Bt(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=sr.call(arguments,0);return be(this,ye(this,t))},includes:function(t){return this.some(function(e){return X(e,t)})},entries:function(){return this.__iterator(Sr)},every:function(t,e){ft(this.size);var r=!0;return this.__iterate(function(n,i,o){return t.call(e,n,i,o)?void 0:(r=!1,!1)}),r},filter:function(t,e){return be(this,fe(this,t,e,!0))},find:function(t,e,r){var n=this.findEntry(t,e);return n?n[1]:r},forEach:function(t,e){return ft(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){ft(this.size),t=void 0!==t?""+t:",";var e="",r=!0;return this.__iterate(function(n){r?r=!1:e+=t,e+=null!==n&&void 0!==n?""+n:""}),e},keys:function(){return this.__iterator(gr)},map:function(t,e){return be(this,ae(this,t,e))},reduce:function(t,e,r){ft(this.size);var n,i;return arguments.length<2?i=!0:n=e,this.__iterate(function(e,o,u){i?(i=!1,n=e):n=t.call(r,n,e,o,u)}),n},reduceRight:function(t,e,r){var n=this.toKeyedSeq().reverse();return n.reduce.apply(n,arguments)},reverse:function(){return be(this,he(this,!0))},slice:function(t,e){return be(this,pe(this,t,e,!0))},some:function(t,e){return!this.every($e(t),e)},sort:function(t){return be(this,we(this,t))},values:function(){return this.__iterator(wr)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(t,e){return v(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return ce(this,t,e)},equals:function(t){return F(this,t)},entrySeq:function(){var t=this;if(t._cache)return new j(t._cache);var e=t.toSeq().map(Ze).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){
+return this.filter($e(t),e)},findEntry:function(t,e,r){var n=r;return this.__iterate(function(r,i,o){return t.call(e,r,i,o)?(n=[i,r],!1):void 0}),n},findKey:function(t,e){var r=this.findEntry(t,e);return r&&r[0]},findLast:function(t,e,r){return this.toKeyedSeq().reverse().find(t,e,r)},findLastEntry:function(t,e,r){return this.toKeyedSeq().reverse().findEntry(t,e,r)},findLastKey:function(t,e){return this.toKeyedSeq().reverse().findKey(t,e)},first:function(){return this.find(y)},flatMap:function(t,e){return be(this,me(this,t,e))},flatten:function(t){return be(this,de(this,t,!0))},fromEntrySeq:function(){return new ue(this)},get:function(t,e){return this.find(function(e,r){return X(r,t)},void 0,e)},getIn:function(t,e){for(var r,n=this,i=ke(t);!(r=i.next()).done;){var o=r.value;if(n=n&&n.get?n.get(o,yr):yr,n===yr)return e}return n},groupBy:function(t,e){return _e(this,t,e)},has:function(t){return this.get(t,yr)!==yr},hasIn:function(t){return this.getIn(t,yr)!==yr},isSubset:function(t){return t="function"==typeof t.includes?t:e(t),this.every(function(e){return t.includes(e)})},isSuperset:function(t){return t="function"==typeof t.isSubset?t:e(t),t.isSubset(this)},keyOf:function(t){return this.findKey(function(e){return X(e,t)})},keySeq:function(){return this.toSeq().map(Ge).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(t){return this.toKeyedSeq().reverse().keyOf(t)},max:function(t){return Se(this,t)},maxBy:function(t,e){return Se(this,e,t)},min:function(t){return Se(this,t?tr(t):nr)},minBy:function(t,e){return Se(this,e?tr(e):nr,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return be(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return be(this,le(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile($e(t),e)},sortBy:function(t,e){return be(this,we(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return be(this,this.toSeq().reverse().take(t).reverse());
+},takeWhile:function(t,e){return be(this,ve(this,t,e))},takeUntil:function(t,e){return this.takeWhile($e(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=ir(this))}});var sn=e.prototype;sn[ar]=!0,sn[br]=sn.values,sn.__toJS=sn.toArray,sn.__toStringMapper=er,sn.inspect=sn.toSource=function(){return""+this},sn.chain=sn.flatMap,sn.contains=sn.includes,Fe(r,{flip:function(){return be(this,se(this))},mapEntries:function(t,e){var r=this,n=0;return be(this,this.toSeq().map(function(i,o){return t.call(e,[o,i],n++,r)}).fromEntrySeq())},mapKeys:function(t,e){var r=this;return be(this,this.toSeq().flip().map(function(n,i){return t.call(e,n,i,r)}).flip())}});var an=r.prototype;an[hr]=!0,an[br]=sn.entries,an.__toJS=sn.toObject,an.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+er(t)},Fe(n,{toKeyedSeq:function(){return new ne(this,!1)},filter:function(t,e){return be(this,fe(this,t,e,!1))},findIndex:function(t,e){var r=this.findEntry(t,e);return r?r[0]:-1},indexOf:function(t){var e=this.keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){var e=this.lastKeyOf(t);return void 0===e?-1:e},reverse:function(){return be(this,he(this,!1))},slice:function(t,e){return be(this,pe(this,t,e,!1))},splice:function(t,e){var r=arguments.length;if(e=Math.max(0|e,0),0===r||2===r&&!e)return this;t=m(t,0>t?this.count():this.size);var n=this.slice(0,t);return be(this,1===r?n:n.concat(p(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var r=this.findLastEntry(t,e);return r?r[0]:-1},first:function(){return this.get(0)},flatten:function(t){return be(this,de(this,t,!1))},get:function(t,e){return t=l(this,t),0>t||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find(function(e,r){return r===t},void 0,e)},has:function(t){return t=l(this,t),t>=0&&(void 0!==this.size?this.size===1/0||this.size>t:-1!==this.indexOf(t))},interpose:function(t){return be(this,ge(this,t))},interleave:function(){var t=[this].concat(p(arguments)),e=Ie(this.toSeq(),k.of,t),r=e.flatten(!0);return e.size&&(r.size=e.size*t.length),
+be(this,r)},keySeq:function(){return $(0,this.size)},last:function(){return this.get(-1)},skipWhile:function(t,e){return be(this,le(this,t,e,!1))},zip:function(){var t=[this].concat(p(arguments));return be(this,Ie(this,rr,t))},zipWith:function(t){var e=p(arguments);return e[0]=this,be(this,Ie(this,t,e))}}),n.prototype[fr]=!0,n.prototype[cr]=!0,Fe(i,{get:function(t,e){return this.has(t)?t:e},includes:function(t){return this.has(t)},keySeq:function(){return this.valueSeq()}}),i.prototype.has=sn.includes,i.prototype.contains=i.prototype.includes,Fe(x,r.prototype),Fe(k,n.prototype),Fe(A,i.prototype),Fe(et,r.prototype),Fe(rt,n.prototype),Fe(nt,i.prototype);var hn={Iterable:e,Seq:O,Collection:tt,Map:ct,OrderedMap:Zt,List:Bt,Stack:Ve,Set:Le,OrderedSet:Je,Record:Ae,Range:$,Repeat:G,is:X,fromJS:H};return hn});
\ No newline at end of file
diff --git a/third_party/immer/extra/js/lib/lodash.js b/third_party/immer/extra/js/lib/lodash.js
new file mode 100644
index 0000000000..22e73749bd
--- /dev/null
+++ b/third_party/immer/extra/js/lib/lodash.js
@@ -0,0 +1,16607 @@
+/**
+ * @license
+ * lodash <https://lodash.com/>
+ * Copyright jQuery Foundation and other contributors <https://jquery.org/>
+ * Released under MIT license <https://lodash.com/license>
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+;(function() {
+
+  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
+  var undefined;
+
+  /** Used as the semantic version number. */
+  var VERSION = '4.14.1';
+
+  /** Used as the size to enable large array optimizations. */
+  var LARGE_ARRAY_SIZE = 200;
+
+  /** Used as the `TypeError` message for "Functions" methods. */
+  var FUNC_ERROR_TEXT = 'Expected a function';
+
+  /** Used to stand-in for `undefined` hash values. */
+  var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+  /** Used as the internal argument placeholder. */
+  var PLACEHOLDER = '__lodash_placeholder__';
+
+  /** Used to compose bitmasks for function metadata. */
+  var BIND_FLAG = 1,
+      BIND_KEY_FLAG = 2,
+      CURRY_BOUND_FLAG = 4,
+      CURRY_FLAG = 8,
+      CURRY_RIGHT_FLAG = 16,
+      PARTIAL_FLAG = 32,
+      PARTIAL_RIGHT_FLAG = 64,
+      ARY_FLAG = 128,
+      REARG_FLAG = 256,
+      FLIP_FLAG = 512;
+
+  /** Used to compose bitmasks for comparison styles. */
+  var UNORDERED_COMPARE_FLAG = 1,
+      PARTIAL_COMPARE_FLAG = 2;
+
+  /** Used as default options for `_.truncate`. */
+  var DEFAULT_TRUNC_LENGTH = 30,
+      DEFAULT_TRUNC_OMISSION = '...';
+
+  /** Used to detect hot functions by number of calls within a span of milliseconds. */
+  var HOT_COUNT = 150,
+      HOT_SPAN = 16;
+
+  /** Used to indicate the type of lazy iteratees. */
+  var LAZY_FILTER_FLAG = 1,
+      LAZY_MAP_FLAG = 2,
+      LAZY_WHILE_FLAG = 3;
+
+  /** Used as references for various `Number` constants. */
+  var INFINITY = 1 / 0,
+      MAX_SAFE_INTEGER = 9007199254740991,
+      MAX_INTEGER = 1.7976931348623157e+308,
+      NAN = 0 / 0;
+
+  /** Used as references for the maximum length and index of an array. */
+  var MAX_ARRAY_LENGTH = 4294967295,
+      MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
+      HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
+
+  /** Used to associate wrap methods with their bit flags. */
+  var wrapFlags = [
+    ['ary', ARY_FLAG],
+    ['bind', BIND_FLAG],
+    ['bindKey', BIND_KEY_FLAG],
+    ['curry', CURRY_FLAG],
+    ['curryRight', CURRY_RIGHT_FLAG],
+    ['flip', FLIP_FLAG],
+    ['partial', PARTIAL_FLAG],
+    ['partialRight', PARTIAL_RIGHT_FLAG],
+    ['rearg', REARG_FLAG]
+  ];
+
+  /** `Object#toString` result references. */
+  var argsTag = '[object Arguments]',
+      arrayTag = '[object Array]',
+      boolTag = '[object Boolean]',
+      dateTag = '[object Date]',
+      errorTag = '[object Error]',
+      funcTag = '[object Function]',
+      genTag = '[object GeneratorFunction]',
+      mapTag = '[object Map]',
+      numberTag = '[object Number]',
+      objectTag = '[object Object]',
+      promiseTag = '[object Promise]',
+      regexpTag = '[object RegExp]',
+      setTag = '[object Set]',
+      stringTag = '[object String]',
+      symbolTag = '[object Symbol]',
+      weakMapTag = '[object WeakMap]',
+      weakSetTag = '[object WeakSet]';
+
+  var arrayBufferTag = '[object ArrayBuffer]',
+      dataViewTag = '[object DataView]',
+      float32Tag = '[object Float32Array]',
+      float64Tag = '[object Float64Array]',
+      int8Tag = '[object Int8Array]',
+      int16Tag = '[object Int16Array]',
+      int32Tag = '[object Int32Array]',
+      uint8Tag = '[object Uint8Array]',
+      uint8ClampedTag = '[object Uint8ClampedArray]',
+      uint16Tag = '[object Uint16Array]',
+      uint32Tag = '[object Uint32Array]';
+
+  /** Used to match empty string literals in compiled template source. */
+  var reEmptyStringLeading = /\b__p \+= '';/g,
+      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+  /** Used to match HTML entities and HTML characters. */
+  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g,
+      reUnescapedHtml = /[&<>"'`]/g,
+      reHasEscapedHtml = RegExp(reEscapedHtml.source),
+      reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
+
+  /** Used to match template delimiters. */
+  var reEscape = /<%-([\s\S]+?)%>/g,
+      reEvaluate = /<%([\s\S]+?)%>/g,
+      reInterpolate = /<%=([\s\S]+?)%>/g;
+
+  /** Used to match property names within property paths. */
+  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
+      reIsPlainProp = /^\w*$/,
+      reLeadingDot = /^\./,
+      rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
+
+  /**
+   * Used to match `RegExp`
+   * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).
+   */
+  var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
+      reHasRegExpChar = RegExp(reRegExpChar.source);
+
+  /** Used to match leading and trailing whitespace. */
+  var reTrim = /^\s+|\s+$/g,
+      reTrimStart = /^\s+/,
+      reTrimEnd = /\s+$/;
+
+  /** Used to match wrap detail comments. */
+  var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
+      reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
+      reSplitDetails = /,? & /;
+
+  /** Used to match non-compound words composed of alphanumeric characters. */
+  var reBasicWord = /[a-zA-Z0-9]+/g;
+
+  /** Used to match backslashes in property paths. */
+  var reEscapeChar = /\\(\\)?/g;
+
+  /**
+   * Used to match
+   * [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components).
+   */
+  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+  /** Used to match `RegExp` flags from their coerced string values. */
+  var reFlags = /\w*$/;
+
+  /** Used to detect hexadecimal string values. */
+  var reHasHexPrefix = /^0x/i;
+
+  /** Used to detect bad signed hexadecimal string values. */
+  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+  /** Used to detect binary string values. */
+  var reIsBinary = /^0b[01]+$/i;
+
+  /** Used to detect host constructors (Safari). */
+  var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+  /** Used to detect octal string values. */
+  var reIsOctal = /^0o[0-7]+$/i;
+
+  /** Used to detect unsigned integer values. */
+  var reIsUint = /^(?:0|[1-9]\d*)$/;
+
+  /** Used to match latin-1 supplementary letters (excluding mathematical operators). */
+  var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
+
+  /** Used to ensure capturing order of template delimiters. */
+  var reNoMatch = /($^)/;
+
+  /** Used to match unescaped characters in compiled string literals. */
+  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
+
+  /** Used to compose unicode character classes. */
+  var rsAstralRange = '\\ud800-\\udfff',
+      rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
+      rsComboSymbolsRange = '\\u20d0-\\u20f0',
+      rsDingbatRange = '\\u2700-\\u27bf',
+      rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
+      rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
+      rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
+      rsPunctuationRange = '\\u2000-\\u206f',
+      rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',
+      rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
+      rsVarRange = '\\ufe0e\\ufe0f',
+      rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
+
+  /** Used to compose unicode capture groups. */
+  var rsApos = "['\u2019]",
+      rsAstral = '[' + rsAstralRange + ']',
+      rsBreak = '[' + rsBreakRange + ']',
+      rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
+      rsDigits = '\\d+',
+      rsDingbat = '[' + rsDingbatRange + ']',
+      rsLower = '[' + rsLowerRange + ']',
+      rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
+      rsFitz = '\\ud83c[\\udffb-\\udfff]',
+      rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
+      rsNonAstral = '[^' + rsAstralRange + ']',
+      rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
+      rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
+      rsUpper = '[' + rsUpperRange + ']',
+      rsZWJ = '\\u200d';
+
+  /** Used to compose unicode regexes. */
+  var rsLowerMisc = '(?:' + rsLower + '|' + rsMisc + ')',
+      rsUpperMisc = '(?:' + rsUpper + '|' + rsMisc + ')',
+      rsOptLowerContr = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
+      rsOptUpperContr = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
+      reOptMod = rsModifier + '?',
+      rsOptVar = '[' + rsVarRange + ']?',
+      rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
+      rsSeq = rsOptVar + reOptMod + rsOptJoin,
+      rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
+      rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
+
+  /** Used to match apostrophes. */
+  var reApos = RegExp(rsApos, 'g');
+
+  /**
+   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
+   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
+   */
+  var reComboMark = RegExp(rsCombo, 'g');
+
+  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
+  var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
+
+  /** Used to match complex or compound words. */
+  var reComplexWord = RegExp([
+    rsUpper + '?' + rsLower + '+' + rsOptLowerContr + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',
+    rsUpperMisc + '+' + rsOptUpperContr + '(?=' + [rsBreak, rsUpper + rsLowerMisc, '$'].join('|') + ')',
+    rsUpper + '?' + rsLowerMisc + '+' + rsOptLowerContr,
+    rsUpper + '+' + rsOptUpperContr,
+    rsDigits,
+    rsEmoji
+  ].join('|'), 'g');
+
+  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
+  var reHasComplexSymbol = RegExp('[' + rsZWJ + rsAstralRange  + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
+
+  /** Used to detect strings that need a more robust regexp to match words. */
+  var reHasComplexWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
+
+  /** Used to assign default `context` object properties. */
+  var contextProps = [
+    'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',
+    'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',
+    'Promise', 'Reflect', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError',
+    'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
+    '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'
+  ];
+
+  /** Used to make template sourceURLs easier to identify. */
+  var templateCounter = -1;
+
+  /** Used to identify `toStringTag` values of typed arrays. */
+  var typedArrayTags = {};
+  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+  typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+  typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+  typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+  typedArrayTags[uint32Tag] = true;
+  typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+  typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+  typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+  typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+  typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+  typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+  typedArrayTags[setTag] = typedArrayTags[stringTag] =
+  typedArrayTags[weakMapTag] = false;
+
+  /** Used to identify `toStringTag` values supported by `_.clone`. */
+  var cloneableTags = {};
+  cloneableTags[argsTag] = cloneableTags[arrayTag] =
+  cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
+  cloneableTags[boolTag] = cloneableTags[dateTag] =
+  cloneableTags[float32Tag] = cloneableTags[float64Tag] =
+  cloneableTags[int8Tag] = cloneableTags[int16Tag] =
+  cloneableTags[int32Tag] = cloneableTags[mapTag] =
+  cloneableTags[numberTag] = cloneableTags[objectTag] =
+  cloneableTags[regexpTag] = cloneableTags[setTag] =
+  cloneableTags[stringTag] = cloneableTags[symbolTag] =
+  cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+  cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+  cloneableTags[errorTag] = cloneableTags[funcTag] =
+  cloneableTags[weakMapTag] = false;
+
+  /** Used to map latin-1 supplementary letters to basic latin letters. */
+  var deburredLetters = {
+    '\xc0': 'A',  '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
+    '\xe0': 'a',  '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
+    '\xc7': 'C',  '\xe7': 'c',
+    '\xd0': 'D',  '\xf0': 'd',
+    '\xc8': 'E',  '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
+    '\xe8': 'e',  '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
+    '\xcC': 'I',  '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
+    '\xeC': 'i',  '\xed': 'i', '\xee': 'i', '\xef': 'i',
+    '\xd1': 'N',  '\xf1': 'n',
+    '\xd2': 'O',  '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
+    '\xf2': 'o',  '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
+    '\xd9': 'U',  '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
+    '\xf9': 'u',  '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
+    '\xdd': 'Y',  '\xfd': 'y', '\xff': 'y',
+    '\xc6': 'Ae', '\xe6': 'ae',
+    '\xde': 'Th', '\xfe': 'th',
+    '\xdf': 'ss'
+  };
+
+  /** Used to map characters to HTML entities. */
+  var htmlEscapes = {
+    '&': '&amp;',
+    '<': '&lt;',
+    '>': '&gt;',
+    '"': '&quot;',
+    "'": '&#39;',
+    '`': '&#96;'
+  };
+
+  /** Used to map HTML entities to characters. */
+  var htmlUnescapes = {
+    '&amp;': '&',
+    '&lt;': '<',
+    '&gt;': '>',
+    '&quot;': '"',
+    '&#39;': "'",
+    '&#96;': '`'
+  };
+
+  /** Used to escape characters for inclusion in compiled string literals. */
+  var stringEscapes = {
+    '\\': '\\',
+    "'": "'",
+    '\n': 'n',
+    '\r': 'r',
+    '\u2028': 'u2028',
+    '\u2029': 'u2029'
+  };
+
+  /** Built-in method references without a dependency on `root`. */
+  var freeParseFloat = parseFloat,
+      freeParseInt = parseInt;
+
+  /** Detect free variable `global` from Node.js. */
+  var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+  /** Detect free variable `self`. */
+  var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+  /** Used as a reference to the global object. */
+  var root = freeGlobal || freeSelf || Function('return this')();
+
+  /** Detect free variable `exports`. */
+  var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+
+  /** Detect free variable `module`. */
+  var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+
+  /** Detect the popular CommonJS extension `module.exports`. */
+  var moduleExports = freeModule && freeModule.exports === freeExports;
+
+  /** Detect free variable `process` from Node.js. */
+  var freeProcess = moduleExports && freeGlobal.process;
+
+  /** Used to access faster Node.js helpers. */
+  var nodeUtil = (function() {
+    try {
+      return freeProcess && freeProcess.binding('util');
+    } catch (e) {}
+  }());
+
+  /* Node.js helper references. */
+  var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
+      nodeIsDate = nodeUtil && nodeUtil.isDate,
+      nodeIsMap = nodeUtil && nodeUtil.isMap,
+      nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
+      nodeIsSet = nodeUtil && nodeUtil.isSet,
+      nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Adds the key-value `pair` to `map`.
+   *
+   * @private
+   * @param {Object} map The map to modify.
+   * @param {Array} pair The key-value pair to add.
+   * @returns {Object} Returns `map`.
+   */
+  function addMapEntry(map, pair) {
+    // Don't return `map.set` because it's not chainable in IE 11.
+    map.set(pair[0], pair[1]);
+    return map;
+  }
+
+  /**
+   * Adds `value` to `set`.
+   *
+   * @private
+   * @param {Object} set The set to modify.
+   * @param {*} value The value to add.
+   * @returns {Object} Returns `set`.
+   */
+  function addSetEntry(set, value) {
+    // Don't return `set.add` because it's not chainable in IE 11.
+    set.add(value);
+    return set;
+  }
+
+  /**
+   * A faster alternative to `Function#apply`, this function invokes `func`
+   * with the `this` binding of `thisArg` and the arguments of `args`.
+   *
+   * @private
+   * @param {Function} func The function to invoke.
+   * @param {*} thisArg The `this` binding of `func`.
+   * @param {Array} args The arguments to invoke `func` with.
+   * @returns {*} Returns the result of `func`.
+   */
+  function apply(func, thisArg, args) {
+    switch (args.length) {
+      case 0: return func.call(thisArg);
+      case 1: return func.call(thisArg, args[0]);
+      case 2: return func.call(thisArg, args[0], args[1]);
+      case 3: return func.call(thisArg, args[0], args[1], args[2]);
+    }
+    return func.apply(thisArg, args);
+  }
+
+  /**
+   * A specialized version of `baseAggregator` for arrays.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} setter The function to set `accumulator` values.
+   * @param {Function} iteratee The iteratee to transform keys.
+   * @param {Object} accumulator The initial aggregated object.
+   * @returns {Function} Returns `accumulator`.
+   */
+  function arrayAggregator(array, setter, iteratee, accumulator) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      var value = array[index];
+      setter(accumulator, value, iteratee(value), array);
+    }
+    return accumulator;
+  }
+
+  /**
+   * A specialized version of `_.forEach` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {Array} Returns `array`.
+   */
+  function arrayEach(array, iteratee) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (iteratee(array[index], index, array) === false) {
+        break;
+      }
+    }
+    return array;
+  }
+
+  /**
+   * A specialized version of `_.forEachRight` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {Array} Returns `array`.
+   */
+  function arrayEachRight(array, iteratee) {
+    var length = array ? array.length : 0;
+
+    while (length--) {
+      if (iteratee(array[length], length, array) === false) {
+        break;
+      }
+    }
+    return array;
+  }
+
+  /**
+   * A specialized version of `_.every` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} predicate The function invoked per iteration.
+   * @returns {boolean} Returns `true` if all elements pass the predicate check,
+   *  else `false`.
+   */
+  function arrayEvery(array, predicate) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (!predicate(array[index], index, array)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * A specialized version of `_.filter` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} predicate The function invoked per iteration.
+   * @returns {Array} Returns the new filtered array.
+   */
+  function arrayFilter(array, predicate) {
+    var index = -1,
+        length = array ? array.length : 0,
+        resIndex = 0,
+        result = [];
+
+    while (++index < length) {
+      var value = array[index];
+      if (predicate(value, index, array)) {
+        result[resIndex++] = value;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * A specialized version of `_.includes` for arrays without support for
+   * specifying an index to search from.
+   *
+   * @private
+   * @param {Array} [array] The array to search.
+   * @param {*} target The value to search for.
+   * @returns {boolean} Returns `true` if `target` is found, else `false`.
+   */
+  function arrayIncludes(array, value) {
+    var length = array ? array.length : 0;
+    return !!length && baseIndexOf(array, value, 0) > -1;
+  }
+
+  /**
+   * This function is like `arrayIncludes` except that it accepts a comparator.
+   *
+   * @private
+   * @param {Array} [array] The array to search.
+   * @param {*} target The value to search for.
+   * @param {Function} comparator The comparator invoked per element.
+   * @returns {boolean} Returns `true` if `target` is found, else `false`.
+   */
+  function arrayIncludesWith(array, value, comparator) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (comparator(value, array[index])) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * A specialized version of `_.map` for arrays without support for iteratee
+   * shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {Array} Returns the new mapped array.
+   */
+  function arrayMap(array, iteratee) {
+    var index = -1,
+        length = array ? array.length : 0,
+        result = Array(length);
+
+    while (++index < length) {
+      result[index] = iteratee(array[index], index, array);
+    }
+    return result;
+  }
+
+  /**
+   * Appends the elements of `values` to `array`.
+   *
+   * @private
+   * @param {Array} array The array to modify.
+   * @param {Array} values The values to append.
+   * @returns {Array} Returns `array`.
+   */
+  function arrayPush(array, values) {
+    var index = -1,
+        length = values.length,
+        offset = array.length;
+
+    while (++index < length) {
+      array[offset + index] = values[index];
+    }
+    return array;
+  }
+
+  /**
+   * A specialized version of `_.reduce` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @param {*} [accumulator] The initial value.
+   * @param {boolean} [initAccum] Specify using the first element of `array` as
+   *  the initial value.
+   * @returns {*} Returns the accumulated value.
+   */
+  function arrayReduce(array, iteratee, accumulator, initAccum) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    if (initAccum && length) {
+      accumulator = array[++index];
+    }
+    while (++index < length) {
+      accumulator = iteratee(accumulator, array[index], index, array);
+    }
+    return accumulator;
+  }
+
+  /**
+   * A specialized version of `_.reduceRight` for arrays without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @param {*} [accumulator] The initial value.
+   * @param {boolean} [initAccum] Specify using the last element of `array` as
+   *  the initial value.
+   * @returns {*} Returns the accumulated value.
+   */
+  function arrayReduceRight(array, iteratee, accumulator, initAccum) {
+    var length = array ? array.length : 0;
+    if (initAccum && length) {
+      accumulator = array[--length];
+    }
+    while (length--) {
+      accumulator = iteratee(accumulator, array[length], length, array);
+    }
+    return accumulator;
+  }
+
+  /**
+   * A specialized version of `_.some` for arrays without support for iteratee
+   * shorthands.
+   *
+   * @private
+   * @param {Array} [array] The array to iterate over.
+   * @param {Function} predicate The function invoked per iteration.
+   * @returns {boolean} Returns `true` if any element passes the predicate check,
+   *  else `false`.
+   */
+  function arraySome(array, predicate) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (predicate(array[index], index, array)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * The base implementation of methods like `_.findKey` and `_.findLastKey`,
+   * without support for iteratee shorthands, which iterates over `collection`
+   * using `eachFunc`.
+   *
+   * @private
+   * @param {Array|Object} collection The collection to search.
+   * @param {Function} predicate The function invoked per iteration.
+   * @param {Function} eachFunc The function to iterate over `collection`.
+   * @returns {*} Returns the found element or its key, else `undefined`.
+   */
+  function baseFindKey(collection, predicate, eachFunc) {
+    var result;
+    eachFunc(collection, function(value, key, collection) {
+      if (predicate(value, key, collection)) {
+        result = key;
+        return false;
+      }
+    });
+    return result;
+  }
+
+  /**
+   * The base implementation of `_.findIndex` and `_.findLastIndex` without
+   * support for iteratee shorthands.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {Function} predicate The function invoked per iteration.
+   * @param {number} fromIndex The index to search from.
+   * @param {boolean} [fromRight] Specify iterating from right to left.
+   * @returns {number} Returns the index of the matched value, else `-1`.
+   */
+  function baseFindIndex(array, predicate, fromIndex, fromRight) {
+    var length = array.length,
+        index = fromIndex + (fromRight ? 1 : -1);
+
+    while ((fromRight ? index-- : ++index < length)) {
+      if (predicate(array[index], index, array)) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {*} value The value to search for.
+   * @param {number} fromIndex The index to search from.
+   * @returns {number} Returns the index of the matched value, else `-1`.
+   */
+  function baseIndexOf(array, value, fromIndex) {
+    if (value !== value) {
+      return baseFindIndex(array, baseIsNaN, fromIndex);
+    }
+    var index = fromIndex - 1,
+        length = array.length;
+
+    while (++index < length) {
+      if (array[index] === value) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * This function is like `baseIndexOf` except that it accepts a comparator.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {*} value The value to search for.
+   * @param {number} fromIndex The index to search from.
+   * @param {Function} comparator The comparator invoked per element.
+   * @returns {number} Returns the index of the matched value, else `-1`.
+   */
+  function baseIndexOfWith(array, value, fromIndex, comparator) {
+    var index = fromIndex - 1,
+        length = array.length;
+
+    while (++index < length) {
+      if (comparator(array[index], value)) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * The base implementation of `_.isNaN` without support for number objects.
+   *
+   * @private
+   * @param {*} value The value to check.
+   * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+   */
+  function baseIsNaN(value) {
+    return value !== value;
+  }
+
+  /**
+   * The base implementation of `_.mean` and `_.meanBy` without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} array The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {number} Returns the mean.
+   */
+  function baseMean(array, iteratee) {
+    var length = array ? array.length : 0;
+    return length ? (baseSum(array, iteratee) / length) : NAN;
+  }
+
+  /**
+   * The base implementation of `_.property` without support for deep paths.
+   *
+   * @private
+   * @param {string} key The key of the property to get.
+   * @returns {Function} Returns the new accessor function.
+   */
+  function baseProperty(key) {
+    return function(object) {
+      return object == null ? undefined : object[key];
+    };
+  }
+
+  /**
+   * The base implementation of `_.propertyOf` without support for deep paths.
+   *
+   * @private
+   * @param {Object} object The object to query.
+   * @returns {Function} Returns the new accessor function.
+   */
+  function basePropertyOf(object) {
+    return function(key) {
+      return object == null ? undefined : object[key];
+    };
+  }
+
+  /**
+   * The base implementation of `_.reduce` and `_.reduceRight`, without support
+   * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
+   *
+   * @private
+   * @param {Array|Object} collection The collection to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @param {*} accumulator The initial value.
+   * @param {boolean} initAccum Specify using the first or last element of
+   *  `collection` as the initial value.
+   * @param {Function} eachFunc The function to iterate over `collection`.
+   * @returns {*} Returns the accumulated value.
+   */
+  function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
+    eachFunc(collection, function(value, index, collection) {
+      accumulator = initAccum
+        ? (initAccum = false, value)
+        : iteratee(accumulator, value, index, collection);
+    });
+    return accumulator;
+  }
+
+  /**
+   * The base implementation of `_.sortBy` which uses `comparer` to define the
+   * sort order of `array` and replaces criteria objects with their corresponding
+   * values.
+   *
+   * @private
+   * @param {Array} array The array to sort.
+   * @param {Function} comparer The function to define sort order.
+   * @returns {Array} Returns `array`.
+   */
+  function baseSortBy(array, comparer) {
+    var length = array.length;
+
+    array.sort(comparer);
+    while (length--) {
+      array[length] = array[length].value;
+    }
+    return array;
+  }
+
+  /**
+   * The base implementation of `_.sum` and `_.sumBy` without support for
+   * iteratee shorthands.
+   *
+   * @private
+   * @param {Array} array The array to iterate over.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {number} Returns the sum.
+   */
+  function baseSum(array, iteratee) {
+    var result,
+        index = -1,
+        length = array.length;
+
+    while (++index < length) {
+      var current = iteratee(array[index]);
+      if (current !== undefined) {
+        result = result === undefined ? current : (result + current);
+      }
+    }
+    return result;
+  }
+
+  /**
+   * The base implementation of `_.times` without support for iteratee shorthands
+   * or max array length checks.
+   *
+   * @private
+   * @param {number} n The number of times to invoke `iteratee`.
+   * @param {Function} iteratee The function invoked per iteration.
+   * @returns {Array} Returns the array of results.
+   */
+  function baseTimes(n, iteratee) {
+    var index = -1,
+        result = Array(n);
+
+    while (++index < n) {
+      result[index] = iteratee(index);
+    }
+    return result;
+  }
+
+  /**
+   * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
+   * of key-value pairs for `object` corresponding to the property names of `props`.
+   *
+   * @private
+   * @param {Object} object The object to query.
+   * @param {Array} props The property names to get values for.
+   * @returns {Object} Returns the key-value pairs.
+   */
+  function baseToPairs(object, props) {
+    return arrayMap(props, function(key) {
+      return [key, object[key]];
+    });
+  }
+
+  /**
+   * The base implementation of `_.unary` without support for storing metadata.
+   *
+   * @private
+   * @param {Function} func The function to cap arguments for.
+   * @returns {Function} Returns the new capped function.
+   */
+  function baseUnary(func) {
+    return function(value) {
+      return func(value);
+    };
+  }
+
+  /**
+   * The base implementation of `_.values` and `_.valuesIn` which creates an
+   * array of `object` property values corresponding to the property names
+   * of `props`.
+   *
+   * @private
+   * @param {Object} object The object to query.
+   * @param {Array} props The property names to get values for.
+   * @returns {Object} Returns the array of property values.
+   */
+  function baseValues(object, props) {
+    return arrayMap(props, function(key) {
+      return object[key];
+    });
+  }
+
+  /**
+   * Checks if a cache value for `key` exists.
+   *
+   * @private
+   * @param {Object} cache The cache to query.
+   * @param {string} key The key of the entry to check.
+   * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+   */
+  function cacheHas(cache, key) {
+    return cache.has(key);
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
+   * that is not found in the character symbols.
+   *
+   * @private
+   * @param {Array} strSymbols The string symbols to inspect.
+   * @param {Array} chrSymbols The character symbols to find.
+   * @returns {number} Returns the index of the first unmatched string symbol.
+   */
+  function charsStartIndex(strSymbols, chrSymbols) {
+    var index = -1,
+        length = strSymbols.length;
+
+    while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+    return index;
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+   * that is not found in the character symbols.
+   *
+   * @private
+   * @param {Array} strSymbols The string symbols to inspect.
+   * @param {Array} chrSymbols The character symbols to find.
+   * @returns {number} Returns the index of the last unmatched string symbol.
+   */
+  function charsEndIndex(strSymbols, chrSymbols) {
+    var index = strSymbols.length;
+
+    while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+    return index;
+  }
+
+  /**
+   * Gets the number of `placeholder` occurrences in `array`.
+   *
+   * @private
+   * @param {Array} array The array to inspect.
+   * @param {*} placeholder The placeholder to search for.
+   * @returns {number} Returns the placeholder count.
+   */
+  function countHolders(array, placeholder) {
+    var length = array.length,
+        result = 0;
+
+    while (length--) {
+      if (array[length] === placeholder) {
+        result++;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.
+   *
+   * @private
+   * @param {string} letter The matched letter to deburr.
+   * @returns {string} Returns the deburred letter.
+   */
+  var deburrLetter = basePropertyOf(deburredLetters);
+
+  /**
+   * Used by `_.escape` to convert characters to HTML entities.
+   *
+   * @private
+   * @param {string} chr The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  var escapeHtmlChar = basePropertyOf(htmlEscapes);
+
+  /**
+   * Used by `_.template` to escape characters for inclusion in compiled string literals.
+   *
+   * @private
+   * @param {string} chr The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeStringChar(chr) {
+    return '\\' + stringEscapes[chr];
+  }
+
+  /**
+   * Gets the value at `key` of `object`.
+   *
+   * @private
+   * @param {Object} [object] The object to query.
+   * @param {string} key The key of the property to get.
+   * @returns {*} Returns the property value.
+   */
+  function getValue(object, key) {
+    return object == null ? undefined : object[key];
+  }
+
+  /**
+   * Checks if `value` is a host object in IE < 9.
+   *
+   * @private
+   * @param {*} value The value to check.
+   * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
+   */
+  function isHostObject(value) {
+    // Many host objects are `Object` objects that can coerce to strings
+    // despite having improperly defined `toString` methods.
+    var result = false;
+    if (value != null && typeof value.toString != 'function') {
+      try {
+        result = !!(value + '');
+      } catch (e) {}
+    }
+    return result;
+  }
+
+  /**
+   * Converts `iterator` to an array.
+   *
+   * @private
+   * @param {Object} iterator The iterator to convert.
+   * @returns {Array} Returns the converted array.
+   */
+  function iteratorToArray(iterator) {
+    var data,
+        result = [];
+
+    while (!(data = iterator.next()).done) {
+      result.push(data.value);
+    }
+    return result;
+  }
+
+  /**
+   * Converts `map` to its key-value pairs.
+   *
+   * @private
+   * @param {Object} map The map to convert.
+   * @returns {Array} Returns the key-value pairs.
+   */
+  function mapToArray(map) {
+    var index = -1,
+        result = Array(map.size);
+
+    map.forEach(function(value, key) {
+      result[++index] = [key, value];
+    });
+    return result;
+  }
+
+  /**
+   * Creates a function that invokes `func` with its first argument transformed.
+   *
+   * @private
+   * @param {Function} func The function to wrap.
+   * @param {Function} transform The argument transform.
+   * @returns {Function} Returns the new function.
+   */
+  function overArg(func, transform) {
+    return function(arg) {
+      return func(transform(arg));
+    };
+  }
+
+  /**
+   * Replaces all `placeholder` elements in `array` with an internal placeholder
+   * and returns an array of their indexes.
+   *
+   * @private
+   * @param {Array} array The array to modify.
+   * @param {*} placeholder The placeholder to replace.
+   * @returns {Array} Returns the new array of placeholder indexes.
+   */
+  function replaceHolders(array, placeholder) {
+    var index = -1,
+        length = array.length,
+        resIndex = 0,
+        result = [];
+
+    while (++index < length) {
+      var value = array[index];
+      if (value === placeholder || value === PLACEHOLDER) {
+        array[index] = PLACEHOLDER;
+        result[resIndex++] = index;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Converts `set` to an array of its values.
+   *
+   * @private
+   * @param {Object} set The set to convert.
+   * @returns {Array} Returns the values.
+   */
+  function setToArray(set) {
+    var index = -1,
+        result = Array(set.size);
+
+    set.forEach(function(value) {
+      result[++index] = value;
+    });
+    return result;
+  }
+
+  /**
+   * Converts `set` to its value-value pairs.
+   *
+   * @private
+   * @param {Object} set The set to convert.
+   * @returns {Array} Returns the value-value pairs.
+   */
+  function setToPairs(set) {
+    var index = -1,
+        result = Array(set.size);
+
+    set.forEach(function(value) {
+      result[++index] = [value, value];
+    });
+    return result;
+  }
+
+  /**
+   * Gets the number of symbols in `string`.
+   *
+   * @private
+   * @param {string} string The string to inspect.
+   * @returns {number} Returns the string size.
+   */
+  function stringSize(string) {
+    if (!(string && reHasComplexSymbol.test(string))) {
+      return string.length;
+    }
+    var result = reComplexSymbol.lastIndex = 0;
+    while (reComplexSymbol.test(string)) {
+      result++;
+    }
+    return result;
+  }
+
+  /**
+   * Converts `string` to an array.
+   *
+   * @private
+   * @param {string} string The string to convert.
+   * @returns {Array} Returns the converted array.
+   */
+  function stringToArray(string) {
+    return string.match(reComplexSymbol);
+  }
+
+  /**
+   * Used by `_.unescape` to convert HTML entities to characters.
+   *
+   * @private
+   * @param {string} chr The matched character to unescape.
+   * @returns {string} Returns the unescaped character.
+   */
+  var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Create a new pristine `lodash` function using the `context` object.
+   *
+   * @static
+   * @memberOf _
+   * @since 1.1.0
+   * @category Util
+   * @param {Object} [context=root] The context object.
+   * @returns {Function} Returns a new `lodash` function.
+   * @example
+   *
+   * _.mixin({ 'foo': _.constant('foo') });
+   *
+   * var lodash = _.runInContext();
+   * lodash.mixin({ 'bar': lodash.constant('bar') });
+   *
+   * _.isFunction(_.foo);
+   * // => true
+   * _.isFunction(_.bar);
+   * // => false
+   *
+   * lodash.isFunction(lodash.foo);
+   * // => false
+   * lodash.isFunction(lodash.bar);
+   * // => true
+   *
+   * // Use `context` to stub `Date#getTime` use in `_.now`.
+   * var stubbed = _.runInContext({
+   *   'Date': function() {
+   *     return { 'getTime': stubGetTime };
+   *   }
+   * });
+   *
+   * // Create a suped-up `defer` in Node.js.
+   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
+   */
+  function runInContext(context) {
+    context = context ? _.defaults({}, context, _.pick(root, contextProps)) : root;
+
+    /** Built-in constructor references. */
+    var Array = context.Array,
+        Date = context.Date,
+        Error = context.Error,
+        Math = context.Math,
+        RegExp = context.RegExp,
+        TypeError = context.TypeError;
+
+    /** Used for built-in method references. */
+    var arrayProto = context.Array.prototype,
+        objectProto = context.Object.prototype,
+        stringProto = context.String.prototype;
+
+    /** Used to detect overreaching core-js shims. */
+    var coreJsData = context['__core-js_shared__'];
+
+    /** Used to detect methods masquerading as native. */
+    var maskSrcKey = (function() {
+      var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+      return uid ? ('Symbol(src)_1.' + uid) : '';
+    }());
+
+    /** Used to resolve the decompiled source of functions. */
+    var funcToString = context.Function.prototype.toString;
+
+    /** Used to check objects for own properties. */
+    var hasOwnProperty = objectProto.hasOwnProperty;
+
+    /** Used to generate unique IDs. */
+    var idCounter = 0;
+
+    /** Used to infer the `Object` constructor. */
+    var objectCtorString = funcToString.call(Object);
+
+    /**
+     * Used to resolve the
+     * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
+     * of values.
+     */
+    var objectToString = objectProto.toString;
+
+    /** Used to restore the original `_` reference in `_.noConflict`. */
+    var oldDash = root._;
+
+    /** Used to detect if a method is native. */
+    var reIsNative = RegExp('^' +
+      funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+      .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+    );
+
+    /** Built-in value references. */
+    var Buffer = moduleExports ? context.Buffer : undefined,
+        Reflect = context.Reflect,
+        Symbol = context.Symbol,
+        Uint8Array = context.Uint8Array,
+        enumerate = Reflect ? Reflect.enumerate : undefined,
+        iteratorSymbol = Symbol ? Symbol.iterator : undefined,
+        objectCreate = context.Object.create,
+        propertyIsEnumerable = objectProto.propertyIsEnumerable,
+        splice = arrayProto.splice,
+        spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;
+
+    /** Built-in method references that are mockable. */
+    var clearTimeout = function(id) { return context.clearTimeout.call(root, id); },
+        setTimeout = function(func, wait) { return context.setTimeout.call(root, func, wait); };
+
+    /* Built-in method references for those with the same name as other `lodash` methods. */
+    var nativeCeil = Math.ceil,
+        nativeFloor = Math.floor,
+        nativeGetPrototype = Object.getPrototypeOf,
+        nativeGetSymbols = Object.getOwnPropertySymbols,
+        nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
+        nativeIsFinite = context.isFinite,
+        nativeJoin = arrayProto.join,
+        nativeKeys = Object.keys,
+        nativeMax = Math.max,
+        nativeMin = Math.min,
+        nativeParseInt = context.parseInt,
+        nativeRandom = Math.random,
+        nativeReplace = stringProto.replace,
+        nativeReverse = arrayProto.reverse,
+        nativeSplit = stringProto.split;
+
+    /* Built-in method references that are verified to be native. */
+    var DataView = getNative(context, 'DataView'),
+        Map = getNative(context, 'Map'),
+        Promise = getNative(context, 'Promise'),
+        Set = getNative(context, 'Set'),
+        WeakMap = getNative(context, 'WeakMap'),
+        nativeCreate = getNative(context.Object, 'create');
+
+    /* Used to set `toString` methods. */
+    var defineProperty = (function() {
+      var func = getNative(context.Object, 'defineProperty'),
+          name = getNative.name;
+
+      return (name && name.length > 2) ? func : undefined;
+    }());
+
+    /** Used to store function metadata. */
+    var metaMap = WeakMap && new WeakMap;
+
+    /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */
+    var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');
+
+    /** Used to lookup unminified function names. */
+    var realNames = {};
+
+    /** Used to detect maps, sets, and weakmaps. */
+    var dataViewCtorString = toSource(DataView),
+        mapCtorString = toSource(Map),
+        promiseCtorString = toSource(Promise),
+        setCtorString = toSource(Set),
+        weakMapCtorString = toSource(WeakMap);
+
+    /** Used to convert symbols to primitives and strings. */
+    var symbolProto = Symbol ? Symbol.prototype : undefined,
+        symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
+        symbolToString = symbolProto ? symbolProto.toString : undefined;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object which wraps `value` to enable implicit method
+     * chain sequences. Methods that operate on and return arrays, collections,
+     * and functions can be chained together. Methods that retrieve a single value
+     * or may return a primitive value will automatically end the chain sequence
+     * and return the unwrapped value. Otherwise, the value must be unwrapped
+     * with `_#value`.
+     *
+     * Explicit chain sequences, which must be unwrapped with `_#value`, may be
+     * enabled using `_.chain`.
+     *
+     * The execution of chained methods is lazy, that is, it's deferred until
+     * `_#value` is implicitly or explicitly called.
+     *
+     * Lazy evaluation allows several methods to support shortcut fusion.
+     * Shortcut fusion is an optimization to merge iteratee calls; this avoids
+     * the creation of intermediate arrays and can greatly reduce the number of
+     * iteratee executions. Sections of a chain sequence qualify for shortcut
+     * fusion if the section is applied to an array of at least `200` elements
+     * and any iteratees accept only one argument. The heuristic for whether a
+     * section qualifies for shortcut fusion is subject to change.
+     *
+     * Chaining is supported in custom builds as long as the `_#value` method is
+     * directly or indirectly included in the build.
+     *
+     * In addition to lodash methods, wrappers have `Array` and `String` methods.
+     *
+     * The wrapper `Array` methods are:
+     * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
+     *
+     * The wrapper `String` methods are:
+     * `replace` and `split`
+     *
+     * The wrapper methods that support shortcut fusion are:
+     * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
+     * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
+     * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
+     *
+     * The chainable wrapper methods are:
+     * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
+     * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
+     * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
+     * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
+     * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
+     * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
+     * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
+     * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
+     * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
+     * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
+     * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
+     * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
+     * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
+     * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
+     * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
+     * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
+     * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
+     * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
+     * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
+     * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
+     * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
+     * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
+     * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
+     * `zipObject`, `zipObjectDeep`, and `zipWith`
+     *
+     * The wrapper methods that are **not** chainable by default are:
+     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
+     * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
+     * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
+     * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
+     * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
+     * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
+     * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
+     * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
+     * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
+     * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
+     * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
+     * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
+     * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
+     * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
+     * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
+     * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
+     * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
+     * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
+     * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
+     * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
+     * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
+     * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
+     * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
+     * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
+     * `upperFirst`, `value`, and `words`
+     *
+     * @name _
+     * @constructor
+     * @category Seq
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var wrapped = _([1, 2, 3]);
+     *
+     * // Returns an unwrapped value.
+     * wrapped.reduce(_.add);
+     * // => 6
+     *
+     * // Returns a wrapped value.
+     * var squares = wrapped.map(square);
+     *
+     * _.isArray(squares);
+     * // => false
+     *
+     * _.isArray(squares.value());
+     * // => true
+     */
+    function lodash(value) {
+      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
+        if (value instanceof LodashWrapper) {
+          return value;
+        }
+        if (hasOwnProperty.call(value, '__wrapped__')) {
+          return wrapperClone(value);
+        }
+      }
+      return new LodashWrapper(value);
+    }
+
+    /**
+     * The function whose prototype chain sequence wrappers inherit from.
+     *
+     * @private
+     */
+    function baseLodash() {
+      // No operation performed.
+    }
+
+    /**
+     * The base constructor for creating `lodash` wrapper objects.
+     *
+     * @private
+     * @param {*} value The value to wrap.
+     * @param {boolean} [chainAll] Enable explicit method chain sequences.
+     */
+    function LodashWrapper(value, chainAll) {
+      this.__wrapped__ = value;
+      this.__actions__ = [];
+      this.__chain__ = !!chainAll;
+      this.__index__ = 0;
+      this.__values__ = undefined;
+    }
+
+    /**
+     * By default, the template delimiters used by lodash are like those in
+     * embedded Ruby (ERB). Change the following template settings to use
+     * alternative delimiters.
+     *
+     * @static
+     * @memberOf _
+     * @type {Object}
+     */
+    lodash.templateSettings = {
+
+      /**
+       * Used to detect `data` property values to be HTML-escaped.
+       *
+       * @memberOf _.templateSettings
+       * @type {RegExp}
+       */
+      'escape': reEscape,
+
+      /**
+       * Used to detect code to be evaluated.
+       *
+       * @memberOf _.templateSettings
+       * @type {RegExp}
+       */
+      'evaluate': reEvaluate,
+
+      /**
+       * Used to detect `data` property values to inject.
+       *
+       * @memberOf _.templateSettings
+       * @type {RegExp}
+       */
+      'interpolate': reInterpolate,
+
+      /**
+       * Used to reference the data object in the template text.
+       *
+       * @memberOf _.templateSettings
+       * @type {string}
+       */
+      'variable': '',
+
+      /**
+       * Used to import variables into the compiled template.
+       *
+       * @memberOf _.templateSettings
+       * @type {Object}
+       */
+      'imports': {
+
+        /**
+         * A reference to the `lodash` function.
+         *
+         * @memberOf _.templateSettings.imports
+         * @type {Function}
+         */
+        '_': lodash
+      }
+    };
+
+    // Ensure wrappers are instances of `baseLodash`.
+    lodash.prototype = baseLodash.prototype;
+    lodash.prototype.constructor = lodash;
+
+    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
+    LodashWrapper.prototype.constructor = LodashWrapper;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+     *
+     * @private
+     * @constructor
+     * @param {*} value The value to wrap.
+     */
+    function LazyWrapper(value) {
+      this.__wrapped__ = value;
+      this.__actions__ = [];
+      this.__dir__ = 1;
+      this.__filtered__ = false;
+      this.__iteratees__ = [];
+      this.__takeCount__ = MAX_ARRAY_LENGTH;
+      this.__views__ = [];
+    }
+
+    /**
+     * Creates a clone of the lazy wrapper object.
+     *
+     * @private
+     * @name clone
+     * @memberOf LazyWrapper
+     * @returns {Object} Returns the cloned `LazyWrapper` object.
+     */
+    function lazyClone() {
+      var result = new LazyWrapper(this.__wrapped__);
+      result.__actions__ = copyArray(this.__actions__);
+      result.__dir__ = this.__dir__;
+      result.__filtered__ = this.__filtered__;
+      result.__iteratees__ = copyArray(this.__iteratees__);
+      result.__takeCount__ = this.__takeCount__;
+      result.__views__ = copyArray(this.__views__);
+      return result;
+    }
+
+    /**
+     * Reverses the direction of lazy iteration.
+     *
+     * @private
+     * @name reverse
+     * @memberOf LazyWrapper
+     * @returns {Object} Returns the new reversed `LazyWrapper` object.
+     */
+    function lazyReverse() {
+      if (this.__filtered__) {
+        var result = new LazyWrapper(this);
+        result.__dir__ = -1;
+        result.__filtered__ = true;
+      } else {
+        result = this.clone();
+        result.__dir__ *= -1;
+      }
+      return result;
+    }
+
+    /**
+     * Extracts the unwrapped value from its lazy wrapper.
+     *
+     * @private
+     * @name value
+     * @memberOf LazyWrapper
+     * @returns {*} Returns the unwrapped value.
+     */
+    function lazyValue() {
+      var array = this.__wrapped__.value(),
+          dir = this.__dir__,
+          isArr = isArray(array),
+          isRight = dir < 0,
+          arrLength = isArr ? array.length : 0,
+          view = getView(0, arrLength, this.__views__),
+          start = view.start,
+          end = view.end,
+          length = end - start,
+          index = isRight ? end : (start - 1),
+          iteratees = this.__iteratees__,
+          iterLength = iteratees.length,
+          resIndex = 0,
+          takeCount = nativeMin(length, this.__takeCount__);
+
+      if (!isArr || arrLength < LARGE_ARRAY_SIZE ||
+          (arrLength == length && takeCount == length)) {
+        return baseWrapperValue(array, this.__actions__);
+      }
+      var result = [];
+
+      outer:
+      while (length-- && resIndex < takeCount) {
+        index += dir;
+
+        var iterIndex = -1,
+            value = array[index];
+
+        while (++iterIndex < iterLength) {
+          var data = iteratees[iterIndex],
+              iteratee = data.iteratee,
+              type = data.type,
+              computed = iteratee(value);
+
+          if (type == LAZY_MAP_FLAG) {
+            value = computed;
+          } else if (!computed) {
+            if (type == LAZY_FILTER_FLAG) {
+              continue outer;
+            } else {
+              break outer;
+            }
+          }
+        }
+        result[resIndex++] = value;
+      }
+      return result;
+    }
+
+    // Ensure `LazyWrapper` is an instance of `baseLodash`.
+    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
+    LazyWrapper.prototype.constructor = LazyWrapper;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a hash object.
+     *
+     * @private
+     * @constructor
+     * @param {Array} [entries] The key-value pairs to cache.
+     */
+    function Hash(entries) {
+      var index = -1,
+          length = entries ? entries.length : 0;
+
+      this.clear();
+      while (++index < length) {
+        var entry = entries[index];
+        this.set(entry[0], entry[1]);
+      }
+    }
+
+    /**
+     * Removes all key-value entries from the hash.
+     *
+     * @private
+     * @name clear
+     * @memberOf Hash
+     */
+    function hashClear() {
+      this.__data__ = nativeCreate ? nativeCreate(null) : {};
+    }
+
+    /**
+     * Removes `key` and its value from the hash.
+     *
+     * @private
+     * @name delete
+     * @memberOf Hash
+     * @param {Object} hash The hash to modify.
+     * @param {string} key The key of the value to remove.
+     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+     */
+    function hashDelete(key) {
+      return this.has(key) && delete this.__data__[key];
+    }
+
+    /**
+     * Gets the hash value for `key`.
+     *
+     * @private
+     * @name get
+     * @memberOf Hash
+     * @param {string} key The key of the value to get.
+     * @returns {*} Returns the entry value.
+     */
+    function hashGet(key) {
+      var data = this.__data__;
+      if (nativeCreate) {
+        var result = data[key];
+        return result === HASH_UNDEFINED ? undefined : result;
+      }
+      return hasOwnProperty.call(data, key) ? data[key] : undefined;
+    }
+
+    /**
+     * Checks if a hash value for `key` exists.
+     *
+     * @private
+     * @name has
+     * @memberOf Hash
+     * @param {string} key The key of the entry to check.
+     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+     */
+    function hashHas(key) {
+      var data = this.__data__;
+      return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
+    }
+
+    /**
+     * Sets the hash `key` to `value`.
+     *
+     * @private
+     * @name set
+     * @memberOf Hash
+     * @param {string} key The key of the value to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns the hash instance.
+     */
+    function hashSet(key, value) {
+      var data = this.__data__;
+      data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+      return this;
+    }
+
+    // Add methods to `Hash`.
+    Hash.prototype.clear = hashClear;
+    Hash.prototype['delete'] = hashDelete;
+    Hash.prototype.get = hashGet;
+    Hash.prototype.has = hashHas;
+    Hash.prototype.set = hashSet;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates an list cache object.
+     *
+     * @private
+     * @constructor
+     * @param {Array} [entries] The key-value pairs to cache.
+     */
+    function ListCache(entries) {
+      var index = -1,
+          length = entries ? entries.length : 0;
+
+      this.clear();
+      while (++index < length) {
+        var entry = entries[index];
+        this.set(entry[0], entry[1]);
+      }
+    }
+
+    /**
+     * Removes all key-value entries from the list cache.
+     *
+     * @private
+     * @name clear
+     * @memberOf ListCache
+     */
+    function listCacheClear() {
+      this.__data__ = [];
+    }
+
+    /**
+     * Removes `key` and its value from the list cache.
+     *
+     * @private
+     * @name delete
+     * @memberOf ListCache
+     * @param {string} key The key of the value to remove.
+     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+     */
+    function listCacheDelete(key) {
+      var data = this.__data__,
+          index = assocIndexOf(data, key);
+
+      if (index < 0) {
+        return false;
+      }
+      var lastIndex = data.length - 1;
+      if (index == lastIndex) {
+        data.pop();
+      } else {
+        splice.call(data, index, 1);
+      }
+      return true;
+    }
+
+    /**
+     * Gets the list cache value for `key`.
+     *
+     * @private
+     * @name get
+     * @memberOf ListCache
+     * @param {string} key The key of the value to get.
+     * @returns {*} Returns the entry value.
+     */
+    function listCacheGet(key) {
+      var data = this.__data__,
+          index = assocIndexOf(data, key);
+
+      return index < 0 ? undefined : data[index][1];
+    }
+
+    /**
+     * Checks if a list cache value for `key` exists.
+     *
+     * @private
+     * @name has
+     * @memberOf ListCache
+     * @param {string} key The key of the entry to check.
+     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+     */
+    function listCacheHas(key) {
+      return assocIndexOf(this.__data__, key) > -1;
+    }
+
+    /**
+     * Sets the list cache `key` to `value`.
+     *
+     * @private
+     * @name set
+     * @memberOf ListCache
+     * @param {string} key The key of the value to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns the list cache instance.
+     */
+    function listCacheSet(key, value) {
+      var data = this.__data__,
+          index = assocIndexOf(data, key);
+
+      if (index < 0) {
+        data.push([key, value]);
+      } else {
+        data[index][1] = value;
+      }
+      return this;
+    }
+
+    // Add methods to `ListCache`.
+    ListCache.prototype.clear = listCacheClear;
+    ListCache.prototype['delete'] = listCacheDelete;
+    ListCache.prototype.get = listCacheGet;
+    ListCache.prototype.has = listCacheHas;
+    ListCache.prototype.set = listCacheSet;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a map cache object to store key-value pairs.
+     *
+     * @private
+     * @constructor
+     * @param {Array} [entries] The key-value pairs to cache.
+     */
+    function MapCache(entries) {
+      var index = -1,
+          length = entries ? entries.length : 0;
+
+      this.clear();
+      while (++index < length) {
+        var entry = entries[index];
+        this.set(entry[0], entry[1]);
+      }
+    }
+
+    /**
+     * Removes all key-value entries from the map.
+     *
+     * @private
+     * @name clear
+     * @memberOf MapCache
+     */
+    function mapCacheClear() {
+      this.__data__ = {
+        'hash': new Hash,
+        'map': new (Map || ListCache),
+        'string': new Hash
+      };
+    }
+
+    /**
+     * Removes `key` and its value from the map.
+     *
+     * @private
+     * @name delete
+     * @memberOf MapCache
+     * @param {string} key The key of the value to remove.
+     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+     */
+    function mapCacheDelete(key) {
+      return getMapData(this, key)['delete'](key);
+    }
+
+    /**
+     * Gets the map value for `key`.
+     *
+     * @private
+     * @name get
+     * @memberOf MapCache
+     * @param {string} key The key of the value to get.
+     * @returns {*} Returns the entry value.
+     */
+    function mapCacheGet(key) {
+      return getMapData(this, key).get(key);
+    }
+
+    /**
+     * Checks if a map value for `key` exists.
+     *
+     * @private
+     * @name has
+     * @memberOf MapCache
+     * @param {string} key The key of the entry to check.
+     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+     */
+    function mapCacheHas(key) {
+      return getMapData(this, key).has(key);
+    }
+
+    /**
+     * Sets the map `key` to `value`.
+     *
+     * @private
+     * @name set
+     * @memberOf MapCache
+     * @param {string} key The key of the value to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns the map cache instance.
+     */
+    function mapCacheSet(key, value) {
+      getMapData(this, key).set(key, value);
+      return this;
+    }
+
+    // Add methods to `MapCache`.
+    MapCache.prototype.clear = mapCacheClear;
+    MapCache.prototype['delete'] = mapCacheDelete;
+    MapCache.prototype.get = mapCacheGet;
+    MapCache.prototype.has = mapCacheHas;
+    MapCache.prototype.set = mapCacheSet;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     *
+     * Creates an array cache object to store unique values.
+     *
+     * @private
+     * @constructor
+     * @param {Array} [values] The values to cache.
+     */
+    function SetCache(values) {
+      var index = -1,
+          length = values ? values.length : 0;
+
+      this.__data__ = new MapCache;
+      while (++index < length) {
+        this.add(values[index]);
+      }
+    }
+
+    /**
+     * Adds `value` to the array cache.
+     *
+     * @private
+     * @name add
+     * @memberOf SetCache
+     * @alias push
+     * @param {*} value The value to cache.
+     * @returns {Object} Returns the cache instance.
+     */
+    function setCacheAdd(value) {
+      this.__data__.set(value, HASH_UNDEFINED);
+      return this;
+    }
+
+    /**
+     * Checks if `value` is in the array cache.
+     *
+     * @private
+     * @name has
+     * @memberOf SetCache
+     * @param {*} value The value to search for.
+     * @returns {number} Returns `true` if `value` is found, else `false`.
+     */
+    function setCacheHas(value) {
+      return this.__data__.has(value);
+    }
+
+    // Add methods to `SetCache`.
+    SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+    SetCache.prototype.has = setCacheHas;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a stack cache object to store key-value pairs.
+     *
+     * @private
+     * @constructor
+     * @param {Array} [entries] The key-value pairs to cache.
+     */
+    function Stack(entries) {
+      this.__data__ = new ListCache(entries);
+    }
+
+    /**
+     * Removes all key-value entries from the stack.
+     *
+     * @private
+     * @name clear
+     * @memberOf Stack
+     */
+    function stackClear() {
+      this.__data__ = new ListCache;
+    }
+
+    /**
+     * Removes `key` and its value from the stack.
+     *
+     * @private
+     * @name delete
+     * @memberOf Stack
+     * @param {string} key The key of the value to remove.
+     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+     */
+    function stackDelete(key) {
+      return this.__data__['delete'](key);
+    }
+
+    /**
+     * Gets the stack value for `key`.
+     *
+     * @private
+     * @name get
+     * @memberOf Stack
+     * @param {string} key The key of the value to get.
+     * @returns {*} Returns the entry value.
+     */
+    function stackGet(key) {
+      return this.__data__.get(key);
+    }
+
+    /**
+     * Checks if a stack value for `key` exists.
+     *
+     * @private
+     * @name has
+     * @memberOf Stack
+     * @param {string} key The key of the entry to check.
+     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+     */
+    function stackHas(key) {
+      return this.__data__.has(key);
+    }
+
+    /**
+     * Sets the stack `key` to `value`.
+     *
+     * @private
+     * @name set
+     * @memberOf Stack
+     * @param {string} key The key of the value to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns the stack cache instance.
+     */
+    function stackSet(key, value) {
+      var cache = this.__data__;
+      if (cache instanceof ListCache) {
+        var pairs = cache.__data__;
+        if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
+          pairs.push([key, value]);
+          return this;
+        }
+        cache = this.__data__ = new MapCache(pairs);
+      }
+      cache.set(key, value);
+      return this;
+    }
+
+    // Add methods to `Stack`.
+    Stack.prototype.clear = stackClear;
+    Stack.prototype['delete'] = stackDelete;
+    Stack.prototype.get = stackGet;
+    Stack.prototype.has = stackHas;
+    Stack.prototype.set = stackSet;
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Used by `_.defaults` to customize its `_.assignIn` use.
+     *
+     * @private
+     * @param {*} objValue The destination value.
+     * @param {*} srcValue The source value.
+     * @param {string} key The key of the property to assign.
+     * @param {Object} object The parent object of `objValue`.
+     * @returns {*} Returns the value to assign.
+     */
+    function assignInDefaults(objValue, srcValue, key, object) {
+      if (objValue === undefined ||
+          (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {
+        return srcValue;
+      }
+      return objValue;
+    }
+
+    /**
+     * This function is like `assignValue` except that it doesn't assign
+     * `undefined` values.
+     *
+     * @private
+     * @param {Object} object The object to modify.
+     * @param {string} key The key of the property to assign.
+     * @param {*} value The value to assign.
+     */
+    function assignMergeValue(object, key, value) {
+      if ((value !== undefined && !eq(object[key], value)) ||
+          (typeof key == 'number' && value === undefined && !(key in object))) {
+        object[key] = value;
+      }
+    }
+
+    /**
+     * Assigns `value` to `key` of `object` if the existing value is not equivalent
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @private
+     * @param {Object} object The object to modify.
+     * @param {string} key The key of the property to assign.
+     * @param {*} value The value to assign.
+     */
+    function assignValue(object, key, value) {
+      var objValue = object[key];
+      if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
+          (value === undefined && !(key in object))) {
+        object[key] = value;
+      }
+    }
+
+    /**
+     * Gets the index at which the `key` is found in `array` of key-value pairs.
+     *
+     * @private
+     * @param {Array} array The array to search.
+     * @param {*} key The key to search for.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     */
+    function assocIndexOf(array, key) {
+      var length = array.length;
+      while (length--) {
+        if (eq(array[length][0], key)) {
+          return length;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Aggregates elements of `collection` on `accumulator` with keys transformed
+     * by `iteratee` and values set by `setter`.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} setter The function to set `accumulator` values.
+     * @param {Function} iteratee The iteratee to transform keys.
+     * @param {Object} accumulator The initial aggregated object.
+     * @returns {Function} Returns `accumulator`.
+     */
+    function baseAggregator(collection, setter, iteratee, accumulator) {
+      baseEach(collection, function(value, key, collection) {
+        setter(accumulator, value, iteratee(value), collection);
+      });
+      return accumulator;
+    }
+
+    /**
+     * The base implementation of `_.assign` without support for multiple sources
+     * or `customizer` functions.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @returns {Object} Returns `object`.
+     */
+    function baseAssign(object, source) {
+      return object && copyObject(source, keys(source), object);
+    }
+
+    /**
+     * The base implementation of `_.at` without support for individual paths.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {string[]} paths The property paths of elements to pick.
+     * @returns {Array} Returns the picked elements.
+     */
+    function baseAt(object, paths) {
+      var index = -1,
+          isNil = object == null,
+          length = paths.length,
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = isNil ? undefined : get(object, paths[index]);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.clamp` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {number} number The number to clamp.
+     * @param {number} [lower] The lower bound.
+     * @param {number} upper The upper bound.
+     * @returns {number} Returns the clamped number.
+     */
+    function baseClamp(number, lower, upper) {
+      if (number === number) {
+        if (upper !== undefined) {
+          number = number <= upper ? number : upper;
+        }
+        if (lower !== undefined) {
+          number = number >= lower ? number : lower;
+        }
+      }
+      return number;
+    }
+
+    /**
+     * The base implementation of `_.clone` and `_.cloneDeep` which tracks
+     * traversed objects.
+     *
+     * @private
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @param {boolean} [isFull] Specify a clone including symbols.
+     * @param {Function} [customizer] The function to customize cloning.
+     * @param {string} [key] The key of `value`.
+     * @param {Object} [object] The parent object of `value`.
+     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
+     * @returns {*} Returns the cloned value.
+     */
+    function baseClone(value, isDeep, isFull, customizer, key, object, stack) {
+      var result;
+      if (customizer) {
+        result = object ? customizer(value, key, object, stack) : customizer(value);
+      }
+      if (result !== undefined) {
+        return result;
+      }
+      if (!isObject(value)) {
+        return value;
+      }
+      var isArr = isArray(value);
+      if (isArr) {
+        result = initCloneArray(value);
+        if (!isDeep) {
+          return copyArray(value, result);
+        }
+      } else {
+        var tag = getTag(value),
+            isFunc = tag == funcTag || tag == genTag;
+
+        if (isBuffer(value)) {
+          return cloneBuffer(value, isDeep);
+        }
+        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+          if (isHostObject(value)) {
+            return object ? value : {};
+          }
+          result = initCloneObject(isFunc ? {} : value);
+          if (!isDeep) {
+            return copySymbols(value, baseAssign(result, value));
+          }
+        } else {
+          if (!cloneableTags[tag]) {
+            return object ? value : {};
+          }
+          result = initCloneByTag(value, tag, baseClone, isDeep);
+        }
+      }
+      // Check for circular references and return its corresponding clone.
+      stack || (stack = new Stack);
+      var stacked = stack.get(value);
+      if (stacked) {
+        return stacked;
+      }
+      stack.set(value, result);
+
+      if (!isArr) {
+        var props = isFull ? getAllKeys(value) : keys(value);
+      }
+      arrayEach(props || value, function(subValue, key) {
+        if (props) {
+          key = subValue;
+          subValue = value[key];
+        }
+        // Recursively populate clone (susceptible to call stack limits).
+        assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.conforms` which doesn't clone `source`.
+     *
+     * @private
+     * @param {Object} source The object of property predicates to conform to.
+     * @returns {Function} Returns the new spec function.
+     */
+    function baseConforms(source) {
+      var props = keys(source);
+      return function(object) {
+        return baseConformsTo(object, source, props);
+      };
+    }
+
+    /**
+     * The base implementation of `_.conformsTo` which accepts `props` to check.
+     *
+     * @private
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property predicates to conform to.
+     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+     */
+    function baseConformsTo(object, source, props) {
+      var length = props.length;
+      if (object == null) {
+        return !length;
+      }
+      var index = length;
+      while (index--) {
+        var key = props[index],
+            predicate = source[key],
+            value = object[key];
+
+        if ((value === undefined &&
+            !(key in Object(object))) || !predicate(value)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * The base implementation of `_.create` without support for assigning
+     * properties to the created object.
+     *
+     * @private
+     * @param {Object} prototype The object to inherit from.
+     * @returns {Object} Returns the new object.
+     */
+    function baseCreate(proto) {
+      return isObject(proto) ? objectCreate(proto) : {};
+    }
+
+    /**
+     * The base implementation of `_.delay` and `_.defer` which accepts `args`
+     * to provide to `func`.
+     *
+     * @private
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay invocation.
+     * @param {Array} args The arguments to provide to `func`.
+     * @returns {number} Returns the timer id.
+     */
+    function baseDelay(func, wait, args) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      return setTimeout(function() { func.apply(undefined, args); }, wait);
+    }
+
+    /**
+     * The base implementation of methods like `_.difference` without support
+     * for excluding multiple arrays or iteratee shorthands.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {Array} values The values to exclude.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of filtered values.
+     */
+    function baseDifference(array, values, iteratee, comparator) {
+      var index = -1,
+          includes = arrayIncludes,
+          isCommon = true,
+          length = array.length,
+          result = [],
+          valuesLength = values.length;
+
+      if (!length) {
+        return result;
+      }
+      if (iteratee) {
+        values = arrayMap(values, baseUnary(iteratee));
+      }
+      if (comparator) {
+        includes = arrayIncludesWith;
+        isCommon = false;
+      }
+      else if (values.length >= LARGE_ARRAY_SIZE) {
+        includes = cacheHas;
+        isCommon = false;
+        values = new SetCache(values);
+      }
+      outer:
+      while (++index < length) {
+        var value = array[index],
+            computed = iteratee ? iteratee(value) : value;
+
+        value = (comparator || value !== 0) ? value : 0;
+        if (isCommon && computed === computed) {
+          var valuesIndex = valuesLength;
+          while (valuesIndex--) {
+            if (values[valuesIndex] === computed) {
+              continue outer;
+            }
+          }
+          result.push(value);
+        }
+        else if (!includes(values, computed, comparator)) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.forEach` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array|Object} Returns `collection`.
+     */
+    var baseEach = createBaseEach(baseForOwn);
+
+    /**
+     * The base implementation of `_.forEachRight` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array|Object} Returns `collection`.
+     */
+    var baseEachRight = createBaseEach(baseForOwnRight, true);
+
+    /**
+     * The base implementation of `_.every` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if all elements pass the predicate check,
+     *  else `false`
+     */
+    function baseEvery(collection, predicate) {
+      var result = true;
+      baseEach(collection, function(value, index, collection) {
+        result = !!predicate(value, index, collection);
+        return result;
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of methods like `_.max` and `_.min` which accepts a
+     * `comparator` to determine the extremum value.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The iteratee invoked per iteration.
+     * @param {Function} comparator The comparator used to compare values.
+     * @returns {*} Returns the extremum value.
+     */
+    function baseExtremum(array, iteratee, comparator) {
+      var index = -1,
+          length = array.length;
+
+      while (++index < length) {
+        var value = array[index],
+            current = iteratee(value);
+
+        if (current != null && (computed === undefined
+              ? (current === current && !isSymbol(current))
+              : comparator(current, computed)
+            )) {
+          var computed = current,
+              result = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.fill` without an iteratee call guard.
+     *
+     * @private
+     * @param {Array} array The array to fill.
+     * @param {*} value The value to fill `array` with.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns `array`.
+     */
+    function baseFill(array, value, start, end) {
+      var length = array.length;
+
+      start = toInteger(start);
+      if (start < 0) {
+        start = -start > length ? 0 : (length + start);
+      }
+      end = (end === undefined || end > length) ? length : toInteger(end);
+      if (end < 0) {
+        end += length;
+      }
+      end = start > end ? 0 : toLength(end);
+      while (start < end) {
+        array[start++] = value;
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.filter` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {Array} Returns the new filtered array.
+     */
+    function baseFilter(collection, predicate) {
+      var result = [];
+      baseEach(collection, function(value, index, collection) {
+        if (predicate(value, index, collection)) {
+          result.push(value);
+        }
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.flatten` with support for restricting flattening.
+     *
+     * @private
+     * @param {Array} array The array to flatten.
+     * @param {number} depth The maximum recursion depth.
+     * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
+     * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
+     * @param {Array} [result=[]] The initial result value.
+     * @returns {Array} Returns the new flattened array.
+     */
+    function baseFlatten(array, depth, predicate, isStrict, result) {
+      var index = -1,
+          length = array.length;
+
+      predicate || (predicate = isFlattenable);
+      result || (result = []);
+
+      while (++index < length) {
+        var value = array[index];
+        if (depth > 0 && predicate(value)) {
+          if (depth > 1) {
+            // Recursively flatten arrays (susceptible to call stack limits).
+            baseFlatten(value, depth - 1, predicate, isStrict, result);
+          } else {
+            arrayPush(result, value);
+          }
+        } else if (!isStrict) {
+          result[result.length] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `baseForOwn` which iterates over `object`
+     * properties returned by `keysFunc` and invokes `iteratee` for each property.
+     * Iteratee functions may exit iteration early by explicitly returning `false`.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} keysFunc The function to get the keys of `object`.
+     * @returns {Object} Returns `object`.
+     */
+    var baseFor = createBaseFor();
+
+    /**
+     * This function is like `baseFor` except that it iterates over properties
+     * in the opposite order.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} keysFunc The function to get the keys of `object`.
+     * @returns {Object} Returns `object`.
+     */
+    var baseForRight = createBaseFor(true);
+
+    /**
+     * The base implementation of `_.forOwn` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     */
+    function baseForOwn(object, iteratee) {
+      return object && baseFor(object, iteratee, keys);
+    }
+
+    /**
+     * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     */
+    function baseForOwnRight(object, iteratee) {
+      return object && baseForRight(object, iteratee, keys);
+    }
+
+    /**
+     * The base implementation of `_.functions` which creates an array of
+     * `object` function property names filtered from `props`.
+     *
+     * @private
+     * @param {Object} object The object to inspect.
+     * @param {Array} props The property names to filter.
+     * @returns {Array} Returns the function names.
+     */
+    function baseFunctions(object, props) {
+      return arrayFilter(props, function(key) {
+        return isFunction(object[key]);
+      });
+    }
+
+    /**
+     * The base implementation of `_.get` without support for default values.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to get.
+     * @returns {*} Returns the resolved value.
+     */
+    function baseGet(object, path) {
+      path = isKey(path, object) ? [path] : castPath(path);
+
+      var index = 0,
+          length = path.length;
+
+      while (object != null && index < length) {
+        object = object[toKey(path[index++])];
+      }
+      return (index && index == length) ? object : undefined;
+    }
+
+    /**
+     * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
+     * `keysFunc` and `symbolsFunc` to get the enumerable property names and
+     * symbols of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Function} keysFunc The function to get the keys of `object`.
+     * @param {Function} symbolsFunc The function to get the symbols of `object`.
+     * @returns {Array} Returns the array of property names and symbols.
+     */
+    function baseGetAllKeys(object, keysFunc, symbolsFunc) {
+      var result = keysFunc(object);
+      return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
+    }
+
+    /**
+     * The base implementation of `getTag`.
+     *
+     * @private
+     * @param {*} value The value to query.
+     * @returns {string} Returns the `toStringTag`.
+     */
+    function baseGetTag(value) {
+      return objectToString.call(value);
+    }
+
+    /**
+     * The base implementation of `_.gt` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is greater than `other`,
+     *  else `false`.
+     */
+    function baseGt(value, other) {
+      return value > other;
+    }
+
+    /**
+     * The base implementation of `_.has` without support for deep paths.
+     *
+     * @private
+     * @param {Object} [object] The object to query.
+     * @param {Array|string} key The key to check.
+     * @returns {boolean} Returns `true` if `key` exists, else `false`.
+     */
+    function baseHas(object, key) {
+      // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,
+      // that are composed entirely of index properties, return `false` for
+      // `hasOwnProperty` checks of them.
+      return object != null &&
+        (hasOwnProperty.call(object, key) ||
+          (typeof object == 'object' && key in object && getPrototype(object) === null));
+    }
+
+    /**
+     * The base implementation of `_.hasIn` without support for deep paths.
+     *
+     * @private
+     * @param {Object} [object] The object to query.
+     * @param {Array|string} key The key to check.
+     * @returns {boolean} Returns `true` if `key` exists, else `false`.
+     */
+    function baseHasIn(object, key) {
+      return object != null && key in Object(object);
+    }
+
+    /**
+     * The base implementation of `_.inRange` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {number} number The number to check.
+     * @param {number} start The start of the range.
+     * @param {number} end The end of the range.
+     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
+     */
+    function baseInRange(number, start, end) {
+      return number >= nativeMin(start, end) && number < nativeMax(start, end);
+    }
+
+    /**
+     * The base implementation of methods like `_.intersection`, without support
+     * for iteratee shorthands, that accepts an array of arrays to inspect.
+     *
+     * @private
+     * @param {Array} arrays The arrays to inspect.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of shared values.
+     */
+    function baseIntersection(arrays, iteratee, comparator) {
+      var includes = comparator ? arrayIncludesWith : arrayIncludes,
+          length = arrays[0].length,
+          othLength = arrays.length,
+          othIndex = othLength,
+          caches = Array(othLength),
+          maxLength = Infinity,
+          result = [];
+
+      while (othIndex--) {
+        var array = arrays[othIndex];
+        if (othIndex && iteratee) {
+          array = arrayMap(array, baseUnary(iteratee));
+        }
+        maxLength = nativeMin(array.length, maxLength);
+        caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
+          ? new SetCache(othIndex && array)
+          : undefined;
+      }
+      array = arrays[0];
+
+      var index = -1,
+          seen = caches[0];
+
+      outer:
+      while (++index < length && result.length < maxLength) {
+        var value = array[index],
+            computed = iteratee ? iteratee(value) : value;
+
+        value = (comparator || value !== 0) ? value : 0;
+        if (!(seen
+              ? cacheHas(seen, computed)
+              : includes(result, computed, comparator)
+            )) {
+          othIndex = othLength;
+          while (--othIndex) {
+            var cache = caches[othIndex];
+            if (!(cache
+                  ? cacheHas(cache, computed)
+                  : includes(arrays[othIndex], computed, comparator))
+                ) {
+              continue outer;
+            }
+          }
+          if (seen) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.invert` and `_.invertBy` which inverts
+     * `object` with values transformed by `iteratee` and set by `setter`.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} setter The function to set `accumulator` values.
+     * @param {Function} iteratee The iteratee to transform values.
+     * @param {Object} accumulator The initial inverted object.
+     * @returns {Function} Returns `accumulator`.
+     */
+    function baseInverter(object, setter, iteratee, accumulator) {
+      baseForOwn(object, function(value, key, object) {
+        setter(accumulator, iteratee(value), key, object);
+      });
+      return accumulator;
+    }
+
+    /**
+     * The base implementation of `_.invoke` without support for individual
+     * method arguments.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the method to invoke.
+     * @param {Array} args The arguments to invoke the method with.
+     * @returns {*} Returns the result of the invoked method.
+     */
+    function baseInvoke(object, path, args) {
+      if (!isKey(path, object)) {
+        path = castPath(path);
+        object = parent(object, path);
+        path = last(path);
+      }
+      var func = object == null ? object : object[toKey(path)];
+      return func == null ? undefined : apply(func, object, args);
+    }
+
+    /**
+     * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
+     */
+    function baseIsArrayBuffer(value) {
+      return isObjectLike(value) && objectToString.call(value) == arrayBufferTag;
+    }
+
+    /**
+     * The base implementation of `_.isDate` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+     */
+    function baseIsDate(value) {
+      return isObjectLike(value) && objectToString.call(value) == dateTag;
+    }
+
+    /**
+     * The base implementation of `_.isEqual` which supports partial comparisons
+     * and tracks traversed objects.
+     *
+     * @private
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @param {Function} [customizer] The function to customize comparisons.
+     * @param {boolean} [bitmask] The bitmask of comparison flags.
+     *  The bitmask may be composed of the following flags:
+     *     1 - Unordered comparison
+     *     2 - Partial comparison
+     * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     */
+    function baseIsEqual(value, other, customizer, bitmask, stack) {
+      if (value === other) {
+        return true;
+      }
+      if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
+        return value !== value && other !== other;
+      }
+      return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
+    }
+
+    /**
+     * A specialized version of `baseIsEqual` for arrays and objects which performs
+     * deep comparisons and tracks traversed objects enabling objects with circular
+     * references to be compared.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} [customizer] The function to customize comparisons.
+     * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`
+     *  for more details.
+     * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {
+      var objIsArr = isArray(object),
+          othIsArr = isArray(other),
+          objTag = arrayTag,
+          othTag = arrayTag;
+
+      if (!objIsArr) {
+        objTag = getTag(object);
+        objTag = objTag == argsTag ? objectTag : objTag;
+      }
+      if (!othIsArr) {
+        othTag = getTag(other);
+        othTag = othTag == argsTag ? objectTag : othTag;
+      }
+      var objIsObj = objTag == objectTag && !isHostObject(object),
+          othIsObj = othTag == objectTag && !isHostObject(other),
+          isSameTag = objTag == othTag;
+
+      if (isSameTag && !objIsObj) {
+        stack || (stack = new Stack);
+        return (objIsArr || isTypedArray(object))
+          ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)
+          : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
+      }
+      if (!(bitmask & PARTIAL_COMPARE_FLAG)) {
+        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+            othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+        if (objIsWrapped || othIsWrapped) {
+          var objUnwrapped = objIsWrapped ? object.value() : object,
+              othUnwrapped = othIsWrapped ? other.value() : other;
+
+          stack || (stack = new Stack);
+          return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);
+        }
+      }
+      if (!isSameTag) {
+        return false;
+      }
+      stack || (stack = new Stack);
+      return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
+    }
+
+    /**
+     * The base implementation of `_.isMap` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+     */
+    function baseIsMap(value) {
+      return isObjectLike(value) && getTag(value) == mapTag;
+    }
+
+    /**
+     * The base implementation of `_.isMatch` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property values to match.
+     * @param {Array} matchData The property names, values, and compare flags to match.
+     * @param {Function} [customizer] The function to customize comparisons.
+     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+     */
+    function baseIsMatch(object, source, matchData, customizer) {
+      var index = matchData.length,
+          length = index,
+          noCustomizer = !customizer;
+
+      if (object == null) {
+        return !length;
+      }
+      object = Object(object);
+      while (index--) {
+        var data = matchData[index];
+        if ((noCustomizer && data[2])
+              ? data[1] !== object[data[0]]
+              : !(data[0] in object)
+            ) {
+          return false;
+        }
+      }
+      while (++index < length) {
+        data = matchData[index];
+        var key = data[0],
+            objValue = object[key],
+            srcValue = data[1];
+
+        if (noCustomizer && data[2]) {
+          if (objValue === undefined && !(key in object)) {
+            return false;
+          }
+        } else {
+          var stack = new Stack;
+          if (customizer) {
+            var result = customizer(objValue, srcValue, key, object, source, stack);
+          }
+          if (!(result === undefined
+                ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)
+                : result
+              )) {
+            return false;
+          }
+        }
+      }
+      return true;
+    }
+
+    /**
+     * The base implementation of `_.isNative` without bad shim checks.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a native function,
+     *  else `false`.
+     */
+    function baseIsNative(value) {
+      if (!isObject(value) || isMasked(value)) {
+        return false;
+      }
+      var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
+      return pattern.test(toSource(value));
+    }
+
+    /**
+     * The base implementation of `_.isRegExp` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+     */
+    function baseIsRegExp(value) {
+      return isObject(value) && objectToString.call(value) == regexpTag;
+    }
+
+    /**
+     * The base implementation of `_.isSet` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+     */
+    function baseIsSet(value) {
+      return isObjectLike(value) && getTag(value) == setTag;
+    }
+
+    /**
+     * The base implementation of `_.isTypedArray` without Node.js optimizations.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+     */
+    function baseIsTypedArray(value) {
+      return isObjectLike(value) &&
+        isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
+    }
+
+    /**
+     * The base implementation of `_.iteratee`.
+     *
+     * @private
+     * @param {*} [value=_.identity] The value to convert to an iteratee.
+     * @returns {Function} Returns the iteratee.
+     */
+    function baseIteratee(value) {
+      // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+      // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+      if (typeof value == 'function') {
+        return value;
+      }
+      if (value == null) {
+        return identity;
+      }
+      if (typeof value == 'object') {
+        return isArray(value)
+          ? baseMatchesProperty(value[0], value[1])
+          : baseMatches(value);
+      }
+      return property(value);
+    }
+
+    /**
+     * The base implementation of `_.keys` which doesn't skip the constructor
+     * property of prototypes or treat sparse arrays as dense.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     */
+    var baseKeys = overArg(nativeKeys, Object);
+
+    /**
+     * The base implementation of `_.keysIn` which doesn't skip the constructor
+     * property of prototypes or treat sparse arrays as dense.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     */
+    function baseKeysIn(object) {
+      object = object == null ? object : Object(object);
+
+      var result = [];
+      for (var key in object) {
+        result.push(key);
+      }
+      return result;
+    }
+
+    // Fallback for IE < 9 with es6-shim.
+    if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
+      baseKeysIn = function(object) {
+        return iteratorToArray(enumerate(object));
+      };
+    }
+
+    /**
+     * The base implementation of `_.lt` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is less than `other`,
+     *  else `false`.
+     */
+    function baseLt(value, other) {
+      return value < other;
+    }
+
+    /**
+     * The base implementation of `_.map` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array} Returns the new mapped array.
+     */
+    function baseMap(collection, iteratee) {
+      var index = -1,
+          result = isArrayLike(collection) ? Array(collection.length) : [];
+
+      baseEach(collection, function(value, key, collection) {
+        result[++index] = iteratee(value, key, collection);
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.matches` which doesn't clone `source`.
+     *
+     * @private
+     * @param {Object} source The object of property values to match.
+     * @returns {Function} Returns the new spec function.
+     */
+    function baseMatches(source) {
+      var matchData = getMatchData(source);
+      if (matchData.length == 1 && matchData[0][2]) {
+        return matchesStrictComparable(matchData[0][0], matchData[0][1]);
+      }
+      return function(object) {
+        return object === source || baseIsMatch(object, source, matchData);
+      };
+    }
+
+    /**
+     * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
+     *
+     * @private
+     * @param {string} path The path of the property to get.
+     * @param {*} srcValue The value to match.
+     * @returns {Function} Returns the new spec function.
+     */
+    function baseMatchesProperty(path, srcValue) {
+      if (isKey(path) && isStrictComparable(srcValue)) {
+        return matchesStrictComparable(toKey(path), srcValue);
+      }
+      return function(object) {
+        var objValue = get(object, path);
+        return (objValue === undefined && objValue === srcValue)
+          ? hasIn(object, path)
+          : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
+      };
+    }
+
+    /**
+     * The base implementation of `_.merge` without support for multiple sources.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {number} srcIndex The index of `source`.
+     * @param {Function} [customizer] The function to customize merged values.
+     * @param {Object} [stack] Tracks traversed source values and their merged
+     *  counterparts.
+     */
+    function baseMerge(object, source, srcIndex, customizer, stack) {
+      if (object === source) {
+        return;
+      }
+      if (!(isArray(source) || isTypedArray(source))) {
+        var props = keysIn(source);
+      }
+      arrayEach(props || source, function(srcValue, key) {
+        if (props) {
+          key = srcValue;
+          srcValue = source[key];
+        }
+        if (isObject(srcValue)) {
+          stack || (stack = new Stack);
+          baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
+        }
+        else {
+          var newValue = customizer
+            ? customizer(object[key], srcValue, (key + ''), object, source, stack)
+            : undefined;
+
+          if (newValue === undefined) {
+            newValue = srcValue;
+          }
+          assignMergeValue(object, key, newValue);
+        }
+      });
+    }
+
+    /**
+     * A specialized version of `baseMerge` for arrays and objects which performs
+     * deep merges and tracks traversed objects enabling objects with circular
+     * references to be merged.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {string} key The key of the value to merge.
+     * @param {number} srcIndex The index of `source`.
+     * @param {Function} mergeFunc The function to merge values.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @param {Object} [stack] Tracks traversed source values and their merged
+     *  counterparts.
+     */
+    function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
+      var objValue = object[key],
+          srcValue = source[key],
+          stacked = stack.get(srcValue);
+
+      if (stacked) {
+        assignMergeValue(object, key, stacked);
+        return;
+      }
+      var newValue = customizer
+        ? customizer(objValue, srcValue, (key + ''), object, source, stack)
+        : undefined;
+
+      var isCommon = newValue === undefined;
+
+      if (isCommon) {
+        newValue = srcValue;
+        if (isArray(srcValue) || isTypedArray(srcValue)) {
+          if (isArray(objValue)) {
+            newValue = objValue;
+          }
+          else if (isArrayLikeObject(objValue)) {
+            newValue = copyArray(objValue);
+          }
+          else {
+            isCommon = false;
+            newValue = baseClone(srcValue, true);
+          }
+        }
+        else if (isPlainObject(srcValue) || isArguments(srcValue)) {
+          if (isArguments(objValue)) {
+            newValue = toPlainObject(objValue);
+          }
+          else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
+            isCommon = false;
+            newValue = baseClone(srcValue, true);
+          }
+          else {
+            newValue = objValue;
+          }
+        }
+        else {
+          isCommon = false;
+        }
+      }
+      if (isCommon) {
+        // Recursively merge objects and arrays (susceptible to call stack limits).
+        stack.set(srcValue, newValue);
+        mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
+        stack['delete'](srcValue);
+      }
+      assignMergeValue(object, key, newValue);
+    }
+
+    /**
+     * The base implementation of `_.nth` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {Array} array The array to query.
+     * @param {number} n The index of the element to return.
+     * @returns {*} Returns the nth element of `array`.
+     */
+    function baseNth(array, n) {
+      var length = array.length;
+      if (!length) {
+        return;
+      }
+      n += n < 0 ? length : 0;
+      return isIndex(n, length) ? array[n] : undefined;
+    }
+
+    /**
+     * The base implementation of `_.orderBy` without param guards.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+     * @param {string[]} orders The sort orders of `iteratees`.
+     * @returns {Array} Returns the new sorted array.
+     */
+    function baseOrderBy(collection, iteratees, orders) {
+      var index = -1;
+      iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee()));
+
+      var result = baseMap(collection, function(value, key, collection) {
+        var criteria = arrayMap(iteratees, function(iteratee) {
+          return iteratee(value);
+        });
+        return { 'criteria': criteria, 'index': ++index, 'value': value };
+      });
+
+      return baseSortBy(result, function(object, other) {
+        return compareMultiple(object, other, orders);
+      });
+    }
+
+    /**
+     * The base implementation of `_.pick` without support for individual
+     * property identifiers.
+     *
+     * @private
+     * @param {Object} object The source object.
+     * @param {string[]} props The property identifiers to pick.
+     * @returns {Object} Returns the new object.
+     */
+    function basePick(object, props) {
+      object = Object(object);
+      return basePickBy(object, props, function(value, key) {
+        return key in object;
+      });
+    }
+
+    /**
+     * The base implementation of  `_.pickBy` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Object} object The source object.
+     * @param {string[]} props The property identifiers to pick from.
+     * @param {Function} predicate The function invoked per property.
+     * @returns {Object} Returns the new object.
+     */
+    function basePickBy(object, props, predicate) {
+      var index = -1,
+          length = props.length,
+          result = {};
+
+      while (++index < length) {
+        var key = props[index],
+            value = object[key];
+
+        if (predicate(value, key)) {
+          result[key] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * A specialized version of `baseProperty` which supports deep paths.
+     *
+     * @private
+     * @param {Array|string} path The path of the property to get.
+     * @returns {Function} Returns the new accessor function.
+     */
+    function basePropertyDeep(path) {
+      return function(object) {
+        return baseGet(object, path);
+      };
+    }
+
+    /**
+     * The base implementation of `_.pullAllBy` without support for iteratee
+     * shorthands.
+     *
+     * @private
+     * @param {Array} array The array to modify.
+     * @param {Array} values The values to remove.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns `array`.
+     */
+    function basePullAll(array, values, iteratee, comparator) {
+      var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
+          index = -1,
+          length = values.length,
+          seen = array;
+
+      if (array === values) {
+        values = copyArray(values);
+      }
+      if (iteratee) {
+        seen = arrayMap(array, baseUnary(iteratee));
+      }
+      while (++index < length) {
+        var fromIndex = 0,
+            value = values[index],
+            computed = iteratee ? iteratee(value) : value;
+
+        while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
+          if (seen !== array) {
+            splice.call(seen, fromIndex, 1);
+          }
+          splice.call(array, fromIndex, 1);
+        }
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.pullAt` without support for individual
+     * indexes or capturing the removed elements.
+     *
+     * @private
+     * @param {Array} array The array to modify.
+     * @param {number[]} indexes The indexes of elements to remove.
+     * @returns {Array} Returns `array`.
+     */
+    function basePullAt(array, indexes) {
+      var length = array ? indexes.length : 0,
+          lastIndex = length - 1;
+
+      while (length--) {
+        var index = indexes[length];
+        if (length == lastIndex || index !== previous) {
+          var previous = index;
+          if (isIndex(index)) {
+            splice.call(array, index, 1);
+          }
+          else if (!isKey(index, array)) {
+            var path = castPath(index),
+                object = parent(array, path);
+
+            if (object != null) {
+              delete object[toKey(last(path))];
+            }
+          }
+          else {
+            delete array[toKey(index)];
+          }
+        }
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.random` without support for returning
+     * floating-point numbers.
+     *
+     * @private
+     * @param {number} lower The lower bound.
+     * @param {number} upper The upper bound.
+     * @returns {number} Returns the random number.
+     */
+    function baseRandom(lower, upper) {
+      return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
+    }
+
+    /**
+     * The base implementation of `_.range` and `_.rangeRight` which doesn't
+     * coerce arguments.
+     *
+     * @private
+     * @param {number} start The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} step The value to increment or decrement by.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Array} Returns the range of numbers.
+     */
+    function baseRange(start, end, step, fromRight) {
+      var index = -1,
+          length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
+          result = Array(length);
+
+      while (length--) {
+        result[fromRight ? length : ++index] = start;
+        start += step;
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.repeat` which doesn't coerce arguments.
+     *
+     * @private
+     * @param {string} string The string to repeat.
+     * @param {number} n The number of times to repeat the string.
+     * @returns {string} Returns the repeated string.
+     */
+    function baseRepeat(string, n) {
+      var result = '';
+      if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
+        return result;
+      }
+      // Leverage the exponentiation by squaring algorithm for a faster repeat.
+      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+      do {
+        if (n % 2) {
+          result += string;
+        }
+        n = nativeFloor(n / 2);
+        if (n) {
+          string += string;
+        }
+      } while (n);
+
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.rest` which doesn't validate or coerce arguments.
+     *
+     * @private
+     * @param {Function} func The function to apply a rest parameter to.
+     * @param {number} [start=func.length-1] The start position of the rest parameter.
+     * @returns {Function} Returns the new function.
+     */
+    function baseRest(func, start) {
+      start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
+      return function() {
+        var args = arguments,
+            index = -1,
+            length = nativeMax(args.length - start, 0),
+            array = Array(length);
+
+        while (++index < length) {
+          array[index] = args[start + index];
+        }
+        index = -1;
+        var otherArgs = Array(start + 1);
+        while (++index < start) {
+          otherArgs[index] = args[index];
+        }
+        otherArgs[start] = array;
+        return apply(func, this, otherArgs);
+      };
+    }
+
+    /**
+     * The base implementation of `_.set`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to set.
+     * @param {*} value The value to set.
+     * @param {Function} [customizer] The function to customize path creation.
+     * @returns {Object} Returns `object`.
+     */
+    function baseSet(object, path, value, customizer) {
+      path = isKey(path, object) ? [path] : castPath(path);
+
+      var index = -1,
+          length = path.length,
+          lastIndex = length - 1,
+          nested = object;
+
+      while (nested != null && ++index < length) {
+        var key = toKey(path[index]);
+        if (isObject(nested)) {
+          var newValue = value;
+          if (index != lastIndex) {
+            var objValue = nested[key];
+            newValue = customizer ? customizer(objValue, key, nested) : undefined;
+            if (newValue === undefined) {
+              newValue = objValue == null
+                ? (isIndex(path[index + 1]) ? [] : {})
+                : objValue;
+            }
+          }
+          assignValue(nested, key, newValue);
+        }
+        nested = nested[key];
+      }
+      return object;
+    }
+
+    /**
+     * The base implementation of `setData` without support for hot loop detection.
+     *
+     * @private
+     * @param {Function} func The function to associate metadata with.
+     * @param {*} data The metadata.
+     * @returns {Function} Returns `func`.
+     */
+    var baseSetData = !metaMap ? identity : function(func, data) {
+      metaMap.set(func, data);
+      return func;
+    };
+
+    /**
+     * The base implementation of `_.slice` without an iteratee call guard.
+     *
+     * @private
+     * @param {Array} array The array to slice.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function baseSlice(array, start, end) {
+      var index = -1,
+          length = array.length;
+
+      if (start < 0) {
+        start = -start > length ? 0 : (length + start);
+      }
+      end = end > length ? length : end;
+      if (end < 0) {
+        end += length;
+      }
+      length = start > end ? 0 : ((end - start) >>> 0);
+      start >>>= 0;
+
+      var result = Array(length);
+      while (++index < length) {
+        result[index] = array[index + start];
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.some` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if any element passes the predicate check,
+     *  else `false`.
+     */
+    function baseSome(collection, predicate) {
+      var result;
+
+      baseEach(collection, function(value, index, collection) {
+        result = predicate(value, index, collection);
+        return !result;
+      });
+      return !!result;
+    }
+
+    /**
+     * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
+     * performs a binary search of `array` to determine the index at which `value`
+     * should be inserted into `array` in order to maintain its sort order.
+     *
+     * @private
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {boolean} [retHighest] Specify returning the highest qualified index.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     */
+    function baseSortedIndex(array, value, retHighest) {
+      var low = 0,
+          high = array ? array.length : low;
+
+      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
+        while (low < high) {
+          var mid = (low + high) >>> 1,
+              computed = array[mid];
+
+          if (computed !== null && !isSymbol(computed) &&
+              (retHighest ? (computed <= value) : (computed < value))) {
+            low = mid + 1;
+          } else {
+            high = mid;
+          }
+        }
+        return high;
+      }
+      return baseSortedIndexBy(array, value, identity, retHighest);
+    }
+
+    /**
+     * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
+     * which invokes `iteratee` for `value` and each element of `array` to compute
+     * their sort ranking. The iteratee is invoked with one argument; (value).
+     *
+     * @private
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function} iteratee The iteratee invoked per element.
+     * @param {boolean} [retHighest] Specify returning the highest qualified index.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     */
+    function baseSortedIndexBy(array, value, iteratee, retHighest) {
+      value = iteratee(value);
+
+      var low = 0,
+          high = array ? array.length : 0,
+          valIsNaN = value !== value,
+          valIsNull = value === null,
+          valIsSymbol = isSymbol(value),
+          valIsUndefined = value === undefined;
+
+      while (low < high) {
+        var mid = nativeFloor((low + high) / 2),
+            computed = iteratee(array[mid]),
+            othIsDefined = computed !== undefined,
+            othIsNull = computed === null,
+            othIsReflexive = computed === computed,
+            othIsSymbol = isSymbol(computed);
+
+        if (valIsNaN) {
+          var setLow = retHighest || othIsReflexive;
+        } else if (valIsUndefined) {
+          setLow = othIsReflexive && (retHighest || othIsDefined);
+        } else if (valIsNull) {
+          setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
+        } else if (valIsSymbol) {
+          setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
+        } else if (othIsNull || othIsSymbol) {
+          setLow = false;
+        } else {
+          setLow = retHighest ? (computed <= value) : (computed < value);
+        }
+        if (setLow) {
+          low = mid + 1;
+        } else {
+          high = mid;
+        }
+      }
+      return nativeMin(high, MAX_ARRAY_INDEX);
+    }
+
+    /**
+     * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
+     * support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @returns {Array} Returns the new duplicate free array.
+     */
+    function baseSortedUniq(array, iteratee) {
+      var index = -1,
+          length = array.length,
+          resIndex = 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index],
+            computed = iteratee ? iteratee(value) : value;
+
+        if (!index || !eq(computed, seen)) {
+          var seen = computed;
+          result[resIndex++] = value === 0 ? 0 : value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.toNumber` which doesn't ensure correct
+     * conversions of binary, hexadecimal, or octal string values.
+     *
+     * @private
+     * @param {*} value The value to process.
+     * @returns {number} Returns the number.
+     */
+    function baseToNumber(value) {
+      if (typeof value == 'number') {
+        return value;
+      }
+      if (isSymbol(value)) {
+        return NAN;
+      }
+      return +value;
+    }
+
+    /**
+     * The base implementation of `_.toString` which doesn't convert nullish
+     * values to empty strings.
+     *
+     * @private
+     * @param {*} value The value to process.
+     * @returns {string} Returns the string.
+     */
+    function baseToString(value) {
+      // Exit early for strings to avoid a performance hit in some environments.
+      if (typeof value == 'string') {
+        return value;
+      }
+      if (isSymbol(value)) {
+        return symbolToString ? symbolToString.call(value) : '';
+      }
+      var result = (value + '');
+      return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+    }
+
+    /**
+     * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new duplicate free array.
+     */
+    function baseUniq(array, iteratee, comparator) {
+      var index = -1,
+          includes = arrayIncludes,
+          length = array.length,
+          isCommon = true,
+          result = [],
+          seen = result;
+
+      if (comparator) {
+        isCommon = false;
+        includes = arrayIncludesWith;
+      }
+      else if (length >= LARGE_ARRAY_SIZE) {
+        var set = iteratee ? null : createSet(array);
+        if (set) {
+          return setToArray(set);
+        }
+        isCommon = false;
+        includes = cacheHas;
+        seen = new SetCache;
+      }
+      else {
+        seen = iteratee ? [] : result;
+      }
+      outer:
+      while (++index < length) {
+        var value = array[index],
+            computed = iteratee ? iteratee(value) : value;
+
+        value = (comparator || value !== 0) ? value : 0;
+        if (isCommon && computed === computed) {
+          var seenIndex = seen.length;
+          while (seenIndex--) {
+            if (seen[seenIndex] === computed) {
+              continue outer;
+            }
+          }
+          if (iteratee) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+        else if (!includes(seen, computed, comparator)) {
+          if (seen !== result) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.unset`.
+     *
+     * @private
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to unset.
+     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
+     */
+    function baseUnset(object, path) {
+      path = isKey(path, object) ? [path] : castPath(path);
+      object = parent(object, path);
+
+      var key = toKey(last(path));
+      return !(object != null && baseHas(object, key)) || delete object[key];
+    }
+
+    /**
+     * The base implementation of `_.update`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to update.
+     * @param {Function} updater The function to produce the updated value.
+     * @param {Function} [customizer] The function to customize path creation.
+     * @returns {Object} Returns `object`.
+     */
+    function baseUpdate(object, path, updater, customizer) {
+      return baseSet(object, path, updater(baseGet(object, path)), customizer);
+    }
+
+    /**
+     * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
+     * without support for iteratee shorthands.
+     *
+     * @private
+     * @param {Array} array The array to query.
+     * @param {Function} predicate The function invoked per iteration.
+     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function baseWhile(array, predicate, isDrop, fromRight) {
+      var length = array.length,
+          index = fromRight ? length : -1;
+
+      while ((fromRight ? index-- : ++index < length) &&
+        predicate(array[index], index, array)) {}
+
+      return isDrop
+        ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
+        : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
+    }
+
+    /**
+     * The base implementation of `wrapperValue` which returns the result of
+     * performing a sequence of actions on the unwrapped `value`, where each
+     * successive action is supplied the return value of the previous.
+     *
+     * @private
+     * @param {*} value The unwrapped value.
+     * @param {Array} actions Actions to perform to resolve the unwrapped value.
+     * @returns {*} Returns the resolved value.
+     */
+    function baseWrapperValue(value, actions) {
+      var result = value;
+      if (result instanceof LazyWrapper) {
+        result = result.value();
+      }
+      return arrayReduce(actions, function(result, action) {
+        return action.func.apply(action.thisArg, arrayPush([result], action.args));
+      }, result);
+    }
+
+    /**
+     * The base implementation of methods like `_.xor`, without support for
+     * iteratee shorthands, that accepts an array of arrays to inspect.
+     *
+     * @private
+     * @param {Array} arrays The arrays to inspect.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of values.
+     */
+    function baseXor(arrays, iteratee, comparator) {
+      var index = -1,
+          length = arrays.length;
+
+      while (++index < length) {
+        var result = result
+          ? arrayPush(
+              baseDifference(result, arrays[index], iteratee, comparator),
+              baseDifference(arrays[index], result, iteratee, comparator)
+            )
+          : arrays[index];
+      }
+      return (result && result.length) ? baseUniq(result, iteratee, comparator) : [];
+    }
+
+    /**
+     * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
+     *
+     * @private
+     * @param {Array} props The property identifiers.
+     * @param {Array} values The property values.
+     * @param {Function} assignFunc The function to assign values.
+     * @returns {Object} Returns the new object.
+     */
+    function baseZipObject(props, values, assignFunc) {
+      var index = -1,
+          length = props.length,
+          valsLength = values.length,
+          result = {};
+
+      while (++index < length) {
+        var value = index < valsLength ? values[index] : undefined;
+        assignFunc(result, props[index], value);
+      }
+      return result;
+    }
+
+    /**
+     * Casts `value` to an empty array if it's not an array like object.
+     *
+     * @private
+     * @param {*} value The value to inspect.
+     * @returns {Array|Object} Returns the cast array-like object.
+     */
+    function castArrayLikeObject(value) {
+      return isArrayLikeObject(value) ? value : [];
+    }
+
+    /**
+     * Casts `value` to `identity` if it's not a function.
+     *
+     * @private
+     * @param {*} value The value to inspect.
+     * @returns {Function} Returns cast function.
+     */
+    function castFunction(value) {
+      return typeof value == 'function' ? value : identity;
+    }
+
+    /**
+     * Casts `value` to a path array if it's not one.
+     *
+     * @private
+     * @param {*} value The value to inspect.
+     * @returns {Array} Returns the cast property path array.
+     */
+    function castPath(value) {
+      return isArray(value) ? value : stringToPath(value);
+    }
+
+    /**
+     * Casts `array` to a slice if it's needed.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {number} start The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns the cast slice.
+     */
+    function castSlice(array, start, end) {
+      var length = array.length;
+      end = end === undefined ? length : end;
+      return (!start && end >= length) ? array : baseSlice(array, start, end);
+    }
+
+    /**
+     * Creates a clone of  `buffer`.
+     *
+     * @private
+     * @param {Buffer} buffer The buffer to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Buffer} Returns the cloned buffer.
+     */
+    function cloneBuffer(buffer, isDeep) {
+      if (isDeep) {
+        return buffer.slice();
+      }
+      var result = new buffer.constructor(buffer.length);
+      buffer.copy(result);
+      return result;
+    }
+
+    /**
+     * Creates a clone of `arrayBuffer`.
+     *
+     * @private
+     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
+     * @returns {ArrayBuffer} Returns the cloned array buffer.
+     */
+    function cloneArrayBuffer(arrayBuffer) {
+      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
+      new Uint8Array(result).set(new Uint8Array(arrayBuffer));
+      return result;
+    }
+
+    /**
+     * Creates a clone of `dataView`.
+     *
+     * @private
+     * @param {Object} dataView The data view to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the cloned data view.
+     */
+    function cloneDataView(dataView, isDeep) {
+      var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
+      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
+    }
+
+    /**
+     * Creates a clone of `map`.
+     *
+     * @private
+     * @param {Object} map The map to clone.
+     * @param {Function} cloneFunc The function to clone values.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the cloned map.
+     */
+    function cloneMap(map, isDeep, cloneFunc) {
+      var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);
+      return arrayReduce(array, addMapEntry, new map.constructor);
+    }
+
+    /**
+     * Creates a clone of `regexp`.
+     *
+     * @private
+     * @param {Object} regexp The regexp to clone.
+     * @returns {Object} Returns the cloned regexp.
+     */
+    function cloneRegExp(regexp) {
+      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
+      result.lastIndex = regexp.lastIndex;
+      return result;
+    }
+
+    /**
+     * Creates a clone of `set`.
+     *
+     * @private
+     * @param {Object} set The set to clone.
+     * @param {Function} cloneFunc The function to clone values.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the cloned set.
+     */
+    function cloneSet(set, isDeep, cloneFunc) {
+      var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);
+      return arrayReduce(array, addSetEntry, new set.constructor);
+    }
+
+    /**
+     * Creates a clone of the `symbol` object.
+     *
+     * @private
+     * @param {Object} symbol The symbol object to clone.
+     * @returns {Object} Returns the cloned symbol object.
+     */
+    function cloneSymbol(symbol) {
+      return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
+    }
+
+    /**
+     * Creates a clone of `typedArray`.
+     *
+     * @private
+     * @param {Object} typedArray The typed array to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the cloned typed array.
+     */
+    function cloneTypedArray(typedArray, isDeep) {
+      var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
+      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
+    }
+
+    /**
+     * Compares values to sort them in ascending order.
+     *
+     * @private
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {number} Returns the sort order indicator for `value`.
+     */
+    function compareAscending(value, other) {
+      if (value !== other) {
+        var valIsDefined = value !== undefined,
+            valIsNull = value === null,
+            valIsReflexive = value === value,
+            valIsSymbol = isSymbol(value);
+
+        var othIsDefined = other !== undefined,
+            othIsNull = other === null,
+            othIsReflexive = other === other,
+            othIsSymbol = isSymbol(other);
+
+        if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
+            (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
+            (valIsNull && othIsDefined && othIsReflexive) ||
+            (!valIsDefined && othIsReflexive) ||
+            !valIsReflexive) {
+          return 1;
+        }
+        if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
+            (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
+            (othIsNull && valIsDefined && valIsReflexive) ||
+            (!othIsDefined && valIsReflexive) ||
+            !othIsReflexive) {
+          return -1;
+        }
+      }
+      return 0;
+    }
+
+    /**
+     * Used by `_.orderBy` to compare multiple properties of a value to another
+     * and stable sort them.
+     *
+     * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
+     * specify an order of "desc" for descending or "asc" for ascending sort order
+     * of corresponding values.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {boolean[]|string[]} orders The order to sort by for each property.
+     * @returns {number} Returns the sort order indicator for `object`.
+     */
+    function compareMultiple(object, other, orders) {
+      var index = -1,
+          objCriteria = object.criteria,
+          othCriteria = other.criteria,
+          length = objCriteria.length,
+          ordersLength = orders.length;
+
+      while (++index < length) {
+        var result = compareAscending(objCriteria[index], othCriteria[index]);
+        if (result) {
+          if (index >= ordersLength) {
+            return result;
+          }
+          var order = orders[index];
+          return result * (order == 'desc' ? -1 : 1);
+        }
+      }
+      // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+      // that causes it, under certain circumstances, to provide the same value for
+      // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+      // for more details.
+      //
+      // This also ensures a stable sort in V8 and other engines.
+      // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
+      return object.index - other.index;
+    }
+
+    /**
+     * Creates an array that is the composition of partially applied arguments,
+     * placeholders, and provided arguments into a single array of arguments.
+     *
+     * @private
+     * @param {Array} args The provided arguments.
+     * @param {Array} partials The arguments to prepend to those provided.
+     * @param {Array} holders The `partials` placeholder indexes.
+     * @params {boolean} [isCurried] Specify composing for a curried function.
+     * @returns {Array} Returns the new array of composed arguments.
+     */
+    function composeArgs(args, partials, holders, isCurried) {
+      var argsIndex = -1,
+          argsLength = args.length,
+          holdersLength = holders.length,
+          leftIndex = -1,
+          leftLength = partials.length,
+          rangeLength = nativeMax(argsLength - holdersLength, 0),
+          result = Array(leftLength + rangeLength),
+          isUncurried = !isCurried;
+
+      while (++leftIndex < leftLength) {
+        result[leftIndex] = partials[leftIndex];
+      }
+      while (++argsIndex < holdersLength) {
+        if (isUncurried || argsIndex < argsLength) {
+          result[holders[argsIndex]] = args[argsIndex];
+        }
+      }
+      while (rangeLength--) {
+        result[leftIndex++] = args[argsIndex++];
+      }
+      return result;
+    }
+
+    /**
+     * This function is like `composeArgs` except that the arguments composition
+     * is tailored for `_.partialRight`.
+     *
+     * @private
+     * @param {Array} args The provided arguments.
+     * @param {Array} partials The arguments to append to those provided.
+     * @param {Array} holders The `partials` placeholder indexes.
+     * @params {boolean} [isCurried] Specify composing for a curried function.
+     * @returns {Array} Returns the new array of composed arguments.
+     */
+    function composeArgsRight(args, partials, holders, isCurried) {
+      var argsIndex = -1,
+          argsLength = args.length,
+          holdersIndex = -1,
+          holdersLength = holders.length,
+          rightIndex = -1,
+          rightLength = partials.length,
+          rangeLength = nativeMax(argsLength - holdersLength, 0),
+          result = Array(rangeLength + rightLength),
+          isUncurried = !isCurried;
+
+      while (++argsIndex < rangeLength) {
+        result[argsIndex] = args[argsIndex];
+      }
+      var offset = argsIndex;
+      while (++rightIndex < rightLength) {
+        result[offset + rightIndex] = partials[rightIndex];
+      }
+      while (++holdersIndex < holdersLength) {
+        if (isUncurried || argsIndex < argsLength) {
+          result[offset + holders[holdersIndex]] = args[argsIndex++];
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Copies the values of `source` to `array`.
+     *
+     * @private
+     * @param {Array} source The array to copy values from.
+     * @param {Array} [array=[]] The array to copy values to.
+     * @returns {Array} Returns `array`.
+     */
+    function copyArray(source, array) {
+      var index = -1,
+          length = source.length;
+
+      array || (array = Array(length));
+      while (++index < length) {
+        array[index] = source[index];
+      }
+      return array;
+    }
+
+    /**
+     * Copies properties of `source` to `object`.
+     *
+     * @private
+     * @param {Object} source The object to copy properties from.
+     * @param {Array} props The property identifiers to copy.
+     * @param {Object} [object={}] The object to copy properties to.
+     * @param {Function} [customizer] The function to customize copied values.
+     * @returns {Object} Returns `object`.
+     */
+    function copyObject(source, props, object, customizer) {
+      object || (object = {});
+
+      var index = -1,
+          length = props.length;
+
+      while (++index < length) {
+        var key = props[index];
+
+        var newValue = customizer
+          ? customizer(object[key], source[key], key, object, source)
+          : undefined;
+
+        assignValue(object, key, newValue === undefined ? source[key] : newValue);
+      }
+      return object;
+    }
+
+    /**
+     * Copies own symbol properties of `source` to `object`.
+     *
+     * @private
+     * @param {Object} source The object to copy symbols from.
+     * @param {Object} [object={}] The object to copy symbols to.
+     * @returns {Object} Returns `object`.
+     */
+    function copySymbols(source, object) {
+      return copyObject(source, getSymbols(source), object);
+    }
+
+    /**
+     * Creates a function like `_.groupBy`.
+     *
+     * @private
+     * @param {Function} setter The function to set accumulator values.
+     * @param {Function} [initializer] The accumulator object initializer.
+     * @returns {Function} Returns the new aggregator function.
+     */
+    function createAggregator(setter, initializer) {
+      return function(collection, iteratee) {
+        var func = isArray(collection) ? arrayAggregator : baseAggregator,
+            accumulator = initializer ? initializer() : {};
+
+        return func(collection, setter, getIteratee(iteratee, 2), accumulator);
+      };
+    }
+
+    /**
+     * Creates a function like `_.assign`.
+     *
+     * @private
+     * @param {Function} assigner The function to assign values.
+     * @returns {Function} Returns the new assigner function.
+     */
+    function createAssigner(assigner) {
+      return baseRest(function(object, sources) {
+        var index = -1,
+            length = sources.length,
+            customizer = length > 1 ? sources[length - 1] : undefined,
+            guard = length > 2 ? sources[2] : undefined;
+
+        customizer = (assigner.length > 3 && typeof customizer == 'function')
+          ? (length--, customizer)
+          : undefined;
+
+        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
+          customizer = length < 3 ? undefined : customizer;
+          length = 1;
+        }
+        object = Object(object);
+        while (++index < length) {
+          var source = sources[index];
+          if (source) {
+            assigner(object, source, index, customizer);
+          }
+        }
+        return object;
+      });
+    }
+
+    /**
+     * Creates a `baseEach` or `baseEachRight` function.
+     *
+     * @private
+     * @param {Function} eachFunc The function to iterate over a collection.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new base function.
+     */
+    function createBaseEach(eachFunc, fromRight) {
+      return function(collection, iteratee) {
+        if (collection == null) {
+          return collection;
+        }
+        if (!isArrayLike(collection)) {
+          return eachFunc(collection, iteratee);
+        }
+        var length = collection.length,
+            index = fromRight ? length : -1,
+            iterable = Object(collection);
+
+        while ((fromRight ? index-- : ++index < length)) {
+          if (iteratee(iterable[index], index, iterable) === false) {
+            break;
+          }
+        }
+        return collection;
+      };
+    }
+
+    /**
+     * Creates a base function for methods like `_.forIn` and `_.forOwn`.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new base function.
+     */
+    function createBaseFor(fromRight) {
+      return function(object, iteratee, keysFunc) {
+        var index = -1,
+            iterable = Object(object),
+            props = keysFunc(object),
+            length = props.length;
+
+        while (length--) {
+          var key = props[fromRight ? length : ++index];
+          if (iteratee(iterable[key], key, iterable) === false) {
+            break;
+          }
+        }
+        return object;
+      };
+    }
+
+    /**
+     * Creates a function that wraps `func` to invoke it with the optional `this`
+     * binding of `thisArg`.
+     *
+     * @private
+     * @param {Function} func The function to wrap.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createBind(func, bitmask, thisArg) {
+      var isBind = bitmask & BIND_FLAG,
+          Ctor = createCtor(func);
+
+      function wrapper() {
+        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+        return fn.apply(isBind ? thisArg : this, arguments);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a function like `_.lowerFirst`.
+     *
+     * @private
+     * @param {string} methodName The name of the `String` case method to use.
+     * @returns {Function} Returns the new case function.
+     */
+    function createCaseFirst(methodName) {
+      return function(string) {
+        string = toString(string);
+
+        var strSymbols = reHasComplexSymbol.test(string)
+          ? stringToArray(string)
+          : undefined;
+
+        var chr = strSymbols
+          ? strSymbols[0]
+          : string.charAt(0);
+
+        var trailing = strSymbols
+          ? castSlice(strSymbols, 1).join('')
+          : string.slice(1);
+
+        return chr[methodName]() + trailing;
+      };
+    }
+
+    /**
+     * Creates a function like `_.camelCase`.
+     *
+     * @private
+     * @param {Function} callback The function to combine each word.
+     * @returns {Function} Returns the new compounder function.
+     */
+    function createCompounder(callback) {
+      return function(string) {
+        return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
+      };
+    }
+
+    /**
+     * Creates a function that produces an instance of `Ctor` regardless of
+     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+     *
+     * @private
+     * @param {Function} Ctor The constructor to wrap.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createCtor(Ctor) {
+      return function() {
+        // Use a `switch` statement to work with class constructors. See
+        // http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
+        // for more details.
+        var args = arguments;
+        switch (args.length) {
+          case 0: return new Ctor;
+          case 1: return new Ctor(args[0]);
+          case 2: return new Ctor(args[0], args[1]);
+          case 3: return new Ctor(args[0], args[1], args[2]);
+          case 4: return new Ctor(args[0], args[1], args[2], args[3]);
+          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
+          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
+          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+        }
+        var thisBinding = baseCreate(Ctor.prototype),
+            result = Ctor.apply(thisBinding, args);
+
+        // Mimic the constructor's `return` behavior.
+        // See https://es5.github.io/#x13.2.2 for more details.
+        return isObject(result) ? result : thisBinding;
+      };
+    }
+
+    /**
+     * Creates a function that wraps `func` to enable currying.
+     *
+     * @private
+     * @param {Function} func The function to wrap.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @param {number} arity The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createCurry(func, bitmask, arity) {
+      var Ctor = createCtor(func);
+
+      function wrapper() {
+        var length = arguments.length,
+            args = Array(length),
+            index = length,
+            placeholder = getHolder(wrapper);
+
+        while (index--) {
+          args[index] = arguments[index];
+        }
+        var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
+          ? []
+          : replaceHolders(args, placeholder);
+
+        length -= holders.length;
+        if (length < arity) {
+          return createRecurry(
+            func, bitmask, createHybrid, wrapper.placeholder, undefined,
+            args, holders, undefined, undefined, arity - length);
+        }
+        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+        return apply(fn, this, args);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a `_.find` or `_.findLast` function.
+     *
+     * @private
+     * @param {Function} findIndexFunc The function to find the collection index.
+     * @returns {Function} Returns the new find function.
+     */
+    function createFind(findIndexFunc) {
+      return function(collection, predicate, fromIndex) {
+        var iterable = Object(collection);
+        if (!isArrayLike(collection)) {
+          var iteratee = getIteratee(predicate, 3);
+          collection = keys(collection);
+          predicate = function(key) { return iteratee(iterable[key], key, iterable); };
+        }
+        var index = findIndexFunc(collection, predicate, fromIndex);
+        return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
+      };
+    }
+
+    /**
+     * Creates a `_.flow` or `_.flowRight` function.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new flow function.
+     */
+    function createFlow(fromRight) {
+      return baseRest(function(funcs) {
+        funcs = baseFlatten(funcs, 1);
+
+        var length = funcs.length,
+            index = length,
+            prereq = LodashWrapper.prototype.thru;
+
+        if (fromRight) {
+          funcs.reverse();
+        }
+        while (index--) {
+          var func = funcs[index];
+          if (typeof func != 'function') {
+            throw new TypeError(FUNC_ERROR_TEXT);
+          }
+          if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
+            var wrapper = new LodashWrapper([], true);
+          }
+        }
+        index = wrapper ? index : length;
+        while (++index < length) {
+          func = funcs[index];
+
+          var funcName = getFuncName(func),
+              data = funcName == 'wrapper' ? getData(func) : undefined;
+
+          if (data && isLaziable(data[0]) &&
+                data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) &&
+                !data[4].length && data[9] == 1
+              ) {
+            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
+          } else {
+            wrapper = (func.length == 1 && isLaziable(func))
+              ? wrapper[funcName]()
+              : wrapper.thru(func);
+          }
+        }
+        return function() {
+          var args = arguments,
+              value = args[0];
+
+          if (wrapper && args.length == 1 &&
+              isArray(value) && value.length >= LARGE_ARRAY_SIZE) {
+            return wrapper.plant(value).value();
+          }
+          var index = 0,
+              result = length ? funcs[index].apply(this, args) : value;
+
+          while (++index < length) {
+            result = funcs[index].call(this, result);
+          }
+          return result;
+        };
+      });
+    }
+
+    /**
+     * Creates a function that wraps `func` to invoke it with optional `this`
+     * binding of `thisArg`, partial application, and currying.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to wrap.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {Array} [partials] The arguments to prepend to those provided to
+     *  the new function.
+     * @param {Array} [holders] The `partials` placeholder indexes.
+     * @param {Array} [partialsRight] The arguments to append to those provided
+     *  to the new function.
+     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+     * @param {Array} [argPos] The argument positions of the new function.
+     * @param {number} [ary] The arity cap of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+      var isAry = bitmask & ARY_FLAG,
+          isBind = bitmask & BIND_FLAG,
+          isBindKey = bitmask & BIND_KEY_FLAG,
+          isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
+          isFlip = bitmask & FLIP_FLAG,
+          Ctor = isBindKey ? undefined : createCtor(func);
+
+      function wrapper() {
+        var length = arguments.length,
+            args = Array(length),
+            index = length;
+
+        while (index--) {
+          args[index] = arguments[index];
+        }
+        if (isCurried) {
+          var placeholder = getHolder(wrapper),
+              holdersCount = countHolders(args, placeholder);
+        }
+        if (partials) {
+          args = composeArgs(args, partials, holders, isCurried);
+        }
+        if (partialsRight) {
+          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
+        }
+        length -= holdersCount;
+        if (isCurried && length < arity) {
+          var newHolders = replaceHolders(args, placeholder);
+          return createRecurry(
+            func, bitmask, createHybrid, wrapper.placeholder, thisArg,
+            args, newHolders, argPos, ary, arity - length
+          );
+        }
+        var thisBinding = isBind ? thisArg : this,
+            fn = isBindKey ? thisBinding[func] : func;
+
+        length = args.length;
+        if (argPos) {
+          args = reorder(args, argPos);
+        } else if (isFlip && length > 1) {
+          args.reverse();
+        }
+        if (isAry && ary < length) {
+          args.length = ary;
+        }
+        if (this && this !== root && this instanceof wrapper) {
+          fn = Ctor || createCtor(fn);
+        }
+        return fn.apply(thisBinding, args);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a function like `_.invertBy`.
+     *
+     * @private
+     * @param {Function} setter The function to set accumulator values.
+     * @param {Function} toIteratee The function to resolve iteratees.
+     * @returns {Function} Returns the new inverter function.
+     */
+    function createInverter(setter, toIteratee) {
+      return function(object, iteratee) {
+        return baseInverter(object, setter, toIteratee(iteratee), {});
+      };
+    }
+
+    /**
+     * Creates a function that performs a mathematical operation on two values.
+     *
+     * @private
+     * @param {Function} operator The function to perform the operation.
+     * @param {number} [defaultValue] The value used for `undefined` arguments.
+     * @returns {Function} Returns the new mathematical operation function.
+     */
+    function createMathOperation(operator, defaultValue) {
+      return function(value, other) {
+        var result;
+        if (value === undefined && other === undefined) {
+          return defaultValue;
+        }
+        if (value !== undefined) {
+          result = value;
+        }
+        if (other !== undefined) {
+          if (result === undefined) {
+            return other;
+          }
+          if (typeof value == 'string' || typeof other == 'string') {
+            value = baseToString(value);
+            other = baseToString(other);
+          } else {
+            value = baseToNumber(value);
+            other = baseToNumber(other);
+          }
+          result = operator(value, other);
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function like `_.over`.
+     *
+     * @private
+     * @param {Function} arrayFunc The function to iterate over iteratees.
+     * @returns {Function} Returns the new over function.
+     */
+    function createOver(arrayFunc) {
+      return baseRest(function(iteratees) {
+        iteratees = (iteratees.length == 1 && isArray(iteratees[0]))
+          ? arrayMap(iteratees[0], baseUnary(getIteratee()))
+          : arrayMap(baseFlatten(iteratees, 1), baseUnary(getIteratee()));
+
+        return baseRest(function(args) {
+          var thisArg = this;
+          return arrayFunc(iteratees, function(iteratee) {
+            return apply(iteratee, thisArg, args);
+          });
+        });
+      });
+    }
+
+    /**
+     * Creates the padding for `string` based on `length`. The `chars` string
+     * is truncated if the number of characters exceeds `length`.
+     *
+     * @private
+     * @param {number} length The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padding for `string`.
+     */
+    function createPadding(length, chars) {
+      chars = chars === undefined ? ' ' : baseToString(chars);
+
+      var charsLength = chars.length;
+      if (charsLength < 2) {
+        return charsLength ? baseRepeat(chars, length) : chars;
+      }
+      var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
+      return reHasComplexSymbol.test(chars)
+        ? castSlice(stringToArray(result), 0, length).join('')
+        : result.slice(0, length);
+    }
+
+    /**
+     * Creates a function that wraps `func` to invoke it with the `this` binding
+     * of `thisArg` and `partials` prepended to the arguments it receives.
+     *
+     * @private
+     * @param {Function} func The function to wrap.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @param {*} thisArg The `this` binding of `func`.
+     * @param {Array} partials The arguments to prepend to those provided to
+     *  the new function.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createPartial(func, bitmask, thisArg, partials) {
+      var isBind = bitmask & BIND_FLAG,
+          Ctor = createCtor(func);
+
+      function wrapper() {
+        var argsIndex = -1,
+            argsLength = arguments.length,
+            leftIndex = -1,
+            leftLength = partials.length,
+            args = Array(leftLength + argsLength),
+            fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+
+        while (++leftIndex < leftLength) {
+          args[leftIndex] = partials[leftIndex];
+        }
+        while (argsLength--) {
+          args[leftIndex++] = arguments[++argsIndex];
+        }
+        return apply(fn, isBind ? thisArg : this, args);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a `_.range` or `_.rangeRight` function.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new range function.
+     */
+    function createRange(fromRight) {
+      return function(start, end, step) {
+        if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
+          end = step = undefined;
+        }
+        // Ensure the sign of `-0` is preserved.
+        start = toFinite(start);
+        if (end === undefined) {
+          end = start;
+          start = 0;
+        } else {
+          end = toFinite(end);
+        }
+        step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
+        return baseRange(start, end, step, fromRight);
+      };
+    }
+
+    /**
+     * Creates a function that performs a relational operation on two values.
+     *
+     * @private
+     * @param {Function} operator The function to perform the operation.
+     * @returns {Function} Returns the new relational operation function.
+     */
+    function createRelationalOperation(operator) {
+      return function(value, other) {
+        if (!(typeof value == 'string' && typeof other == 'string')) {
+          value = toNumber(value);
+          other = toNumber(other);
+        }
+        return operator(value, other);
+      };
+    }
+
+    /**
+     * Creates a function that wraps `func` to continue currying.
+     *
+     * @private
+     * @param {Function} func The function to wrap.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @param {Function} wrapFunc The function to create the `func` wrapper.
+     * @param {*} placeholder The placeholder value.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {Array} [partials] The arguments to prepend to those provided to
+     *  the new function.
+     * @param {Array} [holders] The `partials` placeholder indexes.
+     * @param {Array} [argPos] The argument positions of the new function.
+     * @param {number} [ary] The arity cap of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
+      var isCurry = bitmask & CURRY_FLAG,
+          newHolders = isCurry ? holders : undefined,
+          newHoldersRight = isCurry ? undefined : holders,
+          newPartials = isCurry ? partials : undefined,
+          newPartialsRight = isCurry ? undefined : partials;
+
+      bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
+      bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
+
+      if (!(bitmask & CURRY_BOUND_FLAG)) {
+        bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+      }
+      var newData = [
+        func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
+        newHoldersRight, argPos, ary, arity
+      ];
+
+      var result = wrapFunc.apply(undefined, newData);
+      if (isLaziable(func)) {
+        setData(result, newData);
+      }
+      result.placeholder = placeholder;
+      return setWrapToString(result, func, bitmask);
+    }
+
+    /**
+     * Creates a function like `_.round`.
+     *
+     * @private
+     * @param {string} methodName The name of the `Math` method to use when rounding.
+     * @returns {Function} Returns the new round function.
+     */
+    function createRound(methodName) {
+      var func = Math[methodName];
+      return function(number, precision) {
+        number = toNumber(number);
+        precision = nativeMin(toInteger(precision), 292);
+        if (precision) {
+          // Shift with exponential notation to avoid floating-point issues.
+          // See [MDN](https://mdn.io/round#Examples) for more details.
+          var pair = (toString(number) + 'e').split('e'),
+              value = func(pair[0] + 'e' + (+pair[1] + precision));
+
+          pair = (toString(value) + 'e').split('e');
+          return +(pair[0] + 'e' + (+pair[1] - precision));
+        }
+        return func(number);
+      };
+    }
+
+    /**
+     * Creates a set object of `values`.
+     *
+     * @private
+     * @param {Array} values The values to add to the set.
+     * @returns {Object} Returns the new set.
+     */
+    var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
+      return new Set(values);
+    };
+
+    /**
+     * Creates a `_.toPairs` or `_.toPairsIn` function.
+     *
+     * @private
+     * @param {Function} keysFunc The function to get the keys of a given object.
+     * @returns {Function} Returns the new pairs function.
+     */
+    function createToPairs(keysFunc) {
+      return function(object) {
+        var tag = getTag(object);
+        if (tag == mapTag) {
+          return mapToArray(object);
+        }
+        if (tag == setTag) {
+          return setToPairs(object);
+        }
+        return baseToPairs(object, keysFunc(object));
+      };
+    }
+
+    /**
+     * Creates a function that either curries or invokes `func` with optional
+     * `this` binding and partially applied arguments.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to wrap.
+     * @param {number} bitmask The bitmask flags.
+     *  The bitmask may be composed of the following flags:
+     *     1 - `_.bind`
+     *     2 - `_.bindKey`
+     *     4 - `_.curry` or `_.curryRight` of a bound function
+     *     8 - `_.curry`
+     *    16 - `_.curryRight`
+     *    32 - `_.partial`
+     *    64 - `_.partialRight`
+     *   128 - `_.rearg`
+     *   256 - `_.ary`
+     *   512 - `_.flip`
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {Array} [partials] The arguments to be partially applied.
+     * @param {Array} [holders] The `partials` placeholder indexes.
+     * @param {Array} [argPos] The argument positions of the new function.
+     * @param {number} [ary] The arity cap of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+      var isBindKey = bitmask & BIND_KEY_FLAG;
+      if (!isBindKey && typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      var length = partials ? partials.length : 0;
+      if (!length) {
+        bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
+        partials = holders = undefined;
+      }
+      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
+      arity = arity === undefined ? arity : toInteger(arity);
+      length -= holders ? holders.length : 0;
+
+      if (bitmask & PARTIAL_RIGHT_FLAG) {
+        var partialsRight = partials,
+            holdersRight = holders;
+
+        partials = holders = undefined;
+      }
+      var data = isBindKey ? undefined : getData(func);
+
+      var newData = [
+        func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
+        argPos, ary, arity
+      ];
+
+      if (data) {
+        mergeData(newData, data);
+      }
+      func = newData[0];
+      bitmask = newData[1];
+      thisArg = newData[2];
+      partials = newData[3];
+      holders = newData[4];
+      arity = newData[9] = newData[9] == null
+        ? (isBindKey ? 0 : func.length)
+        : nativeMax(newData[9] - length, 0);
+
+      if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {
+        bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);
+      }
+      if (!bitmask || bitmask == BIND_FLAG) {
+        var result = createBind(func, bitmask, thisArg);
+      } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {
+        result = createCurry(func, bitmask, arity);
+      } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {
+        result = createPartial(func, bitmask, thisArg, partials);
+      } else {
+        result = createHybrid.apply(undefined, newData);
+      }
+      var setter = data ? baseSetData : setData;
+      return setWrapToString(setter(result, newData), func, bitmask);
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for arrays with support for
+     * partial deep comparisons.
+     *
+     * @private
+     * @param {Array} array The array to compare.
+     * @param {Array} other The other array to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} customizer The function to customize comparisons.
+     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+     *  for more details.
+     * @param {Object} stack Tracks traversed `array` and `other` objects.
+     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+     */
+    function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
+      var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+          arrLength = array.length,
+          othLength = other.length;
+
+      if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+        return false;
+      }
+      // Assume cyclic values are equal.
+      var stacked = stack.get(array);
+      if (stacked && stack.get(other)) {
+        return stacked == other;
+      }
+      var index = -1,
+          result = true,
+          seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
+
+      stack.set(array, other);
+      stack.set(other, array);
+
+      // Ignore non-index properties.
+      while (++index < arrLength) {
+        var arrValue = array[index],
+            othValue = other[index];
+
+        if (customizer) {
+          var compared = isPartial
+            ? customizer(othValue, arrValue, index, other, array, stack)
+            : customizer(arrValue, othValue, index, array, other, stack);
+        }
+        if (compared !== undefined) {
+          if (compared) {
+            continue;
+          }
+          result = false;
+          break;
+        }
+        // Recursively compare arrays (susceptible to call stack limits).
+        if (seen) {
+          if (!arraySome(other, function(othValue, othIndex) {
+                if (!seen.has(othIndex) &&
+                    (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
+                  return seen.add(othIndex);
+                }
+              })) {
+            result = false;
+            break;
+          }
+        } else if (!(
+              arrValue === othValue ||
+                equalFunc(arrValue, othValue, customizer, bitmask, stack)
+            )) {
+          result = false;
+          break;
+        }
+      }
+      stack['delete'](array);
+      stack['delete'](other);
+      return result;
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for comparing objects of
+     * the same `toStringTag`.
+     *
+     * **Note:** This function only supports comparing values with tags of
+     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {string} tag The `toStringTag` of the objects to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} customizer The function to customize comparisons.
+     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+     *  for more details.
+     * @param {Object} stack Tracks traversed `object` and `other` objects.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
+      switch (tag) {
+        case dataViewTag:
+          if ((object.byteLength != other.byteLength) ||
+              (object.byteOffset != other.byteOffset)) {
+            return false;
+          }
+          object = object.buffer;
+          other = other.buffer;
+
+        case arrayBufferTag:
+          if ((object.byteLength != other.byteLength) ||
+              !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
+            return false;
+          }
+          return true;
+
+        case boolTag:
+        case dateTag:
+        case numberTag:
+          // Coerce booleans to `1` or `0` and dates to milliseconds.
+          // Invalid dates are coerced to `NaN`.
+          return eq(+object, +other);
+
+        case errorTag:
+          return object.name == other.name && object.message == other.message;
+
+        case regexpTag:
+        case stringTag:
+          // Coerce regexes to strings and treat strings, primitives and objects,
+          // as equal. See http://www.ecma-international.org/ecma-262/6.0/#sec-regexp.prototype.tostring
+          // for more details.
+          return object == (other + '');
+
+        case mapTag:
+          var convert = mapToArray;
+
+        case setTag:
+          var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
+          convert || (convert = setToArray);
+
+          if (object.size != other.size && !isPartial) {
+            return false;
+          }
+          // Assume cyclic values are equal.
+          var stacked = stack.get(object);
+          if (stacked) {
+            return stacked == other;
+          }
+          bitmask |= UNORDERED_COMPARE_FLAG;
+
+          // Recursively compare objects (susceptible to call stack limits).
+          stack.set(object, other);
+          var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);
+          stack['delete'](object);
+          return result;
+
+        case symbolTag:
+          if (symbolValueOf) {
+            return symbolValueOf.call(object) == symbolValueOf.call(other);
+          }
+      }
+      return false;
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for objects with support for
+     * partial deep comparisons.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} customizer The function to customize comparisons.
+     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+     *  for more details.
+     * @param {Object} stack Tracks traversed `object` and `other` objects.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {
+      var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+          objProps = keys(object),
+          objLength = objProps.length,
+          othProps = keys(other),
+          othLength = othProps.length;
+
+      if (objLength != othLength && !isPartial) {
+        return false;
+      }
+      var index = objLength;
+      while (index--) {
+        var key = objProps[index];
+        if (!(isPartial ? key in other : baseHas(other, key))) {
+          return false;
+        }
+      }
+      // Assume cyclic values are equal.
+      var stacked = stack.get(object);
+      if (stacked && stack.get(other)) {
+        return stacked == other;
+      }
+      var result = true;
+      stack.set(object, other);
+      stack.set(other, object);
+
+      var skipCtor = isPartial;
+      while (++index < objLength) {
+        key = objProps[index];
+        var objValue = object[key],
+            othValue = other[key];
+
+        if (customizer) {
+          var compared = isPartial
+            ? customizer(othValue, objValue, key, other, object, stack)
+            : customizer(objValue, othValue, key, object, other, stack);
+        }
+        // Recursively compare objects (susceptible to call stack limits).
+        if (!(compared === undefined
+              ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))
+              : compared
+            )) {
+          result = false;
+          break;
+        }
+        skipCtor || (skipCtor = key == 'constructor');
+      }
+      if (result && !skipCtor) {
+        var objCtor = object.constructor,
+            othCtor = other.constructor;
+
+        // Non `Object` object instances with different constructors are not equal.
+        if (objCtor != othCtor &&
+            ('constructor' in object && 'constructor' in other) &&
+            !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+              typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+          result = false;
+        }
+      }
+      stack['delete'](object);
+      stack['delete'](other);
+      return result;
+    }
+
+    /**
+     * Creates an array of own enumerable property names and symbols of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names and symbols.
+     */
+    function getAllKeys(object) {
+      return baseGetAllKeys(object, keys, getSymbols);
+    }
+
+    /**
+     * Creates an array of own and inherited enumerable property names and
+     * symbols of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names and symbols.
+     */
+    function getAllKeysIn(object) {
+      return baseGetAllKeys(object, keysIn, getSymbolsIn);
+    }
+
+    /**
+     * Gets metadata for `func`.
+     *
+     * @private
+     * @param {Function} func The function to query.
+     * @returns {*} Returns the metadata for `func`.
+     */
+    var getData = !metaMap ? noop : function(func) {
+      return metaMap.get(func);
+    };
+
+    /**
+     * Gets the name of `func`.
+     *
+     * @private
+     * @param {Function} func The function to query.
+     * @returns {string} Returns the function name.
+     */
+    function getFuncName(func) {
+      var result = (func.name + ''),
+          array = realNames[result],
+          length = hasOwnProperty.call(realNames, result) ? array.length : 0;
+
+      while (length--) {
+        var data = array[length],
+            otherFunc = data.func;
+        if (otherFunc == null || otherFunc == func) {
+          return data.name;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Gets the argument placeholder value for `func`.
+     *
+     * @private
+     * @param {Function} func The function to inspect.
+     * @returns {*} Returns the placeholder value.
+     */
+    function getHolder(func) {
+      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
+      return object.placeholder;
+    }
+
+    /**
+     * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
+     * this function returns the custom method, otherwise it returns `baseIteratee`.
+     * If arguments are provided, the chosen function is invoked with them and
+     * its result is returned.
+     *
+     * @private
+     * @param {*} [value] The value to convert to an iteratee.
+     * @param {number} [arity] The arity of the created iteratee.
+     * @returns {Function} Returns the chosen function or its result.
+     */
+    function getIteratee() {
+      var result = lodash.iteratee || iteratee;
+      result = result === iteratee ? baseIteratee : result;
+      return arguments.length ? result(arguments[0], arguments[1]) : result;
+    }
+
+    /**
+     * Gets the "length" property value of `object`.
+     *
+     * **Note:** This function is used to avoid a
+     * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
+     * Safari on at least iOS 8.1-8.3 ARM64.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {*} Returns the "length" value.
+     */
+    var getLength = baseProperty('length');
+
+    /**
+     * Gets the data for `map`.
+     *
+     * @private
+     * @param {Object} map The map to query.
+     * @param {string} key The reference key.
+     * @returns {*} Returns the map data.
+     */
+    function getMapData(map, key) {
+      var data = map.__data__;
+      return isKeyable(key)
+        ? data[typeof key == 'string' ? 'string' : 'hash']
+        : data.map;
+    }
+
+    /**
+     * Gets the property names, values, and compare flags of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the match data of `object`.
+     */
+    function getMatchData(object) {
+      var result = keys(object),
+          length = result.length;
+
+      while (length--) {
+        var key = result[length],
+            value = object[key];
+
+        result[length] = [key, value, isStrictComparable(value)];
+      }
+      return result;
+    }
+
+    /**
+     * Gets the native function at `key` of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {string} key The key of the method to get.
+     * @returns {*} Returns the function if it's native, else `undefined`.
+     */
+    function getNative(object, key) {
+      var value = getValue(object, key);
+      return baseIsNative(value) ? value : undefined;
+    }
+
+    /**
+     * Gets the `[[Prototype]]` of `value`.
+     *
+     * @private
+     * @param {*} value The value to query.
+     * @returns {null|Object} Returns the `[[Prototype]]`.
+     */
+    var getPrototype = overArg(nativeGetPrototype, Object);
+
+    /**
+     * Creates an array of the own enumerable symbol properties of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of symbols.
+     */
+    var getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;
+
+    /**
+     * Creates an array of the own and inherited enumerable symbol properties
+     * of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of symbols.
+     */
+    var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
+      var result = [];
+      while (object) {
+        arrayPush(result, getSymbols(object));
+        object = getPrototype(object);
+      }
+      return result;
+    };
+
+    /**
+     * Gets the `toStringTag` of `value`.
+     *
+     * @private
+     * @param {*} value The value to query.
+     * @returns {string} Returns the `toStringTag`.
+     */
+    var getTag = baseGetTag;
+
+    // Fallback for data views, maps, sets, and weak maps in IE 11,
+    // for data views in Edge, and promises in Node.js.
+    if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+        (Map && getTag(new Map) != mapTag) ||
+        (Promise && getTag(Promise.resolve()) != promiseTag) ||
+        (Set && getTag(new Set) != setTag) ||
+        (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+      getTag = function(value) {
+        var result = objectToString.call(value),
+            Ctor = result == objectTag ? value.constructor : undefined,
+            ctorString = Ctor ? toSource(Ctor) : undefined;
+
+        if (ctorString) {
+          switch (ctorString) {
+            case dataViewCtorString: return dataViewTag;
+            case mapCtorString: return mapTag;
+            case promiseCtorString: return promiseTag;
+            case setCtorString: return setTag;
+            case weakMapCtorString: return weakMapTag;
+          }
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Gets the view, applying any `transforms` to the `start` and `end` positions.
+     *
+     * @private
+     * @param {number} start The start of the view.
+     * @param {number} end The end of the view.
+     * @param {Array} transforms The transformations to apply to the view.
+     * @returns {Object} Returns an object containing the `start` and `end`
+     *  positions of the view.
+     */
+    function getView(start, end, transforms) {
+      var index = -1,
+          length = transforms.length;
+
+      while (++index < length) {
+        var data = transforms[index],
+            size = data.size;
+
+        switch (data.type) {
+          case 'drop':      start += size; break;
+          case 'dropRight': end -= size; break;
+          case 'take':      end = nativeMin(end, start + size); break;
+          case 'takeRight': start = nativeMax(start, end - size); break;
+        }
+      }
+      return { 'start': start, 'end': end };
+    }
+
+    /**
+     * Extracts wrapper details from the `source` body comment.
+     *
+     * @private
+     * @param {string} source The source to inspect.
+     * @returns {Array} Returns the wrapper details.
+     */
+    function getWrapDetails(source) {
+      var match = source.match(reWrapDetails);
+      return match ? match[1].split(reSplitDetails) : [];
+    }
+
+    /**
+     * Checks if `path` exists on `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path to check.
+     * @param {Function} hasFunc The function to check properties.
+     * @returns {boolean} Returns `true` if `path` exists, else `false`.
+     */
+    function hasPath(object, path, hasFunc) {
+      path = isKey(path, object) ? [path] : castPath(path);
+
+      var result,
+          index = -1,
+          length = path.length;
+
+      while (++index < length) {
+        var key = toKey(path[index]);
+        if (!(result = object != null && hasFunc(object, key))) {
+          break;
+        }
+        object = object[key];
+      }
+      if (result) {
+        return result;
+      }
+      var length = object ? object.length : 0;
+      return !!length && isLength(length) && isIndex(key, length) &&
+        (isArray(object) || isString(object) || isArguments(object));
+    }
+
+    /**
+     * Initializes an array clone.
+     *
+     * @private
+     * @param {Array} array The array to clone.
+     * @returns {Array} Returns the initialized clone.
+     */
+    function initCloneArray(array) {
+      var length = array.length,
+          result = array.constructor(length);
+
+      // Add properties assigned by `RegExp#exec`.
+      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+        result.index = array.index;
+        result.input = array.input;
+      }
+      return result;
+    }
+
+    /**
+     * Initializes an object clone.
+     *
+     * @private
+     * @param {Object} object The object to clone.
+     * @returns {Object} Returns the initialized clone.
+     */
+    function initCloneObject(object) {
+      return (typeof object.constructor == 'function' && !isPrototype(object))
+        ? baseCreate(getPrototype(object))
+        : {};
+    }
+
+    /**
+     * Initializes an object clone based on its `toStringTag`.
+     *
+     * **Note:** This function only supports cloning values with tags of
+     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+     *
+     * @private
+     * @param {Object} object The object to clone.
+     * @param {string} tag The `toStringTag` of the object to clone.
+     * @param {Function} cloneFunc The function to clone values.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the initialized clone.
+     */
+    function initCloneByTag(object, tag, cloneFunc, isDeep) {
+      var Ctor = object.constructor;
+      switch (tag) {
+        case arrayBufferTag:
+          return cloneArrayBuffer(object);
+
+        case boolTag:
+        case dateTag:
+          return new Ctor(+object);
+
+        case dataViewTag:
+          return cloneDataView(object, isDeep);
+
+        case float32Tag: case float64Tag:
+        case int8Tag: case int16Tag: case int32Tag:
+        case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+          return cloneTypedArray(object, isDeep);
+
+        case mapTag:
+          return cloneMap(object, isDeep, cloneFunc);
+
+        case numberTag:
+        case stringTag:
+          return new Ctor(object);
+
+        case regexpTag:
+          return cloneRegExp(object);
+
+        case setTag:
+          return cloneSet(object, isDeep, cloneFunc);
+
+        case symbolTag:
+          return cloneSymbol(object);
+      }
+    }
+
+    /**
+     * Creates an array of index keys for `object` values of arrays,
+     * `arguments` objects, and strings, otherwise `null` is returned.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array|null} Returns index keys, else `null`.
+     */
+    function indexKeys(object) {
+      var length = object ? object.length : undefined;
+      if (isLength(length) &&
+          (isArray(object) || isString(object) || isArguments(object))) {
+        return baseTimes(length, String);
+      }
+      return null;
+    }
+
+    /**
+     * Inserts wrapper `details` in a comment at the top of the `source` body.
+     *
+     * @private
+     * @param {string} source The source to modify.
+     * @returns {Array} details The details to insert.
+     * @returns {string} Returns the modified source.
+     */
+    function insertWrapDetails(source, details) {
+      var length = details.length,
+          lastIndex = length - 1;
+
+      details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
+      details = details.join(length > 2 ? ', ' : ' ');
+      return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
+    }
+
+    /**
+     * Checks if `value` is a flattenable `arguments` object or array.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+     */
+    function isFlattenable(value) {
+      return isArray(value) || isArguments(value) ||
+        !!(spreadableSymbol && value && value[spreadableSymbol]);
+    }
+
+    /**
+     * Checks if `value` is a valid array-like index.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+     */
+    function isIndex(value, length) {
+      length = length == null ? MAX_SAFE_INTEGER : length;
+      return !!length &&
+        (typeof value == 'number' || reIsUint.test(value)) &&
+        (value > -1 && value % 1 == 0 && value < length);
+    }
+
+    /**
+     * Checks if the given arguments are from an iteratee call.
+     *
+     * @private
+     * @param {*} value The potential iteratee value argument.
+     * @param {*} index The potential iteratee index or key argument.
+     * @param {*} object The potential iteratee object argument.
+     * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+     *  else `false`.
+     */
+    function isIterateeCall(value, index, object) {
+      if (!isObject(object)) {
+        return false;
+      }
+      var type = typeof index;
+      if (type == 'number'
+            ? (isArrayLike(object) && isIndex(index, object.length))
+            : (type == 'string' && index in object)
+          ) {
+        return eq(object[index], value);
+      }
+      return false;
+    }
+
+    /**
+     * Checks if `value` is a property name and not a property path.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @param {Object} [object] The object to query keys on.
+     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+     */
+    function isKey(value, object) {
+      if (isArray(value)) {
+        return false;
+      }
+      var type = typeof value;
+      if (type == 'number' || type == 'symbol' || type == 'boolean' ||
+          value == null || isSymbol(value)) {
+        return true;
+      }
+      return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
+        (object != null && value in Object(object));
+    }
+
+    /**
+     * Checks if `value` is suitable for use as unique object key.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+     */
+    function isKeyable(value) {
+      var type = typeof value;
+      return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+        ? (value !== '__proto__')
+        : (value === null);
+    }
+
+    /**
+     * Checks if `func` has a lazy counterpart.
+     *
+     * @private
+     * @param {Function} func The function to check.
+     * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
+     *  else `false`.
+     */
+    function isLaziable(func) {
+      var funcName = getFuncName(func),
+          other = lodash[funcName];
+
+      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
+        return false;
+      }
+      if (func === other) {
+        return true;
+      }
+      var data = getData(other);
+      return !!data && func === data[0];
+    }
+
+    /**
+     * Checks if `func` has its source masked.
+     *
+     * @private
+     * @param {Function} func The function to check.
+     * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+     */
+    function isMasked(func) {
+      return !!maskSrcKey && (maskSrcKey in func);
+    }
+
+    /**
+     * Checks if `func` is capable of being masked.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
+     */
+    var isMaskable = coreJsData ? isFunction : stubFalse;
+
+    /**
+     * Checks if `value` is likely a prototype object.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+     */
+    function isPrototype(value) {
+      var Ctor = value && value.constructor,
+          proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+
+      return value === proto;
+    }
+
+    /**
+     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` if suitable for strict
+     *  equality comparisons, else `false`.
+     */
+    function isStrictComparable(value) {
+      return value === value && !isObject(value);
+    }
+
+    /**
+     * A specialized version of `matchesProperty` for source values suitable
+     * for strict equality comparisons, i.e. `===`.
+     *
+     * @private
+     * @param {string} key The key of the property to get.
+     * @param {*} srcValue The value to match.
+     * @returns {Function} Returns the new spec function.
+     */
+    function matchesStrictComparable(key, srcValue) {
+      return function(object) {
+        if (object == null) {
+          return false;
+        }
+        return object[key] === srcValue &&
+          (srcValue !== undefined || (key in Object(object)));
+      };
+    }
+
+    /**
+     * Merges the function metadata of `source` into `data`.
+     *
+     * Merging metadata reduces the number of wrappers used to invoke a function.
+     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+     * may be applied regardless of execution order. Methods like `_.ary` and
+     * `_.rearg` modify function arguments, making the order in which they are
+     * executed important, preventing the merging of metadata. However, we make
+     * an exception for a safe combined case where curried functions have `_.ary`
+     * and or `_.rearg` applied.
+     *
+     * @private
+     * @param {Array} data The destination metadata.
+     * @param {Array} source The source metadata.
+     * @returns {Array} Returns `data`.
+     */
+    function mergeData(data, source) {
+      var bitmask = data[1],
+          srcBitmask = source[1],
+          newBitmask = bitmask | srcBitmask,
+          isCommon = newBitmask < (BIND_FLAG | BIND_KEY_FLAG | ARY_FLAG);
+
+      var isCombo =
+        ((srcBitmask == ARY_FLAG) && (bitmask == CURRY_FLAG)) ||
+        ((srcBitmask == ARY_FLAG) && (bitmask == REARG_FLAG) && (data[7].length <= source[8])) ||
+        ((srcBitmask == (ARY_FLAG | REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == CURRY_FLAG));
+
+      // Exit early if metadata can't be merged.
+      if (!(isCommon || isCombo)) {
+        return data;
+      }
+      // Use source `thisArg` if available.
+      if (srcBitmask & BIND_FLAG) {
+        data[2] = source[2];
+        // Set when currying a bound function.
+        newBitmask |= bitmask & BIND_FLAG ? 0 : CURRY_BOUND_FLAG;
+      }
+      // Compose partial arguments.
+      var value = source[3];
+      if (value) {
+        var partials = data[3];
+        data[3] = partials ? composeArgs(partials, value, source[4]) : value;
+        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
+      }
+      // Compose partial right arguments.
+      value = source[5];
+      if (value) {
+        partials = data[5];
+        data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
+        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
+      }
+      // Use source `argPos` if available.
+      value = source[7];
+      if (value) {
+        data[7] = value;
+      }
+      // Use source `ary` if it's smaller.
+      if (srcBitmask & ARY_FLAG) {
+        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+      }
+      // Use source `arity` if one is not provided.
+      if (data[9] == null) {
+        data[9] = source[9];
+      }
+      // Use source `func` and merge bitmasks.
+      data[0] = source[0];
+      data[1] = newBitmask;
+
+      return data;
+    }
+
+    /**
+     * Used by `_.defaultsDeep` to customize its `_.merge` use.
+     *
+     * @private
+     * @param {*} objValue The destination value.
+     * @param {*} srcValue The source value.
+     * @param {string} key The key of the property to merge.
+     * @param {Object} object The parent object of `objValue`.
+     * @param {Object} source The parent object of `srcValue`.
+     * @param {Object} [stack] Tracks traversed source values and their merged
+     *  counterparts.
+     * @returns {*} Returns the value to assign.
+     */
+    function mergeDefaults(objValue, srcValue, key, object, source, stack) {
+      if (isObject(objValue) && isObject(srcValue)) {
+        // Recursively merge objects and arrays (susceptible to call stack limits).
+        stack.set(srcValue, objValue);
+        baseMerge(objValue, srcValue, undefined, mergeDefaults, stack);
+        stack['delete'](srcValue);
+      }
+      return objValue;
+    }
+
+    /**
+     * Gets the parent value at `path` of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array} path The path to get the parent value of.
+     * @returns {*} Returns the parent value.
+     */
+    function parent(object, path) {
+      return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
+    }
+
+    /**
+     * Reorder `array` according to the specified indexes where the element at
+     * the first index is assigned as the first element, the element at
+     * the second index is assigned as the second element, and so on.
+     *
+     * @private
+     * @param {Array} array The array to reorder.
+     * @param {Array} indexes The arranged array indexes.
+     * @returns {Array} Returns `array`.
+     */
+    function reorder(array, indexes) {
+      var arrLength = array.length,
+          length = nativeMin(indexes.length, arrLength),
+          oldArray = copyArray(array);
+
+      while (length--) {
+        var index = indexes[length];
+        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+      }
+      return array;
+    }
+
+    /**
+     * Sets metadata for `func`.
+     *
+     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+     * period of time, it will trip its breaker and transition to an identity
+     * function to avoid garbage collection pauses in V8. See
+     * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
+     * for more details.
+     *
+     * @private
+     * @param {Function} func The function to associate metadata with.
+     * @param {*} data The metadata.
+     * @returns {Function} Returns `func`.
+     */
+    var setData = (function() {
+      var count = 0,
+          lastCalled = 0;
+
+      return function(key, value) {
+        var stamp = now(),
+            remaining = HOT_SPAN - (stamp - lastCalled);
+
+        lastCalled = stamp;
+        if (remaining > 0) {
+          if (++count >= HOT_COUNT) {
+            return key;
+          }
+        } else {
+          count = 0;
+        }
+        return baseSetData(key, value);
+      };
+    }());
+
+    /**
+     * Sets the `toString` method of `wrapper` to mimic the source of `reference`
+     * with wrapper details in a comment at the top of the source body.
+     *
+     * @private
+     * @param {Function} wrapper The function to modify.
+     * @param {Function} reference The reference function.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @returns {Function} Returns `wrapper`.
+     */
+    var setWrapToString = !defineProperty ? identity : function(wrapper, reference, bitmask) {
+      var source = (reference + '');
+      return defineProperty(wrapper, 'toString', {
+        'configurable': true,
+        'enumerable': false,
+        'value': constant(insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)))
+      });
+    };
+
+    /**
+     * Converts `string` to a property path array.
+     *
+     * @private
+     * @param {string} string The string to convert.
+     * @returns {Array} Returns the property path array.
+     */
+    var stringToPath = memoize(function(string) {
+      string = toString(string);
+
+      var result = [];
+      if (reLeadingDot.test(string)) {
+        result.push('');
+      }
+      string.replace(rePropName, function(match, number, quote, string) {
+        result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
+      });
+      return result;
+    });
+
+    /**
+     * Converts `value` to a string key if it's not a string or symbol.
+     *
+     * @private
+     * @param {*} value The value to inspect.
+     * @returns {string|symbol} Returns the key.
+     */
+    function toKey(value) {
+      if (typeof value == 'string' || isSymbol(value)) {
+        return value;
+      }
+      var result = (value + '');
+      return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+    }
+
+    /**
+     * Converts `func` to its source code.
+     *
+     * @private
+     * @param {Function} func The function to process.
+     * @returns {string} Returns the source code.
+     */
+    function toSource(func) {
+      if (func != null) {
+        try {
+          return funcToString.call(func);
+        } catch (e) {}
+        try {
+          return (func + '');
+        } catch (e) {}
+      }
+      return '';
+    }
+
+    /**
+     * Updates wrapper `details` based on `bitmask` flags.
+     *
+     * @private
+     * @returns {Array} details The details to modify.
+     * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+     * @returns {Array} Returns `details`.
+     */
+    function updateWrapDetails(details, bitmask) {
+      arrayEach(wrapFlags, function(pair) {
+        var value = '_.' + pair[0];
+        if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {
+          details.push(value);
+        }
+      });
+      return details.sort();
+    }
+
+    /**
+     * Creates a clone of `wrapper`.
+     *
+     * @private
+     * @param {Object} wrapper The wrapper to clone.
+     * @returns {Object} Returns the cloned wrapper.
+     */
+    function wrapperClone(wrapper) {
+      if (wrapper instanceof LazyWrapper) {
+        return wrapper.clone();
+      }
+      var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
+      result.__actions__ = copyArray(wrapper.__actions__);
+      result.__index__  = wrapper.__index__;
+      result.__values__ = wrapper.__values__;
+      return result;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array of elements split into groups the length of `size`.
+     * If `array` can't be split evenly, the final chunk will be the remaining
+     * elements.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to process.
+     * @param {number} [size=1] The length of each chunk
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the new array of chunks.
+     * @example
+     *
+     * _.chunk(['a', 'b', 'c', 'd'], 2);
+     * // => [['a', 'b'], ['c', 'd']]
+     *
+     * _.chunk(['a', 'b', 'c', 'd'], 3);
+     * // => [['a', 'b', 'c'], ['d']]
+     */
+    function chunk(array, size, guard) {
+      if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
+        size = 1;
+      } else {
+        size = nativeMax(toInteger(size), 0);
+      }
+      var length = array ? array.length : 0;
+      if (!length || size < 1) {
+        return [];
+      }
+      var index = 0,
+          resIndex = 0,
+          result = Array(nativeCeil(length / size));
+
+      while (index < length) {
+        result[resIndex++] = baseSlice(array, index, (index += size));
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array with all falsey values removed. The values `false`, `null`,
+     * `0`, `""`, `undefined`, and `NaN` are falsey.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to compact.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.compact([0, 1, false, 2, '', 3]);
+     * // => [1, 2, 3]
+     */
+    function compact(array) {
+      var index = -1,
+          length = array ? array.length : 0,
+          resIndex = 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+        if (value) {
+          result[resIndex++] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates a new array concatenating `array` with any additional arrays
+     * and/or values.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to concatenate.
+     * @param {...*} [values] The values to concatenate.
+     * @returns {Array} Returns the new concatenated array.
+     * @example
+     *
+     * var array = [1];
+     * var other = _.concat(array, 2, [3], [[4]]);
+     *
+     * console.log(other);
+     * // => [1, 2, 3, [4]]
+     *
+     * console.log(array);
+     * // => [1]
+     */
+    function concat() {
+      var length = arguments.length,
+          args = Array(length ? length - 1 : 0),
+          array = arguments[0],
+          index = length;
+
+      while (index--) {
+        args[index - 1] = arguments[index];
+      }
+      return length
+        ? arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1))
+        : [];
+    }
+
+    /**
+     * Creates an array of `array` values not included in the other given arrays
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons. The order of result values is determined by the
+     * order they occur in the first array.
+     *
+     * **Note:** Unlike `_.pullAll`, this method returns a new array.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {...Array} [values] The values to exclude.
+     * @returns {Array} Returns the new array of filtered values.
+     * @see _.without, _.xor
+     * @example
+     *
+     * _.difference([2, 1], [2, 3]);
+     * // => [1]
+     */
+    var difference = baseRest(function(array, values) {
+      return isArrayLikeObject(array)
+        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
+        : [];
+    });
+
+    /**
+     * This method is like `_.difference` except that it accepts `iteratee` which
+     * is invoked for each element of `array` and `values` to generate the criterion
+     * by which they're compared. Result values are chosen from the first array.
+     * The iteratee is invoked with one argument: (value).
+     *
+     * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {...Array} [values] The values to exclude.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+     * // => [1.2]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
+     * // => [{ 'x': 2 }]
+     */
+    var differenceBy = baseRest(function(array, values) {
+      var iteratee = last(values);
+      if (isArrayLikeObject(iteratee)) {
+        iteratee = undefined;
+      }
+      return isArrayLikeObject(array)
+        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))
+        : [];
+    });
+
+    /**
+     * This method is like `_.difference` except that it accepts `comparator`
+     * which is invoked to compare elements of `array` to `values`. Result values
+     * are chosen from the first array. The comparator is invoked with two arguments:
+     * (arrVal, othVal).
+     *
+     * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {...Array} [values] The values to exclude.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+     *
+     * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
+     * // => [{ 'x': 2, 'y': 1 }]
+     */
+    var differenceWith = baseRest(function(array, values) {
+      var comparator = last(values);
+      if (isArrayLikeObject(comparator)) {
+        comparator = undefined;
+      }
+      return isArrayLikeObject(array)
+        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
+        : [];
+    });
+
+    /**
+     * Creates a slice of `array` with `n` elements dropped from the beginning.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.5.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to drop.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.drop([1, 2, 3]);
+     * // => [2, 3]
+     *
+     * _.drop([1, 2, 3], 2);
+     * // => [3]
+     *
+     * _.drop([1, 2, 3], 5);
+     * // => []
+     *
+     * _.drop([1, 2, 3], 0);
+     * // => [1, 2, 3]
+     */
+    function drop(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      n = (guard || n === undefined) ? 1 : toInteger(n);
+      return baseSlice(array, n < 0 ? 0 : n, length);
+    }
+
+    /**
+     * Creates a slice of `array` with `n` elements dropped from the end.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to drop.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.dropRight([1, 2, 3]);
+     * // => [1, 2]
+     *
+     * _.dropRight([1, 2, 3], 2);
+     * // => [1]
+     *
+     * _.dropRight([1, 2, 3], 5);
+     * // => []
+     *
+     * _.dropRight([1, 2, 3], 0);
+     * // => [1, 2, 3]
+     */
+    function dropRight(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      n = (guard || n === undefined) ? 1 : toInteger(n);
+      n = length - n;
+      return baseSlice(array, 0, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` excluding elements dropped from the end.
+     * Elements are dropped until `predicate` returns falsey. The predicate is
+     * invoked with three arguments: (value, index, array).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * _.dropRightWhile(users, function(o) { return !o.active; });
+     * // => objects for ['barney']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
+     * // => objects for ['barney', 'fred']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.dropRightWhile(users, ['active', false]);
+     * // => objects for ['barney']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.dropRightWhile(users, 'active');
+     * // => objects for ['barney', 'fred', 'pebbles']
+     */
+    function dropRightWhile(array, predicate) {
+      return (array && array.length)
+        ? baseWhile(array, getIteratee(predicate, 3), true, true)
+        : [];
+    }
+
+    /**
+     * Creates a slice of `array` excluding elements dropped from the beginning.
+     * Elements are dropped until `predicate` returns falsey. The predicate is
+     * invoked with three arguments: (value, index, array).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * _.dropWhile(users, function(o) { return !o.active; });
+     * // => objects for ['pebbles']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.dropWhile(users, { 'user': 'barney', 'active': false });
+     * // => objects for ['fred', 'pebbles']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.dropWhile(users, ['active', false]);
+     * // => objects for ['pebbles']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.dropWhile(users, 'active');
+     * // => objects for ['barney', 'fred', 'pebbles']
+     */
+    function dropWhile(array, predicate) {
+      return (array && array.length)
+        ? baseWhile(array, getIteratee(predicate, 3), true)
+        : [];
+    }
+
+    /**
+     * Fills elements of `array` with `value` from `start` up to, but not
+     * including, `end`.
+     *
+     * **Note:** This method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.2.0
+     * @category Array
+     * @param {Array} array The array to fill.
+     * @param {*} value The value to fill `array` with.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3];
+     *
+     * _.fill(array, 'a');
+     * console.log(array);
+     * // => ['a', 'a', 'a']
+     *
+     * _.fill(Array(3), 2);
+     * // => [2, 2, 2]
+     *
+     * _.fill([4, 6, 8, 10], '*', 1, 3);
+     * // => [4, '*', '*', 10]
+     */
+    function fill(array, value, start, end) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
+        start = 0;
+        end = length;
+      }
+      return baseFill(array, value, start, end);
+    }
+
+    /**
+     * This method is like `_.find` except that it returns the index of the first
+     * element `predicate` returns truthy for instead of the element itself.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.1.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * _.findIndex(users, function(o) { return o.user == 'barney'; });
+     * // => 0
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.findIndex(users, { 'user': 'fred', 'active': false });
+     * // => 1
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.findIndex(users, ['active', false]);
+     * // => 0
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.findIndex(users, 'active');
+     * // => 2
+     */
+    function findIndex(array, predicate, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      var index = fromIndex == null ? 0 : toInteger(fromIndex);
+      if (index < 0) {
+        index = nativeMax(length + index, 0);
+      }
+      return baseFindIndex(array, getIteratee(predicate, 3), index);
+    }
+
+    /**
+     * This method is like `_.findIndex` except that it iterates over elements
+     * of `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @param {number} [fromIndex=array.length-1] The index to search from.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
+     * // => 2
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
+     * // => 0
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.findLastIndex(users, ['active', false]);
+     * // => 2
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.findLastIndex(users, 'active');
+     * // => 0
+     */
+    function findLastIndex(array, predicate, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      var index = length - 1;
+      if (fromIndex !== undefined) {
+        index = toInteger(fromIndex);
+        index = fromIndex < 0
+          ? nativeMax(length + index, 0)
+          : nativeMin(index, length - 1);
+      }
+      return baseFindIndex(array, getIteratee(predicate, 3), index, true);
+    }
+
+    /**
+     * Flattens `array` a single level deep.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to flatten.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * _.flatten([1, [2, [3, [4]], 5]]);
+     * // => [1, 2, [3, [4]], 5]
+     */
+    function flatten(array) {
+      var length = array ? array.length : 0;
+      return length ? baseFlatten(array, 1) : [];
+    }
+
+    /**
+     * Recursively flattens `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to flatten.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * _.flattenDeep([1, [2, [3, [4]], 5]]);
+     * // => [1, 2, 3, 4, 5]
+     */
+    function flattenDeep(array) {
+      var length = array ? array.length : 0;
+      return length ? baseFlatten(array, INFINITY) : [];
+    }
+
+    /**
+     * Recursively flatten `array` up to `depth` times.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.4.0
+     * @category Array
+     * @param {Array} array The array to flatten.
+     * @param {number} [depth=1] The maximum recursion depth.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * var array = [1, [2, [3, [4]], 5]];
+     *
+     * _.flattenDepth(array, 1);
+     * // => [1, 2, [3, [4]], 5]
+     *
+     * _.flattenDepth(array, 2);
+     * // => [1, 2, 3, [4], 5]
+     */
+    function flattenDepth(array, depth) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      depth = depth === undefined ? 1 : toInteger(depth);
+      return baseFlatten(array, depth);
+    }
+
+    /**
+     * The inverse of `_.toPairs`; this method returns an object composed
+     * from key-value `pairs`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} pairs The key-value pairs.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * _.fromPairs([['a', 1], ['b', 2]]);
+     * // => { 'a': 1, 'b': 2 }
+     */
+    function fromPairs(pairs) {
+      var index = -1,
+          length = pairs ? pairs.length : 0,
+          result = {};
+
+      while (++index < length) {
+        var pair = pairs[index];
+        result[pair[0]] = pair[1];
+      }
+      return result;
+    }
+
+    /**
+     * Gets the first element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @alias first
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {*} Returns the first element of `array`.
+     * @example
+     *
+     * _.head([1, 2, 3]);
+     * // => 1
+     *
+     * _.head([]);
+     * // => undefined
+     */
+    function head(array) {
+      return (array && array.length) ? array[0] : undefined;
+    }
+
+    /**
+     * Gets the index at which the first occurrence of `value` is found in `array`
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons. If `fromIndex` is negative, it's used as the
+     * offset from the end of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.indexOf([1, 2, 1, 2], 2);
+     * // => 1
+     *
+     * // Search from the `fromIndex`.
+     * _.indexOf([1, 2, 1, 2], 2, 2);
+     * // => 3
+     */
+    function indexOf(array, value, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      var index = fromIndex == null ? 0 : toInteger(fromIndex);
+      if (index < 0) {
+        index = nativeMax(length + index, 0);
+      }
+      return baseIndexOf(array, value, index);
+    }
+
+    /**
+     * Gets all but the last element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.initial([1, 2, 3]);
+     * // => [1, 2]
+     */
+    function initial(array) {
+      return dropRight(array, 1);
+    }
+
+    /**
+     * Creates an array of unique values that are included in all given arrays
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons. The order of result values is determined by the
+     * order they occur in the first array.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of intersecting values.
+     * @example
+     *
+     * _.intersection([2, 1], [2, 3]);
+     * // => [2]
+     */
+    var intersection = baseRest(function(arrays) {
+      var mapped = arrayMap(arrays, castArrayLikeObject);
+      return (mapped.length && mapped[0] === arrays[0])
+        ? baseIntersection(mapped)
+        : [];
+    });
+
+    /**
+     * This method is like `_.intersection` except that it accepts `iteratee`
+     * which is invoked for each element of each `arrays` to generate the criterion
+     * by which they're compared. Result values are chosen from the first array.
+     * The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {Array} Returns the new array of intersecting values.
+     * @example
+     *
+     * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+     * // => [2.1]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }]
+     */
+    var intersectionBy = baseRest(function(arrays) {
+      var iteratee = last(arrays),
+          mapped = arrayMap(arrays, castArrayLikeObject);
+
+      if (iteratee === last(mapped)) {
+        iteratee = undefined;
+      } else {
+        mapped.pop();
+      }
+      return (mapped.length && mapped[0] === arrays[0])
+        ? baseIntersection(mapped, getIteratee(iteratee, 2))
+        : [];
+    });
+
+    /**
+     * This method is like `_.intersection` except that it accepts `comparator`
+     * which is invoked to compare elements of `arrays`. Result values are chosen
+     * from the first array. The comparator is invoked with two arguments:
+     * (arrVal, othVal).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of intersecting values.
+     * @example
+     *
+     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+     *
+     * _.intersectionWith(objects, others, _.isEqual);
+     * // => [{ 'x': 1, 'y': 2 }]
+     */
+    var intersectionWith = baseRest(function(arrays) {
+      var comparator = last(arrays),
+          mapped = arrayMap(arrays, castArrayLikeObject);
+
+      if (comparator === last(mapped)) {
+        comparator = undefined;
+      } else {
+        mapped.pop();
+      }
+      return (mapped.length && mapped[0] === arrays[0])
+        ? baseIntersection(mapped, undefined, comparator)
+        : [];
+    });
+
+    /**
+     * Converts all elements in `array` into a string separated by `separator`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to convert.
+     * @param {string} [separator=','] The element separator.
+     * @returns {string} Returns the joined string.
+     * @example
+     *
+     * _.join(['a', 'b', 'c'], '~');
+     * // => 'a~b~c'
+     */
+    function join(array, separator) {
+      return array ? nativeJoin.call(array, separator) : '';
+    }
+
+    /**
+     * Gets the last element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {*} Returns the last element of `array`.
+     * @example
+     *
+     * _.last([1, 2, 3]);
+     * // => 3
+     */
+    function last(array) {
+      var length = array ? array.length : 0;
+      return length ? array[length - 1] : undefined;
+    }
+
+    /**
+     * This method is like `_.indexOf` except that it iterates over elements of
+     * `array` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=array.length-1] The index to search from.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.lastIndexOf([1, 2, 1, 2], 2);
+     * // => 3
+     *
+     * // Search from the `fromIndex`.
+     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
+     * // => 1
+     */
+    function lastIndexOf(array, value, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      var index = length;
+      if (fromIndex !== undefined) {
+        index = toInteger(fromIndex);
+        index = (
+          index < 0
+            ? nativeMax(length + index, 0)
+            : nativeMin(index, length - 1)
+        ) + 1;
+      }
+      if (value !== value) {
+        return baseFindIndex(array, baseIsNaN, index - 1, true);
+      }
+      while (index--) {
+        if (array[index] === value) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Gets the element at index `n` of `array`. If `n` is negative, the nth
+     * element from the end is returned.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.11.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=0] The index of the element to return.
+     * @returns {*} Returns the nth element of `array`.
+     * @example
+     *
+     * var array = ['a', 'b', 'c', 'd'];
+     *
+     * _.nth(array, 1);
+     * // => 'b'
+     *
+     * _.nth(array, -2);
+     * // => 'c';
+     */
+    function nth(array, n) {
+      return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;
+    }
+
+    /**
+     * Removes all given values from `array` using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
+     * to remove elements from an array by predicate.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {...*} [values] The values to remove.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+     *
+     * _.pull(array, 'a', 'c');
+     * console.log(array);
+     * // => ['b', 'b']
+     */
+    var pull = baseRest(pullAll);
+
+    /**
+     * This method is like `_.pull` except that it accepts an array of values to remove.
+     *
+     * **Note:** Unlike `_.difference`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {Array} values The values to remove.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+     *
+     * _.pullAll(array, ['a', 'c']);
+     * console.log(array);
+     * // => ['b', 'b']
+     */
+    function pullAll(array, values) {
+      return (array && array.length && values && values.length)
+        ? basePullAll(array, values)
+        : array;
+    }
+
+    /**
+     * This method is like `_.pullAll` except that it accepts `iteratee` which is
+     * invoked for each element of `array` and `values` to generate the criterion
+     * by which they're compared. The iteratee is invoked with one argument: (value).
+     *
+     * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {Array} values The values to remove.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
+     *
+     * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
+     * console.log(array);
+     * // => [{ 'x': 2 }]
+     */
+    function pullAllBy(array, values, iteratee) {
+      return (array && array.length && values && values.length)
+        ? basePullAll(array, values, getIteratee(iteratee, 2))
+        : array;
+    }
+
+    /**
+     * This method is like `_.pullAll` except that it accepts `comparator` which
+     * is invoked to compare elements of `array` to `values`. The comparator is
+     * invoked with two arguments: (arrVal, othVal).
+     *
+     * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.6.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {Array} values The values to remove.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
+     *
+     * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
+     * console.log(array);
+     * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
+     */
+    function pullAllWith(array, values, comparator) {
+      return (array && array.length && values && values.length)
+        ? basePullAll(array, values, undefined, comparator)
+        : array;
+    }
+
+    /**
+     * Removes elements from `array` corresponding to `indexes` and returns an
+     * array of removed elements.
+     *
+     * **Note:** Unlike `_.at`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {...(number|number[])} [indexes] The indexes of elements to remove.
+     * @returns {Array} Returns the new array of removed elements.
+     * @example
+     *
+     * var array = ['a', 'b', 'c', 'd'];
+     * var pulled = _.pullAt(array, [1, 3]);
+     *
+     * console.log(array);
+     * // => ['a', 'c']
+     *
+     * console.log(pulled);
+     * // => ['b', 'd']
+     */
+    var pullAt = baseRest(function(array, indexes) {
+      indexes = baseFlatten(indexes, 1);
+
+      var length = array ? array.length : 0,
+          result = baseAt(array, indexes);
+
+      basePullAt(array, arrayMap(indexes, function(index) {
+        return isIndex(index, length) ? +index : index;
+      }).sort(compareAscending));
+
+      return result;
+    });
+
+    /**
+     * Removes all elements from `array` that `predicate` returns truthy for
+     * and returns an array of the removed elements. The predicate is invoked
+     * with three arguments: (value, index, array).
+     *
+     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
+     * to pull elements from an array by value.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the new array of removed elements.
+     * @example
+     *
+     * var array = [1, 2, 3, 4];
+     * var evens = _.remove(array, function(n) {
+     *   return n % 2 == 0;
+     * });
+     *
+     * console.log(array);
+     * // => [1, 3]
+     *
+     * console.log(evens);
+     * // => [2, 4]
+     */
+    function remove(array, predicate) {
+      var result = [];
+      if (!(array && array.length)) {
+        return result;
+      }
+      var index = -1,
+          indexes = [],
+          length = array.length;
+
+      predicate = getIteratee(predicate, 3);
+      while (++index < length) {
+        var value = array[index];
+        if (predicate(value, index, array)) {
+          result.push(value);
+          indexes.push(index);
+        }
+      }
+      basePullAt(array, indexes);
+      return result;
+    }
+
+    /**
+     * Reverses `array` so that the first element becomes the last, the second
+     * element becomes the second to last, and so on.
+     *
+     * **Note:** This method mutates `array` and is based on
+     * [`Array#reverse`](https://mdn.io/Array/reverse).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3];
+     *
+     * _.reverse(array);
+     * // => [3, 2, 1]
+     *
+     * console.log(array);
+     * // => [3, 2, 1]
+     */
+    function reverse(array) {
+      return array ? nativeReverse.call(array) : array;
+    }
+
+    /**
+     * Creates a slice of `array` from `start` up to, but not including, `end`.
+     *
+     * **Note:** This method is used instead of
+     * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
+     * returned.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to slice.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function slice(array, start, end) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
+        start = 0;
+        end = length;
+      }
+      else {
+        start = start == null ? 0 : toInteger(start);
+        end = end === undefined ? length : toInteger(end);
+      }
+      return baseSlice(array, start, end);
+    }
+
+    /**
+     * Uses a binary search to determine the lowest index at which `value`
+     * should be inserted into `array` in order to maintain its sort order.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedIndex([30, 50], 40);
+     * // => 1
+     */
+    function sortedIndex(array, value) {
+      return baseSortedIndex(array, value);
+    }
+
+    /**
+     * This method is like `_.sortedIndex` except that it accepts `iteratee`
+     * which is invoked for `value` and each element of `array` to compute their
+     * sort ranking. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * var objects = [{ 'x': 4 }, { 'x': 5 }];
+     *
+     * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+     * // => 0
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
+     * // => 0
+     */
+    function sortedIndexBy(array, value, iteratee) {
+      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
+    }
+
+    /**
+     * This method is like `_.indexOf` except that it performs a binary
+     * search on a sorted `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
+     * // => 1
+     */
+    function sortedIndexOf(array, value) {
+      var length = array ? array.length : 0;
+      if (length) {
+        var index = baseSortedIndex(array, value);
+        if (index < length && eq(array[index], value)) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * This method is like `_.sortedIndex` except that it returns the highest
+     * index at which `value` should be inserted into `array` in order to
+     * maintain its sort order.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
+     * // => 4
+     */
+    function sortedLastIndex(array, value) {
+      return baseSortedIndex(array, value, true);
+    }
+
+    /**
+     * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
+     * which is invoked for `value` and each element of `array` to compute their
+     * sort ranking. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * var objects = [{ 'x': 4 }, { 'x': 5 }];
+     *
+     * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+     * // => 1
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
+     * // => 1
+     */
+    function sortedLastIndexBy(array, value, iteratee) {
+      return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
+    }
+
+    /**
+     * This method is like `_.lastIndexOf` except that it performs a binary
+     * search on a sorted `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
+     * // => 3
+     */
+    function sortedLastIndexOf(array, value) {
+      var length = array ? array.length : 0;
+      if (length) {
+        var index = baseSortedIndex(array, value, true) - 1;
+        if (eq(array[index], value)) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * This method is like `_.uniq` except that it's designed and optimized
+     * for sorted arrays.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @returns {Array} Returns the new duplicate free array.
+     * @example
+     *
+     * _.sortedUniq([1, 1, 2]);
+     * // => [1, 2]
+     */
+    function sortedUniq(array) {
+      return (array && array.length)
+        ? baseSortedUniq(array)
+        : [];
+    }
+
+    /**
+     * This method is like `_.uniqBy` except that it's designed and optimized
+     * for sorted arrays.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {Function} [iteratee] The iteratee invoked per element.
+     * @returns {Array} Returns the new duplicate free array.
+     * @example
+     *
+     * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
+     * // => [1.1, 2.3]
+     */
+    function sortedUniqBy(array, iteratee) {
+      return (array && array.length)
+        ? baseSortedUniq(array, getIteratee(iteratee, 2))
+        : [];
+    }
+
+    /**
+     * Gets all but the first element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.tail([1, 2, 3]);
+     * // => [2, 3]
+     */
+    function tail(array) {
+      return drop(array, 1);
+    }
+
+    /**
+     * Creates a slice of `array` with `n` elements taken from the beginning.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to take.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.take([1, 2, 3]);
+     * // => [1]
+     *
+     * _.take([1, 2, 3], 2);
+     * // => [1, 2]
+     *
+     * _.take([1, 2, 3], 5);
+     * // => [1, 2, 3]
+     *
+     * _.take([1, 2, 3], 0);
+     * // => []
+     */
+    function take(array, n, guard) {
+      if (!(array && array.length)) {
+        return [];
+      }
+      n = (guard || n === undefined) ? 1 : toInteger(n);
+      return baseSlice(array, 0, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` with `n` elements taken from the end.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to take.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.takeRight([1, 2, 3]);
+     * // => [3]
+     *
+     * _.takeRight([1, 2, 3], 2);
+     * // => [2, 3]
+     *
+     * _.takeRight([1, 2, 3], 5);
+     * // => [1, 2, 3]
+     *
+     * _.takeRight([1, 2, 3], 0);
+     * // => []
+     */
+    function takeRight(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      n = (guard || n === undefined) ? 1 : toInteger(n);
+      n = length - n;
+      return baseSlice(array, n < 0 ? 0 : n, length);
+    }
+
+    /**
+     * Creates a slice of `array` with elements taken from the end. Elements are
+     * taken until `predicate` returns falsey. The predicate is invoked with
+     * three arguments: (value, index, array).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * _.takeRightWhile(users, function(o) { return !o.active; });
+     * // => objects for ['fred', 'pebbles']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
+     * // => objects for ['pebbles']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.takeRightWhile(users, ['active', false]);
+     * // => objects for ['fred', 'pebbles']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.takeRightWhile(users, 'active');
+     * // => []
+     */
+    function takeRightWhile(array, predicate) {
+      return (array && array.length)
+        ? baseWhile(array, getIteratee(predicate, 3), false, true)
+        : [];
+    }
+
+    /**
+     * Creates a slice of `array` with elements taken from the beginning. Elements
+     * are taken until `predicate` returns falsey. The predicate is invoked with
+     * three arguments: (value, index, array).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false},
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * _.takeWhile(users, function(o) { return !o.active; });
+     * // => objects for ['barney', 'fred']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.takeWhile(users, { 'user': 'barney', 'active': false });
+     * // => objects for ['barney']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.takeWhile(users, ['active', false]);
+     * // => objects for ['barney', 'fred']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.takeWhile(users, 'active');
+     * // => []
+     */
+    function takeWhile(array, predicate) {
+      return (array && array.length)
+        ? baseWhile(array, getIteratee(predicate, 3))
+        : [];
+    }
+
+    /**
+     * Creates an array of unique values, in order, from all given arrays using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of combined values.
+     * @example
+     *
+     * _.union([2], [1, 2]);
+     * // => [2, 1]
+     */
+    var union = baseRest(function(arrays) {
+      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
+    });
+
+    /**
+     * This method is like `_.union` except that it accepts `iteratee` which is
+     * invoked for each element of each `arrays` to generate the criterion by
+     * which uniqueness is computed. Result values are chosen from the first
+     * array in which the value occurs. The iteratee is invoked with one argument:
+     * (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {Array} Returns the new array of combined values.
+     * @example
+     *
+     * _.unionBy([2.1], [1.2, 2.3], Math.floor);
+     * // => [2.1, 1.2]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }, { 'x': 2 }]
+     */
+    var unionBy = baseRest(function(arrays) {
+      var iteratee = last(arrays);
+      if (isArrayLikeObject(iteratee)) {
+        iteratee = undefined;
+      }
+      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
+    });
+
+    /**
+     * This method is like `_.union` except that it accepts `comparator` which
+     * is invoked to compare elements of `arrays`. Result values are chosen from
+     * the first array in which the value occurs. The comparator is invoked
+     * with two arguments: (arrVal, othVal).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of combined values.
+     * @example
+     *
+     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+     *
+     * _.unionWith(objects, others, _.isEqual);
+     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+     */
+    var unionWith = baseRest(function(arrays) {
+      var comparator = last(arrays);
+      if (isArrayLikeObject(comparator)) {
+        comparator = undefined;
+      }
+      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
+    });
+
+    /**
+     * Creates a duplicate-free version of an array, using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons, in which only the first occurrence of each
+     * element is kept.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @returns {Array} Returns the new duplicate free array.
+     * @example
+     *
+     * _.uniq([2, 1, 2]);
+     * // => [2, 1]
+     */
+    function uniq(array) {
+      return (array && array.length)
+        ? baseUniq(array)
+        : [];
+    }
+
+    /**
+     * This method is like `_.uniq` except that it accepts `iteratee` which is
+     * invoked for each element in `array` to generate the criterion by which
+     * uniqueness is computed. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {Array} Returns the new duplicate free array.
+     * @example
+     *
+     * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
+     * // => [2.1, 1.2]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }, { 'x': 2 }]
+     */
+    function uniqBy(array, iteratee) {
+      return (array && array.length)
+        ? baseUniq(array, getIteratee(iteratee, 2))
+        : [];
+    }
+
+    /**
+     * This method is like `_.uniq` except that it accepts `comparator` which
+     * is invoked to compare elements of `array`. The comparator is invoked with
+     * two arguments: (arrVal, othVal).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new duplicate free array.
+     * @example
+     *
+     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
+     *
+     * _.uniqWith(objects, _.isEqual);
+     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
+     */
+    function uniqWith(array, comparator) {
+      return (array && array.length)
+        ? baseUniq(array, undefined, comparator)
+        : [];
+    }
+
+    /**
+     * This method is like `_.zip` except that it accepts an array of grouped
+     * elements and creates an array regrouping the elements to their pre-zip
+     * configuration.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.2.0
+     * @category Array
+     * @param {Array} array The array of grouped elements to process.
+     * @returns {Array} Returns the new array of regrouped elements.
+     * @example
+     *
+     * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
+     * // => [['a', 1, true], ['b', 2, false]]
+     *
+     * _.unzip(zipped);
+     * // => [['a', 'b'], [1, 2], [true, false]]
+     */
+    function unzip(array) {
+      if (!(array && array.length)) {
+        return [];
+      }
+      var length = 0;
+      array = arrayFilter(array, function(group) {
+        if (isArrayLikeObject(group)) {
+          length = nativeMax(group.length, length);
+          return true;
+        }
+      });
+      return baseTimes(length, function(index) {
+        return arrayMap(array, baseProperty(index));
+      });
+    }
+
+    /**
+     * This method is like `_.unzip` except that it accepts `iteratee` to specify
+     * how regrouped values should be combined. The iteratee is invoked with the
+     * elements of each group: (...group).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.8.0
+     * @category Array
+     * @param {Array} array The array of grouped elements to process.
+     * @param {Function} [iteratee=_.identity] The function to combine
+     *  regrouped values.
+     * @returns {Array} Returns the new array of regrouped elements.
+     * @example
+     *
+     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
+     * // => [[1, 10, 100], [2, 20, 200]]
+     *
+     * _.unzipWith(zipped, _.add);
+     * // => [3, 30, 300]
+     */
+    function unzipWith(array, iteratee) {
+      if (!(array && array.length)) {
+        return [];
+      }
+      var result = unzip(array);
+      if (iteratee == null) {
+        return result;
+      }
+      return arrayMap(result, function(group) {
+        return apply(iteratee, undefined, group);
+      });
+    }
+
+    /**
+     * Creates an array excluding all given values using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * **Note:** Unlike `_.pull`, this method returns a new array.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {...*} [values] The values to exclude.
+     * @returns {Array} Returns the new array of filtered values.
+     * @see _.difference, _.xor
+     * @example
+     *
+     * _.without([2, 1, 2, 3], 1, 2);
+     * // => [3]
+     */
+    var without = baseRest(function(array, values) {
+      return isArrayLikeObject(array)
+        ? baseDifference(array, values)
+        : [];
+    });
+
+    /**
+     * Creates an array of unique values that is the
+     * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
+     * of the given arrays. The order of result values is determined by the order
+     * they occur in the arrays.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.4.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of filtered values.
+     * @see _.difference, _.without
+     * @example
+     *
+     * _.xor([2, 1], [2, 3]);
+     * // => [1, 3]
+     */
+    var xor = baseRest(function(arrays) {
+      return baseXor(arrayFilter(arrays, isArrayLikeObject));
+    });
+
+    /**
+     * This method is like `_.xor` except that it accepts `iteratee` which is
+     * invoked for each element of each `arrays` to generate the criterion by
+     * which by which they're compared. The iteratee is invoked with one argument:
+     * (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee invoked per element.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+     * // => [1.2, 3.4]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 2 }]
+     */
+    var xorBy = baseRest(function(arrays) {
+      var iteratee = last(arrays);
+      if (isArrayLikeObject(iteratee)) {
+        iteratee = undefined;
+      }
+      return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
+    });
+
+    /**
+     * This method is like `_.xor` except that it accepts `comparator` which is
+     * invoked to compare elements of `arrays`. The comparator is invoked with
+     * two arguments: (arrVal, othVal).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @param {Function} [comparator] The comparator invoked per element.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+     *
+     * _.xorWith(objects, others, _.isEqual);
+     * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+     */
+    var xorWith = baseRest(function(arrays) {
+      var comparator = last(arrays);
+      if (isArrayLikeObject(comparator)) {
+        comparator = undefined;
+      }
+      return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
+    });
+
+    /**
+     * Creates an array of grouped elements, the first of which contains the
+     * first elements of the given arrays, the second of which contains the
+     * second elements of the given arrays, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to process.
+     * @returns {Array} Returns the new array of grouped elements.
+     * @example
+     *
+     * _.zip(['a', 'b'], [1, 2], [true, false]);
+     * // => [['a', 1, true], ['b', 2, false]]
+     */
+    var zip = baseRest(unzip);
+
+    /**
+     * This method is like `_.fromPairs` except that it accepts two arrays,
+     * one of property identifiers and one of corresponding values.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.4.0
+     * @category Array
+     * @param {Array} [props=[]] The property identifiers.
+     * @param {Array} [values=[]] The property values.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * _.zipObject(['a', 'b'], [1, 2]);
+     * // => { 'a': 1, 'b': 2 }
+     */
+    function zipObject(props, values) {
+      return baseZipObject(props || [], values || [], assignValue);
+    }
+
+    /**
+     * This method is like `_.zipObject` except that it supports property paths.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.1.0
+     * @category Array
+     * @param {Array} [props=[]] The property identifiers.
+     * @param {Array} [values=[]] The property values.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
+     * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
+     */
+    function zipObjectDeep(props, values) {
+      return baseZipObject(props || [], values || [], baseSet);
+    }
+
+    /**
+     * This method is like `_.zip` except that it accepts `iteratee` to specify
+     * how grouped values should be combined. The iteratee is invoked with the
+     * elements of each group: (...group).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.8.0
+     * @category Array
+     * @param {...Array} [arrays] The arrays to process.
+     * @param {Function} [iteratee=_.identity] The function to combine grouped values.
+     * @returns {Array} Returns the new array of grouped elements.
+     * @example
+     *
+     * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
+     *   return a + b + c;
+     * });
+     * // => [111, 222]
+     */
+    var zipWith = baseRest(function(arrays) {
+      var length = arrays.length,
+          iteratee = length > 1 ? arrays[length - 1] : undefined;
+
+      iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
+      return unzipWith(arrays, iteratee);
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` wrapper instance that wraps `value` with explicit method
+     * chain sequences enabled. The result of such sequences must be unwrapped
+     * with `_#value`.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.3.0
+     * @category Seq
+     * @param {*} value The value to wrap.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36 },
+     *   { 'user': 'fred',    'age': 40 },
+     *   { 'user': 'pebbles', 'age': 1 }
+     * ];
+     *
+     * var youngest = _
+     *   .chain(users)
+     *   .sortBy('age')
+     *   .map(function(o) {
+     *     return o.user + ' is ' + o.age;
+     *   })
+     *   .head()
+     *   .value();
+     * // => 'pebbles is 1'
+     */
+    function chain(value) {
+      var result = lodash(value);
+      result.__chain__ = true;
+      return result;
+    }
+
+    /**
+     * This method invokes `interceptor` and returns `value`. The interceptor
+     * is invoked with one argument; (value). The purpose of this method is to
+     * "tap into" a method chain sequence in order to modify intermediate results.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Seq
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * _([1, 2, 3])
+     *  .tap(function(array) {
+     *    // Mutate input array.
+     *    array.pop();
+     *  })
+     *  .reverse()
+     *  .value();
+     * // => [2, 1]
+     */
+    function tap(value, interceptor) {
+      interceptor(value);
+      return value;
+    }
+
+    /**
+     * This method is like `_.tap` except that it returns the result of `interceptor`.
+     * The purpose of this method is to "pass thru" values replacing intermediate
+     * results in a method chain sequence.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Seq
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @returns {*} Returns the result of `interceptor`.
+     * @example
+     *
+     * _('  abc  ')
+     *  .chain()
+     *  .trim()
+     *  .thru(function(value) {
+     *    return [value];
+     *  })
+     *  .value();
+     * // => ['abc']
+     */
+    function thru(value, interceptor) {
+      return interceptor(value);
+    }
+
+    /**
+     * This method is the wrapper version of `_.at`.
+     *
+     * @name at
+     * @memberOf _
+     * @since 1.0.0
+     * @category Seq
+     * @param {...(string|string[])} [paths] The property paths of elements to pick.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+     *
+     * _(object).at(['a[0].b.c', 'a[1]']).value();
+     * // => [3, 4]
+     */
+    var wrapperAt = baseRest(function(paths) {
+      paths = baseFlatten(paths, 1);
+      var length = paths.length,
+          start = length ? paths[0] : 0,
+          value = this.__wrapped__,
+          interceptor = function(object) { return baseAt(object, paths); };
+
+      if (length > 1 || this.__actions__.length ||
+          !(value instanceof LazyWrapper) || !isIndex(start)) {
+        return this.thru(interceptor);
+      }
+      value = value.slice(start, +start + (length ? 1 : 0));
+      value.__actions__.push({
+        'func': thru,
+        'args': [interceptor],
+        'thisArg': undefined
+      });
+      return new LodashWrapper(value, this.__chain__).thru(function(array) {
+        if (length && !array.length) {
+          array.push(undefined);
+        }
+        return array;
+      });
+    });
+
+    /**
+     * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
+     *
+     * @name chain
+     * @memberOf _
+     * @since 0.1.0
+     * @category Seq
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * // A sequence without explicit chaining.
+     * _(users).head();
+     * // => { 'user': 'barney', 'age': 36 }
+     *
+     * // A sequence with explicit chaining.
+     * _(users)
+     *   .chain()
+     *   .head()
+     *   .pick('user')
+     *   .value();
+     * // => { 'user': 'barney' }
+     */
+    function wrapperChain() {
+      return chain(this);
+    }
+
+    /**
+     * Executes the chain sequence and returns the wrapped result.
+     *
+     * @name commit
+     * @memberOf _
+     * @since 3.2.0
+     * @category Seq
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var array = [1, 2];
+     * var wrapped = _(array).push(3);
+     *
+     * console.log(array);
+     * // => [1, 2]
+     *
+     * wrapped = wrapped.commit();
+     * console.log(array);
+     * // => [1, 2, 3]
+     *
+     * wrapped.last();
+     * // => 3
+     *
+     * console.log(array);
+     * // => [1, 2, 3]
+     */
+    function wrapperCommit() {
+      return new LodashWrapper(this.value(), this.__chain__);
+    }
+
+    /**
+     * Gets the next value on a wrapped object following the
+     * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
+     *
+     * @name next
+     * @memberOf _
+     * @since 4.0.0
+     * @category Seq
+     * @returns {Object} Returns the next iterator value.
+     * @example
+     *
+     * var wrapped = _([1, 2]);
+     *
+     * wrapped.next();
+     * // => { 'done': false, 'value': 1 }
+     *
+     * wrapped.next();
+     * // => { 'done': false, 'value': 2 }
+     *
+     * wrapped.next();
+     * // => { 'done': true, 'value': undefined }
+     */
+    function wrapperNext() {
+      if (this.__values__ === undefined) {
+        this.__values__ = toArray(this.value());
+      }
+      var done = this.__index__ >= this.__values__.length,
+          value = done ? undefined : this.__values__[this.__index__++];
+
+      return { 'done': done, 'value': value };
+    }
+
+    /**
+     * Enables the wrapper to be iterable.
+     *
+     * @name Symbol.iterator
+     * @memberOf _
+     * @since 4.0.0
+     * @category Seq
+     * @returns {Object} Returns the wrapper object.
+     * @example
+     *
+     * var wrapped = _([1, 2]);
+     *
+     * wrapped[Symbol.iterator]() === wrapped;
+     * // => true
+     *
+     * Array.from(wrapped);
+     * // => [1, 2]
+     */
+    function wrapperToIterator() {
+      return this;
+    }
+
+    /**
+     * Creates a clone of the chain sequence planting `value` as the wrapped value.
+     *
+     * @name plant
+     * @memberOf _
+     * @since 3.2.0
+     * @category Seq
+     * @param {*} value The value to plant.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var wrapped = _([1, 2]).map(square);
+     * var other = wrapped.plant([3, 4]);
+     *
+     * other.value();
+     * // => [9, 16]
+     *
+     * wrapped.value();
+     * // => [1, 4]
+     */
+    function wrapperPlant(value) {
+      var result,
+          parent = this;
+
+      while (parent instanceof baseLodash) {
+        var clone = wrapperClone(parent);
+        clone.__index__ = 0;
+        clone.__values__ = undefined;
+        if (result) {
+          previous.__wrapped__ = clone;
+        } else {
+          result = clone;
+        }
+        var previous = clone;
+        parent = parent.__wrapped__;
+      }
+      previous.__wrapped__ = value;
+      return result;
+    }
+
+    /**
+     * This method is the wrapper version of `_.reverse`.
+     *
+     * **Note:** This method mutates the wrapped array.
+     *
+     * @name reverse
+     * @memberOf _
+     * @since 0.1.0
+     * @category Seq
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var array = [1, 2, 3];
+     *
+     * _(array).reverse().value()
+     * // => [3, 2, 1]
+     *
+     * console.log(array);
+     * // => [3, 2, 1]
+     */
+    function wrapperReverse() {
+      var value = this.__wrapped__;
+      if (value instanceof LazyWrapper) {
+        var wrapped = value;
+        if (this.__actions__.length) {
+          wrapped = new LazyWrapper(this);
+        }
+        wrapped = wrapped.reverse();
+        wrapped.__actions__.push({
+          'func': thru,
+          'args': [reverse],
+          'thisArg': undefined
+        });
+        return new LodashWrapper(wrapped, this.__chain__);
+      }
+      return this.thru(reverse);
+    }
+
+    /**
+     * Executes the chain sequence to resolve the unwrapped value.
+     *
+     * @name value
+     * @memberOf _
+     * @since 0.1.0
+     * @alias toJSON, valueOf
+     * @category Seq
+     * @returns {*} Returns the resolved unwrapped value.
+     * @example
+     *
+     * _([1, 2, 3]).value();
+     * // => [1, 2, 3]
+     */
+    function wrapperValue() {
+      return baseWrapperValue(this.__wrapped__, this.__actions__);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` thru `iteratee`. The corresponding value of
+     * each key is the number of times the key was returned by `iteratee`. The
+     * iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.5.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee to transform keys.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.countBy([6.1, 4.2, 6.3], Math.floor);
+     * // => { '4': 1, '6': 2 }
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.countBy(['one', 'two', 'three'], 'length');
+     * // => { '3': 2, '5': 1 }
+     */
+    var countBy = createAggregator(function(result, value, key) {
+      hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1);
+    });
+
+    /**
+     * Checks if `predicate` returns truthy for **all** elements of `collection`.
+     * Iteration is stopped once `predicate` returns falsey. The predicate is
+     * invoked with three arguments: (value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {boolean} Returns `true` if all elements pass the predicate check,
+     *  else `false`.
+     * @example
+     *
+     * _.every([true, 1, null, 'yes'], Boolean);
+     * // => false
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': false },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.every(users, { 'user': 'barney', 'active': false });
+     * // => false
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.every(users, ['active', false]);
+     * // => true
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.every(users, 'active');
+     * // => false
+     */
+    function every(collection, predicate, guard) {
+      var func = isArray(collection) ? arrayEvery : baseEvery;
+      if (guard && isIterateeCall(collection, predicate, guard)) {
+        predicate = undefined;
+      }
+      return func(collection, getIteratee(predicate, 3));
+    }
+
+    /**
+     * Iterates over elements of `collection`, returning an array of all elements
+     * `predicate` returns truthy for. The predicate is invoked with three
+     * arguments: (value, index|key, collection).
+     *
+     * **Note:** Unlike `_.remove`, this method returns a new array.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the new filtered array.
+     * @see _.reject
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': true },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * _.filter(users, function(o) { return !o.active; });
+     * // => objects for ['fred']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.filter(users, { 'age': 36, 'active': true });
+     * // => objects for ['barney']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.filter(users, ['active', false]);
+     * // => objects for ['fred']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.filter(users, 'active');
+     * // => objects for ['barney']
+     */
+    function filter(collection, predicate) {
+      var func = isArray(collection) ? arrayFilter : baseFilter;
+      return func(collection, getIteratee(predicate, 3));
+    }
+
+    /**
+     * Iterates over elements of `collection`, returning the first element
+     * `predicate` returns truthy for. The predicate is invoked with three
+     * arguments: (value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to search.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {*} Returns the matched element, else `undefined`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36, 'active': true },
+     *   { 'user': 'fred',    'age': 40, 'active': false },
+     *   { 'user': 'pebbles', 'age': 1,  'active': true }
+     * ];
+     *
+     * _.find(users, function(o) { return o.age < 40; });
+     * // => object for 'barney'
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.find(users, { 'age': 1, 'active': true });
+     * // => object for 'pebbles'
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.find(users, ['active', false]);
+     * // => object for 'fred'
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.find(users, 'active');
+     * // => object for 'barney'
+     */
+    var find = createFind(findIndex);
+
+    /**
+     * This method is like `_.find` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to search.
+     * @param {Function} [predicate=_.identity]
+     *  The function invoked per iteration.
+     * @param {number} [fromIndex=collection.length-1] The index to search from.
+     * @returns {*} Returns the matched element, else `undefined`.
+     * @example
+     *
+     * _.findLast([1, 2, 3, 4], function(n) {
+     *   return n % 2 == 1;
+     * });
+     * // => 3
+     */
+    var findLast = createFind(findLastIndex);
+
+    /**
+     * Creates a flattened array of values by running each element in `collection`
+     * thru `iteratee` and flattening the mapped results. The iteratee is invoked
+     * with three arguments: (value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * function duplicate(n) {
+     *   return [n, n];
+     * }
+     *
+     * _.flatMap([1, 2], duplicate);
+     * // => [1, 1, 2, 2]
+     */
+    function flatMap(collection, iteratee) {
+      return baseFlatten(map(collection, iteratee), 1);
+    }
+
+    /**
+     * This method is like `_.flatMap` except that it recursively flattens the
+     * mapped results.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.7.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The function invoked per iteration.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * function duplicate(n) {
+     *   return [[[n, n]]];
+     * }
+     *
+     * _.flatMapDeep([1, 2], duplicate);
+     * // => [1, 1, 2, 2]
+     */
+    function flatMapDeep(collection, iteratee) {
+      return baseFlatten(map(collection, iteratee), INFINITY);
+    }
+
+    /**
+     * This method is like `_.flatMap` except that it recursively flattens the
+     * mapped results up to `depth` times.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.7.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The function invoked per iteration.
+     * @param {number} [depth=1] The maximum recursion depth.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * function duplicate(n) {
+     *   return [[[n, n]]];
+     * }
+     *
+     * _.flatMapDepth([1, 2], duplicate, 2);
+     * // => [[1, 1], [2, 2]]
+     */
+    function flatMapDepth(collection, iteratee, depth) {
+      depth = depth === undefined ? 1 : toInteger(depth);
+      return baseFlatten(map(collection, iteratee), depth);
+    }
+
+    /**
+     * Iterates over elements of `collection` and invokes `iteratee` for each element.
+     * The iteratee is invoked with three arguments: (value, index|key, collection).
+     * Iteratee functions may exit iteration early by explicitly returning `false`.
+     *
+     * **Note:** As with other "Collections" methods, objects with a "length"
+     * property are iterated like arrays. To avoid this behavior use `_.forIn`
+     * or `_.forOwn` for object iteration.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @alias each
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Array|Object} Returns `collection`.
+     * @see _.forEachRight
+     * @example
+     *
+     * _([1, 2]).forEach(function(value) {
+     *   console.log(value);
+     * });
+     * // => Logs `1` then `2`.
+     *
+     * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
+     */
+    function forEach(collection, iteratee) {
+      var func = isArray(collection) ? arrayEach : baseEach;
+      return func(collection, getIteratee(iteratee, 3));
+    }
+
+    /**
+     * This method is like `_.forEach` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @alias eachRight
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Array|Object} Returns `collection`.
+     * @see _.forEach
+     * @example
+     *
+     * _.forEachRight([1, 2], function(value) {
+     *   console.log(value);
+     * });
+     * // => Logs `2` then `1`.
+     */
+    function forEachRight(collection, iteratee) {
+      var func = isArray(collection) ? arrayEachRight : baseEachRight;
+      return func(collection, getIteratee(iteratee, 3));
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` thru `iteratee`. The order of grouped values
+     * is determined by the order they occur in `collection`. The corresponding
+     * value of each key is an array of elements responsible for generating the
+     * key. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee to transform keys.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.groupBy([6.1, 4.2, 6.3], Math.floor);
+     * // => { '4': [4.2], '6': [6.1, 6.3] }
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.groupBy(['one', 'two', 'three'], 'length');
+     * // => { '3': ['one', 'two'], '5': ['three'] }
+     */
+    var groupBy = createAggregator(function(result, value, key) {
+      if (hasOwnProperty.call(result, key)) {
+        result[key].push(value);
+      } else {
+        result[key] = [value];
+      }
+    });
+
+    /**
+     * Checks if `value` is in `collection`. If `collection` is a string, it's
+     * checked for a substring of `value`, otherwise
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * is used for equality comparisons. If `fromIndex` is negative, it's used as
+     * the offset from the end of `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+     * @returns {boolean} Returns `true` if `value` is found, else `false`.
+     * @example
+     *
+     * _.includes([1, 2, 3], 1);
+     * // => true
+     *
+     * _.includes([1, 2, 3], 1, 2);
+     * // => false
+     *
+     * _.includes({ 'a': 1, 'b': 2 }, 1);
+     * // => true
+     *
+     * _.includes('abcd', 'bc');
+     * // => true
+     */
+    function includes(collection, value, fromIndex, guard) {
+      collection = isArrayLike(collection) ? collection : values(collection);
+      fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
+
+      var length = collection.length;
+      if (fromIndex < 0) {
+        fromIndex = nativeMax(length + fromIndex, 0);
+      }
+      return isString(collection)
+        ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
+        : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
+    }
+
+    /**
+     * Invokes the method at `path` of each element in `collection`, returning
+     * an array of the results of each invoked method. Any additional arguments
+     * are provided to each invoked method. If `path` is a function, it's invoked
+     * for, and `this` bound to, each element in `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Array|Function|string} path The path of the method to invoke or
+     *  the function invoked per iteration.
+     * @param {...*} [args] The arguments to invoke each method with.
+     * @returns {Array} Returns the array of results.
+     * @example
+     *
+     * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
+     * // => [[1, 5, 7], [1, 2, 3]]
+     *
+     * _.invokeMap([123, 456], String.prototype.split, '');
+     * // => [['1', '2', '3'], ['4', '5', '6']]
+     */
+    var invokeMap = baseRest(function(collection, path, args) {
+      var index = -1,
+          isFunc = typeof path == 'function',
+          isProp = isKey(path),
+          result = isArrayLike(collection) ? Array(collection.length) : [];
+
+      baseEach(collection, function(value) {
+        var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined);
+        result[++index] = func ? apply(func, value, args) : baseInvoke(value, path, args);
+      });
+      return result;
+    });
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` thru `iteratee`. The corresponding value of
+     * each key is the last element responsible for generating the key. The
+     * iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity]
+     *  The iteratee to transform keys.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * var array = [
+     *   { 'dir': 'left', 'code': 97 },
+     *   { 'dir': 'right', 'code': 100 }
+     * ];
+     *
+     * _.keyBy(array, function(o) {
+     *   return String.fromCharCode(o.code);
+     * });
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.keyBy(array, 'dir');
+     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+     */
+    var keyBy = createAggregator(function(result, value, key) {
+      result[key] = value;
+    });
+
+    /**
+     * Creates an array of values by running each element in `collection` thru
+     * `iteratee`. The iteratee is invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * Many lodash methods are guarded to work as iteratees for methods like
+     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+     *
+     * The guarded methods are:
+     * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+     * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+     * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+     * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Array} Returns the new mapped array.
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * _.map([4, 8], square);
+     * // => [16, 64]
+     *
+     * _.map({ 'a': 4, 'b': 8 }, square);
+     * // => [16, 64] (iteration order is not guaranteed)
+     *
+     * var users = [
+     *   { 'user': 'barney' },
+     *   { 'user': 'fred' }
+     * ];
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.map(users, 'user');
+     * // => ['barney', 'fred']
+     */
+    function map(collection, iteratee) {
+      var func = isArray(collection) ? arrayMap : baseMap;
+      return func(collection, getIteratee(iteratee, 3));
+    }
+
+    /**
+     * This method is like `_.sortBy` except that it allows specifying the sort
+     * orders of the iteratees to sort by. If `orders` is unspecified, all values
+     * are sorted in ascending order. Otherwise, specify an order of "desc" for
+     * descending or "asc" for ascending sort order of corresponding values.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
+     *  The iteratees to sort by.
+     * @param {string[]} [orders] The sort orders of `iteratees`.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+     * @returns {Array} Returns the new sorted array.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'fred',   'age': 48 },
+     *   { 'user': 'barney', 'age': 34 },
+     *   { 'user': 'fred',   'age': 40 },
+     *   { 'user': 'barney', 'age': 36 }
+     * ];
+     *
+     * // Sort by `user` in ascending order and by `age` in descending order.
+     * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
+     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+     */
+    function orderBy(collection, iteratees, orders, guard) {
+      if (collection == null) {
+        return [];
+      }
+      if (!isArray(iteratees)) {
+        iteratees = iteratees == null ? [] : [iteratees];
+      }
+      orders = guard ? undefined : orders;
+      if (!isArray(orders)) {
+        orders = orders == null ? [] : [orders];
+      }
+      return baseOrderBy(collection, iteratees, orders);
+    }
+
+    /**
+     * Creates an array of elements split into two groups, the first of which
+     * contains elements `predicate` returns truthy for, the second of which
+     * contains elements `predicate` returns falsey for. The predicate is
+     * invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @returns {Array} Returns the array of grouped elements.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36, 'active': false },
+     *   { 'user': 'fred',    'age': 40, 'active': true },
+     *   { 'user': 'pebbles', 'age': 1,  'active': false }
+     * ];
+     *
+     * _.partition(users, function(o) { return o.active; });
+     * // => objects for [['fred'], ['barney', 'pebbles']]
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.partition(users, { 'age': 1, 'active': false });
+     * // => objects for [['pebbles'], ['barney', 'fred']]
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.partition(users, ['active', false]);
+     * // => objects for [['barney', 'pebbles'], ['fred']]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.partition(users, 'active');
+     * // => objects for [['fred'], ['barney', 'pebbles']]
+     */
+    var partition = createAggregator(function(result, value, key) {
+      result[key ? 0 : 1].push(value);
+    }, function() { return [[], []]; });
+
+    /**
+     * Reduces `collection` to a value which is the accumulated result of running
+     * each element in `collection` thru `iteratee`, where each successive
+     * invocation is supplied the return value of the previous. If `accumulator`
+     * is not given, the first element of `collection` is used as the initial
+     * value. The iteratee is invoked with four arguments:
+     * (accumulator, value, index|key, collection).
+     *
+     * Many lodash methods are guarded to work as iteratees for methods like
+     * `_.reduce`, `_.reduceRight`, and `_.transform`.
+     *
+     * The guarded methods are:
+     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
+     * and `sortBy`
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @returns {*} Returns the accumulated value.
+     * @see _.reduceRight
+     * @example
+     *
+     * _.reduce([1, 2], function(sum, n) {
+     *   return sum + n;
+     * }, 0);
+     * // => 3
+     *
+     * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+     *   (result[value] || (result[value] = [])).push(key);
+     *   return result;
+     * }, {});
+     * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
+     */
+    function reduce(collection, iteratee, accumulator) {
+      var func = isArray(collection) ? arrayReduce : baseReduce,
+          initAccum = arguments.length < 3;
+
+      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
+    }
+
+    /**
+     * This method is like `_.reduce` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @returns {*} Returns the accumulated value.
+     * @see _.reduce
+     * @example
+     *
+     * var array = [[0, 1], [2, 3], [4, 5]];
+     *
+     * _.reduceRight(array, function(flattened, other) {
+     *   return flattened.concat(other);
+     * }, []);
+     * // => [4, 5, 2, 3, 0, 1]
+     */
+    function reduceRight(collection, iteratee, accumulator) {
+      var func = isArray(collection) ? arrayReduceRight : baseReduce,
+          initAccum = arguments.length < 3;
+
+      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
+    }
+
+    /**
+     * The opposite of `_.filter`; this method returns the elements of `collection`
+     * that `predicate` does **not** return truthy for.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @returns {Array} Returns the new filtered array.
+     * @see _.filter
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': false },
+     *   { 'user': 'fred',   'age': 40, 'active': true }
+     * ];
+     *
+     * _.reject(users, function(o) { return !o.active; });
+     * // => objects for ['fred']
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.reject(users, { 'age': 40, 'active': true });
+     * // => objects for ['barney']
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.reject(users, ['active', false]);
+     * // => objects for ['fred']
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.reject(users, 'active');
+     * // => objects for ['barney']
+     */
+    function reject(collection, predicate) {
+      var func = isArray(collection) ? arrayFilter : baseFilter;
+      return func(collection, negate(getIteratee(predicate, 3)));
+    }
+
+    /**
+     * Gets a random element from `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to sample.
+     * @returns {*} Returns the random element.
+     * @example
+     *
+     * _.sample([1, 2, 3, 4]);
+     * // => 2
+     */
+    function sample(collection) {
+      var array = isArrayLike(collection) ? collection : values(collection),
+          length = array.length;
+
+      return length > 0 ? array[baseRandom(0, length - 1)] : undefined;
+    }
+
+    /**
+     * Gets `n` random elements at unique keys from `collection` up to the
+     * size of `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to sample.
+     * @param {number} [n=1] The number of elements to sample.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the random elements.
+     * @example
+     *
+     * _.sampleSize([1, 2, 3], 2);
+     * // => [3, 1]
+     *
+     * _.sampleSize([1, 2, 3], 4);
+     * // => [2, 3, 1]
+     */
+    function sampleSize(collection, n, guard) {
+      var index = -1,
+          result = toArray(collection),
+          length = result.length,
+          lastIndex = length - 1;
+
+      if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {
+        n = 1;
+      } else {
+        n = baseClamp(toInteger(n), 0, length);
+      }
+      while (++index < n) {
+        var rand = baseRandom(index, lastIndex),
+            value = result[rand];
+
+        result[rand] = result[index];
+        result[index] = value;
+      }
+      result.length = n;
+      return result;
+    }
+
+    /**
+     * Creates an array of shuffled values, using a version of the
+     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to shuffle.
+     * @returns {Array} Returns the new shuffled array.
+     * @example
+     *
+     * _.shuffle([1, 2, 3, 4]);
+     * // => [4, 1, 3, 2]
+     */
+    function shuffle(collection) {
+      return sampleSize(collection, MAX_ARRAY_LENGTH);
+    }
+
+    /**
+     * Gets the size of `collection` by returning its length for array-like
+     * values or the number of own enumerable string keyed properties for objects.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to inspect.
+     * @returns {number} Returns the collection size.
+     * @example
+     *
+     * _.size([1, 2, 3]);
+     * // => 3
+     *
+     * _.size({ 'a': 1, 'b': 2 });
+     * // => 2
+     *
+     * _.size('pebbles');
+     * // => 7
+     */
+    function size(collection) {
+      if (collection == null) {
+        return 0;
+      }
+      if (isArrayLike(collection)) {
+        var result = collection.length;
+        return (result && isString(collection)) ? stringSize(collection) : result;
+      }
+      if (isObjectLike(collection)) {
+        var tag = getTag(collection);
+        if (tag == mapTag || tag == setTag) {
+          return collection.size;
+        }
+      }
+      return keys(collection).length;
+    }
+
+    /**
+     * Checks if `predicate` returns truthy for **any** element of `collection`.
+     * Iteration is stopped once `predicate` returns truthy. The predicate is
+     * invoked with three arguments: (value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {boolean} Returns `true` if any element passes the predicate check,
+     *  else `false`.
+     * @example
+     *
+     * _.some([null, 0, 'yes', false], Boolean);
+     * // => true
+     *
+     * var users = [
+     *   { 'user': 'barney', 'active': true },
+     *   { 'user': 'fred',   'active': false }
+     * ];
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.some(users, { 'user': 'barney', 'active': false });
+     * // => false
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.some(users, ['active', false]);
+     * // => true
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.some(users, 'active');
+     * // => true
+     */
+    function some(collection, predicate, guard) {
+      var func = isArray(collection) ? arraySome : baseSome;
+      if (guard && isIterateeCall(collection, predicate, guard)) {
+        predicate = undefined;
+      }
+      return func(collection, getIteratee(predicate, 3));
+    }
+
+    /**
+     * Creates an array of elements, sorted in ascending order by the results of
+     * running each element in a collection thru each iteratee. This method
+     * performs a stable sort, that is, it preserves the original sort order of
+     * equal elements. The iteratees are invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Collection
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {...(Function|Function[])} [iteratees=[_.identity]]
+     *  The iteratees to sort by.
+     * @returns {Array} Returns the new sorted array.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'fred',   'age': 48 },
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 },
+     *   { 'user': 'barney', 'age': 34 }
+     * ];
+     *
+     * _.sortBy(users, function(o) { return o.user; });
+     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+     *
+     * _.sortBy(users, ['user', 'age']);
+     * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
+     *
+     * _.sortBy(users, 'user', function(o) {
+     *   return Math.floor(o.age / 10);
+     * });
+     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+     */
+    var sortBy = baseRest(function(collection, iteratees) {
+      if (collection == null) {
+        return [];
+      }
+      var length = iteratees.length;
+      if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
+        iteratees = [];
+      } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
+        iteratees = [iteratees[0]];
+      }
+      return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Gets the timestamp of the number of milliseconds that have elapsed since
+     * the Unix epoch (1 January 1970 00:00:00 UTC).
+     *
+     * @static
+     * @memberOf _
+     * @since 2.4.0
+     * @category Date
+     * @returns {number} Returns the timestamp.
+     * @example
+     *
+     * _.defer(function(stamp) {
+     *   console.log(_.now() - stamp);
+     * }, _.now());
+     * // => Logs the number of milliseconds it took for the deferred invocation.
+     */
+    function now() {
+      return Date.now();
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The opposite of `_.before`; this method creates a function that invokes
+     * `func` once it's called `n` or more times.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {number} n The number of calls before `func` is invoked.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var saves = ['profile', 'settings'];
+     *
+     * var done = _.after(saves.length, function() {
+     *   console.log('done saving!');
+     * });
+     *
+     * _.forEach(saves, function(type) {
+     *   asyncSave({ 'type': type, 'complete': done });
+     * });
+     * // => Logs 'done saving!' after the two async saves have completed.
+     */
+    function after(n, func) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      n = toInteger(n);
+      return function() {
+        if (--n < 1) {
+          return func.apply(this, arguments);
+        }
+      };
+    }
+
+    /**
+     * Creates a function that invokes `func`, with up to `n` arguments,
+     * ignoring any additional arguments.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Function
+     * @param {Function} func The function to cap arguments for.
+     * @param {number} [n=func.length] The arity cap.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Function} Returns the new capped function.
+     * @example
+     *
+     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
+     * // => [6, 8, 10]
+     */
+    function ary(func, n, guard) {
+      n = guard ? undefined : n;
+      n = (func && n == null) ? func.length : n;
+      return createWrap(func, ARY_FLAG, undefined, undefined, undefined, undefined, n);
+    }
+
+    /**
+     * Creates a function that invokes `func`, with the `this` binding and arguments
+     * of the created function, while it's called less than `n` times. Subsequent
+     * calls to the created function return the result of the last `func` invocation.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Function
+     * @param {number} n The number of calls at which `func` is no longer invoked.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * jQuery(element).on('click', _.before(5, addContactToList));
+     * // => Allows adding up to 4 contacts to the list.
+     */
+    function before(n, func) {
+      var result;
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      n = toInteger(n);
+      return function() {
+        if (--n > 0) {
+          result = func.apply(this, arguments);
+        }
+        if (n <= 1) {
+          func = undefined;
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of `thisArg`
+     * and `partials` prepended to the arguments it receives.
+     *
+     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+     * may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
+     * property of bound functions.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to bind.
+     * @param {*} thisArg The `this` binding of `func`.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * function greet(greeting, punctuation) {
+     *   return greeting + ' ' + this.user + punctuation;
+     * }
+     *
+     * var object = { 'user': 'fred' };
+     *
+     * var bound = _.bind(greet, object, 'hi');
+     * bound('!');
+     * // => 'hi fred!'
+     *
+     * // Bound with placeholders.
+     * var bound = _.bind(greet, object, _, '!');
+     * bound('hi');
+     * // => 'hi fred!'
+     */
+    var bind = baseRest(function(func, thisArg, partials) {
+      var bitmask = BIND_FLAG;
+      if (partials.length) {
+        var holders = replaceHolders(partials, getHolder(bind));
+        bitmask |= PARTIAL_FLAG;
+      }
+      return createWrap(func, bitmask, thisArg, partials, holders);
+    });
+
+    /**
+     * Creates a function that invokes the method at `object[key]` with `partials`
+     * prepended to the arguments it receives.
+     *
+     * This method differs from `_.bind` by allowing bound functions to reference
+     * methods that may be redefined or don't yet exist. See
+     * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
+     * for more details.
+     *
+     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.10.0
+     * @category Function
+     * @param {Object} object The object to invoke the method on.
+     * @param {string} key The key of the method.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var object = {
+     *   'user': 'fred',
+     *   'greet': function(greeting, punctuation) {
+     *     return greeting + ' ' + this.user + punctuation;
+     *   }
+     * };
+     *
+     * var bound = _.bindKey(object, 'greet', 'hi');
+     * bound('!');
+     * // => 'hi fred!'
+     *
+     * object.greet = function(greeting, punctuation) {
+     *   return greeting + 'ya ' + this.user + punctuation;
+     * };
+     *
+     * bound('!');
+     * // => 'hiya fred!'
+     *
+     * // Bound with placeholders.
+     * var bound = _.bindKey(object, 'greet', _, '!');
+     * bound('hi');
+     * // => 'hiya fred!'
+     */
+    var bindKey = baseRest(function(object, key, partials) {
+      var bitmask = BIND_FLAG | BIND_KEY_FLAG;
+      if (partials.length) {
+        var holders = replaceHolders(partials, getHolder(bindKey));
+        bitmask |= PARTIAL_FLAG;
+      }
+      return createWrap(key, bitmask, object, partials, holders);
+    });
+
+    /**
+     * Creates a function that accepts arguments of `func` and either invokes
+     * `func` returning its result, if at least `arity` number of arguments have
+     * been provided, or returns a function that accepts the remaining `func`
+     * arguments, and so on. The arity of `func` may be specified if `func.length`
+     * is not sufficient.
+     *
+     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
+     * may be used as a placeholder for provided arguments.
+     *
+     * **Note:** This method doesn't set the "length" property of curried functions.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Function
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var abc = function(a, b, c) {
+     *   return [a, b, c];
+     * };
+     *
+     * var curried = _.curry(abc);
+     *
+     * curried(1)(2)(3);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2)(3);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2, 3);
+     * // => [1, 2, 3]
+     *
+     * // Curried with placeholders.
+     * curried(1)(_, 3)(2);
+     * // => [1, 2, 3]
+     */
+    function curry(func, arity, guard) {
+      arity = guard ? undefined : arity;
+      var result = createWrap(func, CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
+      result.placeholder = curry.placeholder;
+      return result;
+    }
+
+    /**
+     * This method is like `_.curry` except that arguments are applied to `func`
+     * in the manner of `_.partialRight` instead of `_.partial`.
+     *
+     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for provided arguments.
+     *
+     * **Note:** This method doesn't set the "length" property of curried functions.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Function
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var abc = function(a, b, c) {
+     *   return [a, b, c];
+     * };
+     *
+     * var curried = _.curryRight(abc);
+     *
+     * curried(3)(2)(1);
+     * // => [1, 2, 3]
+     *
+     * curried(2, 3)(1);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2, 3);
+     * // => [1, 2, 3]
+     *
+     * // Curried with placeholders.
+     * curried(3)(1, _)(2);
+     * // => [1, 2, 3]
+     */
+    function curryRight(func, arity, guard) {
+      arity = guard ? undefined : arity;
+      var result = createWrap(func, CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
+      result.placeholder = curryRight.placeholder;
+      return result;
+    }
+
+    /**
+     * Creates a debounced function that delays invoking `func` until after `wait`
+     * milliseconds have elapsed since the last time the debounced function was
+     * invoked. The debounced function comes with a `cancel` method to cancel
+     * delayed `func` invocations and a `flush` method to immediately invoke them.
+     * Provide `options` to indicate whether `func` should be invoked on the
+     * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+     * with the last arguments provided to the debounced function. Subsequent
+     * calls to the debounced function return the result of the last `func`
+     * invocation.
+     *
+     * **Note:** If `leading` and `trailing` options are `true`, `func` is
+     * invoked on the trailing edge of the timeout only if the debounced function
+     * is invoked more than once during the `wait` timeout.
+     *
+     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+     *
+     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+     * for details over the differences between `_.debounce` and `_.throttle`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to debounce.
+     * @param {number} [wait=0] The number of milliseconds to delay.
+     * @param {Object} [options={}] The options object.
+     * @param {boolean} [options.leading=false]
+     *  Specify invoking on the leading edge of the timeout.
+     * @param {number} [options.maxWait]
+     *  The maximum time `func` is allowed to be delayed before it's invoked.
+     * @param {boolean} [options.trailing=true]
+     *  Specify invoking on the trailing edge of the timeout.
+     * @returns {Function} Returns the new debounced function.
+     * @example
+     *
+     * // Avoid costly calculations while the window size is in flux.
+     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+     *
+     * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+     * jQuery(element).on('click', _.debounce(sendMail, 300, {
+     *   'leading': true,
+     *   'trailing': false
+     * }));
+     *
+     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+     * var source = new EventSource('/stream');
+     * jQuery(source).on('message', debounced);
+     *
+     * // Cancel the trailing debounced invocation.
+     * jQuery(window).on('popstate', debounced.cancel);
+     */
+    function debounce(func, wait, options) {
+      var lastArgs,
+          lastThis,
+          maxWait,
+          result,
+          timerId,
+          lastCallTime,
+          lastInvokeTime = 0,
+          leading = false,
+          maxing = false,
+          trailing = true;
+
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      wait = toNumber(wait) || 0;
+      if (isObject(options)) {
+        leading = !!options.leading;
+        maxing = 'maxWait' in options;
+        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
+        trailing = 'trailing' in options ? !!options.trailing : trailing;
+      }
+
+      function invokeFunc(time) {
+        var args = lastArgs,
+            thisArg = lastThis;
+
+        lastArgs = lastThis = undefined;
+        lastInvokeTime = time;
+        result = func.apply(thisArg, args);
+        return result;
+      }
+
+      function leadingEdge(time) {
+        // Reset any `maxWait` timer.
+        lastInvokeTime = time;
+        // Start the timer for the trailing edge.
+        timerId = setTimeout(timerExpired, wait);
+        // Invoke the leading edge.
+        return leading ? invokeFunc(time) : result;
+      }
+
+      function remainingWait(time) {
+        var timeSinceLastCall = time - lastCallTime,
+            timeSinceLastInvoke = time - lastInvokeTime,
+            result = wait - timeSinceLastCall;
+
+        return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
+      }
+
+      function shouldInvoke(time) {
+        var timeSinceLastCall = time - lastCallTime,
+            timeSinceLastInvoke = time - lastInvokeTime;
+
+        // Either this is the first call, activity has stopped and we're at the
+        // trailing edge, the system time has gone backwards and we're treating
+        // it as the trailing edge, or we've hit the `maxWait` limit.
+        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+      }
+
+      function timerExpired() {
+        var time = now();
+        if (shouldInvoke(time)) {
+          return trailingEdge(time);
+        }
+        // Restart the timer.
+        timerId = setTimeout(timerExpired, remainingWait(time));
+      }
+
+      function trailingEdge(time) {
+        timerId = undefined;
+
+        // Only invoke if we have `lastArgs` which means `func` has been
+        // debounced at least once.
+        if (trailing && lastArgs) {
+          return invokeFunc(time);
+        }
+        lastArgs = lastThis = undefined;
+        return result;
+      }
+
+      function cancel() {
+        if (timerId !== undefined) {
+          clearTimeout(timerId);
+        }
+        lastInvokeTime = 0;
+        lastArgs = lastCallTime = lastThis = timerId = undefined;
+      }
+
+      function flush() {
+        return timerId === undefined ? result : trailingEdge(now());
+      }
+
+      function debounced() {
+        var time = now(),
+            isInvoking = shouldInvoke(time);
+
+        lastArgs = arguments;
+        lastThis = this;
+        lastCallTime = time;
+
+        if (isInvoking) {
+          if (timerId === undefined) {
+            return leadingEdge(lastCallTime);
+          }
+          if (maxing) {
+            // Handle invocations in a tight loop.
+            timerId = setTimeout(timerExpired, wait);
+            return invokeFunc(lastCallTime);
+          }
+        }
+        if (timerId === undefined) {
+          timerId = setTimeout(timerExpired, wait);
+        }
+        return result;
+      }
+      debounced.cancel = cancel;
+      debounced.flush = flush;
+      return debounced;
+    }
+
+    /**
+     * Defers invoking the `func` until the current call stack has cleared. Any
+     * additional arguments are provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to defer.
+     * @param {...*} [args] The arguments to invoke `func` with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.defer(function(text) {
+     *   console.log(text);
+     * }, 'deferred');
+     * // => Logs 'deferred' after one or more milliseconds.
+     */
+    var defer = baseRest(function(func, args) {
+      return baseDelay(func, 1, args);
+    });
+
+    /**
+     * Invokes `func` after `wait` milliseconds. Any additional arguments are
+     * provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay invocation.
+     * @param {...*} [args] The arguments to invoke `func` with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.delay(function(text) {
+     *   console.log(text);
+     * }, 1000, 'later');
+     * // => Logs 'later' after one second.
+     */
+    var delay = baseRest(function(func, wait, args) {
+      return baseDelay(func, toNumber(wait) || 0, args);
+    });
+
+    /**
+     * Creates a function that invokes `func` with arguments reversed.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Function
+     * @param {Function} func The function to flip arguments for.
+     * @returns {Function} Returns the new flipped function.
+     * @example
+     *
+     * var flipped = _.flip(function() {
+     *   return _.toArray(arguments);
+     * });
+     *
+     * flipped('a', 'b', 'c', 'd');
+     * // => ['d', 'c', 'b', 'a']
+     */
+    function flip(func) {
+      return createWrap(func, FLIP_FLAG);
+    }
+
+    /**
+     * Creates a function that memoizes the result of `func`. If `resolver` is
+     * provided, it determines the cache key for storing the result based on the
+     * arguments provided to the memoized function. By default, the first argument
+     * provided to the memoized function is used as the map cache key. The `func`
+     * is invoked with the `this` binding of the memoized function.
+     *
+     * **Note:** The cache is exposed as the `cache` property on the memoized
+     * function. Its creation may be customized by replacing the `_.memoize.Cache`
+     * constructor with one whose instances implement the
+     * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
+     * method interface of `delete`, `get`, `has`, and `set`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to have its output memoized.
+     * @param {Function} [resolver] The function to resolve the cache key.
+     * @returns {Function} Returns the new memoized function.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2 };
+     * var other = { 'c': 3, 'd': 4 };
+     *
+     * var values = _.memoize(_.values);
+     * values(object);
+     * // => [1, 2]
+     *
+     * values(other);
+     * // => [3, 4]
+     *
+     * object.a = 2;
+     * values(object);
+     * // => [1, 2]
+     *
+     * // Modify the result cache.
+     * values.cache.set(object, ['a', 'b']);
+     * values(object);
+     * // => ['a', 'b']
+     *
+     * // Replace `_.memoize.Cache`.
+     * _.memoize.Cache = WeakMap;
+     */
+    function memoize(func, resolver) {
+      if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      var memoized = function() {
+        var args = arguments,
+            key = resolver ? resolver.apply(this, args) : args[0],
+            cache = memoized.cache;
+
+        if (cache.has(key)) {
+          return cache.get(key);
+        }
+        var result = func.apply(this, args);
+        memoized.cache = cache.set(key, result);
+        return result;
+      };
+      memoized.cache = new (memoize.Cache || MapCache);
+      return memoized;
+    }
+
+    // Assign cache to `_.memoize`.
+    memoize.Cache = MapCache;
+
+    /**
+     * Creates a function that negates the result of the predicate `func`. The
+     * `func` predicate is invoked with the `this` binding and arguments of the
+     * created function.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Function
+     * @param {Function} predicate The predicate to negate.
+     * @returns {Function} Returns the new negated function.
+     * @example
+     *
+     * function isEven(n) {
+     *   return n % 2 == 0;
+     * }
+     *
+     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+     * // => [1, 3, 5]
+     */
+    function negate(predicate) {
+      if (typeof predicate != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      return function() {
+        var args = arguments;
+        switch (args.length) {
+          case 0: return !predicate.call(this);
+          case 1: return !predicate.call(this, args[0]);
+          case 2: return !predicate.call(this, args[0], args[1]);
+          case 3: return !predicate.call(this, args[0], args[1], args[2]);
+        }
+        return !predicate.apply(this, args);
+      };
+    }
+
+    /**
+     * Creates a function that is restricted to invoking `func` once. Repeat calls
+     * to the function return the value of the first invocation. The `func` is
+     * invoked with the `this` binding and arguments of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var initialize = _.once(createApplication);
+     * initialize();
+     * initialize();
+     * // => `createApplication` is invoked once
+     */
+    function once(func) {
+      return before(2, func);
+    }
+
+    /**
+     * Creates a function that invokes `func` with its arguments transformed.
+     *
+     * @static
+     * @since 4.0.0
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to wrap.
+     * @param {...(Function|Function[])} [transforms=[_.identity]]
+     *  The argument transforms.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * function doubled(n) {
+     *   return n * 2;
+     * }
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var func = _.overArgs(function(x, y) {
+     *   return [x, y];
+     * }, [square, doubled]);
+     *
+     * func(9, 3);
+     * // => [81, 6]
+     *
+     * func(10, 5);
+     * // => [100, 10]
+     */
+    var overArgs = baseRest(function(func, transforms) {
+      transforms = (transforms.length == 1 && isArray(transforms[0]))
+        ? arrayMap(transforms[0], baseUnary(getIteratee()))
+        : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));
+
+      var funcsLength = transforms.length;
+      return baseRest(function(args) {
+        var index = -1,
+            length = nativeMin(args.length, funcsLength);
+
+        while (++index < length) {
+          args[index] = transforms[index].call(this, args[index]);
+        }
+        return apply(func, this, args);
+      });
+    });
+
+    /**
+     * Creates a function that invokes `func` with `partials` prepended to the
+     * arguments it receives. This method is like `_.bind` except it does **not**
+     * alter the `this` binding.
+     *
+     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** This method doesn't set the "length" property of partially
+     * applied functions.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.2.0
+     * @category Function
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * function greet(greeting, name) {
+     *   return greeting + ' ' + name;
+     * }
+     *
+     * var sayHelloTo = _.partial(greet, 'hello');
+     * sayHelloTo('fred');
+     * // => 'hello fred'
+     *
+     * // Partially applied with placeholders.
+     * var greetFred = _.partial(greet, _, 'fred');
+     * greetFred('hi');
+     * // => 'hi fred'
+     */
+    var partial = baseRest(function(func, partials) {
+      var holders = replaceHolders(partials, getHolder(partial));
+      return createWrap(func, PARTIAL_FLAG, undefined, partials, holders);
+    });
+
+    /**
+     * This method is like `_.partial` except that partially applied arguments
+     * are appended to the arguments it receives.
+     *
+     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** This method doesn't set the "length" property of partially
+     * applied functions.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.0.0
+     * @category Function
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * function greet(greeting, name) {
+     *   return greeting + ' ' + name;
+     * }
+     *
+     * var greetFred = _.partialRight(greet, 'fred');
+     * greetFred('hi');
+     * // => 'hi fred'
+     *
+     * // Partially applied with placeholders.
+     * var sayHelloTo = _.partialRight(greet, 'hello', _);
+     * sayHelloTo('fred');
+     * // => 'hello fred'
+     */
+    var partialRight = baseRest(function(func, partials) {
+      var holders = replaceHolders(partials, getHolder(partialRight));
+      return createWrap(func, PARTIAL_RIGHT_FLAG, undefined, partials, holders);
+    });
+
+    /**
+     * Creates a function that invokes `func` with arguments arranged according
+     * to the specified `indexes` where the argument value at the first index is
+     * provided as the first argument, the argument value at the second index is
+     * provided as the second argument, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Function
+     * @param {Function} func The function to rearrange arguments for.
+     * @param {...(number|number[])} indexes The arranged argument indexes.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var rearged = _.rearg(function(a, b, c) {
+     *   return [a, b, c];
+     * }, [2, 0, 1]);
+     *
+     * rearged('b', 'c', 'a')
+     * // => ['a', 'b', 'c']
+     */
+    var rearg = baseRest(function(func, indexes) {
+      return createWrap(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes, 1));
+    });
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of the
+     * created function and arguments from `start` and beyond provided as
+     * an array.
+     *
+     * **Note:** This method is based on the
+     * [rest parameter](https://mdn.io/rest_parameters).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Function
+     * @param {Function} func The function to apply a rest parameter to.
+     * @param {number} [start=func.length-1] The start position of the rest parameter.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var say = _.rest(function(what, names) {
+     *   return what + ' ' + _.initial(names).join(', ') +
+     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+     * });
+     *
+     * say('hello', 'fred', 'barney', 'pebbles');
+     * // => 'hello fred, barney, & pebbles'
+     */
+    function rest(func, start) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      start = start === undefined ? start : toInteger(start);
+      return baseRest(func, start);
+    }
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of the
+     * create function and an array of arguments much like
+     * [`Function#apply`](http://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply).
+     *
+     * **Note:** This method is based on the
+     * [spread operator](https://mdn.io/spread_operator).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.2.0
+     * @category Function
+     * @param {Function} func The function to spread arguments over.
+     * @param {number} [start=0] The start position of the spread.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var say = _.spread(function(who, what) {
+     *   return who + ' says ' + what;
+     * });
+     *
+     * say(['fred', 'hello']);
+     * // => 'fred says hello'
+     *
+     * var numbers = Promise.all([
+     *   Promise.resolve(40),
+     *   Promise.resolve(36)
+     * ]);
+     *
+     * numbers.then(_.spread(function(x, y) {
+     *   return x + y;
+     * }));
+     * // => a Promise of 76
+     */
+    function spread(func, start) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      start = start === undefined ? 0 : nativeMax(toInteger(start), 0);
+      return baseRest(function(args) {
+        var array = args[start],
+            otherArgs = castSlice(args, 0, start);
+
+        if (array) {
+          arrayPush(otherArgs, array);
+        }
+        return apply(func, this, otherArgs);
+      });
+    }
+
+    /**
+     * Creates a throttled function that only invokes `func` at most once per
+     * every `wait` milliseconds. The throttled function comes with a `cancel`
+     * method to cancel delayed `func` invocations and a `flush` method to
+     * immediately invoke them. Provide `options` to indicate whether `func`
+     * should be invoked on the leading and/or trailing edge of the `wait`
+     * timeout. The `func` is invoked with the last arguments provided to the
+     * throttled function. Subsequent calls to the throttled function return the
+     * result of the last `func` invocation.
+     *
+     * **Note:** If `leading` and `trailing` options are `true`, `func` is
+     * invoked on the trailing edge of the timeout only if the throttled function
+     * is invoked more than once during the `wait` timeout.
+     *
+     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+     *
+     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+     * for details over the differences between `_.throttle` and `_.debounce`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {Function} func The function to throttle.
+     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+     * @param {Object} [options={}] The options object.
+     * @param {boolean} [options.leading=true]
+     *  Specify invoking on the leading edge of the timeout.
+     * @param {boolean} [options.trailing=true]
+     *  Specify invoking on the trailing edge of the timeout.
+     * @returns {Function} Returns the new throttled function.
+     * @example
+     *
+     * // Avoid excessively updating the position while scrolling.
+     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+     *
+     * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+     * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+     * jQuery(element).on('click', throttled);
+     *
+     * // Cancel the trailing throttled invocation.
+     * jQuery(window).on('popstate', throttled.cancel);
+     */
+    function throttle(func, wait, options) {
+      var leading = true,
+          trailing = true;
+
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      if (isObject(options)) {
+        leading = 'leading' in options ? !!options.leading : leading;
+        trailing = 'trailing' in options ? !!options.trailing : trailing;
+      }
+      return debounce(func, wait, {
+        'leading': leading,
+        'maxWait': wait,
+        'trailing': trailing
+      });
+    }
+
+    /**
+     * Creates a function that accepts up to one argument, ignoring any
+     * additional arguments.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Function
+     * @param {Function} func The function to cap arguments for.
+     * @returns {Function} Returns the new capped function.
+     * @example
+     *
+     * _.map(['6', '8', '10'], _.unary(parseInt));
+     * // => [6, 8, 10]
+     */
+    function unary(func) {
+      return ary(func, 1);
+    }
+
+    /**
+     * Creates a function that provides `value` to `wrapper` as its first
+     * argument. Any additional arguments provided to the function are appended
+     * to those provided to the `wrapper`. The wrapper is invoked with the `this`
+     * binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Function
+     * @param {*} value The value to wrap.
+     * @param {Function} [wrapper=identity] The wrapper function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var p = _.wrap(_.escape, function(func, text) {
+     *   return '<p>' + func(text) + '</p>';
+     * });
+     *
+     * p('fred, barney, & pebbles');
+     * // => '<p>fred, barney, &amp; pebbles</p>'
+     */
+    function wrap(value, wrapper) {
+      wrapper = wrapper == null ? identity : wrapper;
+      return partial(wrapper, value);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Casts `value` as an array if it's not one.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.4.0
+     * @category Lang
+     * @param {*} value The value to inspect.
+     * @returns {Array} Returns the cast array.
+     * @example
+     *
+     * _.castArray(1);
+     * // => [1]
+     *
+     * _.castArray({ 'a': 1 });
+     * // => [{ 'a': 1 }]
+     *
+     * _.castArray('abc');
+     * // => ['abc']
+     *
+     * _.castArray(null);
+     * // => [null]
+     *
+     * _.castArray(undefined);
+     * // => [undefined]
+     *
+     * _.castArray();
+     * // => []
+     *
+     * var array = [1, 2, 3];
+     * console.log(_.castArray(array) === array);
+     * // => true
+     */
+    function castArray() {
+      if (!arguments.length) {
+        return [];
+      }
+      var value = arguments[0];
+      return isArray(value) ? value : [value];
+    }
+
+    /**
+     * Creates a shallow clone of `value`.
+     *
+     * **Note:** This method is loosely based on the
+     * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
+     * and supports cloning arrays, array buffers, booleans, date objects, maps,
+     * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
+     * arrays. The own enumerable properties of `arguments` objects are cloned
+     * as plain objects. An empty object is returned for uncloneable values such
+     * as error objects, functions, DOM nodes, and WeakMaps.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to clone.
+     * @returns {*} Returns the cloned value.
+     * @see _.cloneDeep
+     * @example
+     *
+     * var objects = [{ 'a': 1 }, { 'b': 2 }];
+     *
+     * var shallow = _.clone(objects);
+     * console.log(shallow[0] === objects[0]);
+     * // => true
+     */
+    function clone(value) {
+      return baseClone(value, false, true);
+    }
+
+    /**
+     * This method is like `_.clone` except that it accepts `customizer` which
+     * is invoked to produce the cloned value. If `customizer` returns `undefined`,
+     * cloning is handled by the method instead. The `customizer` is invoked with
+     * up to four arguments; (value [, index|key, object, stack]).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to clone.
+     * @param {Function} [customizer] The function to customize cloning.
+     * @returns {*} Returns the cloned value.
+     * @see _.cloneDeepWith
+     * @example
+     *
+     * function customizer(value) {
+     *   if (_.isElement(value)) {
+     *     return value.cloneNode(false);
+     *   }
+     * }
+     *
+     * var el = _.cloneWith(document.body, customizer);
+     *
+     * console.log(el === document.body);
+     * // => false
+     * console.log(el.nodeName);
+     * // => 'BODY'
+     * console.log(el.childNodes.length);
+     * // => 0
+     */
+    function cloneWith(value, customizer) {
+      return baseClone(value, false, true, customizer);
+    }
+
+    /**
+     * This method is like `_.clone` except that it recursively clones `value`.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.0.0
+     * @category Lang
+     * @param {*} value The value to recursively clone.
+     * @returns {*} Returns the deep cloned value.
+     * @see _.clone
+     * @example
+     *
+     * var objects = [{ 'a': 1 }, { 'b': 2 }];
+     *
+     * var deep = _.cloneDeep(objects);
+     * console.log(deep[0] === objects[0]);
+     * // => false
+     */
+    function cloneDeep(value) {
+      return baseClone(value, true, true);
+    }
+
+    /**
+     * This method is like `_.cloneWith` except that it recursively clones `value`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to recursively clone.
+     * @param {Function} [customizer] The function to customize cloning.
+     * @returns {*} Returns the deep cloned value.
+     * @see _.cloneWith
+     * @example
+     *
+     * function customizer(value) {
+     *   if (_.isElement(value)) {
+     *     return value.cloneNode(true);
+     *   }
+     * }
+     *
+     * var el = _.cloneDeepWith(document.body, customizer);
+     *
+     * console.log(el === document.body);
+     * // => false
+     * console.log(el.nodeName);
+     * // => 'BODY'
+     * console.log(el.childNodes.length);
+     * // => 20
+     */
+    function cloneDeepWith(value, customizer) {
+      return baseClone(value, true, true, customizer);
+    }
+
+    /**
+     * Checks if `object` conforms to `source` by invoking the predicate
+     * properties of `source` with the corresponding property values of `object`.
+     *
+     * **Note:** This method is equivalent to `_.conforms` when `source` is
+     * partially applied.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.14.0
+     * @category Lang
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property predicates to conform to.
+     * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2 };
+     *
+     * _.conformsTo(object, { 'b': function(n) { return n > 1; } });
+     * // => true
+     *
+     * _.conformsTo(object, { 'b': function(n) { return n > 2; } });
+     * // => false
+     */
+    function conformsTo(object, source) {
+      return source == null || baseConformsTo(object, source, keys(source));
+    }
+
+    /**
+     * Performs a
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * comparison between two values to determine if they are equivalent.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     * @example
+     *
+     * var object = { 'a': 1 };
+     * var other = { 'a': 1 };
+     *
+     * _.eq(object, object);
+     * // => true
+     *
+     * _.eq(object, other);
+     * // => false
+     *
+     * _.eq('a', 'a');
+     * // => true
+     *
+     * _.eq('a', Object('a'));
+     * // => false
+     *
+     * _.eq(NaN, NaN);
+     * // => true
+     */
+    function eq(value, other) {
+      return value === other || (value !== value && other !== other);
+    }
+
+    /**
+     * Checks if `value` is greater than `other`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.9.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is greater than `other`,
+     *  else `false`.
+     * @see _.lt
+     * @example
+     *
+     * _.gt(3, 1);
+     * // => true
+     *
+     * _.gt(3, 3);
+     * // => false
+     *
+     * _.gt(1, 3);
+     * // => false
+     */
+    var gt = createRelationalOperation(baseGt);
+
+    /**
+     * Checks if `value` is greater than or equal to `other`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.9.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is greater than or equal to
+     *  `other`, else `false`.
+     * @see _.lte
+     * @example
+     *
+     * _.gte(3, 1);
+     * // => true
+     *
+     * _.gte(3, 3);
+     * // => true
+     *
+     * _.gte(1, 3);
+     * // => false
+     */
+    var gte = createRelationalOperation(function(value, other) {
+      return value >= other;
+    });
+
+    /**
+     * Checks if `value` is likely an `arguments` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+     *  else `false`.
+     * @example
+     *
+     * _.isArguments(function() { return arguments; }());
+     * // => true
+     *
+     * _.isArguments([1, 2, 3]);
+     * // => false
+     */
+    function isArguments(value) {
+      // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
+      return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
+        (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
+    }
+
+    /**
+     * Checks if `value` is classified as an `Array` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+     * @example
+     *
+     * _.isArray([1, 2, 3]);
+     * // => true
+     *
+     * _.isArray(document.body.children);
+     * // => false
+     *
+     * _.isArray('abc');
+     * // => false
+     *
+     * _.isArray(_.noop);
+     * // => false
+     */
+    var isArray = Array.isArray;
+
+    /**
+     * Checks if `value` is classified as an `ArrayBuffer` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
+     * @example
+     *
+     * _.isArrayBuffer(new ArrayBuffer(2));
+     * // => true
+     *
+     * _.isArrayBuffer(new Array(2));
+     * // => false
+     */
+    var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;
+
+    /**
+     * Checks if `value` is array-like. A value is considered array-like if it's
+     * not a function and has a `value.length` that's an integer greater than or
+     * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+     * @example
+     *
+     * _.isArrayLike([1, 2, 3]);
+     * // => true
+     *
+     * _.isArrayLike(document.body.children);
+     * // => true
+     *
+     * _.isArrayLike('abc');
+     * // => true
+     *
+     * _.isArrayLike(_.noop);
+     * // => false
+     */
+    function isArrayLike(value) {
+      return value != null && isLength(getLength(value)) && !isFunction(value);
+    }
+
+    /**
+     * This method is like `_.isArrayLike` except that it also checks if `value`
+     * is an object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an array-like object,
+     *  else `false`.
+     * @example
+     *
+     * _.isArrayLikeObject([1, 2, 3]);
+     * // => true
+     *
+     * _.isArrayLikeObject(document.body.children);
+     * // => true
+     *
+     * _.isArrayLikeObject('abc');
+     * // => false
+     *
+     * _.isArrayLikeObject(_.noop);
+     * // => false
+     */
+    function isArrayLikeObject(value) {
+      return isObjectLike(value) && isArrayLike(value);
+    }
+
+    /**
+     * Checks if `value` is classified as a boolean primitive or object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
+     * @example
+     *
+     * _.isBoolean(false);
+     * // => true
+     *
+     * _.isBoolean(null);
+     * // => false
+     */
+    function isBoolean(value) {
+      return value === true || value === false ||
+        (isObjectLike(value) && objectToString.call(value) == boolTag);
+    }
+
+    /**
+     * Checks if `value` is a buffer.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
+     * @example
+     *
+     * _.isBuffer(new Buffer(2));
+     * // => true
+     *
+     * _.isBuffer(new Uint8Array(2));
+     * // => false
+     */
+    var isBuffer = nativeIsBuffer || stubFalse;
+
+    /**
+     * Checks if `value` is classified as a `Date` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+     * @example
+     *
+     * _.isDate(new Date);
+     * // => true
+     *
+     * _.isDate('Mon April 23 2012');
+     * // => false
+     */
+    var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
+
+    /**
+     * Checks if `value` is likely a DOM element.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a DOM element,
+     *  else `false`.
+     * @example
+     *
+     * _.isElement(document.body);
+     * // => true
+     *
+     * _.isElement('<body>');
+     * // => false
+     */
+    function isElement(value) {
+      return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value);
+    }
+
+    /**
+     * Checks if `value` is an empty object, collection, map, or set.
+     *
+     * Objects are considered empty if they have no own enumerable string keyed
+     * properties.
+     *
+     * Array-like values such as `arguments` objects, arrays, buffers, strings, or
+     * jQuery-like collections are considered empty if they have a `length` of `0`.
+     * Similarly, maps and sets are considered empty if they have a `size` of `0`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+     * @example
+     *
+     * _.isEmpty(null);
+     * // => true
+     *
+     * _.isEmpty(true);
+     * // => true
+     *
+     * _.isEmpty(1);
+     * // => true
+     *
+     * _.isEmpty([1, 2, 3]);
+     * // => false
+     *
+     * _.isEmpty({ 'a': 1 });
+     * // => false
+     */
+    function isEmpty(value) {
+      if (isArrayLike(value) &&
+          (isArray(value) || isString(value) || isFunction(value.splice) ||
+            isArguments(value) || isBuffer(value))) {
+        return !value.length;
+      }
+      if (isObjectLike(value)) {
+        var tag = getTag(value);
+        if (tag == mapTag || tag == setTag) {
+          return !value.size;
+        }
+      }
+      for (var key in value) {
+        if (hasOwnProperty.call(value, key)) {
+          return false;
+        }
+      }
+      return !(nonEnumShadows && keys(value).length);
+    }
+
+    /**
+     * Performs a deep comparison between two values to determine if they are
+     * equivalent.
+     *
+     * **Note:** This method supports comparing arrays, array buffers, booleans,
+     * date objects, error objects, maps, numbers, `Object` objects, regexes,
+     * sets, strings, symbols, and typed arrays. `Object` objects are compared
+     * by their own, not inherited, enumerable properties. Functions and DOM
+     * nodes are **not** supported.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if the values are equivalent,
+     *  else `false`.
+     * @example
+     *
+     * var object = { 'a': 1 };
+     * var other = { 'a': 1 };
+     *
+     * _.isEqual(object, other);
+     * // => true
+     *
+     * object === other;
+     * // => false
+     */
+    function isEqual(value, other) {
+      return baseIsEqual(value, other);
+    }
+
+    /**
+     * This method is like `_.isEqual` except that it accepts `customizer` which
+     * is invoked to compare values. If `customizer` returns `undefined`, comparisons
+     * are handled by the method instead. The `customizer` is invoked with up to
+     * six arguments: (objValue, othValue [, index|key, object, other, stack]).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @param {Function} [customizer] The function to customize comparisons.
+     * @returns {boolean} Returns `true` if the values are equivalent,
+     *  else `false`.
+     * @example
+     *
+     * function isGreeting(value) {
+     *   return /^h(?:i|ello)$/.test(value);
+     * }
+     *
+     * function customizer(objValue, othValue) {
+     *   if (isGreeting(objValue) && isGreeting(othValue)) {
+     *     return true;
+     *   }
+     * }
+     *
+     * var array = ['hello', 'goodbye'];
+     * var other = ['hi', 'goodbye'];
+     *
+     * _.isEqualWith(array, other, customizer);
+     * // => true
+     */
+    function isEqualWith(value, other, customizer) {
+      customizer = typeof customizer == 'function' ? customizer : undefined;
+      var result = customizer ? customizer(value, other) : undefined;
+      return result === undefined ? baseIsEqual(value, other, customizer) : !!result;
+    }
+
+    /**
+     * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
+     * `SyntaxError`, `TypeError`, or `URIError` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an error object,
+     *  else `false`.
+     * @example
+     *
+     * _.isError(new Error);
+     * // => true
+     *
+     * _.isError(Error);
+     * // => false
+     */
+    function isError(value) {
+      if (!isObjectLike(value)) {
+        return false;
+      }
+      return (objectToString.call(value) == errorTag) ||
+        (typeof value.message == 'string' && typeof value.name == 'string');
+    }
+
+    /**
+     * Checks if `value` is a finite primitive number.
+     *
+     * **Note:** This method is based on
+     * [`Number.isFinite`](https://mdn.io/Number/isFinite).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a finite number,
+     *  else `false`.
+     * @example
+     *
+     * _.isFinite(3);
+     * // => true
+     *
+     * _.isFinite(Number.MIN_VALUE);
+     * // => true
+     *
+     * _.isFinite(Infinity);
+     * // => false
+     *
+     * _.isFinite('3');
+     * // => false
+     */
+    function isFinite(value) {
+      return typeof value == 'number' && nativeIsFinite(value);
+    }
+
+    /**
+     * Checks if `value` is classified as a `Function` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+     * @example
+     *
+     * _.isFunction(_);
+     * // => true
+     *
+     * _.isFunction(/abc/);
+     * // => false
+     */
+    function isFunction(value) {
+      // The use of `Object#toString` avoids issues with the `typeof` operator
+      // in Safari 8 which returns 'object' for typed array and weak map constructors,
+      // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
+      var tag = isObject(value) ? objectToString.call(value) : '';
+      return tag == funcTag || tag == genTag;
+    }
+
+    /**
+     * Checks if `value` is an integer.
+     *
+     * **Note:** This method is based on
+     * [`Number.isInteger`](https://mdn.io/Number/isInteger).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an integer, else `false`.
+     * @example
+     *
+     * _.isInteger(3);
+     * // => true
+     *
+     * _.isInteger(Number.MIN_VALUE);
+     * // => false
+     *
+     * _.isInteger(Infinity);
+     * // => false
+     *
+     * _.isInteger('3');
+     * // => false
+     */
+    function isInteger(value) {
+      return typeof value == 'number' && value == toInteger(value);
+    }
+
+    /**
+     * Checks if `value` is a valid array-like length.
+     *
+     * **Note:** This function is loosely based on
+     * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a valid length,
+     *  else `false`.
+     * @example
+     *
+     * _.isLength(3);
+     * // => true
+     *
+     * _.isLength(Number.MIN_VALUE);
+     * // => false
+     *
+     * _.isLength(Infinity);
+     * // => false
+     *
+     * _.isLength('3');
+     * // => false
+     */
+    function isLength(value) {
+      return typeof value == 'number' &&
+        value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+    }
+
+    /**
+     * Checks if `value` is the
+     * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
+     * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+     * @example
+     *
+     * _.isObject({});
+     * // => true
+     *
+     * _.isObject([1, 2, 3]);
+     * // => true
+     *
+     * _.isObject(_.noop);
+     * // => true
+     *
+     * _.isObject(null);
+     * // => false
+     */
+    function isObject(value) {
+      var type = typeof value;
+      return !!value && (type == 'object' || type == 'function');
+    }
+
+    /**
+     * Checks if `value` is object-like. A value is object-like if it's not `null`
+     * and has a `typeof` result of "object".
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+     * @example
+     *
+     * _.isObjectLike({});
+     * // => true
+     *
+     * _.isObjectLike([1, 2, 3]);
+     * // => true
+     *
+     * _.isObjectLike(_.noop);
+     * // => false
+     *
+     * _.isObjectLike(null);
+     * // => false
+     */
+    function isObjectLike(value) {
+      return !!value && typeof value == 'object';
+    }
+
+    /**
+     * Checks if `value` is classified as a `Map` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+     * @example
+     *
+     * _.isMap(new Map);
+     * // => true
+     *
+     * _.isMap(new WeakMap);
+     * // => false
+     */
+    var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;
+
+    /**
+     * Performs a partial deep comparison between `object` and `source` to
+     * determine if `object` contains equivalent property values.
+     *
+     * **Note:** This method supports comparing the same values as `_.isEqual`
+     * and is equivalent to `_.matches` when `source` is partially applied.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Lang
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property values to match.
+     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2 };
+     *
+     * _.isMatch(object, { 'b': 2 });
+     * // => true
+     *
+     * _.isMatch(object, { 'b': 1 });
+     * // => false
+     */
+    function isMatch(object, source) {
+      return object === source || baseIsMatch(object, source, getMatchData(source));
+    }
+
+    /**
+     * This method is like `_.isMatch` except that it accepts `customizer` which
+     * is invoked to compare values. If `customizer` returns `undefined`, comparisons
+     * are handled by the method instead. The `customizer` is invoked with five
+     * arguments: (objValue, srcValue, index|key, object, source).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property values to match.
+     * @param {Function} [customizer] The function to customize comparisons.
+     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+     * @example
+     *
+     * function isGreeting(value) {
+     *   return /^h(?:i|ello)$/.test(value);
+     * }
+     *
+     * function customizer(objValue, srcValue) {
+     *   if (isGreeting(objValue) && isGreeting(srcValue)) {
+     *     return true;
+     *   }
+     * }
+     *
+     * var object = { 'greeting': 'hello' };
+     * var source = { 'greeting': 'hi' };
+     *
+     * _.isMatchWith(object, source, customizer);
+     * // => true
+     */
+    function isMatchWith(object, source, customizer) {
+      customizer = typeof customizer == 'function' ? customizer : undefined;
+      return baseIsMatch(object, source, getMatchData(source), customizer);
+    }
+
+    /**
+     * Checks if `value` is `NaN`.
+     *
+     * **Note:** This method is based on
+     * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as
+     * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for
+     * `undefined` and other non-number values.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+     * @example
+     *
+     * _.isNaN(NaN);
+     * // => true
+     *
+     * _.isNaN(new Number(NaN));
+     * // => true
+     *
+     * isNaN(undefined);
+     * // => true
+     *
+     * _.isNaN(undefined);
+     * // => false
+     */
+    function isNaN(value) {
+      // An `NaN` primitive is the only value that is not equal to itself.
+      // Perform the `toStringTag` check first to avoid errors with some
+      // ActiveX objects in IE.
+      return isNumber(value) && value != +value;
+    }
+
+    /**
+     * Checks if `value` is a pristine native function.
+     *
+     * **Note:** This method can't reliably detect native functions in the presence
+     * of the core-js package because core-js circumvents this kind of detection.
+     * Despite multiple requests, the core-js maintainer has made it clear: any
+     * attempt to fix the detection will be obstructed. As a result, we're left
+     * with little choice but to throw an error. Unfortunately, this also affects
+     * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),
+     * which rely on core-js.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a native function,
+     *  else `false`.
+     * @example
+     *
+     * _.isNative(Array.prototype.push);
+     * // => true
+     *
+     * _.isNative(_);
+     * // => false
+     */
+    function isNative(value) {
+      if (isMaskable(value)) {
+        throw new Error('This method is not supported with core-js. Try https://github.com/es-shims.');
+      }
+      return baseIsNative(value);
+    }
+
+    /**
+     * Checks if `value` is `null`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
+     * @example
+     *
+     * _.isNull(null);
+     * // => true
+     *
+     * _.isNull(void 0);
+     * // => false
+     */
+    function isNull(value) {
+      return value === null;
+    }
+
+    /**
+     * Checks if `value` is `null` or `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
+     * @example
+     *
+     * _.isNil(null);
+     * // => true
+     *
+     * _.isNil(void 0);
+     * // => true
+     *
+     * _.isNil(NaN);
+     * // => false
+     */
+    function isNil(value) {
+      return value == null;
+    }
+
+    /**
+     * Checks if `value` is classified as a `Number` primitive or object.
+     *
+     * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
+     * classified as numbers, use the `_.isFinite` method.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a number, else `false`.
+     * @example
+     *
+     * _.isNumber(3);
+     * // => true
+     *
+     * _.isNumber(Number.MIN_VALUE);
+     * // => true
+     *
+     * _.isNumber(Infinity);
+     * // => true
+     *
+     * _.isNumber('3');
+     * // => false
+     */
+    function isNumber(value) {
+      return typeof value == 'number' ||
+        (isObjectLike(value) && objectToString.call(value) == numberTag);
+    }
+
+    /**
+     * Checks if `value` is a plain object, that is, an object created by the
+     * `Object` constructor or one with a `[[Prototype]]` of `null`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.8.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object,
+     *  else `false`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     * }
+     *
+     * _.isPlainObject(new Foo);
+     * // => false
+     *
+     * _.isPlainObject([1, 2, 3]);
+     * // => false
+     *
+     * _.isPlainObject({ 'x': 0, 'y': 0 });
+     * // => true
+     *
+     * _.isPlainObject(Object.create(null));
+     * // => true
+     */
+    function isPlainObject(value) {
+      if (!isObjectLike(value) ||
+          objectToString.call(value) != objectTag || isHostObject(value)) {
+        return false;
+      }
+      var proto = getPrototype(value);
+      if (proto === null) {
+        return true;
+      }
+      var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
+      return (typeof Ctor == 'function' &&
+        Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
+    }
+
+    /**
+     * Checks if `value` is classified as a `RegExp` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.1.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+     * @example
+     *
+     * _.isRegExp(/abc/);
+     * // => true
+     *
+     * _.isRegExp('/abc/');
+     * // => false
+     */
+    var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;
+
+    /**
+     * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754
+     * double precision number which isn't the result of a rounded unsafe integer.
+     *
+     * **Note:** This method is based on
+     * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a safe integer,
+     *  else `false`.
+     * @example
+     *
+     * _.isSafeInteger(3);
+     * // => true
+     *
+     * _.isSafeInteger(Number.MIN_VALUE);
+     * // => false
+     *
+     * _.isSafeInteger(Infinity);
+     * // => false
+     *
+     * _.isSafeInteger('3');
+     * // => false
+     */
+    function isSafeInteger(value) {
+      return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;
+    }
+
+    /**
+     * Checks if `value` is classified as a `Set` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+     * @example
+     *
+     * _.isSet(new Set);
+     * // => true
+     *
+     * _.isSet(new WeakSet);
+     * // => false
+     */
+    var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;
+
+    /**
+     * Checks if `value` is classified as a `String` primitive or object.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a string, else `false`.
+     * @example
+     *
+     * _.isString('abc');
+     * // => true
+     *
+     * _.isString(1);
+     * // => false
+     */
+    function isString(value) {
+      return typeof value == 'string' ||
+        (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
+    }
+
+    /**
+     * Checks if `value` is classified as a `Symbol` primitive or object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+     * @example
+     *
+     * _.isSymbol(Symbol.iterator);
+     * // => true
+     *
+     * _.isSymbol('abc');
+     * // => false
+     */
+    function isSymbol(value) {
+      return typeof value == 'symbol' ||
+        (isObjectLike(value) && objectToString.call(value) == symbolTag);
+    }
+
+    /**
+     * Checks if `value` is classified as a typed array.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+     * @example
+     *
+     * _.isTypedArray(new Uint8Array);
+     * // => true
+     *
+     * _.isTypedArray([]);
+     * // => false
+     */
+    var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+
+    /**
+     * Checks if `value` is `undefined`.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+     * @example
+     *
+     * _.isUndefined(void 0);
+     * // => true
+     *
+     * _.isUndefined(null);
+     * // => false
+     */
+    function isUndefined(value) {
+      return value === undefined;
+    }
+
+    /**
+     * Checks if `value` is classified as a `WeakMap` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.
+     * @example
+     *
+     * _.isWeakMap(new WeakMap);
+     * // => true
+     *
+     * _.isWeakMap(new Map);
+     * // => false
+     */
+    function isWeakMap(value) {
+      return isObjectLike(value) && getTag(value) == weakMapTag;
+    }
+
+    /**
+     * Checks if `value` is classified as a `WeakSet` object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.3.0
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.
+     * @example
+     *
+     * _.isWeakSet(new WeakSet);
+     * // => true
+     *
+     * _.isWeakSet(new Set);
+     * // => false
+     */
+    function isWeakSet(value) {
+      return isObjectLike(value) && objectToString.call(value) == weakSetTag;
+    }
+
+    /**
+     * Checks if `value` is less than `other`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.9.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is less than `other`,
+     *  else `false`.
+     * @see _.gt
+     * @example
+     *
+     * _.lt(1, 3);
+     * // => true
+     *
+     * _.lt(3, 3);
+     * // => false
+     *
+     * _.lt(3, 1);
+     * // => false
+     */
+    var lt = createRelationalOperation(baseLt);
+
+    /**
+     * Checks if `value` is less than or equal to `other`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.9.0
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is less than or equal to
+     *  `other`, else `false`.
+     * @see _.gte
+     * @example
+     *
+     * _.lte(1, 3);
+     * // => true
+     *
+     * _.lte(3, 3);
+     * // => true
+     *
+     * _.lte(3, 1);
+     * // => false
+     */
+    var lte = createRelationalOperation(function(value, other) {
+      return value <= other;
+    });
+
+    /**
+     * Converts `value` to an array.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {Array} Returns the converted array.
+     * @example
+     *
+     * _.toArray({ 'a': 1, 'b': 2 });
+     * // => [1, 2]
+     *
+     * _.toArray('abc');
+     * // => ['a', 'b', 'c']
+     *
+     * _.toArray(1);
+     * // => []
+     *
+     * _.toArray(null);
+     * // => []
+     */
+    function toArray(value) {
+      if (!value) {
+        return [];
+      }
+      if (isArrayLike(value)) {
+        return isString(value) ? stringToArray(value) : copyArray(value);
+      }
+      if (iteratorSymbol && value[iteratorSymbol]) {
+        return iteratorToArray(value[iteratorSymbol]());
+      }
+      var tag = getTag(value),
+          func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
+
+      return func(value);
+    }
+
+    /**
+     * Converts `value` to a finite number.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.12.0
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {number} Returns the converted number.
+     * @example
+     *
+     * _.toFinite(3.2);
+     * // => 3.2
+     *
+     * _.toFinite(Number.MIN_VALUE);
+     * // => 5e-324
+     *
+     * _.toFinite(Infinity);
+     * // => 1.7976931348623157e+308
+     *
+     * _.toFinite('3.2');
+     * // => 3.2
+     */
+    function toFinite(value) {
+      if (!value) {
+        return value === 0 ? value : 0;
+      }
+      value = toNumber(value);
+      if (value === INFINITY || value === -INFINITY) {
+        var sign = (value < 0 ? -1 : 1);
+        return sign * MAX_INTEGER;
+      }
+      return value === value ? value : 0;
+    }
+
+    /**
+     * Converts `value` to an integer.
+     *
+     * **Note:** This method is loosely based on
+     * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {number} Returns the converted integer.
+     * @example
+     *
+     * _.toInteger(3.2);
+     * // => 3
+     *
+     * _.toInteger(Number.MIN_VALUE);
+     * // => 0
+     *
+     * _.toInteger(Infinity);
+     * // => 1.7976931348623157e+308
+     *
+     * _.toInteger('3.2');
+     * // => 3
+     */
+    function toInteger(value) {
+      var result = toFinite(value),
+          remainder = result % 1;
+
+      return result === result ? (remainder ? result - remainder : result) : 0;
+    }
+
+    /**
+     * Converts `value` to an integer suitable for use as the length of an
+     * array-like object.
+     *
+     * **Note:** This method is based on
+     * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {number} Returns the converted integer.
+     * @example
+     *
+     * _.toLength(3.2);
+     * // => 3
+     *
+     * _.toLength(Number.MIN_VALUE);
+     * // => 0
+     *
+     * _.toLength(Infinity);
+     * // => 4294967295
+     *
+     * _.toLength('3.2');
+     * // => 3
+     */
+    function toLength(value) {
+      return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;
+    }
+
+    /**
+     * Converts `value` to a number.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to process.
+     * @returns {number} Returns the number.
+     * @example
+     *
+     * _.toNumber(3.2);
+     * // => 3.2
+     *
+     * _.toNumber(Number.MIN_VALUE);
+     * // => 5e-324
+     *
+     * _.toNumber(Infinity);
+     * // => Infinity
+     *
+     * _.toNumber('3.2');
+     * // => 3.2
+     */
+    function toNumber(value) {
+      if (typeof value == 'number') {
+        return value;
+      }
+      if (isSymbol(value)) {
+        return NAN;
+      }
+      if (isObject(value)) {
+        var other = isFunction(value.valueOf) ? value.valueOf() : value;
+        value = isObject(other) ? (other + '') : other;
+      }
+      if (typeof value != 'string') {
+        return value === 0 ? value : +value;
+      }
+      value = value.replace(reTrim, '');
+      var isBinary = reIsBinary.test(value);
+      return (isBinary || reIsOctal.test(value))
+        ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+        : (reIsBadHex.test(value) ? NAN : +value);
+    }
+
+    /**
+     * Converts `value` to a plain object flattening inherited enumerable string
+     * keyed properties of `value` to own properties of the plain object.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {Object} Returns the converted plain object.
+     * @example
+     *
+     * function Foo() {
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.assign({ 'a': 1 }, new Foo);
+     * // => { 'a': 1, 'b': 2 }
+     *
+     * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
+     * // => { 'a': 1, 'b': 2, 'c': 3 }
+     */
+    function toPlainObject(value) {
+      return copyObject(value, keysIn(value));
+    }
+
+    /**
+     * Converts `value` to a safe integer. A safe integer can be compared and
+     * represented correctly.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {number} Returns the converted integer.
+     * @example
+     *
+     * _.toSafeInteger(3.2);
+     * // => 3
+     *
+     * _.toSafeInteger(Number.MIN_VALUE);
+     * // => 0
+     *
+     * _.toSafeInteger(Infinity);
+     * // => 9007199254740991
+     *
+     * _.toSafeInteger('3.2');
+     * // => 3
+     */
+    function toSafeInteger(value) {
+      return baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER);
+    }
+
+    /**
+     * Converts `value` to a string. An empty string is returned for `null`
+     * and `undefined` values. The sign of `-0` is preserved.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Lang
+     * @param {*} value The value to process.
+     * @returns {string} Returns the string.
+     * @example
+     *
+     * _.toString(null);
+     * // => ''
+     *
+     * _.toString(-0);
+     * // => '-0'
+     *
+     * _.toString([1, 2, 3]);
+     * // => '1,2,3'
+     */
+    function toString(value) {
+      return value == null ? '' : baseToString(value);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Assigns own enumerable string keyed properties of source objects to the
+     * destination object. Source objects are applied from left to right.
+     * Subsequent sources overwrite property assignments of previous sources.
+     *
+     * **Note:** This method mutates `object` and is loosely based on
+     * [`Object.assign`](https://mdn.io/Object/assign).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.10.0
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @see _.assignIn
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     * }
+     *
+     * function Bar() {
+     *   this.c = 3;
+     * }
+     *
+     * Foo.prototype.b = 2;
+     * Bar.prototype.d = 4;
+     *
+     * _.assign({ 'a': 0 }, new Foo, new Bar);
+     * // => { 'a': 1, 'c': 3 }
+     */
+    var assign = createAssigner(function(object, source) {
+      if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
+        copyObject(source, keys(source), object);
+        return;
+      }
+      for (var key in source) {
+        if (hasOwnProperty.call(source, key)) {
+          assignValue(object, key, source[key]);
+        }
+      }
+    });
+
+    /**
+     * This method is like `_.assign` except that it iterates over own and
+     * inherited source properties.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @alias extend
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @see _.assign
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     * }
+     *
+     * function Bar() {
+     *   this.c = 3;
+     * }
+     *
+     * Foo.prototype.b = 2;
+     * Bar.prototype.d = 4;
+     *
+     * _.assignIn({ 'a': 0 }, new Foo, new Bar);
+     * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
+     */
+    var assignIn = createAssigner(function(object, source) {
+      if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
+        copyObject(source, keysIn(source), object);
+        return;
+      }
+      for (var key in source) {
+        assignValue(object, key, source[key]);
+      }
+    });
+
+    /**
+     * This method is like `_.assignIn` except that it accepts `customizer`
+     * which is invoked to produce the assigned values. If `customizer` returns
+     * `undefined`, assignment is handled by the method instead. The `customizer`
+     * is invoked with five arguments: (objValue, srcValue, key, object, source).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @alias extendWith
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} sources The source objects.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     * @see _.assignWith
+     * @example
+     *
+     * function customizer(objValue, srcValue) {
+     *   return _.isUndefined(objValue) ? srcValue : objValue;
+     * }
+     *
+     * var defaults = _.partialRight(_.assignInWith, customizer);
+     *
+     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+     * // => { 'a': 1, 'b': 2 }
+     */
+    var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
+      copyObject(source, keysIn(source), object, customizer);
+    });
+
+    /**
+     * This method is like `_.assign` except that it accepts `customizer`
+     * which is invoked to produce the assigned values. If `customizer` returns
+     * `undefined`, assignment is handled by the method instead. The `customizer`
+     * is invoked with five arguments: (objValue, srcValue, key, object, source).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} sources The source objects.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     * @see _.assignInWith
+     * @example
+     *
+     * function customizer(objValue, srcValue) {
+     *   return _.isUndefined(objValue) ? srcValue : objValue;
+     * }
+     *
+     * var defaults = _.partialRight(_.assignWith, customizer);
+     *
+     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+     * // => { 'a': 1, 'b': 2 }
+     */
+    var assignWith = createAssigner(function(object, source, srcIndex, customizer) {
+      copyObject(source, keys(source), object, customizer);
+    });
+
+    /**
+     * Creates an array of values corresponding to `paths` of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.0.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {...(string|string[])} [paths] The property paths of elements to pick.
+     * @returns {Array} Returns the picked values.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+     *
+     * _.at(object, ['a[0].b.c', 'a[1]']);
+     * // => [3, 4]
+     */
+    var at = baseRest(function(object, paths) {
+      return baseAt(object, baseFlatten(paths, 1));
+    });
+
+    /**
+     * Creates an object that inherits from the `prototype` object. If a
+     * `properties` object is given, its own enumerable string keyed properties
+     * are assigned to the created object.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.3.0
+     * @category Object
+     * @param {Object} prototype The object to inherit from.
+     * @param {Object} [properties] The properties to assign to the object.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * function Circle() {
+     *   Shape.call(this);
+     * }
+     *
+     * Circle.prototype = _.create(Shape.prototype, {
+     *   'constructor': Circle
+     * });
+     *
+     * var circle = new Circle;
+     * circle instanceof Circle;
+     * // => true
+     *
+     * circle instanceof Shape;
+     * // => true
+     */
+    function create(prototype, properties) {
+      var result = baseCreate(prototype);
+      return properties ? baseAssign(result, properties) : result;
+    }
+
+    /**
+     * Assigns own and inherited enumerable string keyed properties of source
+     * objects to the destination object for all destination properties that
+     * resolve to `undefined`. Source objects are applied from left to right.
+     * Once a property is set, additional values of the same property are ignored.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @see _.defaultsDeep
+     * @example
+     *
+     * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+     * // => { 'a': 1, 'b': 2 }
+     */
+    var defaults = baseRest(function(args) {
+      args.push(undefined, assignInDefaults);
+      return apply(assignInWith, undefined, args);
+    });
+
+    /**
+     * This method is like `_.defaults` except that it recursively assigns
+     * default properties.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.10.0
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @see _.defaults
+     * @example
+     *
+     * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
+     * // => { 'a': { 'b': 2, 'c': 3 } }
+     */
+    var defaultsDeep = baseRest(function(args) {
+      args.push(undefined, mergeDefaults);
+      return apply(mergeWith, undefined, args);
+    });
+
+    /**
+     * This method is like `_.find` except that it returns the key of the first
+     * element `predicate` returns truthy for instead of the element itself.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.1.0
+     * @category Object
+     * @param {Object} object The object to search.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @returns {string|undefined} Returns the key of the matched element,
+     *  else `undefined`.
+     * @example
+     *
+     * var users = {
+     *   'barney':  { 'age': 36, 'active': true },
+     *   'fred':    { 'age': 40, 'active': false },
+     *   'pebbles': { 'age': 1,  'active': true }
+     * };
+     *
+     * _.findKey(users, function(o) { return o.age < 40; });
+     * // => 'barney' (iteration order is not guaranteed)
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.findKey(users, { 'age': 1, 'active': true });
+     * // => 'pebbles'
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.findKey(users, ['active', false]);
+     * // => 'fred'
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.findKey(users, 'active');
+     * // => 'barney'
+     */
+    function findKey(object, predicate) {
+      return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);
+    }
+
+    /**
+     * This method is like `_.findKey` except that it iterates over elements of
+     * a collection in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Object
+     * @param {Object} object The object to search.
+     * @param {Function} [predicate=_.identity] The function invoked per iteration.
+     * @returns {string|undefined} Returns the key of the matched element,
+     *  else `undefined`.
+     * @example
+     *
+     * var users = {
+     *   'barney':  { 'age': 36, 'active': true },
+     *   'fred':    { 'age': 40, 'active': false },
+     *   'pebbles': { 'age': 1,  'active': true }
+     * };
+     *
+     * _.findLastKey(users, function(o) { return o.age < 40; });
+     * // => returns 'pebbles' assuming `_.findKey` returns 'barney'
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.findLastKey(users, { 'age': 36, 'active': true });
+     * // => 'barney'
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.findLastKey(users, ['active', false]);
+     * // => 'fred'
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.findLastKey(users, 'active');
+     * // => 'pebbles'
+     */
+    function findLastKey(object, predicate) {
+      return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);
+    }
+
+    /**
+     * Iterates over own and inherited enumerable string keyed properties of an
+     * object and invokes `iteratee` for each property. The iteratee is invoked
+     * with three arguments: (value, key, object). Iteratee functions may exit
+     * iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.3.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     * @see _.forInRight
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forIn(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).
+     */
+    function forIn(object, iteratee) {
+      return object == null
+        ? object
+        : baseFor(object, getIteratee(iteratee, 3), keysIn);
+    }
+
+    /**
+     * This method is like `_.forIn` except that it iterates over properties of
+     * `object` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     * @see _.forIn
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forInRight(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.
+     */
+    function forInRight(object, iteratee) {
+      return object == null
+        ? object
+        : baseForRight(object, getIteratee(iteratee, 3), keysIn);
+    }
+
+    /**
+     * Iterates over own enumerable string keyed properties of an object and
+     * invokes `iteratee` for each property. The iteratee is invoked with three
+     * arguments: (value, key, object). Iteratee functions may exit iteration
+     * early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.3.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     * @see _.forOwnRight
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forOwn(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => Logs 'a' then 'b' (iteration order is not guaranteed).
+     */
+    function forOwn(object, iteratee) {
+      return object && baseForOwn(object, getIteratee(iteratee, 3));
+    }
+
+    /**
+     * This method is like `_.forOwn` except that it iterates over properties of
+     * `object` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.0.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     * @see _.forOwn
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forOwnRight(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.
+     */
+    function forOwnRight(object, iteratee) {
+      return object && baseForOwnRight(object, getIteratee(iteratee, 3));
+    }
+
+    /**
+     * Creates an array of function property names from own enumerable properties
+     * of `object`.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns the function names.
+     * @see _.functionsIn
+     * @example
+     *
+     * function Foo() {
+     *   this.a = _.constant('a');
+     *   this.b = _.constant('b');
+     * }
+     *
+     * Foo.prototype.c = _.constant('c');
+     *
+     * _.functions(new Foo);
+     * // => ['a', 'b']
+     */
+    function functions(object) {
+      return object == null ? [] : baseFunctions(object, keys(object));
+    }
+
+    /**
+     * Creates an array of function property names from own and inherited
+     * enumerable properties of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns the function names.
+     * @see _.functions
+     * @example
+     *
+     * function Foo() {
+     *   this.a = _.constant('a');
+     *   this.b = _.constant('b');
+     * }
+     *
+     * Foo.prototype.c = _.constant('c');
+     *
+     * _.functionsIn(new Foo);
+     * // => ['a', 'b', 'c']
+     */
+    function functionsIn(object) {
+      return object == null ? [] : baseFunctions(object, keysIn(object));
+    }
+
+    /**
+     * Gets the value at `path` of `object`. If the resolved value is
+     * `undefined`, the `defaultValue` is returned in its place.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.7.0
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to get.
+     * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+     *
+     * _.get(object, 'a[0].b.c');
+     * // => 3
+     *
+     * _.get(object, ['a', '0', 'b', 'c']);
+     * // => 3
+     *
+     * _.get(object, 'a.b.c', 'default');
+     * // => 'default'
+     */
+    function get(object, path, defaultValue) {
+      var result = object == null ? undefined : baseGet(object, path);
+      return result === undefined ? defaultValue : result;
+    }
+
+    /**
+     * Checks if `path` is a direct property of `object`.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path to check.
+     * @returns {boolean} Returns `true` if `path` exists, else `false`.
+     * @example
+     *
+     * var object = { 'a': { 'b': 2 } };
+     * var other = _.create({ 'a': _.create({ 'b': 2 }) });
+     *
+     * _.has(object, 'a');
+     * // => true
+     *
+     * _.has(object, 'a.b');
+     * // => true
+     *
+     * _.has(object, ['a', 'b']);
+     * // => true
+     *
+     * _.has(other, 'a');
+     * // => false
+     */
+    function has(object, path) {
+      return object != null && hasPath(object, path, baseHas);
+    }
+
+    /**
+     * Checks if `path` is a direct or inherited property of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path to check.
+     * @returns {boolean} Returns `true` if `path` exists, else `false`.
+     * @example
+     *
+     * var object = _.create({ 'a': _.create({ 'b': 2 }) });
+     *
+     * _.hasIn(object, 'a');
+     * // => true
+     *
+     * _.hasIn(object, 'a.b');
+     * // => true
+     *
+     * _.hasIn(object, ['a', 'b']);
+     * // => true
+     *
+     * _.hasIn(object, 'b');
+     * // => false
+     */
+    function hasIn(object, path) {
+      return object != null && hasPath(object, path, baseHasIn);
+    }
+
+    /**
+     * Creates an object composed of the inverted keys and values of `object`.
+     * If `object` contains duplicate values, subsequent values overwrite
+     * property assignments of previous values.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.7.0
+     * @category Object
+     * @param {Object} object The object to invert.
+     * @returns {Object} Returns the new inverted object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2, 'c': 1 };
+     *
+     * _.invert(object);
+     * // => { '1': 'c', '2': 'b' }
+     */
+    var invert = createInverter(function(result, value, key) {
+      result[value] = key;
+    }, constant(identity));
+
+    /**
+     * This method is like `_.invert` except that the inverted object is generated
+     * from the results of running each element of `object` thru `iteratee`. The
+     * corresponding inverted value of each inverted key is an array of keys
+     * responsible for generating the inverted value. The iteratee is invoked
+     * with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.1.0
+     * @category Object
+     * @param {Object} object The object to invert.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {Object} Returns the new inverted object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2, 'c': 1 };
+     *
+     * _.invertBy(object);
+     * // => { '1': ['a', 'c'], '2': ['b'] }
+     *
+     * _.invertBy(object, function(value) {
+     *   return 'group' + value;
+     * });
+     * // => { 'group1': ['a', 'c'], 'group2': ['b'] }
+     */
+    var invertBy = createInverter(function(result, value, key) {
+      if (hasOwnProperty.call(result, value)) {
+        result[value].push(key);
+      } else {
+        result[value] = [key];
+      }
+    }, getIteratee);
+
+    /**
+     * Invokes the method at `path` of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the method to invoke.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {*} Returns the result of the invoked method.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };
+     *
+     * _.invoke(object, 'a[0].b.c.slice', 1, 3);
+     * // => [2, 3]
+     */
+    var invoke = baseRest(baseInvoke);
+
+    /**
+     * Creates an array of the own enumerable property names of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects. See the
+     * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
+     * for more details.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.keys(new Foo);
+     * // => ['a', 'b'] (iteration order is not guaranteed)
+     *
+     * _.keys('hi');
+     * // => ['0', '1']
+     */
+    function keys(object) {
+      var isProto = isPrototype(object);
+      if (!(isProto || isArrayLike(object))) {
+        return baseKeys(object);
+      }
+      var indexes = indexKeys(object),
+          skipIndexes = !!indexes,
+          result = indexes || [],
+          length = result.length;
+
+      for (var key in object) {
+        if (baseHas(object, key) &&
+            !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
+            !(isProto && key == 'constructor')) {
+          result.push(key);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array of the own and inherited enumerable property names of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.keysIn(new Foo);
+     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
+     */
+    function keysIn(object) {
+      var index = -1,
+          isProto = isPrototype(object),
+          props = baseKeysIn(object),
+          propsLength = props.length,
+          indexes = indexKeys(object),
+          skipIndexes = !!indexes,
+          result = indexes || [],
+          length = result.length;
+
+      while (++index < propsLength) {
+        var key = props[index];
+        if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
+            !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+          result.push(key);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The opposite of `_.mapValues`; this method creates an object with the
+     * same values as `object` and keys generated by running each own enumerable
+     * string keyed property of `object` thru `iteratee`. The iteratee is invoked
+     * with three arguments: (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.8.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns the new mapped object.
+     * @see _.mapValues
+     * @example
+     *
+     * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
+     *   return key + value;
+     * });
+     * // => { 'a1': 1, 'b2': 2 }
+     */
+    function mapKeys(object, iteratee) {
+      var result = {};
+      iteratee = getIteratee(iteratee, 3);
+
+      baseForOwn(object, function(value, key, object) {
+        result[iteratee(value, key, object)] = value;
+      });
+      return result;
+    }
+
+    /**
+     * Creates an object with the same keys as `object` and values generated
+     * by running each own enumerable string keyed property of `object` thru
+     * `iteratee`. The iteratee is invoked with three arguments:
+     * (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @since 2.4.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Object} Returns the new mapped object.
+     * @see _.mapKeys
+     * @example
+     *
+     * var users = {
+     *   'fred':    { 'user': 'fred',    'age': 40 },
+     *   'pebbles': { 'user': 'pebbles', 'age': 1 }
+     * };
+     *
+     * _.mapValues(users, function(o) { return o.age; });
+     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.mapValues(users, 'age');
+     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
+     */
+    function mapValues(object, iteratee) {
+      var result = {};
+      iteratee = getIteratee(iteratee, 3);
+
+      baseForOwn(object, function(value, key, object) {
+        result[key] = iteratee(value, key, object);
+      });
+      return result;
+    }
+
+    /**
+     * This method is like `_.assign` except that it recursively merges own and
+     * inherited enumerable string keyed properties of source objects into the
+     * destination object. Source properties that resolve to `undefined` are
+     * skipped if a destination value exists. Array and plain object properties
+     * are merged recursively. Other objects and value types are overridden by
+     * assignment. Source objects are applied from left to right. Subsequent
+     * sources overwrite property assignments of previous sources.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.5.0
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = {
+     *   'a': [{ 'b': 2 }, { 'd': 4 }]
+     * };
+     *
+     * var other = {
+     *   'a': [{ 'c': 3 }, { 'e': 5 }]
+     * };
+     *
+     * _.merge(object, other);
+     * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
+     */
+    var merge = createAssigner(function(object, source, srcIndex) {
+      baseMerge(object, source, srcIndex);
+    });
+
+    /**
+     * This method is like `_.merge` except that it accepts `customizer` which
+     * is invoked to produce the merged values of the destination and source
+     * properties. If `customizer` returns `undefined`, merging is handled by the
+     * method instead. The `customizer` is invoked with seven arguments:
+     * (objValue, srcValue, key, object, source, stack).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} sources The source objects.
+     * @param {Function} customizer The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function customizer(objValue, srcValue) {
+     *   if (_.isArray(objValue)) {
+     *     return objValue.concat(srcValue);
+     *   }
+     * }
+     *
+     * var object = { 'a': [1], 'b': [2] };
+     * var other = { 'a': [3], 'b': [4] };
+     *
+     * _.mergeWith(object, other, customizer);
+     * // => { 'a': [1, 3], 'b': [2, 4] }
+     */
+    var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {
+      baseMerge(object, source, srcIndex, customizer);
+    });
+
+    /**
+     * The opposite of `_.pick`; this method creates an object composed of the
+     * own and inherited enumerable string keyed properties of `object` that are
+     * not omitted.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {...(string|string[])} [props] The property identifiers to omit.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': '2', 'c': 3 };
+     *
+     * _.omit(object, ['a', 'c']);
+     * // => { 'b': '2' }
+     */
+    var omit = baseRest(function(object, props) {
+      if (object == null) {
+        return {};
+      }
+      props = arrayMap(baseFlatten(props, 1), toKey);
+      return basePick(object, baseDifference(getAllKeysIn(object), props));
+    });
+
+    /**
+     * The opposite of `_.pickBy`; this method creates an object composed of
+     * the own and inherited enumerable string keyed properties of `object` that
+     * `predicate` doesn't return truthy for. The predicate is invoked with two
+     * arguments: (value, key).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {Function} [predicate=_.identity] The function invoked per property.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': '2', 'c': 3 };
+     *
+     * _.omitBy(object, _.isNumber);
+     * // => { 'b': '2' }
+     */
+    function omitBy(object, predicate) {
+      return pickBy(object, negate(getIteratee(predicate)));
+    }
+
+    /**
+     * Creates an object composed of the picked `object` properties.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {...(string|string[])} [props] The property identifiers to pick.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': '2', 'c': 3 };
+     *
+     * _.pick(object, ['a', 'c']);
+     * // => { 'a': 1, 'c': 3 }
+     */
+    var pick = baseRest(function(object, props) {
+      return object == null ? {} : basePick(object, arrayMap(baseFlatten(props, 1), toKey));
+    });
+
+    /**
+     * Creates an object composed of the `object` properties `predicate` returns
+     * truthy for. The predicate is invoked with two arguments: (value, key).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {Function} [predicate=_.identity] The function invoked per property.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': '2', 'c': 3 };
+     *
+     * _.pickBy(object, _.isNumber);
+     * // => { 'a': 1, 'c': 3 }
+     */
+    function pickBy(object, predicate) {
+      return object == null ? {} : basePickBy(object, getAllKeysIn(object), getIteratee(predicate));
+    }
+
+    /**
+     * This method is like `_.get` except that if the resolved value is a
+     * function it's invoked with the `this` binding of its parent object and
+     * its result is returned.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to resolve.
+     * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
+     *
+     * _.result(object, 'a[0].b.c1');
+     * // => 3
+     *
+     * _.result(object, 'a[0].b.c2');
+     * // => 4
+     *
+     * _.result(object, 'a[0].b.c3', 'default');
+     * // => 'default'
+     *
+     * _.result(object, 'a[0].b.c3', _.constant('default'));
+     * // => 'default'
+     */
+    function result(object, path, defaultValue) {
+      path = isKey(path, object) ? [path] : castPath(path);
+
+      var index = -1,
+          length = path.length;
+
+      // Ensure the loop is entered when path is empty.
+      if (!length) {
+        object = undefined;
+        length = 1;
+      }
+      while (++index < length) {
+        var value = object == null ? undefined : object[toKey(path[index])];
+        if (value === undefined) {
+          index = length;
+          value = defaultValue;
+        }
+        object = isFunction(value) ? value.call(object) : value;
+      }
+      return object;
+    }
+
+    /**
+     * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
+     * it's created. Arrays are created for missing index properties while objects
+     * are created for all other missing properties. Use `_.setWith` to customize
+     * `path` creation.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.7.0
+     * @category Object
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+     *
+     * _.set(object, 'a[0].b.c', 4);
+     * console.log(object.a[0].b.c);
+     * // => 4
+     *
+     * _.set(object, ['x', '0', 'y', 'z'], 5);
+     * console.log(object.x[0].y.z);
+     * // => 5
+     */
+    function set(object, path, value) {
+      return object == null ? object : baseSet(object, path, value);
+    }
+
+    /**
+     * This method is like `_.set` except that it accepts `customizer` which is
+     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`
+     * path creation is handled by the method instead. The `customizer` is invoked
+     * with three arguments: (nsValue, key, nsObject).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to set.
+     * @param {*} value The value to set.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = {};
+     *
+     * _.setWith(object, '[0][1]', 'a', Object);
+     * // => { '0': { '1': 'a' } }
+     */
+    function setWith(object, path, value, customizer) {
+      customizer = typeof customizer == 'function' ? customizer : undefined;
+      return object == null ? object : baseSet(object, path, value, customizer);
+    }
+
+    /**
+     * Creates an array of own enumerable string keyed-value pairs for `object`
+     * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
+     * entries are returned.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @alias entries
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the key-value pairs.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.toPairs(new Foo);
+     * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
+     */
+    var toPairs = createToPairs(keys);
+
+    /**
+     * Creates an array of own and inherited enumerable string keyed-value pairs
+     * for `object` which can be consumed by `_.fromPairs`. If `object` is a map
+     * or set, its entries are returned.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @alias entriesIn
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the key-value pairs.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.toPairsIn(new Foo);
+     * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
+     */
+    var toPairsIn = createToPairs(keysIn);
+
+    /**
+     * An alternative to `_.reduce`; this method transforms `object` to a new
+     * `accumulator` object which is the result of running each of its own
+     * enumerable string keyed properties thru `iteratee`, with each invocation
+     * potentially mutating the `accumulator` object. If `accumulator` is not
+     * provided, a new object with the same `[[Prototype]]` will be used. The
+     * iteratee is invoked with four arguments: (accumulator, value, key, object).
+     * Iteratee functions may exit iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.3.0
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The custom accumulator value.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * _.transform([2, 3, 4], function(result, n) {
+     *   result.push(n *= n);
+     *   return n % 2 == 0;
+     * }, []);
+     * // => [4, 9]
+     *
+     * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+     *   (result[value] || (result[value] = [])).push(key);
+     * }, {});
+     * // => { '1': ['a', 'c'], '2': ['b'] }
+     */
+    function transform(object, iteratee, accumulator) {
+      var isArr = isArray(object) || isTypedArray(object);
+      iteratee = getIteratee(iteratee, 4);
+
+      if (accumulator == null) {
+        if (isArr || isObject(object)) {
+          var Ctor = object.constructor;
+          if (isArr) {
+            accumulator = isArray(object) ? new Ctor : [];
+          } else {
+            accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};
+          }
+        } else {
+          accumulator = {};
+        }
+      }
+      (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {
+        return iteratee(accumulator, value, index, object);
+      });
+      return accumulator;
+    }
+
+    /**
+     * Removes the property at `path` of `object`.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Object
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to unset.
+     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 7 } }] };
+     * _.unset(object, 'a[0].b.c');
+     * // => true
+     *
+     * console.log(object);
+     * // => { 'a': [{ 'b': {} }] };
+     *
+     * _.unset(object, ['a', '0', 'b', 'c']);
+     * // => true
+     *
+     * console.log(object);
+     * // => { 'a': [{ 'b': {} }] };
+     */
+    function unset(object, path) {
+      return object == null ? true : baseUnset(object, path);
+    }
+
+    /**
+     * This method is like `_.set` except that accepts `updater` to produce the
+     * value to set. Use `_.updateWith` to customize `path` creation. The `updater`
+     * is invoked with one argument: (value).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.6.0
+     * @category Object
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to set.
+     * @param {Function} updater The function to produce the updated value.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+     *
+     * _.update(object, 'a[0].b.c', function(n) { return n * n; });
+     * console.log(object.a[0].b.c);
+     * // => 9
+     *
+     * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });
+     * console.log(object.x[0].y.z);
+     * // => 0
+     */
+    function update(object, path, updater) {
+      return object == null ? object : baseUpdate(object, path, castFunction(updater));
+    }
+
+    /**
+     * This method is like `_.update` except that it accepts `customizer` which is
+     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`
+     * path creation is handled by the method instead. The `customizer` is invoked
+     * with three arguments: (nsValue, key, nsObject).
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.6.0
+     * @category Object
+     * @param {Object} object The object to modify.
+     * @param {Array|string} path The path of the property to set.
+     * @param {Function} updater The function to produce the updated value.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = {};
+     *
+     * _.updateWith(object, '[0][1]', _.constant('a'), Object);
+     * // => { '0': { '1': 'a' } }
+     */
+    function updateWith(object, path, updater, customizer) {
+      customizer = typeof customizer == 'function' ? customizer : undefined;
+      return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);
+    }
+
+    /**
+     * Creates an array of the own enumerable string keyed property values of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property values.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.values(new Foo);
+     * // => [1, 2] (iteration order is not guaranteed)
+     *
+     * _.values('hi');
+     * // => ['h', 'i']
+     */
+    function values(object) {
+      return object ? baseValues(object, keys(object)) : [];
+    }
+
+    /**
+     * Creates an array of the own and inherited enumerable string keyed property
+     * values of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property values.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.valuesIn(new Foo);
+     * // => [1, 2, 3] (iteration order is not guaranteed)
+     */
+    function valuesIn(object) {
+      return object == null ? [] : baseValues(object, keysIn(object));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Clamps `number` within the inclusive `lower` and `upper` bounds.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Number
+     * @param {number} number The number to clamp.
+     * @param {number} [lower] The lower bound.
+     * @param {number} upper The upper bound.
+     * @returns {number} Returns the clamped number.
+     * @example
+     *
+     * _.clamp(-10, -5, 5);
+     * // => -5
+     *
+     * _.clamp(10, -5, 5);
+     * // => 5
+     */
+    function clamp(number, lower, upper) {
+      if (upper === undefined) {
+        upper = lower;
+        lower = undefined;
+      }
+      if (upper !== undefined) {
+        upper = toNumber(upper);
+        upper = upper === upper ? upper : 0;
+      }
+      if (lower !== undefined) {
+        lower = toNumber(lower);
+        lower = lower === lower ? lower : 0;
+      }
+      return baseClamp(toNumber(number), lower, upper);
+    }
+
+    /**
+     * Checks if `n` is between `start` and up to, but not including, `end`. If
+     * `end` is not specified, it's set to `start` with `start` then set to `0`.
+     * If `start` is greater than `end` the params are swapped to support
+     * negative ranges.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.3.0
+     * @category Number
+     * @param {number} number The number to check.
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
+     * @see _.range, _.rangeRight
+     * @example
+     *
+     * _.inRange(3, 2, 4);
+     * // => true
+     *
+     * _.inRange(4, 8);
+     * // => true
+     *
+     * _.inRange(4, 2);
+     * // => false
+     *
+     * _.inRange(2, 2);
+     * // => false
+     *
+     * _.inRange(1.2, 2);
+     * // => true
+     *
+     * _.inRange(5.2, 4);
+     * // => false
+     *
+     * _.inRange(-3, -2, -6);
+     * // => true
+     */
+    function inRange(number, start, end) {
+      start = toFinite(start);
+      if (end === undefined) {
+        end = start;
+        start = 0;
+      } else {
+        end = toFinite(end);
+      }
+      number = toNumber(number);
+      return baseInRange(number, start, end);
+    }
+
+    /**
+     * Produces a random number between the inclusive `lower` and `upper` bounds.
+     * If only one argument is provided a number between `0` and the given number
+     * is returned. If `floating` is `true`, or either `lower` or `upper` are
+     * floats, a floating-point number is returned instead of an integer.
+     *
+     * **Note:** JavaScript follows the IEEE-754 standard for resolving
+     * floating-point values which can produce unexpected results.
+     *
+     * @static
+     * @memberOf _
+     * @since 0.7.0
+     * @category Number
+     * @param {number} [lower=0] The lower bound.
+     * @param {number} [upper=1] The upper bound.
+     * @param {boolean} [floating] Specify returning a floating-point number.
+     * @returns {number} Returns the random number.
+     * @example
+     *
+     * _.random(0, 5);
+     * // => an integer between 0 and 5
+     *
+     * _.random(5);
+     * // => also an integer between 0 and 5
+     *
+     * _.random(5, true);
+     * // => a floating-point number between 0 and 5
+     *
+     * _.random(1.2, 5.2);
+     * // => a floating-point number between 1.2 and 5.2
+     */
+    function random(lower, upper, floating) {
+      if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {
+        upper = floating = undefined;
+      }
+      if (floating === undefined) {
+        if (typeof upper == 'boolean') {
+          floating = upper;
+          upper = undefined;
+        }
+        else if (typeof lower == 'boolean') {
+          floating = lower;
+          lower = undefined;
+        }
+      }
+      if (lower === undefined && upper === undefined) {
+        lower = 0;
+        upper = 1;
+      }
+      else {
+        lower = toFinite(lower);
+        if (upper === undefined) {
+          upper = lower;
+          lower = 0;
+        } else {
+          upper = toFinite(upper);
+        }
+      }
+      if (lower > upper) {
+        var temp = lower;
+        lower = upper;
+        upper = temp;
+      }
+      if (floating || lower % 1 || upper % 1) {
+        var rand = nativeRandom();
+        return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);
+      }
+      return baseRandom(lower, upper);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the camel cased string.
+     * @example
+     *
+     * _.camelCase('Foo Bar');
+     * // => 'fooBar'
+     *
+     * _.camelCase('--foo-bar--');
+     * // => 'fooBar'
+     *
+     * _.camelCase('__FOO_BAR__');
+     * // => 'fooBar'
+     */
+    var camelCase = createCompounder(function(result, word, index) {
+      word = word.toLowerCase();
+      return result + (index ? capitalize(word) : word);
+    });
+
+    /**
+     * Converts the first character of `string` to upper case and the remaining
+     * to lower case.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to capitalize.
+     * @returns {string} Returns the capitalized string.
+     * @example
+     *
+     * _.capitalize('FRED');
+     * // => 'Fred'
+     */
+    function capitalize(string) {
+      return upperFirst(toString(string).toLowerCase());
+    }
+
+    /**
+     * Deburrs `string` by converting
+     * [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
+     * to basic latin letters and removing
+     * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to deburr.
+     * @returns {string} Returns the deburred string.
+     * @example
+     *
+     * _.deburr('déjà vu');
+     * // => 'deja vu'
+     */
+    function deburr(string) {
+      string = toString(string);
+      return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, '');
+    }
+
+    /**
+     * Checks if `string` ends with the given target string.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to search.
+     * @param {string} [target] The string to search for.
+     * @param {number} [position=string.length] The position to search up to.
+     * @returns {boolean} Returns `true` if `string` ends with `target`,
+     *  else `false`.
+     * @example
+     *
+     * _.endsWith('abc', 'c');
+     * // => true
+     *
+     * _.endsWith('abc', 'b');
+     * // => false
+     *
+     * _.endsWith('abc', 'b', 2);
+     * // => true
+     */
+    function endsWith(string, target, position) {
+      string = toString(string);
+      target = baseToString(target);
+
+      var length = string.length;
+      position = position === undefined
+        ? length
+        : baseClamp(toInteger(position), 0, length);
+
+      var end = position;
+      position -= target.length;
+      return position >= 0 && string.slice(position, end) == target;
+    }
+
+    /**
+     * Converts the characters "&", "<", ">", '"', "'", and "\`" in `string` to
+     * their corresponding HTML entities.
+     *
+     * **Note:** No other characters are escaped. To escape additional
+     * characters use a third-party library like [_he_](https://mths.be/he).
+     *
+     * Though the ">" character is escaped for symmetry, characters like
+     * ">" and "/" don't need escaping in HTML and have no special meaning
+     * unless they're part of a tag or unquoted attribute value. See
+     * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
+     * (under "semi-related fun fact") for more details.
+     *
+     * Backticks are escaped because in IE < 9, they can break out of
+     * attribute values or HTML comments. See [#59](https://html5sec.org/#59),
+     * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and
+     * [#133](https://html5sec.org/#133) of the
+     * [HTML5 Security Cheatsheet](https://html5sec.org/) for more details.
+     *
+     * When working with HTML you should always
+     * [quote attribute values](http://wonko.com/post/html-escaping) to reduce
+     * XSS vectors.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escape('fred, barney, & pebbles');
+     * // => 'fred, barney, &amp; pebbles'
+     */
+    function escape(string) {
+      string = toString(string);
+      return (string && reHasUnescapedHtml.test(string))
+        ? string.replace(reUnescapedHtml, escapeHtmlChar)
+        : string;
+    }
+
+    /**
+     * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
+     * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escapeRegExp('[lodash](https://lodash.com/)');
+     * // => '\[lodash\]\(https://lodash\.com/\)'
+     */
+    function escapeRegExp(string) {
+      string = toString(string);
+      return (string && reHasRegExpChar.test(string))
+        ? string.replace(reRegExpChar, '\\$&')
+        : string;
+    }
+
+    /**
+     * Converts `string` to
+     * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the kebab cased string.
+     * @example
+     *
+     * _.kebabCase('Foo Bar');
+     * // => 'foo-bar'
+     *
+     * _.kebabCase('fooBar');
+     * // => 'foo-bar'
+     *
+     * _.kebabCase('__FOO_BAR__');
+     * // => 'foo-bar'
+     */
+    var kebabCase = createCompounder(function(result, word, index) {
+      return result + (index ? '-' : '') + word.toLowerCase();
+    });
+
+    /**
+     * Converts `string`, as space separated words, to lower case.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the lower cased string.
+     * @example
+     *
+     * _.lowerCase('--Foo-Bar--');
+     * // => 'foo bar'
+     *
+     * _.lowerCase('fooBar');
+     * // => 'foo bar'
+     *
+     * _.lowerCase('__FOO_BAR__');
+     * // => 'foo bar'
+     */
+    var lowerCase = createCompounder(function(result, word, index) {
+      return result + (index ? ' ' : '') + word.toLowerCase();
+    });
+
+    /**
+     * Converts the first character of `string` to lower case.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the converted string.
+     * @example
+     *
+     * _.lowerFirst('Fred');
+     * // => 'fred'
+     *
+     * _.lowerFirst('FRED');
+     * // => 'fRED'
+     */
+    var lowerFirst = createCaseFirst('toLowerCase');
+
+    /**
+     * Pads `string` on the left and right sides if it's shorter than `length`.
+     * Padding characters are truncated if they can't be evenly divided by `length`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.pad('abc', 8);
+     * // => '  abc   '
+     *
+     * _.pad('abc', 8, '_-');
+     * // => '_-abc_-_'
+     *
+     * _.pad('abc', 3);
+     * // => 'abc'
+     */
+    function pad(string, length, chars) {
+      string = toString(string);
+      length = toInteger(length);
+
+      var strLength = length ? stringSize(string) : 0;
+      if (!length || strLength >= length) {
+        return string;
+      }
+      var mid = (length - strLength) / 2;
+      return (
+        createPadding(nativeFloor(mid), chars) +
+        string +
+        createPadding(nativeCeil(mid), chars)
+      );
+    }
+
+    /**
+     * Pads `string` on the right side if it's shorter than `length`. Padding
+     * characters are truncated if they exceed `length`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.padEnd('abc', 6);
+     * // => 'abc   '
+     *
+     * _.padEnd('abc', 6, '_-');
+     * // => 'abc_-_'
+     *
+     * _.padEnd('abc', 3);
+     * // => 'abc'
+     */
+    function padEnd(string, length, chars) {
+      string = toString(string);
+      length = toInteger(length);
+
+      var strLength = length ? stringSize(string) : 0;
+      return (length && strLength < length)
+        ? (string + createPadding(length - strLength, chars))
+        : string;
+    }
+
+    /**
+     * Pads `string` on the left side if it's shorter than `length`. Padding
+     * characters are truncated if they exceed `length`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.padStart('abc', 6);
+     * // => '   abc'
+     *
+     * _.padStart('abc', 6, '_-');
+     * // => '_-_abc'
+     *
+     * _.padStart('abc', 3);
+     * // => 'abc'
+     */
+    function padStart(string, length, chars) {
+      string = toString(string);
+      length = toInteger(length);
+
+      var strLength = length ? stringSize(string) : 0;
+      return (length && strLength < length)
+        ? (createPadding(length - strLength, chars) + string)
+        : string;
+    }
+
+    /**
+     * Converts `string` to an integer of the specified radix. If `radix` is
+     * `undefined` or `0`, a `radix` of `10` is used unless `value` is a
+     * hexadecimal, in which case a `radix` of `16` is used.
+     *
+     * **Note:** This method aligns with the
+     * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.
+     *
+     * @static
+     * @memberOf _
+     * @since 1.1.0
+     * @category String
+     * @param {string} string The string to convert.
+     * @param {number} [radix=10] The radix to interpret `value` by.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {number} Returns the converted integer.
+     * @example
+     *
+     * _.parseInt('08');
+     * // => 8
+     *
+     * _.map(['6', '08', '10'], _.parseInt);
+     * // => [6, 8, 10]
+     */
+    function parseInt(string, radix, guard) {
+      // Chrome fails to trim leading <BOM> whitespace characters.
+      // See https://bugs.chromium.org/p/v8/issues/detail?id=3109 for more details.
+      if (guard || radix == null) {
+        radix = 0;
+      } else if (radix) {
+        radix = +radix;
+      }
+      string = toString(string).replace(reTrim, '');
+      return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10));
+    }
+
+    /**
+     * Repeats the given string `n` times.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to repeat.
+     * @param {number} [n=1] The number of times to repeat the string.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {string} Returns the repeated string.
+     * @example
+     *
+     * _.repeat('*', 3);
+     * // => '***'
+     *
+     * _.repeat('abc', 2);
+     * // => 'abcabc'
+     *
+     * _.repeat('abc', 0);
+     * // => ''
+     */
+    function repeat(string, n, guard) {
+      if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {
+        n = 1;
+      } else {
+        n = toInteger(n);
+      }
+      return baseRepeat(toString(string), n);
+    }
+
+    /**
+     * Replaces matches for `pattern` in `string` with `replacement`.
+     *
+     * **Note:** This method is based on
+     * [`String#replace`](https://mdn.io/String/replace).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to modify.
+     * @param {RegExp|string} pattern The pattern to replace.
+     * @param {Function|string} replacement The match replacement.
+     * @returns {string} Returns the modified string.
+     * @example
+     *
+     * _.replace('Hi Fred', 'Fred', 'Barney');
+     * // => 'Hi Barney'
+     */
+    function replace() {
+      var args = arguments,
+          string = toString(args[0]);
+
+      return args.length < 3 ? string : nativeReplace.call(string, args[1], args[2]);
+    }
+
+    /**
+     * Converts `string` to
+     * [snake case](https://en.wikipedia.org/wiki/Snake_case).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the snake cased string.
+     * @example
+     *
+     * _.snakeCase('Foo Bar');
+     * // => 'foo_bar'
+     *
+     * _.snakeCase('fooBar');
+     * // => 'foo_bar'
+     *
+     * _.snakeCase('--FOO-BAR--');
+     * // => 'foo_bar'
+     */
+    var snakeCase = createCompounder(function(result, word, index) {
+      return result + (index ? '_' : '') + word.toLowerCase();
+    });
+
+    /**
+     * Splits `string` by `separator`.
+     *
+     * **Note:** This method is based on
+     * [`String#split`](https://mdn.io/String/split).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to split.
+     * @param {RegExp|string} separator The separator pattern to split by.
+     * @param {number} [limit] The length to truncate results to.
+     * @returns {Array} Returns the string segments.
+     * @example
+     *
+     * _.split('a-b-c', '-', 2);
+     * // => ['a', 'b']
+     */
+    function split(string, separator, limit) {
+      if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {
+        separator = limit = undefined;
+      }
+      limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;
+      if (!limit) {
+        return [];
+      }
+      string = toString(string);
+      if (string && (
+            typeof separator == 'string' ||
+            (separator != null && !isRegExp(separator))
+          )) {
+        separator = baseToString(separator);
+        if (separator == '' && reHasComplexSymbol.test(string)) {
+          return castSlice(stringToArray(string), 0, limit);
+        }
+      }
+      return nativeSplit.call(string, separator, limit);
+    }
+
+    /**
+     * Converts `string` to
+     * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
+     *
+     * @static
+     * @memberOf _
+     * @since 3.1.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the start cased string.
+     * @example
+     *
+     * _.startCase('--foo-bar--');
+     * // => 'Foo Bar'
+     *
+     * _.startCase('fooBar');
+     * // => 'Foo Bar'
+     *
+     * _.startCase('__FOO_BAR__');
+     * // => 'FOO BAR'
+     */
+    var startCase = createCompounder(function(result, word, index) {
+      return result + (index ? ' ' : '') + upperFirst(word);
+    });
+
+    /**
+     * Checks if `string` starts with the given target string.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to search.
+     * @param {string} [target] The string to search for.
+     * @param {number} [position=0] The position to search from.
+     * @returns {boolean} Returns `true` if `string` starts with `target`,
+     *  else `false`.
+     * @example
+     *
+     * _.startsWith('abc', 'a');
+     * // => true
+     *
+     * _.startsWith('abc', 'b');
+     * // => false
+     *
+     * _.startsWith('abc', 'b', 1);
+     * // => true
+     */
+    function startsWith(string, target, position) {
+      string = toString(string);
+      position = baseClamp(toInteger(position), 0, string.length);
+      target = baseToString(target);
+      return string.slice(position, position + target.length) == target;
+    }
+
+    /**
+     * Creates a compiled template function that can interpolate data properties
+     * in "interpolate" delimiters, HTML-escape interpolated data properties in
+     * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
+     * properties may be accessed as free variables in the template. If a setting
+     * object is given, it takes precedence over `_.templateSettings` values.
+     *
+     * **Note:** In the development build `_.template` utilizes
+     * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
+     * for easier debugging.
+     *
+     * For more information on precompiling templates see
+     * [lodash's custom builds documentation](https://lodash.com/custom-builds).
+     *
+     * For more information on Chrome extension sandboxes see
+     * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The template string.
+     * @param {Object} [options={}] The options object.
+     * @param {RegExp} [options.escape=_.templateSettings.escape]
+     *  The HTML "escape" delimiter.
+     * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]
+     *  The "evaluate" delimiter.
+     * @param {Object} [options.imports=_.templateSettings.imports]
+     *  An object to import into the template as free variables.
+     * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]
+     *  The "interpolate" delimiter.
+     * @param {string} [options.sourceURL='lodash.templateSources[n]']
+     *  The sourceURL of the compiled template.
+     * @param {string} [options.variable='obj']
+     *  The data object variable name.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Function} Returns the compiled template function.
+     * @example
+     *
+     * // Use the "interpolate" delimiter to create a compiled template.
+     * var compiled = _.template('hello <%= user %>!');
+     * compiled({ 'user': 'fred' });
+     * // => 'hello fred!'
+     *
+     * // Use the HTML "escape" delimiter to escape data property values.
+     * var compiled = _.template('<b><%- value %></b>');
+     * compiled({ 'value': '<script>' });
+     * // => '<b>&lt;script&gt;</b>'
+     *
+     * // Use the "evaluate" delimiter to execute JavaScript and generate HTML.
+     * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>');
+     * compiled({ 'users': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // Use the internal `print` function in "evaluate" delimiters.
+     * var compiled = _.template('<% print("hello " + user); %>!');
+     * compiled({ 'user': 'barney' });
+     * // => 'hello barney!'
+     *
+     * // Use the ES delimiter as an alternative to the default "interpolate" delimiter.
+     * var compiled = _.template('hello ${ user }!');
+     * compiled({ 'user': 'pebbles' });
+     * // => 'hello pebbles!'
+     *
+     * // Use backslashes to treat delimiters as plain text.
+     * var compiled = _.template('<%= "\\<%- value %\\>" %>');
+     * compiled({ 'value': 'ignored' });
+     * // => '<%- value %>'
+     *
+     * // Use the `imports` option to import `jQuery` as `jq`.
+     * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>';
+     * var compiled = _.template(text, { 'imports': { 'jq': jQuery } });
+     * compiled({ 'users': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // Use the `sourceURL` option to specify a custom sourceURL for the template.
+     * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' });
+     * compiled(data);
+     * // => Find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector.
+     *
+     * // Use the `variable` option to ensure a with-statement isn't used in the compiled template.
+     * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });
+     * compiled.source;
+     * // => function(data) {
+     * //   var __t, __p = '';
+     * //   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';
+     * //   return __p;
+     * // }
+     *
+     * // Use custom template delimiters.
+     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
+     * var compiled = _.template('hello {{ user }}!');
+     * compiled({ 'user': 'mustache' });
+     * // => 'hello mustache!'
+     *
+     * // Use the `source` property to inline compiled templates for meaningful
+     * // line numbers in error messages and stack traces.
+     * fs.writeFileSync(path.join(process.cwd(), 'jst.js'), '\
+     *   var JST = {\
+     *     "main": ' + _.template(mainText).source + '\
+     *   };\
+     * ');
+     */
+    function template(string, options, guard) {
+      // Based on John Resig's `tmpl` implementation
+      // (http://ejohn.org/blog/javascript-micro-templating/)
+      // and Laura Doktorova's doT.js (https://github.com/olado/doT).
+      var settings = lodash.templateSettings;
+
+      if (guard && isIterateeCall(string, options, guard)) {
+        options = undefined;
+      }
+      string = toString(string);
+      options = assignInWith({}, options, settings, assignInDefaults);
+
+      var imports = assignInWith({}, options.imports, settings.imports, assignInDefaults),
+          importsKeys = keys(imports),
+          importsValues = baseValues(imports, importsKeys);
+
+      var isEscaping,
+          isEvaluating,
+          index = 0,
+          interpolate = options.interpolate || reNoMatch,
+          source = "__p += '";
+
+      // Compile the regexp to match each delimiter.
+      var reDelimiters = RegExp(
+        (options.escape || reNoMatch).source + '|' +
+        interpolate.source + '|' +
+        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+        (options.evaluate || reNoMatch).source + '|$'
+      , 'g');
+
+      // Use a sourceURL for easier debugging.
+      var sourceURL = '//# sourceURL=' +
+        ('sourceURL' in options
+          ? options.sourceURL
+          : ('lodash.templateSources[' + (++templateCounter) + ']')
+        ) + '\n';
+
+      string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+        interpolateValue || (interpolateValue = esTemplateValue);
+
+        // Escape characters that can't be included in string literals.
+        source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+        // Replace delimiters with snippets.
+        if (escapeValue) {
+          isEscaping = true;
+          source += "' +\n__e(" + escapeValue + ") +\n'";
+        }
+        if (evaluateValue) {
+          isEvaluating = true;
+          source += "';\n" + evaluateValue + ";\n__p += '";
+        }
+        if (interpolateValue) {
+          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+        }
+        index = offset + match.length;
+
+        // The JS engine embedded in Adobe products needs `match` returned in
+        // order to produce the correct `offset` value.
+        return match;
+      });
+
+      source += "';\n";
+
+      // If `variable` is not specified wrap a with-statement around the generated
+      // code to add the data object to the top of the scope chain.
+      var variable = options.variable;
+      if (!variable) {
+        source = 'with (obj) {\n' + source + '\n}\n';
+      }
+      // Cleanup code by stripping empty strings.
+      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+        .replace(reEmptyStringMiddle, '$1')
+        .replace(reEmptyStringTrailing, '$1;');
+
+      // Frame code as the function body.
+      source = 'function(' + (variable || 'obj') + ') {\n' +
+        (variable
+          ? ''
+          : 'obj || (obj = {});\n'
+        ) +
+        "var __t, __p = ''" +
+        (isEscaping
+           ? ', __e = _.escape'
+           : ''
+        ) +
+        (isEvaluating
+          ? ', __j = Array.prototype.join;\n' +
+            "function print() { __p += __j.call(arguments, '') }\n"
+          : ';\n'
+        ) +
+        source +
+        'return __p\n}';
+
+      var result = attempt(function() {
+        return Function(importsKeys, sourceURL + 'return ' + source)
+          .apply(undefined, importsValues);
+      });
+
+      // Provide the compiled function's source by its `toString` method or
+      // the `source` property as a convenience for inlining compiled templates.
+      result.source = source;
+      if (isError(result)) {
+        throw result;
+      }
+      return result;
+    }
+
+    /**
+     * Converts `string`, as a whole, to lower case just like
+     * [String#toLowerCase](https://mdn.io/toLowerCase).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the lower cased string.
+     * @example
+     *
+     * _.toLower('--Foo-Bar--');
+     * // => '--foo-bar--'
+     *
+     * _.toLower('fooBar');
+     * // => 'foobar'
+     *
+     * _.toLower('__FOO_BAR__');
+     * // => '__foo_bar__'
+     */
+    function toLower(value) {
+      return toString(value).toLowerCase();
+    }
+
+    /**
+     * Converts `string`, as a whole, to upper case just like
+     * [String#toUpperCase](https://mdn.io/toUpperCase).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the upper cased string.
+     * @example
+     *
+     * _.toUpper('--foo-bar--');
+     * // => '--FOO-BAR--'
+     *
+     * _.toUpper('fooBar');
+     * // => 'FOOBAR'
+     *
+     * _.toUpper('__foo_bar__');
+     * // => '__FOO_BAR__'
+     */
+    function toUpper(value) {
+      return toString(value).toUpperCase();
+    }
+
+    /**
+     * Removes leading and trailing whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trim('  abc  ');
+     * // => 'abc'
+     *
+     * _.trim('-_-abc-_-', '_-');
+     * // => 'abc'
+     *
+     * _.map(['  foo  ', '  bar  '], _.trim);
+     * // => ['foo', 'bar']
+     */
+    function trim(string, chars, guard) {
+      string = toString(string);
+      if (string && (guard || chars === undefined)) {
+        return string.replace(reTrim, '');
+      }
+      if (!string || !(chars = baseToString(chars))) {
+        return string;
+      }
+      var strSymbols = stringToArray(string),
+          chrSymbols = stringToArray(chars),
+          start = charsStartIndex(strSymbols, chrSymbols),
+          end = charsEndIndex(strSymbols, chrSymbols) + 1;
+
+      return castSlice(strSymbols, start, end).join('');
+    }
+
+    /**
+     * Removes trailing whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trimEnd('  abc  ');
+     * // => '  abc'
+     *
+     * _.trimEnd('-_-abc-_-', '_-');
+     * // => '-_-abc'
+     */
+    function trimEnd(string, chars, guard) {
+      string = toString(string);
+      if (string && (guard || chars === undefined)) {
+        return string.replace(reTrimEnd, '');
+      }
+      if (!string || !(chars = baseToString(chars))) {
+        return string;
+      }
+      var strSymbols = stringToArray(string),
+          end = charsEndIndex(strSymbols, stringToArray(chars)) + 1;
+
+      return castSlice(strSymbols, 0, end).join('');
+    }
+
+    /**
+     * Removes leading whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trimStart('  abc  ');
+     * // => 'abc  '
+     *
+     * _.trimStart('-_-abc-_-', '_-');
+     * // => 'abc-_-'
+     */
+    function trimStart(string, chars, guard) {
+      string = toString(string);
+      if (string && (guard || chars === undefined)) {
+        return string.replace(reTrimStart, '');
+      }
+      if (!string || !(chars = baseToString(chars))) {
+        return string;
+      }
+      var strSymbols = stringToArray(string),
+          start = charsStartIndex(strSymbols, stringToArray(chars));
+
+      return castSlice(strSymbols, start).join('');
+    }
+
+    /**
+     * Truncates `string` if it's longer than the given maximum string length.
+     * The last characters of the truncated string are replaced with the omission
+     * string which defaults to "...".
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to truncate.
+     * @param {Object} [options={}] The options object.
+     * @param {number} [options.length=30] The maximum string length.
+     * @param {string} [options.omission='...'] The string to indicate text is omitted.
+     * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
+     * @returns {string} Returns the truncated string.
+     * @example
+     *
+     * _.truncate('hi-diddly-ho there, neighborino');
+     * // => 'hi-diddly-ho there, neighbo...'
+     *
+     * _.truncate('hi-diddly-ho there, neighborino', {
+     *   'length': 24,
+     *   'separator': ' '
+     * });
+     * // => 'hi-diddly-ho there,...'
+     *
+     * _.truncate('hi-diddly-ho there, neighborino', {
+     *   'length': 24,
+     *   'separator': /,? +/
+     * });
+     * // => 'hi-diddly-ho there...'
+     *
+     * _.truncate('hi-diddly-ho there, neighborino', {
+     *   'omission': ' [...]'
+     * });
+     * // => 'hi-diddly-ho there, neig [...]'
+     */
+    function truncate(string, options) {
+      var length = DEFAULT_TRUNC_LENGTH,
+          omission = DEFAULT_TRUNC_OMISSION;
+
+      if (isObject(options)) {
+        var separator = 'separator' in options ? options.separator : separator;
+        length = 'length' in options ? toInteger(options.length) : length;
+        omission = 'omission' in options ? baseToString(options.omission) : omission;
+      }
+      string = toString(string);
+
+      var strLength = string.length;
+      if (reHasComplexSymbol.test(string)) {
+        var strSymbols = stringToArray(string);
+        strLength = strSymbols.length;
+      }
+      if (length >= strLength) {
+        return string;
+      }
+      var end = length - stringSize(omission);
+      if (end < 1) {
+        return omission;
+      }
+      var result = strSymbols
+        ? castSlice(strSymbols, 0, end).join('')
+        : string.slice(0, end);
+
+      if (separator === undefined) {
+        return result + omission;
+      }
+      if (strSymbols) {
+        end += (result.length - end);
+      }
+      if (isRegExp(separator)) {
+        if (string.slice(end).search(separator)) {
+          var match,
+              substring = result;
+
+          if (!separator.global) {
+            separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');
+          }
+          separator.lastIndex = 0;
+          while ((match = separator.exec(substring))) {
+            var newEnd = match.index;
+          }
+          result = result.slice(0, newEnd === undefined ? end : newEnd);
+        }
+      } else if (string.indexOf(baseToString(separator), end) != end) {
+        var index = result.lastIndexOf(separator);
+        if (index > -1) {
+          result = result.slice(0, index);
+        }
+      }
+      return result + omission;
+    }
+
+    /**
+     * The inverse of `_.escape`; this method converts the HTML entities
+     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, `&#39;`, and `&#96;` in `string` to
+     * their corresponding characters.
+     *
+     * **Note:** No other HTML entities are unescaped. To unescape additional
+     * HTML entities use a third-party library like [_he_](https://mths.be/he).
+     *
+     * @static
+     * @memberOf _
+     * @since 0.6.0
+     * @category String
+     * @param {string} [string=''] The string to unescape.
+     * @returns {string} Returns the unescaped string.
+     * @example
+     *
+     * _.unescape('fred, barney, &amp; pebbles');
+     * // => 'fred, barney, & pebbles'
+     */
+    function unescape(string) {
+      string = toString(string);
+      return (string && reHasEscapedHtml.test(string))
+        ? string.replace(reEscapedHtml, unescapeHtmlChar)
+        : string;
+    }
+
+    /**
+     * Converts `string`, as space separated words, to upper case.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the upper cased string.
+     * @example
+     *
+     * _.upperCase('--foo-bar');
+     * // => 'FOO BAR'
+     *
+     * _.upperCase('fooBar');
+     * // => 'FOO BAR'
+     *
+     * _.upperCase('__foo_bar__');
+     * // => 'FOO BAR'
+     */
+    var upperCase = createCompounder(function(result, word, index) {
+      return result + (index ? ' ' : '') + word.toUpperCase();
+    });
+
+    /**
+     * Converts the first character of `string` to upper case.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the converted string.
+     * @example
+     *
+     * _.upperFirst('fred');
+     * // => 'Fred'
+     *
+     * _.upperFirst('FRED');
+     * // => 'FRED'
+     */
+    var upperFirst = createCaseFirst('toUpperCase');
+
+    /**
+     * Splits `string` into an array of its words.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category String
+     * @param {string} [string=''] The string to inspect.
+     * @param {RegExp|string} [pattern] The pattern to match words.
+     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+     * @returns {Array} Returns the words of `string`.
+     * @example
+     *
+     * _.words('fred, barney, & pebbles');
+     * // => ['fred', 'barney', 'pebbles']
+     *
+     * _.words('fred, barney, & pebbles', /[^, ]+/g);
+     * // => ['fred', 'barney', '&', 'pebbles']
+     */
+    function words(string, pattern, guard) {
+      string = toString(string);
+      pattern = guard ? undefined : pattern;
+
+      if (pattern === undefined) {
+        pattern = reHasComplexWord.test(string) ? reComplexWord : reBasicWord;
+      }
+      return string.match(pattern) || [];
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Attempts to invoke `func`, returning either the result or the caught error
+     * object. Any additional arguments are provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Util
+     * @param {Function} func The function to attempt.
+     * @param {...*} [args] The arguments to invoke `func` with.
+     * @returns {*} Returns the `func` result or error object.
+     * @example
+     *
+     * // Avoid throwing errors for invalid selectors.
+     * var elements = _.attempt(function(selector) {
+     *   return document.querySelectorAll(selector);
+     * }, '>_>');
+     *
+     * if (_.isError(elements)) {
+     *   elements = [];
+     * }
+     */
+    var attempt = baseRest(function(func, args) {
+      try {
+        return apply(func, undefined, args);
+      } catch (e) {
+        return isError(e) ? e : new Error(e);
+      }
+    });
+
+    /**
+     * Binds methods of an object to the object itself, overwriting the existing
+     * method.
+     *
+     * **Note:** This method doesn't set the "length" property of bound functions.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {Object} object The object to bind and assign the bound methods to.
+     * @param {...(string|string[])} methodNames The object method names to bind.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'click': function() {
+     *     console.log('clicked ' + this.label);
+     *   }
+     * };
+     *
+     * _.bindAll(view, ['click']);
+     * jQuery(element).on('click', view.click);
+     * // => Logs 'clicked docs' when clicked.
+     */
+    var bindAll = baseRest(function(object, methodNames) {
+      arrayEach(baseFlatten(methodNames, 1), function(key) {
+        key = toKey(key);
+        object[key] = bind(object[key], object);
+      });
+      return object;
+    });
+
+    /**
+     * Creates a function that iterates over `pairs` and invokes the corresponding
+     * function of the first predicate to return truthy. The predicate-function
+     * pairs are invoked with the `this` binding and arguments of the created
+     * function.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {Array} pairs The predicate-function pairs.
+     * @returns {Function} Returns the new composite function.
+     * @example
+     *
+     * var func = _.cond([
+     *   [_.matches({ 'a': 1 }),           _.constant('matches A')],
+     *   [_.conforms({ 'b': _.isNumber }), _.constant('matches B')],
+     *   [_.stubTrue,                      _.constant('no match')]
+     * ]);
+     *
+     * func({ 'a': 1, 'b': 2 });
+     * // => 'matches A'
+     *
+     * func({ 'a': 0, 'b': 1 });
+     * // => 'matches B'
+     *
+     * func({ 'a': '1', 'b': '2' });
+     * // => 'no match'
+     */
+    function cond(pairs) {
+      var length = pairs ? pairs.length : 0,
+          toIteratee = getIteratee();
+
+      pairs = !length ? [] : arrayMap(pairs, function(pair) {
+        if (typeof pair[1] != 'function') {
+          throw new TypeError(FUNC_ERROR_TEXT);
+        }
+        return [toIteratee(pair[0]), pair[1]];
+      });
+
+      return baseRest(function(args) {
+        var index = -1;
+        while (++index < length) {
+          var pair = pairs[index];
+          if (apply(pair[0], this, args)) {
+            return apply(pair[1], this, args);
+          }
+        }
+      });
+    }
+
+    /**
+     * Creates a function that invokes the predicate properties of `source` with
+     * the corresponding property values of a given object, returning `true` if
+     * all predicates return truthy, else `false`.
+     *
+     * **Note:** The created function is equivalent to `_.conformsTo` with
+     * `source` partially applied.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {Object} source The object of property predicates to conform to.
+     * @returns {Function} Returns the new spec function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': 2, 'b': 1 },
+     *   { 'a': 1, 'b': 2 }
+     * ];
+     *
+     * _.filter(objects, _.conforms({ 'b': function(n) { return n > 1; } }));
+     * // => [{ 'a': 1, 'b': 2 }]
+     */
+    function conforms(source) {
+      return baseConforms(baseClone(source, true));
+    }
+
+    /**
+     * Creates a function that returns `value`.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.4.0
+     * @category Util
+     * @param {*} value The value to return from the new function.
+     * @returns {Function} Returns the new constant function.
+     * @example
+     *
+     * var objects = _.times(2, _.constant({ 'a': 1 }));
+     *
+     * console.log(objects);
+     * // => [{ 'a': 1 }, { 'a': 1 }]
+     *
+     * console.log(objects[0] === objects[1]);
+     * // => true
+     */
+    function constant(value) {
+      return function() {
+        return value;
+      };
+    }
+
+    /**
+     * Checks `value` to determine whether a default value should be returned in
+     * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
+     * or `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.14.0
+     * @category Util
+     * @param {*} value The value to check.
+     * @param {*} defaultValue The default value.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * _.defaultTo(1, 10);
+     * // => 1
+     *
+     * _.defaultTo(undefined, 10);
+     * // => 10
+     */
+    function defaultTo(value, defaultValue) {
+      return (value == null || value !== value) ? defaultValue : value;
+    }
+
+    /**
+     * Creates a function that returns the result of invoking the given functions
+     * with the `this` binding of the created function, where each successive
+     * invocation is supplied the return value of the previous.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Util
+     * @param {...(Function|Function[])} [funcs] The functions to invoke.
+     * @returns {Function} Returns the new composite function.
+     * @see _.flowRight
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var addSquare = _.flow([_.add, square]);
+     * addSquare(1, 2);
+     * // => 9
+     */
+    var flow = createFlow();
+
+    /**
+     * This method is like `_.flow` except that it creates a function that
+     * invokes the given functions from right to left.
+     *
+     * @static
+     * @since 3.0.0
+     * @memberOf _
+     * @category Util
+     * @param {...(Function|Function[])} [funcs] The functions to invoke.
+     * @returns {Function} Returns the new composite function.
+     * @see _.flow
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var addSquare = _.flowRight([square, _.add]);
+     * addSquare(1, 2);
+     * // => 9
+     */
+    var flowRight = createFlow(true);
+
+    /**
+     * This method returns the first argument it receives.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {*} value Any value.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * var object = { 'a': 1 };
+     *
+     * console.log(_.identity(object) === object);
+     * // => true
+     */
+    function identity(value) {
+      return value;
+    }
+
+    /**
+     * Creates a function that invokes `func` with the arguments of the created
+     * function. If `func` is a property name, the created function returns the
+     * property value for a given element. If `func` is an array or object, the
+     * created function returns `true` for elements that contain the equivalent
+     * source properties, otherwise it returns `false`.
+     *
+     * @static
+     * @since 4.0.0
+     * @memberOf _
+     * @category Util
+     * @param {*} [func=_.identity] The value to convert to a callback.
+     * @returns {Function} Returns the callback.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': true },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * // The `_.matches` iteratee shorthand.
+     * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
+     * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
+     *
+     * // The `_.matchesProperty` iteratee shorthand.
+     * _.filter(users, _.iteratee(['user', 'fred']));
+     * // => [{ 'user': 'fred', 'age': 40 }]
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.map(users, _.iteratee('user'));
+     * // => ['barney', 'fred']
+     *
+     * // Create custom iteratee shorthands.
+     * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
+     *   return !_.isRegExp(func) ? iteratee(func) : function(string) {
+     *     return func.test(string);
+     *   };
+     * });
+     *
+     * _.filter(['abc', 'def'], /ef/);
+     * // => ['def']
+     */
+    function iteratee(func) {
+      return baseIteratee(typeof func == 'function' ? func : baseClone(func, true));
+    }
+
+    /**
+     * Creates a function that performs a partial deep comparison between a given
+     * object and `source`, returning `true` if the given object has equivalent
+     * property values, else `false`.
+     *
+     * **Note:** The created function supports comparing the same values as
+     * `_.isEqual` is equivalent to `_.isMatch` with `source` partially applied.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Util
+     * @param {Object} source The object of property values to match.
+     * @returns {Function} Returns the new spec function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': 1, 'b': 2, 'c': 3 },
+     *   { 'a': 4, 'b': 5, 'c': 6 }
+     * ];
+     *
+     * _.filter(objects, _.matches({ 'a': 4, 'c': 6 }));
+     * // => [{ 'a': 4, 'b': 5, 'c': 6 }]
+     */
+    function matches(source) {
+      return baseMatches(baseClone(source, true));
+    }
+
+    /**
+     * Creates a function that performs a partial deep comparison between the
+     * value at `path` of a given object to `srcValue`, returning `true` if the
+     * object value is equivalent, else `false`.
+     *
+     * **Note:** This method supports comparing the same values as `_.isEqual`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.2.0
+     * @category Util
+     * @param {Array|string} path The path of the property to get.
+     * @param {*} srcValue The value to match.
+     * @returns {Function} Returns the new spec function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': 1, 'b': 2, 'c': 3 },
+     *   { 'a': 4, 'b': 5, 'c': 6 }
+     * ];
+     *
+     * _.find(objects, _.matchesProperty('a', 4));
+     * // => { 'a': 4, 'b': 5, 'c': 6 }
+     */
+    function matchesProperty(path, srcValue) {
+      return baseMatchesProperty(path, baseClone(srcValue, true));
+    }
+
+    /**
+     * Creates a function that invokes the method at `path` of a given object.
+     * Any additional arguments are provided to the invoked method.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.7.0
+     * @category Util
+     * @param {Array|string} path The path of the method to invoke.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {Function} Returns the new invoker function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': { 'b': _.constant(2) } },
+     *   { 'a': { 'b': _.constant(1) } }
+     * ];
+     *
+     * _.map(objects, _.method('a.b'));
+     * // => [2, 1]
+     *
+     * _.map(objects, _.method(['a', 'b']));
+     * // => [2, 1]
+     */
+    var method = baseRest(function(path, args) {
+      return function(object) {
+        return baseInvoke(object, path, args);
+      };
+    });
+
+    /**
+     * The opposite of `_.method`; this method creates a function that invokes
+     * the method at a given path of `object`. Any additional arguments are
+     * provided to the invoked method.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.7.0
+     * @category Util
+     * @param {Object} object The object to query.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {Function} Returns the new invoker function.
+     * @example
+     *
+     * var array = _.times(3, _.constant),
+     *     object = { 'a': array, 'b': array, 'c': array };
+     *
+     * _.map(['a[2]', 'c[0]'], _.methodOf(object));
+     * // => [2, 0]
+     *
+     * _.map([['a', '2'], ['c', '0']], _.methodOf(object));
+     * // => [2, 0]
+     */
+    var methodOf = baseRest(function(object, args) {
+      return function(path) {
+        return baseInvoke(object, path, args);
+      };
+    });
+
+    /**
+     * Adds all own enumerable string keyed function properties of a source
+     * object to the destination object. If `object` is a function, then methods
+     * are added to its prototype as well.
+     *
+     * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
+     * avoid conflicts caused by modifying the original.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {Function|Object} [object=lodash] The destination object.
+     * @param {Object} source The object of functions to add.
+     * @param {Object} [options={}] The options object.
+     * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
+     * @returns {Function|Object} Returns `object`.
+     * @example
+     *
+     * function vowels(string) {
+     *   return _.filter(string, function(v) {
+     *     return /[aeiou]/i.test(v);
+     *   });
+     * }
+     *
+     * _.mixin({ 'vowels': vowels });
+     * _.vowels('fred');
+     * // => ['e']
+     *
+     * _('fred').vowels().value();
+     * // => ['e']
+     *
+     * _.mixin({ 'vowels': vowels }, { 'chain': false });
+     * _('fred').vowels();
+     * // => ['e']
+     */
+    function mixin(object, source, options) {
+      var props = keys(source),
+          methodNames = baseFunctions(source, props);
+
+      if (options == null &&
+          !(isObject(source) && (methodNames.length || !props.length))) {
+        options = source;
+        source = object;
+        object = this;
+        methodNames = baseFunctions(source, keys(source));
+      }
+      var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
+          isFunc = isFunction(object);
+
+      arrayEach(methodNames, function(methodName) {
+        var func = source[methodName];
+        object[methodName] = func;
+        if (isFunc) {
+          object.prototype[methodName] = function() {
+            var chainAll = this.__chain__;
+            if (chain || chainAll) {
+              var result = object(this.__wrapped__),
+                  actions = result.__actions__ = copyArray(this.__actions__);
+
+              actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
+              result.__chain__ = chainAll;
+              return result;
+            }
+            return func.apply(object, arrayPush([this.value()], arguments));
+          };
+        }
+      });
+
+      return object;
+    }
+
+    /**
+     * Reverts the `_` variable to its previous value and returns a reference to
+     * the `lodash` function.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @returns {Function} Returns the `lodash` function.
+     * @example
+     *
+     * var lodash = _.noConflict();
+     */
+    function noConflict() {
+      if (root._ === this) {
+        root._ = oldDash;
+      }
+      return this;
+    }
+
+    /**
+     * This method returns `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.3.0
+     * @category Util
+     * @example
+     *
+     * _.times(2, _.noop);
+     * // => [undefined, undefined]
+     */
+    function noop() {
+      // No operation performed.
+    }
+
+    /**
+     * Creates a function that gets the argument at index `n`. If `n` is negative,
+     * the nth argument from the end is returned.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {number} [n=0] The index of the argument to return.
+     * @returns {Function} Returns the new pass-thru function.
+     * @example
+     *
+     * var func = _.nthArg(1);
+     * func('a', 'b', 'c', 'd');
+     * // => 'b'
+     *
+     * var func = _.nthArg(-2);
+     * func('a', 'b', 'c', 'd');
+     * // => 'c'
+     */
+    function nthArg(n) {
+      n = toInteger(n);
+      return baseRest(function(args) {
+        return baseNth(args, n);
+      });
+    }
+
+    /**
+     * Creates a function that invokes `iteratees` with the arguments it receives
+     * and returns their results.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {...(Function|Function[])} [iteratees=[_.identity]]
+     *  The iteratees to invoke.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var func = _.over([Math.max, Math.min]);
+     *
+     * func(1, 2, 3, 4);
+     * // => [4, 1]
+     */
+    var over = createOver(arrayMap);
+
+    /**
+     * Creates a function that checks if **all** of the `predicates` return
+     * truthy when invoked with the arguments it receives.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {...(Function|Function[])} [predicates=[_.identity]]
+     *  The predicates to check.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var func = _.overEvery([Boolean, isFinite]);
+     *
+     * func('1');
+     * // => true
+     *
+     * func(null);
+     * // => false
+     *
+     * func(NaN);
+     * // => false
+     */
+    var overEvery = createOver(arrayEvery);
+
+    /**
+     * Creates a function that checks if **any** of the `predicates` return
+     * truthy when invoked with the arguments it receives.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {...(Function|Function[])} [predicates=[_.identity]]
+     *  The predicates to check.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var func = _.overSome([Boolean, isFinite]);
+     *
+     * func('1');
+     * // => true
+     *
+     * func(null);
+     * // => true
+     *
+     * func(NaN);
+     * // => false
+     */
+    var overSome = createOver(arraySome);
+
+    /**
+     * Creates a function that returns the value at `path` of a given object.
+     *
+     * @static
+     * @memberOf _
+     * @since 2.4.0
+     * @category Util
+     * @param {Array|string} path The path of the property to get.
+     * @returns {Function} Returns the new accessor function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': { 'b': 2 } },
+     *   { 'a': { 'b': 1 } }
+     * ];
+     *
+     * _.map(objects, _.property('a.b'));
+     * // => [2, 1]
+     *
+     * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
+     * // => [1, 2]
+     */
+    function property(path) {
+      return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
+    }
+
+    /**
+     * The opposite of `_.property`; this method creates a function that returns
+     * the value at a given path of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.0.0
+     * @category Util
+     * @param {Object} object The object to query.
+     * @returns {Function} Returns the new accessor function.
+     * @example
+     *
+     * var array = [0, 1, 2],
+     *     object = { 'a': array, 'b': array, 'c': array };
+     *
+     * _.map(['a[2]', 'c[0]'], _.propertyOf(object));
+     * // => [2, 0]
+     *
+     * _.map([['a', '2'], ['c', '0']], _.propertyOf(object));
+     * // => [2, 0]
+     */
+    function propertyOf(object) {
+      return function(path) {
+        return object == null ? undefined : baseGet(object, path);
+      };
+    }
+
+    /**
+     * Creates an array of numbers (positive and/or negative) progressing from
+     * `start` up to, but not including, `end`. A step of `-1` is used if a negative
+     * `start` is specified without an `end` or `step`. If `end` is not specified,
+     * it's set to `start` with `start` then set to `0`.
+     *
+     * **Note:** JavaScript follows the IEEE-754 standard for resolving
+     * floating-point values which can produce unexpected results.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} [step=1] The value to increment or decrement by.
+     * @returns {Array} Returns the range of numbers.
+     * @see _.inRange, _.rangeRight
+     * @example
+     *
+     * _.range(4);
+     * // => [0, 1, 2, 3]
+     *
+     * _.range(-4);
+     * // => [0, -1, -2, -3]
+     *
+     * _.range(1, 5);
+     * // => [1, 2, 3, 4]
+     *
+     * _.range(0, 20, 5);
+     * // => [0, 5, 10, 15]
+     *
+     * _.range(0, -4, -1);
+     * // => [0, -1, -2, -3]
+     *
+     * _.range(1, 4, 0);
+     * // => [1, 1, 1]
+     *
+     * _.range(0);
+     * // => []
+     */
+    var range = createRange();
+
+    /**
+     * This method is like `_.range` except that it populates values in
+     * descending order.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} [step=1] The value to increment or decrement by.
+     * @returns {Array} Returns the range of numbers.
+     * @see _.inRange, _.range
+     * @example
+     *
+     * _.rangeRight(4);
+     * // => [3, 2, 1, 0]
+     *
+     * _.rangeRight(-4);
+     * // => [-3, -2, -1, 0]
+     *
+     * _.rangeRight(1, 5);
+     * // => [4, 3, 2, 1]
+     *
+     * _.rangeRight(0, 20, 5);
+     * // => [15, 10, 5, 0]
+     *
+     * _.rangeRight(0, -4, -1);
+     * // => [-3, -2, -1, 0]
+     *
+     * _.rangeRight(1, 4, 0);
+     * // => [1, 1, 1]
+     *
+     * _.rangeRight(0);
+     * // => []
+     */
+    var rangeRight = createRange(true);
+
+    /**
+     * This method returns a new empty array.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.13.0
+     * @category Util
+     * @returns {Array} Returns the new empty array.
+     * @example
+     *
+     * var arrays = _.times(2, _.stubArray);
+     *
+     * console.log(arrays);
+     * // => [[], []]
+     *
+     * console.log(arrays[0] === arrays[1]);
+     * // => false
+     */
+    function stubArray() {
+      return [];
+    }
+
+    /**
+     * This method returns `false`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.13.0
+     * @category Util
+     * @returns {boolean} Returns `false`.
+     * @example
+     *
+     * _.times(2, _.stubFalse);
+     * // => [false, false]
+     */
+    function stubFalse() {
+      return false;
+    }
+
+    /**
+     * This method returns a new empty object.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.13.0
+     * @category Util
+     * @returns {Object} Returns the new empty object.
+     * @example
+     *
+     * var objects = _.times(2, _.stubObject);
+     *
+     * console.log(objects);
+     * // => [{}, {}]
+     *
+     * console.log(objects[0] === objects[1]);
+     * // => false
+     */
+    function stubObject() {
+      return {};
+    }
+
+    /**
+     * This method returns an empty string.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.13.0
+     * @category Util
+     * @returns {string} Returns the empty string.
+     * @example
+     *
+     * _.times(2, _.stubString);
+     * // => ['', '']
+     */
+    function stubString() {
+      return '';
+    }
+
+    /**
+     * This method returns `true`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.13.0
+     * @category Util
+     * @returns {boolean} Returns `true`.
+     * @example
+     *
+     * _.times(2, _.stubTrue);
+     * // => [true, true]
+     */
+    function stubTrue() {
+      return true;
+    }
+
+    /**
+     * Invokes the iteratee `n` times, returning an array of the results of
+     * each invocation. The iteratee is invoked with one argument; (index).
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {number} n The number of times to invoke `iteratee`.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @returns {Array} Returns the array of results.
+     * @example
+     *
+     * _.times(3, String);
+     * // => ['0', '1', '2']
+     *
+     *  _.times(4, _.constant(0));
+     * // => [0, 0, 0, 0]
+     */
+    function times(n, iteratee) {
+      n = toInteger(n);
+      if (n < 1 || n > MAX_SAFE_INTEGER) {
+        return [];
+      }
+      var index = MAX_ARRAY_LENGTH,
+          length = nativeMin(n, MAX_ARRAY_LENGTH);
+
+      iteratee = getIteratee(iteratee);
+      n -= MAX_ARRAY_LENGTH;
+
+      var result = baseTimes(length, iteratee);
+      while (++index < n) {
+        iteratee(index);
+      }
+      return result;
+    }
+
+    /**
+     * Converts `value` to a property path array.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Util
+     * @param {*} value The value to convert.
+     * @returns {Array} Returns the new property path array.
+     * @example
+     *
+     * _.toPath('a.b.c');
+     * // => ['a', 'b', 'c']
+     *
+     * _.toPath('a[0].b.c');
+     * // => ['a', '0', 'b', 'c']
+     */
+    function toPath(value) {
+      if (isArray(value)) {
+        return arrayMap(value, toKey);
+      }
+      return isSymbol(value) ? [value] : copyArray(stringToPath(value));
+    }
+
+    /**
+     * Generates a unique ID. If `prefix` is given, the ID is appended to it.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Util
+     * @param {string} [prefix=''] The value to prefix the ID with.
+     * @returns {string} Returns the unique ID.
+     * @example
+     *
+     * _.uniqueId('contact_');
+     * // => 'contact_104'
+     *
+     * _.uniqueId();
+     * // => '105'
+     */
+    function uniqueId(prefix) {
+      var id = ++idCounter;
+      return toString(prefix) + id;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Adds two numbers.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.4.0
+     * @category Math
+     * @param {number} augend The first number in an addition.
+     * @param {number} addend The second number in an addition.
+     * @returns {number} Returns the total.
+     * @example
+     *
+     * _.add(6, 4);
+     * // => 10
+     */
+    var add = createMathOperation(function(augend, addend) {
+      return augend + addend;
+    }, 0);
+
+    /**
+     * Computes `number` rounded up to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.10.0
+     * @category Math
+     * @param {number} number The number to round up.
+     * @param {number} [precision=0] The precision to round up to.
+     * @returns {number} Returns the rounded up number.
+     * @example
+     *
+     * _.ceil(4.006);
+     * // => 5
+     *
+     * _.ceil(6.004, 2);
+     * // => 6.01
+     *
+     * _.ceil(6040, -2);
+     * // => 6100
+     */
+    var ceil = createRound('ceil');
+
+    /**
+     * Divide two numbers.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.7.0
+     * @category Math
+     * @param {number} dividend The first number in a division.
+     * @param {number} divisor The second number in a division.
+     * @returns {number} Returns the quotient.
+     * @example
+     *
+     * _.divide(6, 4);
+     * // => 1.5
+     */
+    var divide = createMathOperation(function(dividend, divisor) {
+      return dividend / divisor;
+    }, 1);
+
+    /**
+     * Computes `number` rounded down to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.10.0
+     * @category Math
+     * @param {number} number The number to round down.
+     * @param {number} [precision=0] The precision to round down to.
+     * @returns {number} Returns the rounded down number.
+     * @example
+     *
+     * _.floor(4.006);
+     * // => 4
+     *
+     * _.floor(0.046, 2);
+     * // => 0.04
+     *
+     * _.floor(4060, -2);
+     * // => 4000
+     */
+    var floor = createRound('floor');
+
+    /**
+     * Computes the maximum value of `array`. If `array` is empty or falsey,
+     * `undefined` is returned.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @returns {*} Returns the maximum value.
+     * @example
+     *
+     * _.max([4, 2, 8, 6]);
+     * // => 8
+     *
+     * _.max([]);
+     * // => undefined
+     */
+    function max(array) {
+      return (array && array.length)
+        ? baseExtremum(array, identity, baseGt)
+        : undefined;
+    }
+
+    /**
+     * This method is like `_.max` except that it accepts `iteratee` which is
+     * invoked for each element in `array` to generate the criterion by which
+     * the value is ranked. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {*} Returns the maximum value.
+     * @example
+     *
+     * var objects = [{ 'n': 1 }, { 'n': 2 }];
+     *
+     * _.maxBy(objects, function(o) { return o.n; });
+     * // => { 'n': 2 }
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.maxBy(objects, 'n');
+     * // => { 'n': 2 }
+     */
+    function maxBy(array, iteratee) {
+      return (array && array.length)
+        ? baseExtremum(array, getIteratee(iteratee, 2), baseGt)
+        : undefined;
+    }
+
+    /**
+     * Computes the mean of the values in `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @returns {number} Returns the mean.
+     * @example
+     *
+     * _.mean([4, 2, 8, 6]);
+     * // => 5
+     */
+    function mean(array) {
+      return baseMean(array, identity);
+    }
+
+    /**
+     * This method is like `_.mean` except that it accepts `iteratee` which is
+     * invoked for each element in `array` to generate the value to be averaged.
+     * The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.7.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {number} Returns the mean.
+     * @example
+     *
+     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
+     *
+     * _.meanBy(objects, function(o) { return o.n; });
+     * // => 5
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.meanBy(objects, 'n');
+     * // => 5
+     */
+    function meanBy(array, iteratee) {
+      return baseMean(array, getIteratee(iteratee, 2));
+    }
+
+    /**
+     * Computes the minimum value of `array`. If `array` is empty or falsey,
+     * `undefined` is returned.
+     *
+     * @static
+     * @since 0.1.0
+     * @memberOf _
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @returns {*} Returns the minimum value.
+     * @example
+     *
+     * _.min([4, 2, 8, 6]);
+     * // => 2
+     *
+     * _.min([]);
+     * // => undefined
+     */
+    function min(array) {
+      return (array && array.length)
+        ? baseExtremum(array, identity, baseLt)
+        : undefined;
+    }
+
+    /**
+     * This method is like `_.min` except that it accepts `iteratee` which is
+     * invoked for each element in `array` to generate the criterion by which
+     * the value is ranked. The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {*} Returns the minimum value.
+     * @example
+     *
+     * var objects = [{ 'n': 1 }, { 'n': 2 }];
+     *
+     * _.minBy(objects, function(o) { return o.n; });
+     * // => { 'n': 1 }
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.minBy(objects, 'n');
+     * // => { 'n': 1 }
+     */
+    function minBy(array, iteratee) {
+      return (array && array.length)
+        ? baseExtremum(array, getIteratee(iteratee, 2), baseLt)
+        : undefined;
+    }
+
+    /**
+     * Multiply two numbers.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.7.0
+     * @category Math
+     * @param {number} multiplier The first number in a multiplication.
+     * @param {number} multiplicand The second number in a multiplication.
+     * @returns {number} Returns the product.
+     * @example
+     *
+     * _.multiply(6, 4);
+     * // => 24
+     */
+    var multiply = createMathOperation(function(multiplier, multiplicand) {
+      return multiplier * multiplicand;
+    }, 1);
+
+    /**
+     * Computes `number` rounded to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.10.0
+     * @category Math
+     * @param {number} number The number to round.
+     * @param {number} [precision=0] The precision to round to.
+     * @returns {number} Returns the rounded number.
+     * @example
+     *
+     * _.round(4.006);
+     * // => 4
+     *
+     * _.round(4.006, 2);
+     * // => 4.01
+     *
+     * _.round(4060, -2);
+     * // => 4100
+     */
+    var round = createRound('round');
+
+    /**
+     * Subtract two numbers.
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Math
+     * @param {number} minuend The first number in a subtraction.
+     * @param {number} subtrahend The second number in a subtraction.
+     * @returns {number} Returns the difference.
+     * @example
+     *
+     * _.subtract(6, 4);
+     * // => 2
+     */
+    var subtract = createMathOperation(function(minuend, subtrahend) {
+      return minuend - subtrahend;
+    }, 0);
+
+    /**
+     * Computes the sum of the values in `array`.
+     *
+     * @static
+     * @memberOf _
+     * @since 3.4.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @returns {number} Returns the sum.
+     * @example
+     *
+     * _.sum([4, 2, 8, 6]);
+     * // => 20
+     */
+    function sum(array) {
+      return (array && array.length)
+        ? baseSum(array, identity)
+        : 0;
+    }
+
+    /**
+     * This method is like `_.sum` except that it accepts `iteratee` which is
+     * invoked for each element in `array` to generate the value to be summed.
+     * The iteratee is invoked with one argument: (value).
+     *
+     * @static
+     * @memberOf _
+     * @since 4.0.0
+     * @category Math
+     * @param {Array} array The array to iterate over.
+     * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+     * @returns {number} Returns the sum.
+     * @example
+     *
+     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
+     *
+     * _.sumBy(objects, function(o) { return o.n; });
+     * // => 20
+     *
+     * // The `_.property` iteratee shorthand.
+     * _.sumBy(objects, 'n');
+     * // => 20
+     */
+    function sumBy(array, iteratee) {
+      return (array && array.length)
+        ? baseSum(array, getIteratee(iteratee, 2))
+        : 0;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // Add methods that return wrapped values in chain sequences.
+    lodash.after = after;
+    lodash.ary = ary;
+    lodash.assign = assign;
+    lodash.assignIn = assignIn;
+    lodash.assignInWith = assignInWith;
+    lodash.assignWith = assignWith;
+    lodash.at = at;
+    lodash.before = before;
+    lodash.bind = bind;
+    lodash.bindAll = bindAll;
+    lodash.bindKey = bindKey;
+    lodash.castArray = castArray;
+    lodash.chain = chain;
+    lodash.chunk = chunk;
+    lodash.compact = compact;
+    lodash.concat = concat;
+    lodash.cond = cond;
+    lodash.conforms = conforms;
+    lodash.constant = constant;
+    lodash.countBy = countBy;
+    lodash.create = create;
+    lodash.curry = curry;
+    lodash.curryRight = curryRight;
+    lodash.debounce = debounce;
+    lodash.defaults = defaults;
+    lodash.defaultsDeep = defaultsDeep;
+    lodash.defer = defer;
+    lodash.delay = delay;
+    lodash.difference = difference;
+    lodash.differenceBy = differenceBy;
+    lodash.differenceWith = differenceWith;
+    lodash.drop = drop;
+    lodash.dropRight = dropRight;
+    lodash.dropRightWhile = dropRightWhile;
+    lodash.dropWhile = dropWhile;
+    lodash.fill = fill;
+    lodash.filter = filter;
+    lodash.flatMap = flatMap;
+    lodash.flatMapDeep = flatMapDeep;
+    lodash.flatMapDepth = flatMapDepth;
+    lodash.flatten = flatten;
+    lodash.flattenDeep = flattenDeep;
+    lodash.flattenDepth = flattenDepth;
+    lodash.flip = flip;
+    lodash.flow = flow;
+    lodash.flowRight = flowRight;
+    lodash.fromPairs = fromPairs;
+    lodash.functions = functions;
+    lodash.functionsIn = functionsIn;
+    lodash.groupBy = groupBy;
+    lodash.initial = initial;
+    lodash.intersection = intersection;
+    lodash.intersectionBy = intersectionBy;
+    lodash.intersectionWith = intersectionWith;
+    lodash.invert = invert;
+    lodash.invertBy = invertBy;
+    lodash.invokeMap = invokeMap;
+    lodash.iteratee = iteratee;
+    lodash.keyBy = keyBy;
+    lodash.keys = keys;
+    lodash.keysIn = keysIn;
+    lodash.map = map;
+    lodash.mapKeys = mapKeys;
+    lodash.mapValues = mapValues;
+    lodash.matches = matches;
+    lodash.matchesProperty = matchesProperty;
+    lodash.memoize = memoize;
+    lodash.merge = merge;
+    lodash.mergeWith = mergeWith;
+    lodash.method = method;
+    lodash.methodOf = methodOf;
+    lodash.mixin = mixin;
+    lodash.negate = negate;
+    lodash.nthArg = nthArg;
+    lodash.omit = omit;
+    lodash.omitBy = omitBy;
+    lodash.once = once;
+    lodash.orderBy = orderBy;
+    lodash.over = over;
+    lodash.overArgs = overArgs;
+    lodash.overEvery = overEvery;
+    lodash.overSome = overSome;
+    lodash.partial = partial;
+    lodash.partialRight = partialRight;
+    lodash.partition = partition;
+    lodash.pick = pick;
+    lodash.pickBy = pickBy;
+    lodash.property = property;
+    lodash.propertyOf = propertyOf;
+    lodash.pull = pull;
+    lodash.pullAll = pullAll;
+    lodash.pullAllBy = pullAllBy;
+    lodash.pullAllWith = pullAllWith;
+    lodash.pullAt = pullAt;
+    lodash.range = range;
+    lodash.rangeRight = rangeRight;
+    lodash.rearg = rearg;
+    lodash.reject = reject;
+    lodash.remove = remove;
+    lodash.rest = rest;
+    lodash.reverse = reverse;
+    lodash.sampleSize = sampleSize;
+    lodash.set = set;
+    lodash.setWith = setWith;
+    lodash.shuffle = shuffle;
+    lodash.slice = slice;
+    lodash.sortBy = sortBy;
+    lodash.sortedUniq = sortedUniq;
+    lodash.sortedUniqBy = sortedUniqBy;
+    lodash.split = split;
+    lodash.spread = spread;
+    lodash.tail = tail;
+    lodash.take = take;
+    lodash.takeRight = takeRight;
+    lodash.takeRightWhile = takeRightWhile;
+    lodash.takeWhile = takeWhile;
+    lodash.tap = tap;
+    lodash.throttle = throttle;
+    lodash.thru = thru;
+    lodash.toArray = toArray;
+    lodash.toPairs = toPairs;
+    lodash.toPairsIn = toPairsIn;
+    lodash.toPath = toPath;
+    lodash.toPlainObject = toPlainObject;
+    lodash.transform = transform;
+    lodash.unary = unary;
+    lodash.union = union;
+    lodash.unionBy = unionBy;
+    lodash.unionWith = unionWith;
+    lodash.uniq = uniq;
+    lodash.uniqBy = uniqBy;
+    lodash.uniqWith = uniqWith;
+    lodash.unset = unset;
+    lodash.unzip = unzip;
+    lodash.unzipWith = unzipWith;
+    lodash.update = update;
+    lodash.updateWith = updateWith;
+    lodash.values = values;
+    lodash.valuesIn = valuesIn;
+    lodash.without = without;
+    lodash.words = words;
+    lodash.wrap = wrap;
+    lodash.xor = xor;
+    lodash.xorBy = xorBy;
+    lodash.xorWith = xorWith;
+    lodash.zip = zip;
+    lodash.zipObject = zipObject;
+    lodash.zipObjectDeep = zipObjectDeep;
+    lodash.zipWith = zipWith;
+
+    // Add aliases.
+    lodash.entries = toPairs;
+    lodash.entriesIn = toPairsIn;
+    lodash.extend = assignIn;
+    lodash.extendWith = assignInWith;
+
+    // Add methods to `lodash.prototype`.
+    mixin(lodash, lodash);
+
+    /*------------------------------------------------------------------------*/
+
+    // Add methods that return unwrapped values in chain sequences.
+    lodash.add = add;
+    lodash.attempt = attempt;
+    lodash.camelCase = camelCase;
+    lodash.capitalize = capitalize;
+    lodash.ceil = ceil;
+    lodash.clamp = clamp;
+    lodash.clone = clone;
+    lodash.cloneDeep = cloneDeep;
+    lodash.cloneDeepWith = cloneDeepWith;
+    lodash.cloneWith = cloneWith;
+    lodash.conformsTo = conformsTo;
+    lodash.deburr = deburr;
+    lodash.defaultTo = defaultTo;
+    lodash.divide = divide;
+    lodash.endsWith = endsWith;
+    lodash.eq = eq;
+    lodash.escape = escape;
+    lodash.escapeRegExp = escapeRegExp;
+    lodash.every = every;
+    lodash.find = find;
+    lodash.findIndex = findIndex;
+    lodash.findKey = findKey;
+    lodash.findLast = findLast;
+    lodash.findLastIndex = findLastIndex;
+    lodash.findLastKey = findLastKey;
+    lodash.floor = floor;
+    lodash.forEach = forEach;
+    lodash.forEachRight = forEachRight;
+    lodash.forIn = forIn;
+    lodash.forInRight = forInRight;
+    lodash.forOwn = forOwn;
+    lodash.forOwnRight = forOwnRight;
+    lodash.get = get;
+    lodash.gt = gt;
+    lodash.gte = gte;
+    lodash.has = has;
+    lodash.hasIn = hasIn;
+    lodash.head = head;
+    lodash.identity = identity;
+    lodash.includes = includes;
+    lodash.indexOf = indexOf;
+    lodash.inRange = inRange;
+    lodash.invoke = invoke;
+    lodash.isArguments = isArguments;
+    lodash.isArray = isArray;
+    lodash.isArrayBuffer = isArrayBuffer;
+    lodash.isArrayLike = isArrayLike;
+    lodash.isArrayLikeObject = isArrayLikeObject;
+    lodash.isBoolean = isBoolean;
+    lodash.isBuffer = isBuffer;
+    lodash.isDate = isDate;
+    lodash.isElement = isElement;
+    lodash.isEmpty = isEmpty;
+    lodash.isEqual = isEqual;
+    lodash.isEqualWith = isEqualWith;
+    lodash.isError = isError;
+    lodash.isFinite = isFinite;
+    lodash.isFunction = isFunction;
+    lodash.isInteger = isInteger;
+    lodash.isLength = isLength;
+    lodash.isMap = isMap;
+    lodash.isMatch = isMatch;
+    lodash.isMatchWith = isMatchWith;
+    lodash.isNaN = isNaN;
+    lodash.isNative = isNative;
+    lodash.isNil = isNil;
+    lodash.isNull = isNull;
+    lodash.isNumber = isNumber;
+    lodash.isObject = isObject;
+    lodash.isObjectLike = isObjectLike;
+    lodash.isPlainObject = isPlainObject;
+    lodash.isRegExp = isRegExp;
+    lodash.isSafeInteger = isSafeInteger;
+    lodash.isSet = isSet;
+    lodash.isString = isString;
+    lodash.isSymbol = isSymbol;
+    lodash.isTypedArray = isTypedArray;
+    lodash.isUndefined = isUndefined;
+    lodash.isWeakMap = isWeakMap;
+    lodash.isWeakSet = isWeakSet;
+    lodash.join = join;
+    lodash.kebabCase = kebabCase;
+    lodash.last = last;
+    lodash.lastIndexOf = lastIndexOf;
+    lodash.lowerCase = lowerCase;
+    lodash.lowerFirst = lowerFirst;
+    lodash.lt = lt;
+    lodash.lte = lte;
+    lodash.max = max;
+    lodash.maxBy = maxBy;
+    lodash.mean = mean;
+    lodash.meanBy = meanBy;
+    lodash.min = min;
+    lodash.minBy = minBy;
+    lodash.stubArray = stubArray;
+    lodash.stubFalse = stubFalse;
+    lodash.stubObject = stubObject;
+    lodash.stubString = stubString;
+    lodash.stubTrue = stubTrue;
+    lodash.multiply = multiply;
+    lodash.nth = nth;
+    lodash.noConflict = noConflict;
+    lodash.noop = noop;
+    lodash.now = now;
+    lodash.pad = pad;
+    lodash.padEnd = padEnd;
+    lodash.padStart = padStart;
+    lodash.parseInt = parseInt;
+    lodash.random = random;
+    lodash.reduce = reduce;
+    lodash.reduceRight = reduceRight;
+    lodash.repeat = repeat;
+    lodash.replace = replace;
+    lodash.result = result;
+    lodash.round = round;
+    lodash.runInContext = runInContext;
+    lodash.sample = sample;
+    lodash.size = size;
+    lodash.snakeCase = snakeCase;
+    lodash.some = some;
+    lodash.sortedIndex = sortedIndex;
+    lodash.sortedIndexBy = sortedIndexBy;
+    lodash.sortedIndexOf = sortedIndexOf;
+    lodash.sortedLastIndex = sortedLastIndex;
+    lodash.sortedLastIndexBy = sortedLastIndexBy;
+    lodash.sortedLastIndexOf = sortedLastIndexOf;
+    lodash.startCase = startCase;
+    lodash.startsWith = startsWith;
+    lodash.subtract = subtract;
+    lodash.sum = sum;
+    lodash.sumBy = sumBy;
+    lodash.template = template;
+    lodash.times = times;
+    lodash.toFinite = toFinite;
+    lodash.toInteger = toInteger;
+    lodash.toLength = toLength;
+    lodash.toLower = toLower;
+    lodash.toNumber = toNumber;
+    lodash.toSafeInteger = toSafeInteger;
+    lodash.toString = toString;
+    lodash.toUpper = toUpper;
+    lodash.trim = trim;
+    lodash.trimEnd = trimEnd;
+    lodash.trimStart = trimStart;
+    lodash.truncate = truncate;
+    lodash.unescape = unescape;
+    lodash.uniqueId = uniqueId;
+    lodash.upperCase = upperCase;
+    lodash.upperFirst = upperFirst;
+
+    // Add aliases.
+    lodash.each = forEach;
+    lodash.eachRight = forEachRight;
+    lodash.first = head;
+
+    mixin(lodash, (function() {
+      var source = {};
+      baseForOwn(lodash, function(func, methodName) {
+        if (!hasOwnProperty.call(lodash.prototype, methodName)) {
+          source[methodName] = func;
+        }
+      });
+      return source;
+    }()), { 'chain': false });
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The semantic version number.
+     *
+     * @static
+     * @memberOf _
+     * @type {string}
+     */
+    lodash.VERSION = VERSION;
+
+    // Assign default placeholders.
+    arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {
+      lodash[methodName].placeholder = lodash;
+    });
+
+    // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
+    arrayEach(['drop', 'take'], function(methodName, index) {
+      LazyWrapper.prototype[methodName] = function(n) {
+        var filtered = this.__filtered__;
+        if (filtered && !index) {
+          return new LazyWrapper(this);
+        }
+        n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
+
+        var result = this.clone();
+        if (filtered) {
+          result.__takeCount__ = nativeMin(n, result.__takeCount__);
+        } else {
+          result.__views__.push({
+            'size': nativeMin(n, MAX_ARRAY_LENGTH),
+            'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
+          });
+        }
+        return result;
+      };
+
+      LazyWrapper.prototype[methodName + 'Right'] = function(n) {
+        return this.reverse()[methodName](n).reverse();
+      };
+    });
+
+    // Add `LazyWrapper` methods that accept an `iteratee` value.
+    arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
+      var type = index + 1,
+          isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
+
+      LazyWrapper.prototype[methodName] = function(iteratee) {
+        var result = this.clone();
+        result.__iteratees__.push({
+          'iteratee': getIteratee(iteratee, 3),
+          'type': type
+        });
+        result.__filtered__ = result.__filtered__ || isFilter;
+        return result;
+      };
+    });
+
+    // Add `LazyWrapper` methods for `_.head` and `_.last`.
+    arrayEach(['head', 'last'], function(methodName, index) {
+      var takeName = 'take' + (index ? 'Right' : '');
+
+      LazyWrapper.prototype[methodName] = function() {
+        return this[takeName](1).value()[0];
+      };
+    });
+
+    // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
+    arrayEach(['initial', 'tail'], function(methodName, index) {
+      var dropName = 'drop' + (index ? '' : 'Right');
+
+      LazyWrapper.prototype[methodName] = function() {
+        return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
+      };
+    });
+
+    LazyWrapper.prototype.compact = function() {
+      return this.filter(identity);
+    };
+
+    LazyWrapper.prototype.find = function(predicate) {
+      return this.filter(predicate).head();
+    };
+
+    LazyWrapper.prototype.findLast = function(predicate) {
+      return this.reverse().find(predicate);
+    };
+
+    LazyWrapper.prototype.invokeMap = baseRest(function(path, args) {
+      if (typeof path == 'function') {
+        return new LazyWrapper(this);
+      }
+      return this.map(function(value) {
+        return baseInvoke(value, path, args);
+      });
+    });
+
+    LazyWrapper.prototype.reject = function(predicate) {
+      return this.filter(negate(getIteratee(predicate)));
+    };
+
+    LazyWrapper.prototype.slice = function(start, end) {
+      start = toInteger(start);
+
+      var result = this;
+      if (result.__filtered__ && (start > 0 || end < 0)) {
+        return new LazyWrapper(result);
+      }
+      if (start < 0) {
+        result = result.takeRight(-start);
+      } else if (start) {
+        result = result.drop(start);
+      }
+      if (end !== undefined) {
+        end = toInteger(end);
+        result = end < 0 ? result.dropRight(-end) : result.take(end - start);
+      }
+      return result;
+    };
+
+    LazyWrapper.prototype.takeRightWhile = function(predicate) {
+      return this.reverse().takeWhile(predicate).reverse();
+    };
+
+    LazyWrapper.prototype.toArray = function() {
+      return this.take(MAX_ARRAY_LENGTH);
+    };
+
+    // Add `LazyWrapper` methods to `lodash.prototype`.
+    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+      var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
+          isTaker = /^(?:head|last)$/.test(methodName),
+          lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
+          retUnwrapped = isTaker || /^find/.test(methodName);
+
+      if (!lodashFunc) {
+        return;
+      }
+      lodash.prototype[methodName] = function() {
+        var value = this.__wrapped__,
+            args = isTaker ? [1] : arguments,
+            isLazy = value instanceof LazyWrapper,
+            iteratee = args[0],
+            useLazy = isLazy || isArray(value);
+
+        var interceptor = function(value) {
+          var result = lodashFunc.apply(lodash, arrayPush([value], args));
+          return (isTaker && chainAll) ? result[0] : result;
+        };
+
+        if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
+          // Avoid lazy use if the iteratee has a "length" value other than `1`.
+          isLazy = useLazy = false;
+        }
+        var chainAll = this.__chain__,
+            isHybrid = !!this.__actions__.length,
+            isUnwrapped = retUnwrapped && !chainAll,
+            onlyLazy = isLazy && !isHybrid;
+
+        if (!retUnwrapped && useLazy) {
+          value = onlyLazy ? value : new LazyWrapper(this);
+          var result = func.apply(value, args);
+          result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
+          return new LodashWrapper(result, chainAll);
+        }
+        if (isUnwrapped && onlyLazy) {
+          return func.apply(this, args);
+        }
+        result = this.thru(interceptor);
+        return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
+      };
+    });
+
+    // Add `Array` methods to `lodash.prototype`.
+    arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
+      var func = arrayProto[methodName],
+          chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
+          retUnwrapped = /^(?:pop|shift)$/.test(methodName);
+
+      lodash.prototype[methodName] = function() {
+        var args = arguments;
+        if (retUnwrapped && !this.__chain__) {
+          var value = this.value();
+          return func.apply(isArray(value) ? value : [], args);
+        }
+        return this[chainName](function(value) {
+          return func.apply(isArray(value) ? value : [], args);
+        });
+      };
+    });
+
+    // Map minified method names to their real names.
+    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+      var lodashFunc = lodash[methodName];
+      if (lodashFunc) {
+        var key = (lodashFunc.name + ''),
+            names = realNames[key] || (realNames[key] = []);
+
+        names.push({ 'name': methodName, 'func': lodashFunc });
+      }
+    });
+
+    realNames[createHybrid(undefined, BIND_KEY_FLAG).name] = [{
+      'name': 'wrapper',
+      'func': undefined
+    }];
+
+    // Add methods to `LazyWrapper`.
+    LazyWrapper.prototype.clone = lazyClone;
+    LazyWrapper.prototype.reverse = lazyReverse;
+    LazyWrapper.prototype.value = lazyValue;
+
+    // Add chain sequence methods to the `lodash` wrapper.
+    lodash.prototype.at = wrapperAt;
+    lodash.prototype.chain = wrapperChain;
+    lodash.prototype.commit = wrapperCommit;
+    lodash.prototype.next = wrapperNext;
+    lodash.prototype.plant = wrapperPlant;
+    lodash.prototype.reverse = wrapperReverse;
+    lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
+
+    // Add lazy aliases.
+    lodash.prototype.first = lodash.prototype.head;
+
+    if (iteratorSymbol) {
+      lodash.prototype[iteratorSymbol] = wrapperToIterator;
+    }
+    return lodash;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // Export lodash.
+  var _ = runInContext();
+
+  // Some AMD build optimizers, like r.js, check for condition patterns like:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // Expose Lodash on the global object to prevent errors when Lodash is
+    // loaded by a script tag in the presence of an AMD loader.
+    // See http://requirejs.org/docs/errors.html#mismatch for more details.
+    // Use `_.noConflict` to remove Lodash from the global object.
+    root._ = _;
+
+    // Define as an anonymous module so, through path mapping, it can be
+    // referenced as the "underscore" module.
+    define(function() {
+      return _;
+    });
+  }
+  // Check for `exports` after `define` in case a build optimizer adds it.
+  else if (freeModule) {
+    // Export for Node.js.
+    (freeModule.exports = _)._ = _;
+    // Export for CommonJS support.
+    freeExports._ = _;
+  }
+  else {
+    // Export to the global object.
+    root._ = _;
+  }
+}.call(this));
diff --git a/third_party/immer/extra/js/lib/mori.js b/third_party/immer/extra/js/lib/mori.js
new file mode 100644
index 0000000000..39954ef9fe
--- /dev/null
+++ b/third_party/immer/extra/js/lib/mori.js
@@ -0,0 +1,435 @@
+/*
+ * immer: immutable data structures for C++
+ * Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+ *
+ * This software is distributed under the Boost Software License, Version 1.0.
+ * See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+ */
+
+(function(definition){if(typeof exports==="object"){module.exports=definition();}else if(typeof define==="function"&&define.amd){define(definition);}else{mori=definition();}})(function(){return function(){
+if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) {
+    Math.imul = function (a, b) {
+        var ah  = (a >>> 16) & 0xffff;
+        var al = a & 0xffff;
+        var bh  = (b >>> 16) & 0xffff;
+        var bl = b & 0xffff;
+        // the shift by 0 fixes the sign on the high part
+        // the final |0 converts the unsigned value into a signed value
+        return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);
+    }
+}
+
+var k,aa=this;
+function n(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==
+b&&"undefined"==typeof a.call)return"object";return b}var ba="closure_uid_"+(1E9*Math.random()>>>0),ca=0;function r(a,b){var c=a.split("."),d=aa;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b};function da(a){return Array.prototype.join.call(arguments,"")};function ea(a,b){for(var c in a)b.call(void 0,a[c],c,a)};function fa(a,b){null!=a&&this.append.apply(this,arguments)}fa.prototype.Za="";fa.prototype.append=function(a,b,c){this.Za+=a;if(null!=b)for(var d=1;d<arguments.length;d++)this.Za+=arguments[d];return this};fa.prototype.clear=function(){this.Za=""};fa.prototype.toString=function(){return this.Za};function ga(a,b){a.sort(b||ha)}function ia(a,b){for(var c=0;c<a.length;c++)a[c]={index:c,value:a[c]};var d=b||ha;ga(a,function(a,b){return d(a.value,b.value)||a.index-b.index});for(c=0;c<a.length;c++)a[c]=a[c].value}function ha(a,b){return a>b?1:a<b?-1:0};var ja;if("undefined"===typeof ka)var ka=function(){throw Error("No *print-fn* fn set for evaluation environment");};var la=null,ma=null;if("undefined"===typeof na)var na=null;function oa(){return new pa(null,5,[sa,!0,ua,!0,wa,!1,ya,!1,za,la],null)}function t(a){return null!=a&&!1!==a}function Aa(a){return t(a)?!1:!0}function w(a,b){return a[n(null==b?null:b)]?!0:a._?!0:!1}function Ba(a){return null==a?null:a.constructor}
+function x(a,b){var c=Ba(b),c=t(t(c)?c.Yb:c)?c.Xb:n(b);return Error(["No protocol method ",a," defined for type ",c,": ",b].join(""))}function Da(a){var b=a.Xb;return t(b)?b:""+z(a)}var Ea="undefined"!==typeof Symbol&&"function"===n(Symbol)?Symbol.Cc:"@@iterator";function Fa(a){for(var b=a.length,c=Array(b),d=0;;)if(d<b)c[d]=a[d],d+=1;else break;return c}function Ha(a){for(var b=Array(arguments.length),c=0;;)if(c<b.length)b[c]=arguments[c],c+=1;else return b}
+var Ia=function(){function a(a,b){function c(a,b){a.push(b);return a}var g=[];return A.c?A.c(c,g,b):A.call(null,c,g,b)}function b(a){return c.a(null,a)}var c=null,c=function(d,c){switch(arguments.length){case 1:return b.call(this,d);case 2:return a.call(this,0,c)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Ja={},La={};function Ma(a){if(a?a.L:a)return a.L(a);var b;b=Ma[n(null==a?null:a)];if(!b&&(b=Ma._,!b))throw x("ICounted.-count",a);return b.call(null,a)}
+function Na(a){if(a?a.J:a)return a.J(a);var b;b=Na[n(null==a?null:a)];if(!b&&(b=Na._,!b))throw x("IEmptyableCollection.-empty",a);return b.call(null,a)}var Qa={};function Ra(a,b){if(a?a.G:a)return a.G(a,b);var c;c=Ra[n(null==a?null:a)];if(!c&&(c=Ra._,!c))throw x("ICollection.-conj",a);return c.call(null,a,b)}
+var Ta={},C=function(){function a(a,b,c){if(a?a.$:a)return a.$(a,b,c);var g;g=C[n(null==a?null:a)];if(!g&&(g=C._,!g))throw x("IIndexed.-nth",a);return g.call(null,a,b,c)}function b(a,b){if(a?a.Q:a)return a.Q(a,b);var c;c=C[n(null==a?null:a)];if(!c&&(c=C._,!c))throw x("IIndexed.-nth",a);return c.call(null,a,b)}var c=null,c=function(d,c,f){switch(arguments.length){case 2:return b.call(this,d,c);case 3:return a.call(this,d,c,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),
+Ua={};function Va(a){if(a?a.N:a)return a.N(a);var b;b=Va[n(null==a?null:a)];if(!b&&(b=Va._,!b))throw x("ISeq.-first",a);return b.call(null,a)}function Wa(a){if(a?a.S:a)return a.S(a);var b;b=Wa[n(null==a?null:a)];if(!b&&(b=Wa._,!b))throw x("ISeq.-rest",a);return b.call(null,a)}
+var Xa={},Za={},$a=function(){function a(a,b,c){if(a?a.s:a)return a.s(a,b,c);var g;g=$a[n(null==a?null:a)];if(!g&&(g=$a._,!g))throw x("ILookup.-lookup",a);return g.call(null,a,b,c)}function b(a,b){if(a?a.t:a)return a.t(a,b);var c;c=$a[n(null==a?null:a)];if(!c&&(c=$a._,!c))throw x("ILookup.-lookup",a);return c.call(null,a,b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=
+a;return c}(),ab={};function bb(a,b){if(a?a.rb:a)return a.rb(a,b);var c;c=bb[n(null==a?null:a)];if(!c&&(c=bb._,!c))throw x("IAssociative.-contains-key?",a);return c.call(null,a,b)}function cb(a,b,c){if(a?a.Ka:a)return a.Ka(a,b,c);var d;d=cb[n(null==a?null:a)];if(!d&&(d=cb._,!d))throw x("IAssociative.-assoc",a);return d.call(null,a,b,c)}var db={};function eb(a,b){if(a?a.wb:a)return a.wb(a,b);var c;c=eb[n(null==a?null:a)];if(!c&&(c=eb._,!c))throw x("IMap.-dissoc",a);return c.call(null,a,b)}var fb={};
+function hb(a){if(a?a.hb:a)return a.hb(a);var b;b=hb[n(null==a?null:a)];if(!b&&(b=hb._,!b))throw x("IMapEntry.-key",a);return b.call(null,a)}function ib(a){if(a?a.ib:a)return a.ib(a);var b;b=ib[n(null==a?null:a)];if(!b&&(b=ib._,!b))throw x("IMapEntry.-val",a);return b.call(null,a)}var jb={};function kb(a,b){if(a?a.Eb:a)return a.Eb(a,b);var c;c=kb[n(null==a?null:a)];if(!c&&(c=kb._,!c))throw x("ISet.-disjoin",a);return c.call(null,a,b)}
+function lb(a){if(a?a.La:a)return a.La(a);var b;b=lb[n(null==a?null:a)];if(!b&&(b=lb._,!b))throw x("IStack.-peek",a);return b.call(null,a)}function mb(a){if(a?a.Ma:a)return a.Ma(a);var b;b=mb[n(null==a?null:a)];if(!b&&(b=mb._,!b))throw x("IStack.-pop",a);return b.call(null,a)}var nb={};function pb(a,b,c){if(a?a.Ua:a)return a.Ua(a,b,c);var d;d=pb[n(null==a?null:a)];if(!d&&(d=pb._,!d))throw x("IVector.-assoc-n",a);return d.call(null,a,b,c)}
+function qb(a){if(a?a.Ra:a)return a.Ra(a);var b;b=qb[n(null==a?null:a)];if(!b&&(b=qb._,!b))throw x("IDeref.-deref",a);return b.call(null,a)}var rb={};function sb(a){if(a?a.H:a)return a.H(a);var b;b=sb[n(null==a?null:a)];if(!b&&(b=sb._,!b))throw x("IMeta.-meta",a);return b.call(null,a)}var tb={};function ub(a,b){if(a?a.F:a)return a.F(a,b);var c;c=ub[n(null==a?null:a)];if(!c&&(c=ub._,!c))throw x("IWithMeta.-with-meta",a);return c.call(null,a,b)}
+var vb={},wb=function(){function a(a,b,c){if(a?a.O:a)return a.O(a,b,c);var g;g=wb[n(null==a?null:a)];if(!g&&(g=wb._,!g))throw x("IReduce.-reduce",a);return g.call(null,a,b,c)}function b(a,b){if(a?a.R:a)return a.R(a,b);var c;c=wb[n(null==a?null:a)];if(!c&&(c=wb._,!c))throw x("IReduce.-reduce",a);return c.call(null,a,b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();
+function xb(a,b,c){if(a?a.gb:a)return a.gb(a,b,c);var d;d=xb[n(null==a?null:a)];if(!d&&(d=xb._,!d))throw x("IKVReduce.-kv-reduce",a);return d.call(null,a,b,c)}function yb(a,b){if(a?a.A:a)return a.A(a,b);var c;c=yb[n(null==a?null:a)];if(!c&&(c=yb._,!c))throw x("IEquiv.-equiv",a);return c.call(null,a,b)}function zb(a){if(a?a.B:a)return a.B(a);var b;b=zb[n(null==a?null:a)];if(!b&&(b=zb._,!b))throw x("IHash.-hash",a);return b.call(null,a)}var Bb={};
+function Cb(a){if(a?a.D:a)return a.D(a);var b;b=Cb[n(null==a?null:a)];if(!b&&(b=Cb._,!b))throw x("ISeqable.-seq",a);return b.call(null,a)}var Db={},Eb={},Fb={};function Gb(a){if(a?a.ab:a)return a.ab(a);var b;b=Gb[n(null==a?null:a)];if(!b&&(b=Gb._,!b))throw x("IReversible.-rseq",a);return b.call(null,a)}function Hb(a,b){if(a?a.Hb:a)return a.Hb(a,b);var c;c=Hb[n(null==a?null:a)];if(!c&&(c=Hb._,!c))throw x("ISorted.-sorted-seq",a);return c.call(null,a,b)}
+function Ib(a,b,c){if(a?a.Ib:a)return a.Ib(a,b,c);var d;d=Ib[n(null==a?null:a)];if(!d&&(d=Ib._,!d))throw x("ISorted.-sorted-seq-from",a);return d.call(null,a,b,c)}function Jb(a,b){if(a?a.Gb:a)return a.Gb(a,b);var c;c=Jb[n(null==a?null:a)];if(!c&&(c=Jb._,!c))throw x("ISorted.-entry-key",a);return c.call(null,a,b)}function Kb(a){if(a?a.Fb:a)return a.Fb(a);var b;b=Kb[n(null==a?null:a)];if(!b&&(b=Kb._,!b))throw x("ISorted.-comparator",a);return b.call(null,a)}
+function Lb(a,b){if(a?a.Wb:a)return a.Wb(0,b);var c;c=Lb[n(null==a?null:a)];if(!c&&(c=Lb._,!c))throw x("IWriter.-write",a);return c.call(null,a,b)}var Mb={};function Nb(a,b,c){if(a?a.v:a)return a.v(a,b,c);var d;d=Nb[n(null==a?null:a)];if(!d&&(d=Nb._,!d))throw x("IPrintWithWriter.-pr-writer",a);return d.call(null,a,b,c)}function Ob(a){if(a?a.$a:a)return a.$a(a);var b;b=Ob[n(null==a?null:a)];if(!b&&(b=Ob._,!b))throw x("IEditableCollection.-as-transient",a);return b.call(null,a)}
+function Pb(a,b){if(a?a.Sa:a)return a.Sa(a,b);var c;c=Pb[n(null==a?null:a)];if(!c&&(c=Pb._,!c))throw x("ITransientCollection.-conj!",a);return c.call(null,a,b)}function Qb(a){if(a?a.Ta:a)return a.Ta(a);var b;b=Qb[n(null==a?null:a)];if(!b&&(b=Qb._,!b))throw x("ITransientCollection.-persistent!",a);return b.call(null,a)}function Rb(a,b,c){if(a?a.kb:a)return a.kb(a,b,c);var d;d=Rb[n(null==a?null:a)];if(!d&&(d=Rb._,!d))throw x("ITransientAssociative.-assoc!",a);return d.call(null,a,b,c)}
+function Sb(a,b){if(a?a.Jb:a)return a.Jb(a,b);var c;c=Sb[n(null==a?null:a)];if(!c&&(c=Sb._,!c))throw x("ITransientMap.-dissoc!",a);return c.call(null,a,b)}function Tb(a,b,c){if(a?a.Ub:a)return a.Ub(0,b,c);var d;d=Tb[n(null==a?null:a)];if(!d&&(d=Tb._,!d))throw x("ITransientVector.-assoc-n!",a);return d.call(null,a,b,c)}function Ub(a){if(a?a.Vb:a)return a.Vb();var b;b=Ub[n(null==a?null:a)];if(!b&&(b=Ub._,!b))throw x("ITransientVector.-pop!",a);return b.call(null,a)}
+function Vb(a,b){if(a?a.Tb:a)return a.Tb(0,b);var c;c=Vb[n(null==a?null:a)];if(!c&&(c=Vb._,!c))throw x("ITransientSet.-disjoin!",a);return c.call(null,a,b)}function Xb(a){if(a?a.Pb:a)return a.Pb();var b;b=Xb[n(null==a?null:a)];if(!b&&(b=Xb._,!b))throw x("IChunk.-drop-first",a);return b.call(null,a)}function Yb(a){if(a?a.Cb:a)return a.Cb(a);var b;b=Yb[n(null==a?null:a)];if(!b&&(b=Yb._,!b))throw x("IChunkedSeq.-chunked-first",a);return b.call(null,a)}
+function Zb(a){if(a?a.Db:a)return a.Db(a);var b;b=Zb[n(null==a?null:a)];if(!b&&(b=Zb._,!b))throw x("IChunkedSeq.-chunked-rest",a);return b.call(null,a)}function $b(a){if(a?a.Bb:a)return a.Bb(a);var b;b=$b[n(null==a?null:a)];if(!b&&(b=$b._,!b))throw x("IChunkedNext.-chunked-next",a);return b.call(null,a)}function ac(a,b){if(a?a.bb:a)return a.bb(0,b);var c;c=ac[n(null==a?null:a)];if(!c&&(c=ac._,!c))throw x("IVolatile.-vreset!",a);return c.call(null,a,b)}var bc={};
+function cc(a){if(a?a.fb:a)return a.fb(a);var b;b=cc[n(null==a?null:a)];if(!b&&(b=cc._,!b))throw x("IIterable.-iterator",a);return b.call(null,a)}function dc(a){this.qc=a;this.q=0;this.j=1073741824}dc.prototype.Wb=function(a,b){return this.qc.append(b)};function ec(a){var b=new fa;a.v(null,new dc(b),oa());return""+z(b)}
+var fc="undefined"!==typeof Math.imul&&0!==(Math.imul.a?Math.imul.a(4294967295,5):Math.imul.call(null,4294967295,5))?function(a,b){return Math.imul.a?Math.imul.a(a,b):Math.imul.call(null,a,b)}:function(a,b){var c=a&65535,d=b&65535;return c*d+((a>>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};function gc(a){a=fc(a,3432918353);return fc(a<<15|a>>>-15,461845907)}function hc(a,b){var c=a^b;return fc(c<<13|c>>>-13,5)+3864292196}
+function ic(a,b){var c=a^b,c=fc(c^c>>>16,2246822507),c=fc(c^c>>>13,3266489909);return c^c>>>16}var kc={},lc=0;function mc(a){255<lc&&(kc={},lc=0);var b=kc[a];if("number"!==typeof b){a:if(null!=a)if(b=a.length,0<b){for(var c=0,d=0;;)if(c<b)var e=c+1,d=fc(31,d)+a.charCodeAt(c),c=e;else{b=d;break a}b=void 0}else b=0;else b=0;kc[a]=b;lc+=1}return a=b}
+function nc(a){a&&(a.j&4194304||a.vc)?a=a.B(null):"number"===typeof a?a=(Math.floor.b?Math.floor.b(a):Math.floor.call(null,a))%2147483647:!0===a?a=1:!1===a?a=0:"string"===typeof a?(a=mc(a),0!==a&&(a=gc(a),a=hc(0,a),a=ic(a,4))):a=a instanceof Date?a.valueOf():null==a?0:zb(a);return a}
+function oc(a){var b;b=a.name;var c;a:{c=1;for(var d=0;;)if(c<b.length){var e=c+2,d=hc(d,gc(b.charCodeAt(c-1)|b.charCodeAt(c)<<16));c=e}else{c=d;break a}c=void 0}c=1===(b.length&1)?c^gc(b.charCodeAt(b.length-1)):c;b=ic(c,fc(2,b.length));a=mc(a.ba);return b^a+2654435769+(b<<6)+(b>>2)}function pc(a,b){if(a.ta===b.ta)return 0;var c=Aa(a.ba);if(t(c?b.ba:c))return-1;if(t(a.ba)){if(Aa(b.ba))return 1;c=ha(a.ba,b.ba);return 0===c?ha(a.name,b.name):c}return ha(a.name,b.name)}
+function qc(a,b,c,d,e){this.ba=a;this.name=b;this.ta=c;this.Ya=d;this.Z=e;this.j=2154168321;this.q=4096}k=qc.prototype;k.v=function(a,b){return Lb(b,this.ta)};k.B=function(){var a=this.Ya;return null!=a?a:this.Ya=a=oc(this)};k.F=function(a,b){return new qc(this.ba,this.name,this.ta,this.Ya,b)};k.H=function(){return this.Z};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return $a.c(c,this,null);case 3:return $a.c(c,this,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return $a.c(c,this,null)};a.c=function(a,c,d){return $a.c(c,this,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return $a.c(a,this,null)};k.a=function(a,b){return $a.c(a,this,b)};k.A=function(a,b){return b instanceof qc?this.ta===b.ta:!1};
+k.toString=function(){return this.ta};var rc=function(){function a(a,b){var c=null!=a?[z(a),z("/"),z(b)].join(""):b;return new qc(a,b,c,null,null)}function b(a){return a instanceof qc?a:c.a(null,a)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();
+function D(a){if(null==a)return null;if(a&&(a.j&8388608||a.mc))return a.D(null);if(a instanceof Array||"string"===typeof a)return 0===a.length?null:new F(a,0);if(w(Bb,a))return Cb(a);throw Error([z(a),z(" is not ISeqable")].join(""));}function G(a){if(null==a)return null;if(a&&(a.j&64||a.jb))return a.N(null);a=D(a);return null==a?null:Va(a)}function H(a){return null!=a?a&&(a.j&64||a.jb)?a.S(null):(a=D(a))?Wa(a):J:J}function K(a){return null==a?null:a&&(a.j&128||a.xb)?a.T(null):D(H(a))}
+var sc=function(){function a(a,b){return null==a?null==b:a===b||yb(a,b)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){for(;;)if(b.a(a,d))if(K(e))a=d,d=G(e),e=K(e);else return b.a(d,G(e));else return!1}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return!0;
+case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(){return!0};b.a=a;b.d=c.d;return b}();function tc(a){this.C=a}tc.prototype.next=function(){if(null!=this.C){var a=G(this.C);this.C=K(this.C);return{done:!1,value:a}}return{done:!0,value:null}};function uc(a){return new tc(D(a))}
+function vc(a,b){var c=gc(a),c=hc(0,c);return ic(c,b)}function wc(a){var b=0,c=1;for(a=D(a);;)if(null!=a)b+=1,c=fc(31,c)+nc(G(a))|0,a=K(a);else return vc(c,b)}function xc(a){var b=0,c=0;for(a=D(a);;)if(null!=a)b+=1,c=c+nc(G(a))|0,a=K(a);else return vc(c,b)}La["null"]=!0;Ma["null"]=function(){return 0};Date.prototype.A=function(a,b){return b instanceof Date&&this.toString()===b.toString()};yb.number=function(a,b){return a===b};rb["function"]=!0;sb["function"]=function(){return null};
+Ja["function"]=!0;zb._=function(a){return a[ba]||(a[ba]=++ca)};function yc(a){this.o=a;this.q=0;this.j=32768}yc.prototype.Ra=function(){return this.o};function Ac(a){return a instanceof yc}function Bc(a){return Ac(a)?L.b?L.b(a):L.call(null,a):a}function L(a){return qb(a)}
+var Cc=function(){function a(a,b,c,d){for(var l=Ma(a);;)if(d<l){var m=C.a(a,d);c=b.a?b.a(c,m):b.call(null,c,m);if(Ac(c))return qb(c);d+=1}else return c}function b(a,b,c){var d=Ma(a),l=c;for(c=0;;)if(c<d){var m=C.a(a,c),l=b.a?b.a(l,m):b.call(null,l,m);if(Ac(l))return qb(l);c+=1}else return l}function c(a,b){var c=Ma(a);if(0===c)return b.l?b.l():b.call(null);for(var d=C.a(a,0),l=1;;)if(l<c){var m=C.a(a,l),d=b.a?b.a(d,m):b.call(null,d,m);if(Ac(d))return qb(d);l+=1}else return d}var d=null,d=function(d,
+f,g,h){switch(arguments.length){case 2:return c.call(this,d,f);case 3:return b.call(this,d,f,g);case 4:return a.call(this,d,f,g,h)}throw Error("Invalid arity: "+arguments.length);};d.a=c;d.c=b;d.n=a;return d}(),Dc=function(){function a(a,b,c,d){for(var l=a.length;;)if(d<l){var m=a[d];c=b.a?b.a(c,m):b.call(null,c,m);if(Ac(c))return qb(c);d+=1}else return c}function b(a,b,c){var d=a.length,l=c;for(c=0;;)if(c<d){var m=a[c],l=b.a?b.a(l,m):b.call(null,l,m);if(Ac(l))return qb(l);c+=1}else return l}function c(a,
+b){var c=a.length;if(0===a.length)return b.l?b.l():b.call(null);for(var d=a[0],l=1;;)if(l<c){var m=a[l],d=b.a?b.a(d,m):b.call(null,d,m);if(Ac(d))return qb(d);l+=1}else return d}var d=null,d=function(d,f,g,h){switch(arguments.length){case 2:return c.call(this,d,f);case 3:return b.call(this,d,f,g);case 4:return a.call(this,d,f,g,h)}throw Error("Invalid arity: "+arguments.length);};d.a=c;d.c=b;d.n=a;return d}();function Ec(a){return a?a.j&2||a.cc?!0:a.j?!1:w(La,a):w(La,a)}
+function Fc(a){return a?a.j&16||a.Qb?!0:a.j?!1:w(Ta,a):w(Ta,a)}function Gc(a,b){this.e=a;this.m=b}Gc.prototype.ga=function(){return this.m<this.e.length};Gc.prototype.next=function(){var a=this.e[this.m];this.m+=1;return a};function F(a,b){this.e=a;this.m=b;this.j=166199550;this.q=8192}k=F.prototype;k.toString=function(){return ec(this)};k.Q=function(a,b){var c=b+this.m;return c<this.e.length?this.e[c]:null};k.$=function(a,b,c){a=b+this.m;return a<this.e.length?this.e[a]:c};k.vb=!0;
+k.fb=function(){return new Gc(this.e,this.m)};k.T=function(){return this.m+1<this.e.length?new F(this.e,this.m+1):null};k.L=function(){return this.e.length-this.m};k.ab=function(){var a=Ma(this);return 0<a?new Hc(this,a-1,null):null};k.B=function(){return wc(this)};k.A=function(a,b){return Ic.a?Ic.a(this,b):Ic.call(null,this,b)};k.J=function(){return J};k.R=function(a,b){return Dc.n(this.e,b,this.e[this.m],this.m+1)};k.O=function(a,b,c){return Dc.n(this.e,b,c,this.m)};k.N=function(){return this.e[this.m]};
+k.S=function(){return this.m+1<this.e.length?new F(this.e,this.m+1):J};k.D=function(){return this};k.G=function(a,b){return M.a?M.a(b,this):M.call(null,b,this)};F.prototype[Ea]=function(){return uc(this)};
+var Jc=function(){function a(a,b){return b<a.length?new F(a,b):null}function b(a){return c.a(a,0)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Kc=function(){function a(a,b){return Jc.a(a,b)}function b(a){return Jc.a(a,0)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+
+arguments.length);};c.b=b;c.a=a;return c}();function Hc(a,b,c){this.qb=a;this.m=b;this.k=c;this.j=32374990;this.q=8192}k=Hc.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.T=function(){return 0<this.m?new Hc(this.qb,this.m-1,null):null};k.L=function(){return this.m+1};k.B=function(){return wc(this)};k.A=function(a,b){return Ic.a?Ic.a(this,b):Ic.call(null,this,b)};k.J=function(){var a=this.k;return O.a?O.a(J,a):O.call(null,J,a)};
+k.R=function(a,b){return P.a?P.a(b,this):P.call(null,b,this)};k.O=function(a,b,c){return P.c?P.c(b,c,this):P.call(null,b,c,this)};k.N=function(){return C.a(this.qb,this.m)};k.S=function(){return 0<this.m?new Hc(this.qb,this.m-1,null):J};k.D=function(){return this};k.F=function(a,b){return new Hc(this.qb,this.m,b)};k.G=function(a,b){return M.a?M.a(b,this):M.call(null,b,this)};Hc.prototype[Ea]=function(){return uc(this)};function Lc(a){return G(K(a))}yb._=function(a,b){return a===b};
+var Nc=function(){function a(a,b){return null!=a?Ra(a,b):Ra(J,b)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){for(;;)if(t(e))a=b.a(a,d),d=G(e),e=K(e);else return b.a(a,d)}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 0:return Mc;case 1:return b;
+case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.l=function(){return Mc};b.b=function(a){return a};b.a=a;b.d=c.d;return b}();function Oc(a){return null==a?null:Na(a)}
+function Q(a){if(null!=a)if(a&&(a.j&2||a.cc))a=a.L(null);else if(a instanceof Array)a=a.length;else if("string"===typeof a)a=a.length;else if(w(La,a))a=Ma(a);else a:{a=D(a);for(var b=0;;){if(Ec(a)){a=b+Ma(a);break a}a=K(a);b+=1}a=void 0}else a=0;return a}
+var Pc=function(){function a(a,b,c){for(;;){if(null==a)return c;if(0===b)return D(a)?G(a):c;if(Fc(a))return C.c(a,b,c);if(D(a))a=K(a),b-=1;else return c}}function b(a,b){for(;;){if(null==a)throw Error("Index out of bounds");if(0===b){if(D(a))return G(a);throw Error("Index out of bounds");}if(Fc(a))return C.a(a,b);if(D(a)){var c=K(a),g=b-1;a=c;b=g}else throw Error("Index out of bounds");}}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,
+c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),R=function(){function a(a,b,c){if("number"!==typeof b)throw Error("index argument to nth must be a number.");if(null==a)return c;if(a&&(a.j&16||a.Qb))return a.$(null,b,c);if(a instanceof Array||"string"===typeof a)return b<a.length?a[b]:c;if(w(Ta,a))return C.a(a,b);if(a?a.j&64||a.jb||(a.j?0:w(Ua,a)):w(Ua,a))return Pc.c(a,b,c);throw Error([z("nth not supported on this type "),z(Da(Ba(a)))].join(""));}function b(a,b){if("number"!==
+typeof b)throw Error("index argument to nth must be a number");if(null==a)return a;if(a&&(a.j&16||a.Qb))return a.Q(null,b);if(a instanceof Array||"string"===typeof a)return b<a.length?a[b]:null;if(w(Ta,a))return C.a(a,b);if(a?a.j&64||a.jb||(a.j?0:w(Ua,a)):w(Ua,a))return Pc.a(a,b);throw Error([z("nth not supported on this type "),z(Da(Ba(a)))].join(""));}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+
+arguments.length);};c.a=b;c.c=a;return c}(),S=function(){function a(a,b,c){return null!=a?a&&(a.j&256||a.Rb)?a.s(null,b,c):a instanceof Array?b<a.length?a[b]:c:"string"===typeof a?b<a.length?a[b]:c:w(Za,a)?$a.c(a,b,c):c:c}function b(a,b){return null==a?null:a&&(a.j&256||a.Rb)?a.t(null,b):a instanceof Array?b<a.length?a[b]:null:"string"===typeof a?b<a.length?a[b]:null:w(Za,a)?$a.a(a,b):null}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,
+c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),Rc=function(){function a(a,b,c){if(null!=a)a=cb(a,b,c);else a:{a=[b];c=[c];b=a.length;for(var g=0,h=Ob(Qc);;)if(g<b)var l=g+1,h=h.kb(null,a[g],c[g]),g=l;else{a=Qb(h);break a}a=void 0}return a}var b=null,c=function(){function a(b,d,h,l){var m=null;if(3<arguments.length){for(var m=0,p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new F(p,0)}return c.call(this,b,d,h,m)}function c(a,d,e,l){for(;;)if(a=b.c(a,
+d,e),t(l))d=G(l),e=Lc(l),l=K(K(l));else return a}a.i=3;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=K(a);var l=G(a);a=H(a);return c(b,d,l,a)};a.d=c;return a}(),b=function(b,e,f,g){switch(arguments.length){case 3:return a.call(this,b,e,f);default:var h=null;if(3<arguments.length){for(var h=0,l=Array(arguments.length-3);h<l.length;)l[h]=arguments[h+3],++h;h=new F(l,0)}return c.d(b,e,f,h)}throw Error("Invalid arity: "+arguments.length);};b.i=3;b.f=c.f;b.c=a;b.d=c.d;return b}(),Sc=function(){function a(a,
+b){return null==a?null:eb(a,b)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){for(;;){if(null==a)return null;a=b.a(a,d);if(t(e))d=G(e),e=K(e);else return a}}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return b;case 2:return a.call(this,b,e);
+default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(a){return a};b.a=a;b.d=c.d;return b}();function Tc(a){var b="function"==n(a);return t(b)?b:a?t(t(null)?null:a.bc)?!0:a.yb?!1:w(Ja,a):w(Ja,a)}function Uc(a,b){this.h=a;this.k=b;this.q=0;this.j=393217}k=Uc.prototype;
+k.call=function(){function a(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra,I){a=this.h;return T.ub?T.ub(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra,I):T.call(null,a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra,I)}function b(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra){a=this;return a.h.Fa?a.h.Fa(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y,ra)}function c(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y){a=this;return a.h.Ea?a.h.Ea(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,
+Y):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N,Y)}function d(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N){a=this;return a.h.Da?a.h.Da(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E,N)}function e(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E){a=this;return a.h.Ca?a.h.Ca(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B,E)}function f(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B){a=this;return a.h.Ba?a.h.Ba(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B):a.h.call(null,
+b,c,d,e,f,g,h,l,m,p,q,u,s,v,y,B)}function g(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y){a=this;return a.h.Aa?a.h.Aa(b,c,d,e,f,g,h,l,m,p,q,u,s,v,y):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v,y)}function h(a,b,c,d,e,f,g,h,l,m,p,q,u,s,v){a=this;return a.h.za?a.h.za(b,c,d,e,f,g,h,l,m,p,q,u,s,v):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s,v)}function l(a,b,c,d,e,f,g,h,l,m,p,q,u,s){a=this;return a.h.ya?a.h.ya(b,c,d,e,f,g,h,l,m,p,q,u,s):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u,s)}function m(a,b,c,d,e,f,g,h,l,m,p,q,u){a=this;
+return a.h.xa?a.h.xa(b,c,d,e,f,g,h,l,m,p,q,u):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q,u)}function p(a,b,c,d,e,f,g,h,l,m,p,q){a=this;return a.h.wa?a.h.wa(b,c,d,e,f,g,h,l,m,p,q):a.h.call(null,b,c,d,e,f,g,h,l,m,p,q)}function q(a,b,c,d,e,f,g,h,l,m,p){a=this;return a.h.va?a.h.va(b,c,d,e,f,g,h,l,m,p):a.h.call(null,b,c,d,e,f,g,h,l,m,p)}function s(a,b,c,d,e,f,g,h,l,m){a=this;return a.h.Ha?a.h.Ha(b,c,d,e,f,g,h,l,m):a.h.call(null,b,c,d,e,f,g,h,l,m)}function u(a,b,c,d,e,f,g,h,l){a=this;return a.h.Ga?a.h.Ga(b,c,
+d,e,f,g,h,l):a.h.call(null,b,c,d,e,f,g,h,l)}function v(a,b,c,d,e,f,g,h){a=this;return a.h.ia?a.h.ia(b,c,d,e,f,g,h):a.h.call(null,b,c,d,e,f,g,h)}function y(a,b,c,d,e,f,g){a=this;return a.h.P?a.h.P(b,c,d,e,f,g):a.h.call(null,b,c,d,e,f,g)}function B(a,b,c,d,e,f){a=this;return a.h.r?a.h.r(b,c,d,e,f):a.h.call(null,b,c,d,e,f)}function E(a,b,c,d,e){a=this;return a.h.n?a.h.n(b,c,d,e):a.h.call(null,b,c,d,e)}function N(a,b,c,d){a=this;return a.h.c?a.h.c(b,c,d):a.h.call(null,b,c,d)}function Y(a,b,c){a=this;
+return a.h.a?a.h.a(b,c):a.h.call(null,b,c)}function ra(a,b){a=this;return a.h.b?a.h.b(b):a.h.call(null,b)}function Pa(a){a=this;return a.h.l?a.h.l():a.h.call(null)}var I=null,I=function(I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc,Gd,De,Wf,dh){switch(arguments.length){case 1:return Pa.call(this,I);case 2:return ra.call(this,I,qa);case 3:return Y.call(this,I,qa,ta);case 4:return N.call(this,I,qa,ta,va);case 5:return E.call(this,I,qa,ta,va,xa);case 6:return B.call(this,I,qa,ta,va,xa,Ca);case 7:return y.call(this,
+I,qa,ta,va,xa,Ca,Ga);case 8:return v.call(this,I,qa,ta,va,xa,Ca,Ga,Ka);case 9:return u.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa);case 10:return s.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa);case 11:return q.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya);case 12:return p.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb);case 13:return m.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob);case 14:return l.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab);case 15:return h.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,
+ob,Ab,Wb);case 16:return g.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc);case 17:return f.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc);case 18:return e.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc);case 19:return d.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc,Gd);case 20:return c.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc,Gd,De);case 21:return b.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc,Gd,De,
+Wf);case 22:return a.call(this,I,qa,ta,va,xa,Ca,Ga,Ka,Oa,Sa,Ya,gb,ob,Ab,Wb,jc,zc,Zc,Gd,De,Wf,dh)}throw Error("Invalid arity: "+arguments.length);};I.b=Pa;I.a=ra;I.c=Y;I.n=N;I.r=E;I.P=B;I.ia=y;I.Ga=v;I.Ha=u;I.va=s;I.wa=q;I.xa=p;I.ya=m;I.za=l;I.Aa=h;I.Ba=g;I.Ca=f;I.Da=e;I.Ea=d;I.Fa=c;I.hc=b;I.ub=a;return I}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.l=function(){return this.h.l?this.h.l():this.h.call(null)};
+k.b=function(a){return this.h.b?this.h.b(a):this.h.call(null,a)};k.a=function(a,b){return this.h.a?this.h.a(a,b):this.h.call(null,a,b)};k.c=function(a,b,c){return this.h.c?this.h.c(a,b,c):this.h.call(null,a,b,c)};k.n=function(a,b,c,d){return this.h.n?this.h.n(a,b,c,d):this.h.call(null,a,b,c,d)};k.r=function(a,b,c,d,e){return this.h.r?this.h.r(a,b,c,d,e):this.h.call(null,a,b,c,d,e)};k.P=function(a,b,c,d,e,f){return this.h.P?this.h.P(a,b,c,d,e,f):this.h.call(null,a,b,c,d,e,f)};
+k.ia=function(a,b,c,d,e,f,g){return this.h.ia?this.h.ia(a,b,c,d,e,f,g):this.h.call(null,a,b,c,d,e,f,g)};k.Ga=function(a,b,c,d,e,f,g,h){return this.h.Ga?this.h.Ga(a,b,c,d,e,f,g,h):this.h.call(null,a,b,c,d,e,f,g,h)};k.Ha=function(a,b,c,d,e,f,g,h,l){return this.h.Ha?this.h.Ha(a,b,c,d,e,f,g,h,l):this.h.call(null,a,b,c,d,e,f,g,h,l)};k.va=function(a,b,c,d,e,f,g,h,l,m){return this.h.va?this.h.va(a,b,c,d,e,f,g,h,l,m):this.h.call(null,a,b,c,d,e,f,g,h,l,m)};
+k.wa=function(a,b,c,d,e,f,g,h,l,m,p){return this.h.wa?this.h.wa(a,b,c,d,e,f,g,h,l,m,p):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p)};k.xa=function(a,b,c,d,e,f,g,h,l,m,p,q){return this.h.xa?this.h.xa(a,b,c,d,e,f,g,h,l,m,p,q):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q)};k.ya=function(a,b,c,d,e,f,g,h,l,m,p,q,s){return this.h.ya?this.h.ya(a,b,c,d,e,f,g,h,l,m,p,q,s):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s)};
+k.za=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u){return this.h.za?this.h.za(a,b,c,d,e,f,g,h,l,m,p,q,s,u):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u)};k.Aa=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v){return this.h.Aa?this.h.Aa(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v)};k.Ba=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y){return this.h.Ba?this.h.Ba(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y)};
+k.Ca=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B){return this.h.Ca?this.h.Ca(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B)};k.Da=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E){return this.h.Da?this.h.Da(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E)};
+k.Ea=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N){return this.h.Ea?this.h.Ea(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N)};k.Fa=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y){return this.h.Fa?this.h.Fa(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y):this.h.call(null,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y)};
+k.hc=function(a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra){var Pa=this.h;return T.ub?T.ub(Pa,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra):T.call(null,Pa,a,b,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra)};k.bc=!0;k.F=function(a,b){return new Uc(this.h,b)};k.H=function(){return this.k};function O(a,b){return Tc(a)&&!(a?a.j&262144||a.Bc||(a.j?0:w(tb,a)):w(tb,a))?new Uc(a,b):null==a?null:ub(a,b)}function Vc(a){var b=null!=a;return(b?a?a.j&131072||a.kc||(a.j?0:w(rb,a)):w(rb,a):b)?sb(a):null}
+function Wc(a){return null==a?null:lb(a)}
+var Xc=function(){function a(a,b){return null==a?null:kb(a,b)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){for(;;){if(null==a)return null;a=b.a(a,d);if(t(e))d=G(e),e=K(e);else return a}}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return b;case 2:return a.call(this,
+b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(a){return a};b.a=a;b.d=c.d;return b}();function Yc(a){return null==a||Aa(D(a))}function $c(a){return null==a?!1:a?a.j&8||a.tc?!0:a.j?!1:w(Qa,a):w(Qa,a)}function ad(a){return null==a?!1:a?a.j&4096||a.zc?!0:a.j?!1:w(jb,a):w(jb,a)}
+function bd(a){return a?a.j&512||a.rc?!0:a.j?!1:w(ab,a):w(ab,a)}function cd(a){return a?a.j&16777216||a.yc?!0:a.j?!1:w(Db,a):w(Db,a)}function dd(a){return null==a?!1:a?a.j&1024||a.ic?!0:a.j?!1:w(db,a):w(db,a)}function ed(a){return a?a.j&16384||a.Ac?!0:a.j?!1:w(nb,a):w(nb,a)}function fd(a){return a?a.q&512||a.sc?!0:!1:!1}function gd(a){var b=[];ea(a,function(a,b){return function(a,c){return b.push(c)}}(a,b));return b}function hd(a,b,c,d,e){for(;0!==e;)c[d]=a[b],d+=1,e-=1,b+=1}
+function id(a,b,c,d,e){b+=e-1;for(d+=e-1;0!==e;)c[d]=a[b],d-=1,e-=1,b-=1}var jd={};function kd(a){return null==a?!1:a?a.j&64||a.jb?!0:a.j?!1:w(Ua,a):w(Ua,a)}function ld(a){return a?a.j&8388608||a.mc?!0:a.j?!1:w(Bb,a):w(Bb,a)}function md(a){return t(a)?!0:!1}function nd(a,b){return S.c(a,b,jd)===jd?!1:!0}
+function od(a,b){if(a===b)return 0;if(null==a)return-1;if(null==b)return 1;if(Ba(a)===Ba(b))return a&&(a.q&2048||a.sb)?a.tb(null,b):ha(a,b);throw Error("compare on non-nil objects of different types");}
+var pd=function(){function a(a,b,c,g){for(;;){var h=od(R.a(a,g),R.a(b,g));if(0===h&&g+1<c)g+=1;else return h}}function b(a,b){var f=Q(a),g=Q(b);return f<g?-1:f>g?1:c.n(a,b,f,0)}var c=null,c=function(c,e,f,g){switch(arguments.length){case 2:return b.call(this,c,e);case 4:return a.call(this,c,e,f,g)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.n=a;return c}();
+function qd(a){return sc.a(a,od)?od:function(b,c){var d=a.a?a.a(b,c):a.call(null,b,c);return"number"===typeof d?d:t(d)?-1:t(a.a?a.a(c,b):a.call(null,c,b))?1:0}}
+var sd=function(){function a(a,b){if(D(b)){var c=rd.b?rd.b(b):rd.call(null,b),g=qd(a);ia(c,g);return D(c)}return J}function b(a){return c.a(od,a)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),td=function(){function a(a,b,c){return sd.a(function(c,f){return qd(b).call(null,a.b?a.b(c):a.call(null,c),a.b?a.b(f):a.call(null,f))},c)}function b(a,b){return c.c(a,od,
+b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),P=function(){function a(a,b,c){for(c=D(c);;)if(c){var g=G(c);b=a.a?a.a(b,g):a.call(null,b,g);if(Ac(b))return qb(b);c=K(c)}else return b}function b(a,b){var c=D(b);if(c){var g=G(c),c=K(c);return A.c?A.c(a,g,c):A.call(null,a,g,c)}return a.l?a.l():a.call(null)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,
+c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),A=function(){function a(a,b,c){return c&&(c.j&524288||c.Sb)?c.O(null,a,b):c instanceof Array?Dc.c(c,a,b):"string"===typeof c?Dc.c(c,a,b):w(vb,c)?wb.c(c,a,b):P.c(a,b,c)}function b(a,b){return b&&(b.j&524288||b.Sb)?b.R(null,a):b instanceof Array?Dc.a(b,a):"string"===typeof b?Dc.a(b,a):w(vb,b)?wb.a(b,a):P.a(a,b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,
+c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();function ud(a){return a}
+var vd=function(){function a(a,b){return function(){function c(b,e){return a.a?a.a(b,e):a.call(null,b,e)}function g(a){return b.b?b.b(a):b.call(null,a)}function h(){return a.l?a.l():a.call(null)}var l=null,l=function(a,b){switch(arguments.length){case 0:return h.call(this);case 1:return g.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};l.l=h;l.b=g;l.a=c;return l}()}function b(a){return c.a(a,ud)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,
+c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),wd=function(){function a(a,b,c,g){a=a.b?a.b(b):a.call(null,b);c=A.c(a,c,g);return a.b?a.b(c):a.call(null,c)}function b(a,b,f){return c.n(a,b,b.l?b.l():b.call(null),f)}var c=null,c=function(c,e,f,g){switch(arguments.length){case 3:return b.call(this,c,e,f);case 4:return a.call(this,c,e,f,g)}throw Error("Invalid arity: "+arguments.length);};c.c=b;c.n=a;return c}(),xd=function(){var a=null,b=function(){function b(a,
+c,g){var h=null;if(2<arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return d.call(this,a,c,h)}function d(b,c,d){return A.c(a,b+c,d)}b.i=2;b.f=function(a){var b=G(a);a=K(a);var c=G(a);a=H(a);return d(b,c,a)};b.d=d;return b}(),a=function(a,d,e){switch(arguments.length){case 0:return 0;case 1:return a;case 2:return a+d;default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,
+0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.l=function(){return 0};a.b=function(a){return a};a.a=function(a,b){return a+b};a.d=b.d;return a}(),yd=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a<c)if(K(d))a=c,c=G(d),d=K(d);else return c<G(d);else return!1}a.i=2;a.f=function(a){var c=
+G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 1:return!0;case 2:return a<d;default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.b=function(){return!0};a.a=function(a,b){return a<b};a.d=b.d;return a}(),zd=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<
+arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a<=c)if(K(d))a=c,c=G(d),d=K(d);else return c<=G(d);else return!1}a.i=2;a.f=function(a){var c=G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 1:return!0;case 2:return a<=d;default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+
+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.b=function(){return!0};a.a=function(a,b){return a<=b};a.d=b.d;return a}(),Ad=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a>c)if(K(d))a=c,c=G(d),d=K(d);else return c>G(d);else return!1}a.i=2;a.f=function(a){var c=
+G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 1:return!0;case 2:return a>d;default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.b=function(){return!0};a.a=function(a,b){return a>b};a.d=b.d;return a}(),Bd=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<
+arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a>=c)if(K(d))a=c,c=G(d),d=K(d);else return c>=G(d);else return!1}a.i=2;a.f=function(a){var c=G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 1:return!0;case 2:return a>=d;default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+
+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.b=function(){return!0};a.a=function(a,b){return a>=b};a.d=b.d;return a}();function Cd(a,b){var c=(a-a%b)/b;return 0<=c?Math.floor.b?Math.floor.b(c):Math.floor.call(null,c):Math.ceil.b?Math.ceil.b(c):Math.ceil.call(null,c)}function Dd(a){a-=a>>1&1431655765;a=(a&858993459)+(a>>2&858993459);return 16843009*(a+(a>>4)&252645135)>>24}
+function Ed(a){var b=1;for(a=D(a);;)if(a&&0<b)b-=1,a=K(a);else return a}
+var z=function(){function a(a){return null==a?"":da(a)}var b=null,c=function(){function a(b,d){var h=null;if(1<arguments.length){for(var h=0,l=Array(arguments.length-1);h<l.length;)l[h]=arguments[h+1],++h;h=new F(l,0)}return c.call(this,b,h)}function c(a,d){for(var e=new fa(b.b(a)),l=d;;)if(t(l))e=e.append(b.b(G(l))),l=K(l);else return e.toString()}a.i=1;a.f=function(a){var b=G(a);a=H(a);return c(b,a)};a.d=c;return a}(),b=function(b,e){switch(arguments.length){case 0:return"";case 1:return a.call(this,
+b);default:var f=null;if(1<arguments.length){for(var f=0,g=Array(arguments.length-1);f<g.length;)g[f]=arguments[f+1],++f;f=new F(g,0)}return c.d(b,f)}throw Error("Invalid arity: "+arguments.length);};b.i=1;b.f=c.f;b.l=function(){return""};b.b=a;b.d=c.d;return b}();function Ic(a,b){var c;if(cd(b))if(Ec(a)&&Ec(b)&&Q(a)!==Q(b))c=!1;else a:{c=D(a);for(var d=D(b);;){if(null==c){c=null==d;break a}if(null!=d&&sc.a(G(c),G(d)))c=K(c),d=K(d);else{c=!1;break a}}c=void 0}else c=null;return md(c)}
+function Fd(a,b,c,d,e){this.k=a;this.first=b;this.M=c;this.count=d;this.p=e;this.j=65937646;this.q=8192}k=Fd.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.T=function(){return 1===this.count?null:this.M};k.L=function(){return this.count};k.La=function(){return this.first};k.Ma=function(){return Wa(this)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return ub(J,this.k)};
+k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return this.first};k.S=function(){return 1===this.count?J:this.M};k.D=function(){return this};k.F=function(a,b){return new Fd(b,this.first,this.M,this.count,this.p)};k.G=function(a,b){return new Fd(this.k,b,this,this.count+1,null)};Fd.prototype[Ea]=function(){return uc(this)};function Hd(a){this.k=a;this.j=65937614;this.q=8192}k=Hd.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};
+k.T=function(){return null};k.L=function(){return 0};k.La=function(){return null};k.Ma=function(){throw Error("Can't pop empty list");};k.B=function(){return 0};k.A=function(a,b){return Ic(this,b)};k.J=function(){return this};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return null};k.S=function(){return J};k.D=function(){return null};k.F=function(a,b){return new Hd(b)};k.G=function(a,b){return new Fd(this.k,b,null,1,null)};var J=new Hd(null);
+Hd.prototype[Ea]=function(){return uc(this)};function Id(a){return a?a.j&134217728||a.xc?!0:a.j?!1:w(Fb,a):w(Fb,a)}function Jd(a){return Id(a)?Gb(a):A.c(Nc,J,a)}
+var Kd=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){var b;if(a instanceof F&&0===a.m)b=a.e;else a:{for(b=[];;)if(null!=a)b.push(a.N(null)),a=a.T(null);else break a;b=void 0}a=b.length;for(var e=J;;)if(0<a){var f=a-1,e=e.G(null,b[a-1]);a=f}else return e}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}();
+function Ld(a,b,c,d){this.k=a;this.first=b;this.M=c;this.p=d;this.j=65929452;this.q=8192}k=Ld.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.T=function(){return null==this.M?null:D(this.M)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return this.first};
+k.S=function(){return null==this.M?J:this.M};k.D=function(){return this};k.F=function(a,b){return new Ld(b,this.first,this.M,this.p)};k.G=function(a,b){return new Ld(null,b,this,this.p)};Ld.prototype[Ea]=function(){return uc(this)};function M(a,b){var c=null==b;return(c?c:b&&(b.j&64||b.jb))?new Ld(null,a,b,null):new Ld(null,a,D(b),null)}
+function Md(a,b){if(a.pa===b.pa)return 0;var c=Aa(a.ba);if(t(c?b.ba:c))return-1;if(t(a.ba)){if(Aa(b.ba))return 1;c=ha(a.ba,b.ba);return 0===c?ha(a.name,b.name):c}return ha(a.name,b.name)}function U(a,b,c,d){this.ba=a;this.name=b;this.pa=c;this.Ya=d;this.j=2153775105;this.q=4096}k=U.prototype;k.v=function(a,b){return Lb(b,[z(":"),z(this.pa)].join(""))};k.B=function(){var a=this.Ya;return null!=a?a:this.Ya=a=oc(this)+2654435769|0};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return S.a(c,this);case 3:return S.c(c,this,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return S.a(c,this)};a.c=function(a,c,d){return S.c(c,this,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return S.a(a,this)};k.a=function(a,b){return S.c(a,this,b)};k.A=function(a,b){return b instanceof U?this.pa===b.pa:!1};
+k.toString=function(){return[z(":"),z(this.pa)].join("")};function Nd(a,b){return a===b?!0:a instanceof U&&b instanceof U?a.pa===b.pa:!1}
+var Pd=function(){function a(a,b){return new U(a,b,[z(t(a)?[z(a),z("/")].join(""):null),z(b)].join(""),null)}function b(a){if(a instanceof U)return a;if(a instanceof qc){var b;if(a&&(a.q&4096||a.lc))b=a.ba;else throw Error([z("Doesn't support namespace: "),z(a)].join(""));return new U(b,Od.b?Od.b(a):Od.call(null,a),a.ta,null)}return"string"===typeof a?(b=a.split("/"),2===b.length?new U(b[0],b[1],a,null):new U(null,b[0],a,null)):null}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,
+c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();function V(a,b,c,d){this.k=a;this.cb=b;this.C=c;this.p=d;this.q=0;this.j=32374988}k=V.prototype;k.toString=function(){return ec(this)};function Qd(a){null!=a.cb&&(a.C=a.cb.l?a.cb.l():a.cb.call(null),a.cb=null);return a.C}k.H=function(){return this.k};k.T=function(){Cb(this);return null==this.C?null:K(this.C)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};
+k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){Cb(this);return null==this.C?null:G(this.C)};k.S=function(){Cb(this);return null!=this.C?H(this.C):J};k.D=function(){Qd(this);if(null==this.C)return null;for(var a=this.C;;)if(a instanceof V)a=Qd(a);else return this.C=a,D(this.C)};k.F=function(a,b){return new V(b,this.cb,this.C,this.p)};k.G=function(a,b){return M(b,this)};
+V.prototype[Ea]=function(){return uc(this)};function Rd(a,b){this.Ab=a;this.end=b;this.q=0;this.j=2}Rd.prototype.L=function(){return this.end};Rd.prototype.add=function(a){this.Ab[this.end]=a;return this.end+=1};Rd.prototype.ca=function(){var a=new Sd(this.Ab,0,this.end);this.Ab=null;return a};function Td(a){return new Rd(Array(a),0)}function Sd(a,b,c){this.e=a;this.V=b;this.end=c;this.q=0;this.j=524306}k=Sd.prototype;k.R=function(a,b){return Dc.n(this.e,b,this.e[this.V],this.V+1)};
+k.O=function(a,b,c){return Dc.n(this.e,b,c,this.V)};k.Pb=function(){if(this.V===this.end)throw Error("-drop-first of empty chunk");return new Sd(this.e,this.V+1,this.end)};k.Q=function(a,b){return this.e[this.V+b]};k.$=function(a,b,c){return 0<=b&&b<this.end-this.V?this.e[this.V+b]:c};k.L=function(){return this.end-this.V};
+var Ud=function(){function a(a,b,c){return new Sd(a,b,c)}function b(a,b){return new Sd(a,b,a.length)}function c(a){return new Sd(a,0,a.length)}var d=null,d=function(d,f,g){switch(arguments.length){case 1:return c.call(this,d);case 2:return b.call(this,d,f);case 3:return a.call(this,d,f,g)}throw Error("Invalid arity: "+arguments.length);};d.b=c;d.a=b;d.c=a;return d}();function Vd(a,b,c,d){this.ca=a;this.ra=b;this.k=c;this.p=d;this.j=31850732;this.q=1536}k=Vd.prototype;k.toString=function(){return ec(this)};
+k.H=function(){return this.k};k.T=function(){if(1<Ma(this.ca))return new Vd(Xb(this.ca),this.ra,this.k,null);var a=Cb(this.ra);return null==a?null:a};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.N=function(){return C.a(this.ca,0)};k.S=function(){return 1<Ma(this.ca)?new Vd(Xb(this.ca),this.ra,this.k,null):null==this.ra?J:this.ra};k.D=function(){return this};k.Cb=function(){return this.ca};
+k.Db=function(){return null==this.ra?J:this.ra};k.F=function(a,b){return new Vd(this.ca,this.ra,b,this.p)};k.G=function(a,b){return M(b,this)};k.Bb=function(){return null==this.ra?null:this.ra};Vd.prototype[Ea]=function(){return uc(this)};function Wd(a,b){return 0===Ma(a)?b:new Vd(a,b,null,null)}function Xd(a,b){a.add(b)}function rd(a){for(var b=[];;)if(D(a))b.push(G(a)),a=K(a);else return b}function Yd(a,b){if(Ec(a))return Q(a);for(var c=a,d=b,e=0;;)if(0<d&&D(c))c=K(c),d-=1,e+=1;else return e}
+var $d=function Zd(b){return null==b?null:null==K(b)?D(G(b)):M(G(b),Zd(K(b)))},ae=function(){function a(a,b){return new V(null,function(){var c=D(a);return c?fd(c)?Wd(Yb(c),d.a(Zb(c),b)):M(G(c),d.a(H(c),b)):b},null,null)}function b(a){return new V(null,function(){return a},null,null)}function c(){return new V(null,function(){return null},null,null)}var d=null,e=function(){function a(c,d,e){var f=null;if(2<arguments.length){for(var f=0,q=Array(arguments.length-2);f<q.length;)q[f]=arguments[f+2],++f;
+f=new F(q,0)}return b.call(this,c,d,f)}function b(a,c,e){return function q(a,b){return new V(null,function(){var c=D(a);return c?fd(c)?Wd(Yb(c),q(Zb(c),b)):M(G(c),q(H(c),b)):t(b)?q(G(b),K(b)):null},null,null)}(d.a(a,c),e)}a.i=2;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=H(a);return b(c,d,a)};a.d=b;return a}(),d=function(d,g,h){switch(arguments.length){case 0:return c.call(this);case 1:return b.call(this,d);case 2:return a.call(this,d,g);default:var l=null;if(2<arguments.length){for(var l=0,m=
+Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return e.d(d,g,l)}throw Error("Invalid arity: "+arguments.length);};d.i=2;d.f=e.f;d.l=c;d.b=b;d.a=a;d.d=e.d;return d}(),be=function(){function a(a,b,c,d){return M(a,M(b,M(c,d)))}function b(a,b,c){return M(a,M(b,c))}var c=null,d=function(){function a(c,d,e,m,p){var q=null;if(4<arguments.length){for(var q=0,s=Array(arguments.length-4);q<s.length;)s[q]=arguments[q+4],++q;q=new F(s,0)}return b.call(this,c,d,e,m,q)}function b(a,
+c,d,e,f){return M(a,M(c,M(d,M(e,$d(f)))))}a.i=4;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var p=G(a);a=H(a);return b(c,d,e,p,a)};a.d=b;return a}(),c=function(c,f,g,h,l){switch(arguments.length){case 1:return D(c);case 2:return M(c,f);case 3:return b.call(this,c,f,g);case 4:return a.call(this,c,f,g,h);default:var m=null;if(4<arguments.length){for(var m=0,p=Array(arguments.length-4);m<p.length;)p[m]=arguments[m+4],++m;m=new F(p,0)}return d.d(c,f,g,h,m)}throw Error("Invalid arity: "+
+arguments.length);};c.i=4;c.f=d.f;c.b=function(a){return D(a)};c.a=function(a,b){return M(a,b)};c.c=b;c.n=a;c.d=d.d;return c}();function ce(a){return Qb(a)}
+var de=function(){function a(){return Ob(Mc)}var b=null,c=function(){function a(c,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return b.call(this,c,d,l)}function b(a,c,d){for(;;)if(a=Pb(a,c),t(d))c=G(d),d=K(d);else return a}a.i=2;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=H(a);return b(c,d,a)};a.d=b;return a}(),b=function(b,e,f){switch(arguments.length){case 0:return a.call(this);case 1:return b;case 2:return Pb(b,
+e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.l=a;b.b=function(a){return a};b.a=function(a,b){return Pb(a,b)};b.d=c.d;return b}(),ee=function(){var a=null,b=function(){function a(c,f,g,h){var l=null;if(3<arguments.length){for(var l=0,m=Array(arguments.length-3);l<m.length;)m[l]=arguments[l+3],++l;l=new F(m,0)}return b.call(this,
+c,f,g,l)}function b(a,c,d,h){for(;;)if(a=Rb(a,c,d),t(h))c=G(h),d=Lc(h),h=K(K(h));else return a}a.i=3;a.f=function(a){var c=G(a);a=K(a);var g=G(a);a=K(a);var h=G(a);a=H(a);return b(c,g,h,a)};a.d=b;return a}(),a=function(a,d,e,f){switch(arguments.length){case 3:return Rb(a,d,e);default:var g=null;if(3<arguments.length){for(var g=0,h=Array(arguments.length-3);g<h.length;)h[g]=arguments[g+3],++g;g=new F(h,0)}return b.d(a,d,e,g)}throw Error("Invalid arity: "+arguments.length);};a.i=3;a.f=b.f;a.c=function(a,
+b,e){return Rb(a,b,e)};a.d=b.d;return a}(),fe=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a=Sb(a,c),t(d))c=G(d),d=K(d);else return a}a.i=2;a.f=function(a){var c=G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 2:return Sb(a,d);default:var f=null;if(2<
+arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.a=function(a,b){return Sb(a,b)};a.d=b.d;return a}(),ge=function(){var a=null,b=function(){function a(c,f,g){var h=null;if(2<arguments.length){for(var h=0,l=Array(arguments.length-2);h<l.length;)l[h]=arguments[h+2],++h;h=new F(l,0)}return b.call(this,c,f,h)}function b(a,c,d){for(;;)if(a=Vb(a,c),t(d))c=G(d),d=K(d);
+else return a}a.i=2;a.f=function(a){var c=G(a);a=K(a);var g=G(a);a=H(a);return b(c,g,a)};a.d=b;return a}(),a=function(a,d,e){switch(arguments.length){case 2:return Vb(a,d);default:var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return b.d(a,d,f)}throw Error("Invalid arity: "+arguments.length);};a.i=2;a.f=b.f;a.a=function(a,b){return Vb(a,b)};a.d=b.d;return a}();
+function he(a,b,c){var d=D(c);if(0===b)return a.l?a.l():a.call(null);c=Va(d);var e=Wa(d);if(1===b)return a.b?a.b(c):a.b?a.b(c):a.call(null,c);var d=Va(e),f=Wa(e);if(2===b)return a.a?a.a(c,d):a.a?a.a(c,d):a.call(null,c,d);var e=Va(f),g=Wa(f);if(3===b)return a.c?a.c(c,d,e):a.c?a.c(c,d,e):a.call(null,c,d,e);var f=Va(g),h=Wa(g);if(4===b)return a.n?a.n(c,d,e,f):a.n?a.n(c,d,e,f):a.call(null,c,d,e,f);var g=Va(h),l=Wa(h);if(5===b)return a.r?a.r(c,d,e,f,g):a.r?a.r(c,d,e,f,g):a.call(null,c,d,e,f,g);var h=Va(l),
+m=Wa(l);if(6===b)return a.P?a.P(c,d,e,f,g,h):a.P?a.P(c,d,e,f,g,h):a.call(null,c,d,e,f,g,h);var l=Va(m),p=Wa(m);if(7===b)return a.ia?a.ia(c,d,e,f,g,h,l):a.ia?a.ia(c,d,e,f,g,h,l):a.call(null,c,d,e,f,g,h,l);var m=Va(p),q=Wa(p);if(8===b)return a.Ga?a.Ga(c,d,e,f,g,h,l,m):a.Ga?a.Ga(c,d,e,f,g,h,l,m):a.call(null,c,d,e,f,g,h,l,m);var p=Va(q),s=Wa(q);if(9===b)return a.Ha?a.Ha(c,d,e,f,g,h,l,m,p):a.Ha?a.Ha(c,d,e,f,g,h,l,m,p):a.call(null,c,d,e,f,g,h,l,m,p);var q=Va(s),u=Wa(s);if(10===b)return a.va?a.va(c,d,e,
+f,g,h,l,m,p,q):a.va?a.va(c,d,e,f,g,h,l,m,p,q):a.call(null,c,d,e,f,g,h,l,m,p,q);var s=Va(u),v=Wa(u);if(11===b)return a.wa?a.wa(c,d,e,f,g,h,l,m,p,q,s):a.wa?a.wa(c,d,e,f,g,h,l,m,p,q,s):a.call(null,c,d,e,f,g,h,l,m,p,q,s);var u=Va(v),y=Wa(v);if(12===b)return a.xa?a.xa(c,d,e,f,g,h,l,m,p,q,s,u):a.xa?a.xa(c,d,e,f,g,h,l,m,p,q,s,u):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u);var v=Va(y),B=Wa(y);if(13===b)return a.ya?a.ya(c,d,e,f,g,h,l,m,p,q,s,u,v):a.ya?a.ya(c,d,e,f,g,h,l,m,p,q,s,u,v):a.call(null,c,d,e,f,g,h,l,m,p,
+q,s,u,v);var y=Va(B),E=Wa(B);if(14===b)return a.za?a.za(c,d,e,f,g,h,l,m,p,q,s,u,v,y):a.za?a.za(c,d,e,f,g,h,l,m,p,q,s,u,v,y):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y);var B=Va(E),N=Wa(E);if(15===b)return a.Aa?a.Aa(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B):a.Aa?a.Aa(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B);var E=Va(N),Y=Wa(N);if(16===b)return a.Ba?a.Ba(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E):a.Ba?a.Ba(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E);var N=
+Va(Y),ra=Wa(Y);if(17===b)return a.Ca?a.Ca(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N):a.Ca?a.Ca(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N);var Y=Va(ra),Pa=Wa(ra);if(18===b)return a.Da?a.Da(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y):a.Da?a.Da(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y);ra=Va(Pa);Pa=Wa(Pa);if(19===b)return a.Ea?a.Ea(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra):a.Ea?a.Ea(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra):a.call(null,
+c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra);var I=Va(Pa);Wa(Pa);if(20===b)return a.Fa?a.Fa(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra,I):a.Fa?a.Fa(c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra,I):a.call(null,c,d,e,f,g,h,l,m,p,q,s,u,v,y,B,E,N,Y,ra,I);throw Error("Only up to 20 arguments supported on functions");}
+var T=function(){function a(a,b,c,d,e){b=be.n(b,c,d,e);c=a.i;return a.f?(d=Yd(b,c+1),d<=c?he(a,d,b):a.f(b)):a.apply(a,rd(b))}function b(a,b,c,d){b=be.c(b,c,d);c=a.i;return a.f?(d=Yd(b,c+1),d<=c?he(a,d,b):a.f(b)):a.apply(a,rd(b))}function c(a,b,c){b=be.a(b,c);c=a.i;if(a.f){var d=Yd(b,c+1);return d<=c?he(a,d,b):a.f(b)}return a.apply(a,rd(b))}function d(a,b){var c=a.i;if(a.f){var d=Yd(b,c+1);return d<=c?he(a,d,b):a.f(b)}return a.apply(a,rd(b))}var e=null,f=function(){function a(c,d,e,f,g,u){var v=null;
+if(5<arguments.length){for(var v=0,y=Array(arguments.length-5);v<y.length;)y[v]=arguments[v+5],++v;v=new F(y,0)}return b.call(this,c,d,e,f,g,v)}function b(a,c,d,e,f,g){c=M(c,M(d,M(e,M(f,$d(g)))));d=a.i;return a.f?(e=Yd(c,d+1),e<=d?he(a,e,c):a.f(c)):a.apply(a,rd(c))}a.i=5;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var f=G(a);a=K(a);var g=G(a);a=H(a);return b(c,d,e,f,g,a)};a.d=b;return a}(),e=function(e,h,l,m,p,q){switch(arguments.length){case 2:return d.call(this,e,h);case 3:return c.call(this,
+e,h,l);case 4:return b.call(this,e,h,l,m);case 5:return a.call(this,e,h,l,m,p);default:var s=null;if(5<arguments.length){for(var s=0,u=Array(arguments.length-5);s<u.length;)u[s]=arguments[s+5],++s;s=new F(u,0)}return f.d(e,h,l,m,p,s)}throw Error("Invalid arity: "+arguments.length);};e.i=5;e.f=f.f;e.a=d;e.c=c;e.n=b;e.r=a;e.d=f.d;return e}(),ie=function(){function a(a,b,c,d,e,f){var g=O,v=Vc(a);b=b.r?b.r(v,c,d,e,f):b.call(null,v,c,d,e,f);return g(a,b)}function b(a,b,c,d,e){var f=O,g=Vc(a);b=b.n?b.n(g,
+c,d,e):b.call(null,g,c,d,e);return f(a,b)}function c(a,b,c,d){var e=O,f=Vc(a);b=b.c?b.c(f,c,d):b.call(null,f,c,d);return e(a,b)}function d(a,b,c){var d=O,e=Vc(a);b=b.a?b.a(e,c):b.call(null,e,c);return d(a,b)}function e(a,b){var c=O,d;d=Vc(a);d=b.b?b.b(d):b.call(null,d);return c(a,d)}var f=null,g=function(){function a(c,d,e,f,g,h,y){var B=null;if(6<arguments.length){for(var B=0,E=Array(arguments.length-6);B<E.length;)E[B]=arguments[B+6],++B;B=new F(E,0)}return b.call(this,c,d,e,f,g,h,B)}function b(a,
+c,d,e,f,g,h){return O(a,T.d(c,Vc(a),d,e,f,Kc([g,h],0)))}a.i=6;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var f=G(a);a=K(a);var g=G(a);a=K(a);var h=G(a);a=H(a);return b(c,d,e,f,g,h,a)};a.d=b;return a}(),f=function(f,l,m,p,q,s,u){switch(arguments.length){case 2:return e.call(this,f,l);case 3:return d.call(this,f,l,m);case 4:return c.call(this,f,l,m,p);case 5:return b.call(this,f,l,m,p,q);case 6:return a.call(this,f,l,m,p,q,s);default:var v=null;if(6<arguments.length){for(var v=
+0,y=Array(arguments.length-6);v<y.length;)y[v]=arguments[v+6],++v;v=new F(y,0)}return g.d(f,l,m,p,q,s,v)}throw Error("Invalid arity: "+arguments.length);};f.i=6;f.f=g.f;f.a=e;f.c=d;f.n=c;f.r=b;f.P=a;f.d=g.d;return f}(),je=function(){function a(a,b){return!sc.a(a,b)}var b=null,c=function(){function a(c,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return b.call(this,c,d,l)}function b(a,c,d){return Aa(T.n(sc,a,c,d))}a.i=
+2;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=H(a);return b(c,d,a)};a.d=b;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return!1;case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(){return!1};b.a=a;b.d=c.d;return b}(),qe=function ke(){"undefined"===typeof ja&&(ja=function(b,c){this.pc=
+b;this.oc=c;this.q=0;this.j=393216},ja.prototype.ga=function(){return!1},ja.prototype.next=function(){return Error("No such element")},ja.prototype.H=function(){return this.oc},ja.prototype.F=function(b,c){return new ja(this.pc,c)},ja.Yb=!0,ja.Xb="cljs.core/t12660",ja.nc=function(b){return Lb(b,"cljs.core/t12660")});return new ja(ke,new pa(null,5,[le,54,me,2998,ne,3,oe,2994,pe,"/Users/davidnolen/development/clojure/mori/out-mori-adv/cljs/core.cljs"],null))};function re(a,b){this.C=a;this.m=b}
+re.prototype.ga=function(){return this.m<this.C.length};re.prototype.next=function(){var a=this.C.charAt(this.m);this.m+=1;return a};function se(a,b){this.e=a;this.m=b}se.prototype.ga=function(){return this.m<this.e.length};se.prototype.next=function(){var a=this.e[this.m];this.m+=1;return a};var te={},ue={};function ve(a,b){this.eb=a;this.Qa=b}ve.prototype.ga=function(){this.eb===te?(this.eb=ue,this.Qa=D(this.Qa)):this.eb===this.Qa&&(this.Qa=K(this.eb));return null!=this.Qa};
+ve.prototype.next=function(){if(Aa(this.ga()))throw Error("No such element");this.eb=this.Qa;return G(this.Qa)};function we(a){if(null==a)return qe();if("string"===typeof a)return new re(a,0);if(a instanceof Array)return new se(a,0);if(a?t(t(null)?null:a.vb)||(a.yb?0:w(bc,a)):w(bc,a))return cc(a);if(ld(a))return new ve(te,a);throw Error([z("Cannot create iterator from "),z(a)].join(""));}function xe(a,b){this.fa=a;this.$b=b}
+xe.prototype.step=function(a){for(var b=this;;){if(t(function(){var c=null!=a.X;return c?b.$b.ga():c}()))if(Ac(function(){var c=b.$b.next();return b.fa.a?b.fa.a(a,c):b.fa.call(null,a,c)}()))null!=a.M&&(a.M.X=null);else continue;break}return null==a.X?null:b.fa.b?b.fa.b(a):b.fa.call(null,a)};
+function ye(a,b){var c=function(){function a(b,c){b.first=c;b.M=new ze(b.X,null,null,null);b.X=null;return b.M}function b(a){(Ac(a)?qb(a):a).X=null;return a}var c=null,c=function(c,f){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,f)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();return new xe(a.b?a.b(c):a.call(null,c),b)}function Ae(a,b,c){this.fa=a;this.Kb=b;this.ac=c}
+Ae.prototype.ga=function(){for(var a=D(this.Kb);;)if(null!=a){var b=G(a);if(Aa(b.ga()))return!1;a=K(a)}else return!0};Ae.prototype.next=function(){for(var a=this.Kb.length,b=0;;)if(b<a)this.ac[b]=this.Kb[b].next(),b+=1;else break;return Jc.a(this.ac,0)};Ae.prototype.step=function(a){for(;;){var b;b=(b=null!=a.X)?this.ga():b;if(t(b))if(Ac(T.a(this.fa,M(a,this.next()))))null!=a.M&&(a.M.X=null);else continue;break}return null==a.X?null:this.fa.b?this.fa.b(a):this.fa.call(null,a)};
+var Be=function(){function a(a,b,c){var g=function(){function a(b,c){b.first=c;b.M=new ze(b.X,null,null,null);b.X=null;return b.M}function b(a){a=Ac(a)?qb(a):a;a.X=null;return a}var c=null,c=function(c,d){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,d)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();return new Ae(a.b?a.b(g):a.call(null,g),b,c)}function b(a,b){return c.c(a,b,Array(b.length))}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,
+c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();function ze(a,b,c,d){this.X=a;this.first=b;this.M=c;this.k=d;this.q=0;this.j=31719628}k=ze.prototype;k.T=function(){null!=this.X&&Cb(this);return null==this.M?null:Cb(this.M)};k.N=function(){null!=this.X&&Cb(this);return null==this.M?null:this.first};k.S=function(){null!=this.X&&Cb(this);return null==this.M?J:this.M};
+k.D=function(){null!=this.X&&this.X.step(this);return null==this.M?null:this};k.B=function(){return wc(this)};k.A=function(a,b){return null!=Cb(this)?Ic(this,b):cd(b)&&null==D(b)};k.J=function(){return J};k.G=function(a,b){return M(b,Cb(this))};k.F=function(a,b){return new ze(this.X,this.first,this.M,b)};ze.prototype[Ea]=function(){return uc(this)};
+var Ce=function(){function a(a){return kd(a)?a:(a=D(a))?a:J}var b=null,c=function(){function a(c,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return b.call(this,c,d,l)}function b(a,c,d){d=rd(M(c,d));c=[];d=D(d);for(var e=null,m=0,p=0;;)if(p<m){var q=e.Q(null,p);c.push(we(q));p+=1}else if(d=D(d))e=d,fd(e)?(d=Yb(e),p=Zb(e),e=d,m=Q(d),d=p):(d=G(e),c.push(we(d)),d=K(e),e=null,m=0),p=0;else break;return new ze(Be.c(a,c,
+Array(c.length)),null,null,null)}a.i=2;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=H(a);return b(c,d,a)};a.d=b;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return a.call(this,b);case 2:return new ze(ye(b,we(e)),null,null,null);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=a;b.a=function(a,b){return new ze(ye(a,
+we(b)),null,null,null)};b.d=c.d;return b}();function Ee(a,b){for(;;){if(null==D(b))return!0;var c;c=G(b);c=a.b?a.b(c):a.call(null,c);if(t(c)){c=a;var d=K(b);a=c;b=d}else return!1}}function Fe(a,b){for(;;)if(D(b)){var c;c=G(b);c=a.b?a.b(c):a.call(null,c);if(t(c))return c;c=a;var d=K(b);a=c;b=d}else return null}function Ge(a){if("number"===typeof a&&Aa(isNaN(a))&&Infinity!==a&&parseFloat(a)===parseInt(a,10))return 0===(a&1);throw Error([z("Argument must be an integer: "),z(a)].join(""));}
+function He(a){return function(){function b(b,c){return Aa(a.a?a.a(b,c):a.call(null,b,c))}function c(b){return Aa(a.b?a.b(b):a.call(null,b))}function d(){return Aa(a.l?a.l():a.call(null))}var e=null,f=function(){function b(a,d,e){var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return c.call(this,a,d,f)}function c(b,d,e){return Aa(T.n(a,b,d,e))}b.i=2;b.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};b.d=c;
+return b}(),e=function(a,e,l){switch(arguments.length){case 0:return d.call(this);case 1:return c.call(this,a);case 2:return b.call(this,a,e);default:var m=null;if(2<arguments.length){for(var m=0,p=Array(arguments.length-2);m<p.length;)p[m]=arguments[m+2],++m;m=new F(p,0)}return f.d(a,e,m)}throw Error("Invalid arity: "+arguments.length);};e.i=2;e.f=f.f;e.l=d;e.b=c;e.a=b;e.d=f.d;return e}()}
+var Ie=function(){function a(a,b,c){return function(){function d(h,l,m){h=c.c?c.c(h,l,m):c.call(null,h,l,m);h=b.b?b.b(h):b.call(null,h);return a.b?a.b(h):a.call(null,h)}function l(d,h){var l;l=c.a?c.a(d,h):c.call(null,d,h);l=b.b?b.b(l):b.call(null,l);return a.b?a.b(l):a.call(null,l)}function m(d){d=c.b?c.b(d):c.call(null,d);d=b.b?b.b(d):b.call(null,d);return a.b?a.b(d):a.call(null,d)}function p(){var d;d=c.l?c.l():c.call(null);d=b.b?b.b(d):b.call(null,d);return a.b?a.b(d):a.call(null,d)}var q=null,
+s=function(){function d(a,b,c,e){var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-3);f<g.length;)g[f]=arguments[f+3],++f;f=new F(g,0)}return h.call(this,a,b,c,f)}function h(d,l,m,p){d=T.r(c,d,l,m,p);d=b.b?b.b(d):b.call(null,d);return a.b?a.b(d):a.call(null,d)}d.i=3;d.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var d=G(a);a=H(a);return h(b,c,d,a)};d.d=h;return d}(),q=function(a,b,c,e){switch(arguments.length){case 0:return p.call(this);case 1:return m.call(this,a);case 2:return l.call(this,
+a,b);case 3:return d.call(this,a,b,c);default:var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-3);f<g.length;)g[f]=arguments[f+3],++f;f=new F(g,0)}return s.d(a,b,c,f)}throw Error("Invalid arity: "+arguments.length);};q.i=3;q.f=s.f;q.l=p;q.b=m;q.a=l;q.c=d;q.d=s.d;return q}()}function b(a,b){return function(){function c(d,g,h){d=b.c?b.c(d,g,h):b.call(null,d,g,h);return a.b?a.b(d):a.call(null,d)}function d(c,g){var h=b.a?b.a(c,g):b.call(null,c,g);return a.b?a.b(h):a.call(null,h)}
+function l(c){c=b.b?b.b(c):b.call(null,c);return a.b?a.b(c):a.call(null,c)}function m(){var c=b.l?b.l():b.call(null);return a.b?a.b(c):a.call(null,c)}var p=null,q=function(){function c(a,b,e,f){var g=null;if(3<arguments.length){for(var g=0,h=Array(arguments.length-3);g<h.length;)h[g]=arguments[g+3],++g;g=new F(h,0)}return d.call(this,a,b,e,g)}function d(c,g,h,l){c=T.r(b,c,g,h,l);return a.b?a.b(c):a.call(null,c)}c.i=3;c.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var e=G(a);a=H(a);return d(b,
+c,e,a)};c.d=d;return c}(),p=function(a,b,e,f){switch(arguments.length){case 0:return m.call(this);case 1:return l.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,e);default:var p=null;if(3<arguments.length){for(var p=0,E=Array(arguments.length-3);p<E.length;)E[p]=arguments[p+3],++p;p=new F(E,0)}return q.d(a,b,e,p)}throw Error("Invalid arity: "+arguments.length);};p.i=3;p.f=q.f;p.l=m;p.b=l;p.a=d;p.c=c;p.d=q.d;return p}()}var c=null,d=function(){function a(c,d,e,m){var p=null;
+if(3<arguments.length){for(var p=0,q=Array(arguments.length-3);p<q.length;)q[p]=arguments[p+3],++p;p=new F(q,0)}return b.call(this,c,d,e,p)}function b(a,c,d,e){return function(a){return function(){function b(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return c.call(this,d)}function c(b){b=T.a(G(a),b);for(var d=K(a);;)if(d)b=G(d).call(null,b),d=K(d);else return b}b.i=0;b.f=function(a){a=D(a);return c(a)};b.d=c;return b}()}(Jd(be.n(a,
+c,d,e)))}a.i=3;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=H(a);return b(c,d,e,a)};a.d=b;return a}(),c=function(c,f,g,h){switch(arguments.length){case 0:return ud;case 1:return c;case 2:return b.call(this,c,f);case 3:return a.call(this,c,f,g);default:var l=null;if(3<arguments.length){for(var l=0,m=Array(arguments.length-3);l<m.length;)m[l]=arguments[l+3],++l;l=new F(m,0)}return d.d(c,f,g,l)}throw Error("Invalid arity: "+arguments.length);};c.i=3;c.f=d.f;c.l=function(){return ud};
+c.b=function(a){return a};c.a=b;c.c=a;c.d=d.d;return c}(),Je=function(){function a(a,b,c,d){return function(){function e(m,p,q){return a.P?a.P(b,c,d,m,p,q):a.call(null,b,c,d,m,p,q)}function p(e,m){return a.r?a.r(b,c,d,e,m):a.call(null,b,c,d,e,m)}function q(e){return a.n?a.n(b,c,d,e):a.call(null,b,c,d,e)}function s(){return a.c?a.c(b,c,d):a.call(null,b,c,d)}var u=null,v=function(){function e(a,b,c,d){var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-3);f<g.length;)g[f]=arguments[f+
+3],++f;f=new F(g,0)}return m.call(this,a,b,c,f)}function m(e,p,q,s){return T.d(a,b,c,d,e,Kc([p,q,s],0))}e.i=3;e.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var d=G(a);a=H(a);return m(b,c,d,a)};e.d=m;return e}(),u=function(a,b,c,d){switch(arguments.length){case 0:return s.call(this);case 1:return q.call(this,a);case 2:return p.call(this,a,b);case 3:return e.call(this,a,b,c);default:var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-3);f<g.length;)g[f]=arguments[f+3],++f;f=
+new F(g,0)}return v.d(a,b,c,f)}throw Error("Invalid arity: "+arguments.length);};u.i=3;u.f=v.f;u.l=s;u.b=q;u.a=p;u.c=e;u.d=v.d;return u}()}function b(a,b,c){return function(){function d(e,l,m){return a.r?a.r(b,c,e,l,m):a.call(null,b,c,e,l,m)}function e(d,l){return a.n?a.n(b,c,d,l):a.call(null,b,c,d,l)}function p(d){return a.c?a.c(b,c,d):a.call(null,b,c,d)}function q(){return a.a?a.a(b,c):a.call(null,b,c)}var s=null,u=function(){function d(a,b,c,f){var g=null;if(3<arguments.length){for(var g=0,h=Array(arguments.length-
+3);g<h.length;)h[g]=arguments[g+3],++g;g=new F(h,0)}return e.call(this,a,b,c,g)}function e(d,l,m,p){return T.d(a,b,c,d,l,Kc([m,p],0))}d.i=3;d.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var d=G(a);a=H(a);return e(b,c,d,a)};d.d=e;return d}(),s=function(a,b,c,f){switch(arguments.length){case 0:return q.call(this);case 1:return p.call(this,a);case 2:return e.call(this,a,b);case 3:return d.call(this,a,b,c);default:var g=null;if(3<arguments.length){for(var g=0,h=Array(arguments.length-3);g<h.length;)h[g]=
+arguments[g+3],++g;g=new F(h,0)}return u.d(a,b,c,g)}throw Error("Invalid arity: "+arguments.length);};s.i=3;s.f=u.f;s.l=q;s.b=p;s.a=e;s.c=d;s.d=u.d;return s}()}function c(a,b){return function(){function c(d,e,h){return a.n?a.n(b,d,e,h):a.call(null,b,d,e,h)}function d(c,e){return a.c?a.c(b,c,e):a.call(null,b,c,e)}function e(c){return a.a?a.a(b,c):a.call(null,b,c)}function p(){return a.b?a.b(b):a.call(null,b)}var q=null,s=function(){function c(a,b,e,f){var g=null;if(3<arguments.length){for(var g=0,
+h=Array(arguments.length-3);g<h.length;)h[g]=arguments[g+3],++g;g=new F(h,0)}return d.call(this,a,b,e,g)}function d(c,e,h,l){return T.d(a,b,c,e,h,Kc([l],0))}c.i=3;c.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var e=G(a);a=H(a);return d(b,c,e,a)};c.d=d;return c}(),q=function(a,b,f,g){switch(arguments.length){case 0:return p.call(this);case 1:return e.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,f);default:var q=null;if(3<arguments.length){for(var q=0,N=Array(arguments.length-
+3);q<N.length;)N[q]=arguments[q+3],++q;q=new F(N,0)}return s.d(a,b,f,q)}throw Error("Invalid arity: "+arguments.length);};q.i=3;q.f=s.f;q.l=p;q.b=e;q.a=d;q.c=c;q.d=s.d;return q}()}var d=null,e=function(){function a(c,d,e,f,q){var s=null;if(4<arguments.length){for(var s=0,u=Array(arguments.length-4);s<u.length;)u[s]=arguments[s+4],++s;s=new F(u,0)}return b.call(this,c,d,e,f,s)}function b(a,c,d,e,f){return function(){function b(a){var c=null;if(0<arguments.length){for(var c=0,d=Array(arguments.length-
+0);c<d.length;)d[c]=arguments[c+0],++c;c=new F(d,0)}return g.call(this,c)}function g(b){return T.r(a,c,d,e,ae.a(f,b))}b.i=0;b.f=function(a){a=D(a);return g(a)};b.d=g;return b}()}a.i=4;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var f=G(a);a=H(a);return b(c,d,e,f,a)};a.d=b;return a}(),d=function(d,g,h,l,m){switch(arguments.length){case 1:return d;case 2:return c.call(this,d,g);case 3:return b.call(this,d,g,h);case 4:return a.call(this,d,g,h,l);default:var p=null;if(4<arguments.length){for(var p=
+0,q=Array(arguments.length-4);p<q.length;)q[p]=arguments[p+4],++p;p=new F(q,0)}return e.d(d,g,h,l,p)}throw Error("Invalid arity: "+arguments.length);};d.i=4;d.f=e.f;d.b=function(a){return a};d.a=c;d.c=b;d.n=a;d.d=e.d;return d}(),Ke=function(){function a(a,b,c,d){return function(){function l(l,m,p){l=null==l?b:l;m=null==m?c:m;p=null==p?d:p;return a.c?a.c(l,m,p):a.call(null,l,m,p)}function m(d,h){var l=null==d?b:d,m=null==h?c:h;return a.a?a.a(l,m):a.call(null,l,m)}var p=null,q=function(){function l(a,
+b,c,d){var e=null;if(3<arguments.length){for(var e=0,f=Array(arguments.length-3);e<f.length;)f[e]=arguments[e+3],++e;e=new F(f,0)}return m.call(this,a,b,c,e)}function m(l,p,q,s){return T.r(a,null==l?b:l,null==p?c:p,null==q?d:q,s)}l.i=3;l.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var d=G(a);a=H(a);return m(b,c,d,a)};l.d=m;return l}(),p=function(a,b,c,d){switch(arguments.length){case 2:return m.call(this,a,b);case 3:return l.call(this,a,b,c);default:var e=null;if(3<arguments.length){for(var e=
+0,f=Array(arguments.length-3);e<f.length;)f[e]=arguments[e+3],++e;e=new F(f,0)}return q.d(a,b,c,e)}throw Error("Invalid arity: "+arguments.length);};p.i=3;p.f=q.f;p.a=m;p.c=l;p.d=q.d;return p}()}function b(a,b,c){return function(){function d(h,l,m){h=null==h?b:h;l=null==l?c:l;return a.c?a.c(h,l,m):a.call(null,h,l,m)}function l(d,h){var l=null==d?b:d,m=null==h?c:h;return a.a?a.a(l,m):a.call(null,l,m)}var m=null,p=function(){function d(a,b,c,e){var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-
+3);f<g.length;)g[f]=arguments[f+3],++f;f=new F(g,0)}return h.call(this,a,b,c,f)}function h(d,l,m,p){return T.r(a,null==d?b:d,null==l?c:l,m,p)}d.i=3;d.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var d=G(a);a=H(a);return h(b,c,d,a)};d.d=h;return d}(),m=function(a,b,c,e){switch(arguments.length){case 2:return l.call(this,a,b);case 3:return d.call(this,a,b,c);default:var f=null;if(3<arguments.length){for(var f=0,g=Array(arguments.length-3);f<g.length;)g[f]=arguments[f+3],++f;f=new F(g,0)}return p.d(a,
+b,c,f)}throw Error("Invalid arity: "+arguments.length);};m.i=3;m.f=p.f;m.a=l;m.c=d;m.d=p.d;return m}()}function c(a,b){return function(){function c(d,g,h){d=null==d?b:d;return a.c?a.c(d,g,h):a.call(null,d,g,h)}function d(c,g){var h=null==c?b:c;return a.a?a.a(h,g):a.call(null,h,g)}function l(c){c=null==c?b:c;return a.b?a.b(c):a.call(null,c)}var m=null,p=function(){function c(a,b,e,f){var g=null;if(3<arguments.length){for(var g=0,h=Array(arguments.length-3);g<h.length;)h[g]=arguments[g+3],++g;g=new F(h,
+0)}return d.call(this,a,b,e,g)}function d(c,g,h,l){return T.r(a,null==c?b:c,g,h,l)}c.i=3;c.f=function(a){var b=G(a);a=K(a);var c=G(a);a=K(a);var e=G(a);a=H(a);return d(b,c,e,a)};c.d=d;return c}(),m=function(a,b,e,f){switch(arguments.length){case 1:return l.call(this,a);case 2:return d.call(this,a,b);case 3:return c.call(this,a,b,e);default:var m=null;if(3<arguments.length){for(var m=0,B=Array(arguments.length-3);m<B.length;)B[m]=arguments[m+3],++m;m=new F(B,0)}return p.d(a,b,e,m)}throw Error("Invalid arity: "+
+arguments.length);};m.i=3;m.f=p.f;m.b=l;m.a=d;m.c=c;m.d=p.d;return m}()}var d=null,d=function(d,f,g,h){switch(arguments.length){case 2:return c.call(this,d,f);case 3:return b.call(this,d,f,g);case 4:return a.call(this,d,f,g,h)}throw Error("Invalid arity: "+arguments.length);};d.a=c;d.c=b;d.n=a;return d}(),Le=function(){function a(a,b){return new V(null,function(){var f=D(b);if(f){if(fd(f)){for(var g=Yb(f),h=Q(g),l=Td(h),m=0;;)if(m<h){var p=function(){var b=C.a(g,m);return a.b?a.b(b):a.call(null,b)}();
+null!=p&&l.add(p);m+=1}else break;return Wd(l.ca(),c.a(a,Zb(f)))}h=function(){var b=G(f);return a.b?a.b(b):a.call(null,b)}();return null==h?c.a(a,H(f)):M(h,c.a(a,H(f)))}return null},null,null)}function b(a){return function(b){return function(){function c(f,g){var h=a.b?a.b(g):a.call(null,g);return null==h?f:b.a?b.a(f,h):b.call(null,f,h)}function g(a){return b.b?b.b(a):b.call(null,a)}function h(){return b.l?b.l():b.call(null)}var l=null,l=function(a,b){switch(arguments.length){case 0:return h.call(this);
+case 1:return g.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};l.l=h;l.b=g;l.a=c;return l}()}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();function Me(a){this.state=a;this.q=0;this.j=32768}Me.prototype.Ra=function(){return this.state};Me.prototype.bb=function(a,b){return this.state=b};
+var Ne=function(){function a(a,b){return function g(b,c){return new V(null,function(){var e=D(c);if(e){if(fd(e)){for(var p=Yb(e),q=Q(p),s=Td(q),u=0;;)if(u<q){var v=function(){var c=b+u,e=C.a(p,u);return a.a?a.a(c,e):a.call(null,c,e)}();null!=v&&s.add(v);u+=1}else break;return Wd(s.ca(),g(b+q,Zb(e)))}q=function(){var c=G(e);return a.a?a.a(b,c):a.call(null,b,c)}();return null==q?g(b+1,H(e)):M(q,g(b+1,H(e)))}return null},null,null)}(0,b)}function b(a){return function(b){return function(c){return function(){function g(g,
+h){var l=c.bb(0,c.Ra(null)+1),l=a.a?a.a(l,h):a.call(null,l,h);return null==l?g:b.a?b.a(g,l):b.call(null,g,l)}function h(a){return b.b?b.b(a):b.call(null,a)}function l(){return b.l?b.l():b.call(null)}var m=null,m=function(a,b){switch(arguments.length){case 0:return l.call(this);case 1:return h.call(this,a);case 2:return g.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};m.l=l;m.b=h;m.a=g;return m}()}(new Me(-1))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,
+c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Oe=function(){function a(a,b,c,d){return new V(null,function(){var f=D(b),q=D(c),s=D(d);if(f&&q&&s){var u=M,v;v=G(f);var y=G(q),B=G(s);v=a.c?a.c(v,y,B):a.call(null,v,y,B);f=u(v,e.n(a,H(f),H(q),H(s)))}else f=null;return f},null,null)}function b(a,b,c){return new V(null,function(){var d=D(b),f=D(c);if(d&&f){var q=M,s;s=G(d);var u=G(f);s=a.a?a.a(s,u):a.call(null,s,u);d=q(s,e.c(a,H(d),H(f)))}else d=
+null;return d},null,null)}function c(a,b){return new V(null,function(){var c=D(b);if(c){if(fd(c)){for(var d=Yb(c),f=Q(d),q=Td(f),s=0;;)if(s<f)Xd(q,function(){var b=C.a(d,s);return a.b?a.b(b):a.call(null,b)}()),s+=1;else break;return Wd(q.ca(),e.a(a,Zb(c)))}return M(function(){var b=G(c);return a.b?a.b(b):a.call(null,b)}(),e.a(a,H(c)))}return null},null,null)}function d(a){return function(b){return function(){function c(d,e){var f=a.b?a.b(e):a.call(null,e);return b.a?b.a(d,f):b.call(null,d,f)}function d(a){return b.b?
+b.b(a):b.call(null,a)}function e(){return b.l?b.l():b.call(null)}var f=null,s=function(){function c(a,b,e){var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return d.call(this,a,b,f)}function d(c,e,f){e=T.c(a,e,f);return b.a?b.a(c,e):b.call(null,c,e)}c.i=2;c.f=function(a){var b=G(a);a=K(a);var c=G(a);a=H(a);return d(b,c,a)};c.d=d;return c}(),f=function(a,b,f){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,
+a);case 2:return c.call(this,a,b);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return s.d(a,b,g)}throw Error("Invalid arity: "+arguments.length);};f.i=2;f.f=s.f;f.l=e;f.b=d;f.a=c;f.d=s.d;return f}()}}var e=null,f=function(){function a(c,d,e,f,g){var u=null;if(4<arguments.length){for(var u=0,v=Array(arguments.length-4);u<v.length;)v[u]=arguments[u+4],++u;u=new F(v,0)}return b.call(this,c,d,e,f,u)}function b(a,c,d,
+f,g){var h=function y(a){return new V(null,function(){var b=e.a(D,a);return Ee(ud,b)?M(e.a(G,b),y(e.a(H,b))):null},null,null)};return e.a(function(){return function(b){return T.a(a,b)}}(h),h(Nc.d(g,f,Kc([d,c],0))))}a.i=4;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var f=G(a);a=H(a);return b(c,d,e,f,a)};a.d=b;return a}(),e=function(e,h,l,m,p){switch(arguments.length){case 1:return d.call(this,e);case 2:return c.call(this,e,h);case 3:return b.call(this,e,h,l);case 4:return a.call(this,
+e,h,l,m);default:var q=null;if(4<arguments.length){for(var q=0,s=Array(arguments.length-4);q<s.length;)s[q]=arguments[q+4],++q;q=new F(s,0)}return f.d(e,h,l,m,q)}throw Error("Invalid arity: "+arguments.length);};e.i=4;e.f=f.f;e.b=d;e.a=c;e.c=b;e.n=a;e.d=f.d;return e}(),Pe=function(){function a(a,b){return new V(null,function(){if(0<a){var f=D(b);return f?M(G(f),c.a(a-1,H(f))):null}return null},null,null)}function b(a){return function(b){return function(a){return function(){function c(d,g){var h=qb(a),
+l=a.bb(0,a.Ra(null)-1),h=0<h?b.a?b.a(d,g):b.call(null,d,g):d;return 0<l?h:Ac(h)?h:new yc(h)}function d(a){return b.b?b.b(a):b.call(null,a)}function l(){return b.l?b.l():b.call(null)}var m=null,m=function(a,b){switch(arguments.length){case 0:return l.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};m.l=l;m.b=d;m.a=c;return m}()}(new Me(a))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,
+c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Qe=function(){function a(a,b){return new V(null,function(c){return function(){return c(a,b)}}(function(a,b){for(;;){var c=D(b);if(0<a&&c){var d=a-1,c=H(c);a=d;b=c}else return c}}),null,null)}function b(a){return function(b){return function(a){return function(){function c(d,g){var h=qb(a);a.bb(0,a.Ra(null)-1);return 0<h?d:b.a?b.a(d,g):b.call(null,d,g)}function d(a){return b.b?b.b(a):b.call(null,a)}function l(){return b.l?
+b.l():b.call(null)}var m=null,m=function(a,b){switch(arguments.length){case 0:return l.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};m.l=l;m.b=d;m.a=c;return m}()}(new Me(a))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Re=function(){function a(a,b){return new V(null,function(c){return function(){return c(a,
+b)}}(function(a,b){for(;;){var c=D(b),d;if(d=c)d=G(c),d=a.b?a.b(d):a.call(null,d);if(t(d))d=a,c=H(c),a=d,b=c;else return c}}),null,null)}function b(a){return function(b){return function(c){return function(){function g(g,h){var l=qb(c);if(t(t(l)?a.b?a.b(h):a.call(null,h):l))return g;ac(c,null);return b.a?b.a(g,h):b.call(null,g,h)}function h(a){return b.b?b.b(a):b.call(null,a)}function l(){return b.l?b.l():b.call(null)}var m=null,m=function(a,b){switch(arguments.length){case 0:return l.call(this);case 1:return h.call(this,
+a);case 2:return g.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};m.l=l;m.b=h;m.a=g;return m}()}(new Me(!0))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Se=function(){function a(a,b){return Pe.a(a,c.b(b))}function b(a){return new V(null,function(){return M(a,c.b(a))},null,null)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,
+c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Te=function(){function a(a,b){return Pe.a(a,c.b(b))}function b(a){return new V(null,function(){return M(a.l?a.l():a.call(null),c.b(a))},null,null)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Ue=function(){function a(a,c){return new V(null,function(){var f=
+D(a),g=D(c);return f&&g?M(G(f),M(G(g),b.a(H(f),H(g)))):null},null,null)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){return new V(null,function(){var c=Oe.a(D,Nc.d(e,d,Kc([a],0)));return Ee(ud,c)?ae.a(Oe.a(G,c),T.a(b,Oe.a(H,c))):null},null,null)}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),
+b=function(b,e,f){switch(arguments.length){case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.a=a;b.d=c.d;return b}(),We=function(){function a(a){return Ie.a(Oe.b(a),Ve)}var b=null,c=function(){function a(c,d){var h=null;if(1<arguments.length){for(var h=0,l=Array(arguments.length-1);h<l.length;)l[h]=arguments[h+
+1],++h;h=new F(l,0)}return b.call(this,c,h)}function b(a,c){return T.a(ae,T.c(Oe,a,c))}a.i=1;a.f=function(a){var c=G(a);a=H(a);return b(c,a)};a.d=b;return a}(),b=function(b,e){switch(arguments.length){case 1:return a.call(this,b);default:var f=null;if(1<arguments.length){for(var f=0,g=Array(arguments.length-1);f<g.length;)g[f]=arguments[f+1],++f;f=new F(g,0)}return c.d(b,f)}throw Error("Invalid arity: "+arguments.length);};b.i=1;b.f=c.f;b.b=a;b.d=c.d;return b}(),Xe=function(){function a(a,b){return new V(null,
+function(){var f=D(b);if(f){if(fd(f)){for(var g=Yb(f),h=Q(g),l=Td(h),m=0;;)if(m<h){var p;p=C.a(g,m);p=a.b?a.b(p):a.call(null,p);t(p)&&(p=C.a(g,m),l.add(p));m+=1}else break;return Wd(l.ca(),c.a(a,Zb(f)))}g=G(f);f=H(f);return t(a.b?a.b(g):a.call(null,g))?M(g,c.a(a,f)):c.a(a,f)}return null},null,null)}function b(a){return function(b){return function(){function c(f,g){return t(a.b?a.b(g):a.call(null,g))?b.a?b.a(f,g):b.call(null,f,g):f}function g(a){return b.b?b.b(a):b.call(null,a)}function h(){return b.l?
+b.l():b.call(null)}var l=null,l=function(a,b){switch(arguments.length){case 0:return h.call(this);case 1:return g.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};l.l=h;l.b=g;l.a=c;return l}()}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),Ye=function(){function a(a,b){return Xe.a(He(a),b)}function b(a){return Xe.b(He(a))}
+var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();function Ze(a){var b=$e;return function d(a){return new V(null,function(){return M(a,t(b.b?b.b(a):b.call(null,a))?We.d(d,Kc([D.b?D.b(a):D.call(null,a)],0)):null)},null,null)}(a)}
+var af=function(){function a(a,b,c){return a&&(a.q&4||a.dc)?O(ce(wd.n(b,de,Ob(a),c)),Vc(a)):wd.n(b,Nc,a,c)}function b(a,b){return null!=a?a&&(a.q&4||a.dc)?O(ce(A.c(Pb,Ob(a),b)),Vc(a)):A.c(Ra,a,b):A.c(Nc,J,b)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),bf=function(){function a(a,b,c,h){return new V(null,function(){var l=D(h);if(l){var m=Pe.a(a,l);return a===
+Q(m)?M(m,d.n(a,b,c,Qe.a(b,l))):Ra(J,Pe.a(a,ae.a(m,c)))}return null},null,null)}function b(a,b,c){return new V(null,function(){var h=D(c);if(h){var l=Pe.a(a,h);return a===Q(l)?M(l,d.c(a,b,Qe.a(b,h))):null}return null},null,null)}function c(a,b){return d.c(a,a,b)}var d=null,d=function(d,f,g,h){switch(arguments.length){case 2:return c.call(this,d,f);case 3:return b.call(this,d,f,g);case 4:return a.call(this,d,f,g,h)}throw Error("Invalid arity: "+arguments.length);};d.a=c;d.c=b;d.n=a;return d}(),cf=function(){function a(a,
+b,c){var g=jd;for(b=D(b);;)if(b){var h=a;if(h?h.j&256||h.Rb||(h.j?0:w(Za,h)):w(Za,h)){a=S.c(a,G(b),g);if(g===a)return c;b=K(b)}else return c}else return a}function b(a,b){return c.c(a,b,null)}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}(),df=function(){function a(a,b,c,d,f,q){var s=R.c(b,0,null);return(b=Ed(b))?Rc.c(a,s,e.P(S.a(a,s),b,c,d,f,q)):Rc.c(a,s,
+function(){var b=S.a(a,s);return c.n?c.n(b,d,f,q):c.call(null,b,d,f,q)}())}function b(a,b,c,d,f){var q=R.c(b,0,null);return(b=Ed(b))?Rc.c(a,q,e.r(S.a(a,q),b,c,d,f)):Rc.c(a,q,function(){var b=S.a(a,q);return c.c?c.c(b,d,f):c.call(null,b,d,f)}())}function c(a,b,c,d){var f=R.c(b,0,null);return(b=Ed(b))?Rc.c(a,f,e.n(S.a(a,f),b,c,d)):Rc.c(a,f,function(){var b=S.a(a,f);return c.a?c.a(b,d):c.call(null,b,d)}())}function d(a,b,c){var d=R.c(b,0,null);return(b=Ed(b))?Rc.c(a,d,e.c(S.a(a,d),b,c)):Rc.c(a,d,function(){var b=
+S.a(a,d);return c.b?c.b(b):c.call(null,b)}())}var e=null,f=function(){function a(c,d,e,f,g,u,v){var y=null;if(6<arguments.length){for(var y=0,B=Array(arguments.length-6);y<B.length;)B[y]=arguments[y+6],++y;y=new F(B,0)}return b.call(this,c,d,e,f,g,u,y)}function b(a,c,d,f,g,h,v){var y=R.c(c,0,null);return(c=Ed(c))?Rc.c(a,y,T.d(e,S.a(a,y),c,d,f,Kc([g,h,v],0))):Rc.c(a,y,T.d(d,S.a(a,y),f,g,h,Kc([v],0)))}a.i=6;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=K(a);var e=G(a);a=K(a);var f=G(a);a=K(a);var g=
+G(a);a=K(a);var v=G(a);a=H(a);return b(c,d,e,f,g,v,a)};a.d=b;return a}(),e=function(e,h,l,m,p,q,s){switch(arguments.length){case 3:return d.call(this,e,h,l);case 4:return c.call(this,e,h,l,m);case 5:return b.call(this,e,h,l,m,p);case 6:return a.call(this,e,h,l,m,p,q);default:var u=null;if(6<arguments.length){for(var u=0,v=Array(arguments.length-6);u<v.length;)v[u]=arguments[u+6],++u;u=new F(v,0)}return f.d(e,h,l,m,p,q,u)}throw Error("Invalid arity: "+arguments.length);};e.i=6;e.f=f.f;e.c=d;e.n=c;
+e.r=b;e.P=a;e.d=f.d;return e}();function ef(a,b){this.u=a;this.e=b}function ff(a){return new ef(a,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null])}function gf(a){return new ef(a.u,Fa(a.e))}function hf(a){a=a.g;return 32>a?0:a-1>>>5<<5}function jf(a,b,c){for(;;){if(0===b)return c;var d=ff(a);d.e[0]=c;c=d;b-=5}}
+var lf=function kf(b,c,d,e){var f=gf(d),g=b.g-1>>>c&31;5===c?f.e[g]=e:(d=d.e[g],b=null!=d?kf(b,c-5,d,e):jf(null,c-5,e),f.e[g]=b);return f};function mf(a,b){throw Error([z("No item "),z(a),z(" in vector of length "),z(b)].join(""));}function nf(a,b){if(b>=hf(a))return a.W;for(var c=a.root,d=a.shift;;)if(0<d)var e=d-5,c=c.e[b>>>d&31],d=e;else return c.e}function of(a,b){return 0<=b&&b<a.g?nf(a,b):mf(b,a.g)}
+var qf=function pf(b,c,d,e,f){var g=gf(d);if(0===c)g.e[e&31]=f;else{var h=e>>>c&31;b=pf(b,c-5,d.e[h],e,f);g.e[h]=b}return g},sf=function rf(b,c,d){var e=b.g-2>>>c&31;if(5<c){b=rf(b,c-5,d.e[e]);if(null==b&&0===e)return null;d=gf(d);d.e[e]=b;return d}if(0===e)return null;d=gf(d);d.e[e]=null;return d};function tf(a,b,c,d,e,f){this.m=a;this.zb=b;this.e=c;this.oa=d;this.start=e;this.end=f}tf.prototype.ga=function(){return this.m<this.end};
+tf.prototype.next=function(){32===this.m-this.zb&&(this.e=nf(this.oa,this.m),this.zb+=32);var a=this.e[this.m&31];this.m+=1;return a};function W(a,b,c,d,e,f){this.k=a;this.g=b;this.shift=c;this.root=d;this.W=e;this.p=f;this.j=167668511;this.q=8196}k=W.prototype;k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){return"number"===typeof b?C.c(this,b,c):c};
+k.gb=function(a,b,c){a=0;for(var d=c;;)if(a<this.g){var e=nf(this,a);c=e.length;a:{for(var f=0;;)if(f<c){var g=f+a,h=e[f],d=b.c?b.c(d,g,h):b.call(null,d,g,h);if(Ac(d)){e=d;break a}f+=1}else{e=d;break a}e=void 0}if(Ac(e))return b=e,L.b?L.b(b):L.call(null,b);a+=c;d=e}else return d};k.Q=function(a,b){return of(this,b)[b&31]};k.$=function(a,b,c){return 0<=b&&b<this.g?nf(this,b)[b&31]:c};
+k.Ua=function(a,b,c){if(0<=b&&b<this.g)return hf(this)<=b?(a=Fa(this.W),a[b&31]=c,new W(this.k,this.g,this.shift,this.root,a,null)):new W(this.k,this.g,this.shift,qf(this,this.shift,this.root,b,c),this.W,null);if(b===this.g)return Ra(this,c);throw Error([z("Index "),z(b),z(" out of bounds  [0,"),z(this.g),z("]")].join(""));};k.vb=!0;k.fb=function(){var a=this.g;return new tf(0,0,0<Q(this)?nf(this,0):null,this,0,a)};k.H=function(){return this.k};k.L=function(){return this.g};
+k.hb=function(){return C.a(this,0)};k.ib=function(){return C.a(this,1)};k.La=function(){return 0<this.g?C.a(this,this.g-1):null};
+k.Ma=function(){if(0===this.g)throw Error("Can't pop empty vector");if(1===this.g)return ub(Mc,this.k);if(1<this.g-hf(this))return new W(this.k,this.g-1,this.shift,this.root,this.W.slice(0,-1),null);var a=nf(this,this.g-2),b=sf(this,this.shift,this.root),b=null==b?uf:b,c=this.g-1;return 5<this.shift&&null==b.e[1]?new W(this.k,c,this.shift-5,b.e[0],a,null):new W(this.k,c,this.shift,b,a,null)};k.ab=function(){return 0<this.g?new Hc(this,this.g-1,null):null};
+k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){if(b instanceof W)if(this.g===Q(b))for(var c=cc(this),d=cc(b);;)if(t(c.ga())){var e=c.next(),f=d.next();if(!sc.a(e,f))return!1}else return!0;else return!1;else return Ic(this,b)};k.$a=function(){var a=this;return new vf(a.g,a.shift,function(){var b=a.root;return wf.b?wf.b(b):wf.call(null,b)}(),function(){var b=a.W;return xf.b?xf.b(b):xf.call(null,b)}())};k.J=function(){return O(Mc,this.k)};
+k.R=function(a,b){return Cc.a(this,b)};k.O=function(a,b,c){a=0;for(var d=c;;)if(a<this.g){var e=nf(this,a);c=e.length;a:{for(var f=0;;)if(f<c){var g=e[f],d=b.a?b.a(d,g):b.call(null,d,g);if(Ac(d)){e=d;break a}f+=1}else{e=d;break a}e=void 0}if(Ac(e))return b=e,L.b?L.b(b):L.call(null,b);a+=c;d=e}else return d};k.Ka=function(a,b,c){if("number"===typeof b)return pb(this,b,c);throw Error("Vector's key for assoc must be a number.");};
+k.D=function(){if(0===this.g)return null;if(32>=this.g)return new F(this.W,0);var a;a:{a=this.root;for(var b=this.shift;;)if(0<b)b-=5,a=a.e[0];else{a=a.e;break a}a=void 0}return yf.n?yf.n(this,a,0,0):yf.call(null,this,a,0,0)};k.F=function(a,b){return new W(b,this.g,this.shift,this.root,this.W,this.p)};
+k.G=function(a,b){if(32>this.g-hf(this)){for(var c=this.W.length,d=Array(c+1),e=0;;)if(e<c)d[e]=this.W[e],e+=1;else break;d[c]=b;return new W(this.k,this.g+1,this.shift,this.root,d,null)}c=(d=this.g>>>5>1<<this.shift)?this.shift+5:this.shift;d?(d=ff(null),d.e[0]=this.root,e=jf(null,this.shift,new ef(null,this.W)),d.e[1]=e):d=lf(this,this.shift,this.root,new ef(null,this.W));return new W(this.k,this.g+1,c,d,[b],null)};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.Q(null,c);case 3:return this.$(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.Q(null,c)};a.c=function(a,c,d){return this.$(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.Q(null,a)};k.a=function(a,b){return this.$(null,a,b)};
+var uf=new ef(null,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]),Mc=new W(null,0,5,uf,[],0);W.prototype[Ea]=function(){return uc(this)};function zf(a){return Qb(A.c(Pb,Ob(Mc),a))}
+var Af=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){if(a instanceof F&&0===a.m)a:{a=a.e;var b=a.length;if(32>b)a=new W(null,b,5,uf,a,null);else{for(var e=32,f=(new W(null,32,5,uf,a.slice(0,32),null)).$a(null);;)if(e<b)var g=e+1,f=de.a(f,a[e]),e=g;else{a=Qb(f);break a}a=void 0}}else a=zf(a);return a}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}();
+function Bf(a,b,c,d,e,f){this.ha=a;this.Ja=b;this.m=c;this.V=d;this.k=e;this.p=f;this.j=32375020;this.q=1536}k=Bf.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.T=function(){if(this.V+1<this.Ja.length){var a;a=this.ha;var b=this.Ja,c=this.m,d=this.V+1;a=yf.n?yf.n(a,b,c,d):yf.call(null,a,b,c,d);return null==a?null:a}return $b(this)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(Mc,this.k)};
+k.R=function(a,b){var c=this;return Cc.a(function(){var a=c.ha,b=c.m+c.V,f=Q(c.ha);return Cf.c?Cf.c(a,b,f):Cf.call(null,a,b,f)}(),b)};k.O=function(a,b,c){var d=this;return Cc.c(function(){var a=d.ha,b=d.m+d.V,c=Q(d.ha);return Cf.c?Cf.c(a,b,c):Cf.call(null,a,b,c)}(),b,c)};k.N=function(){return this.Ja[this.V]};k.S=function(){if(this.V+1<this.Ja.length){var a;a=this.ha;var b=this.Ja,c=this.m,d=this.V+1;a=yf.n?yf.n(a,b,c,d):yf.call(null,a,b,c,d);return null==a?J:a}return Zb(this)};k.D=function(){return this};
+k.Cb=function(){return Ud.a(this.Ja,this.V)};k.Db=function(){var a=this.m+this.Ja.length;if(a<Ma(this.ha)){var b=this.ha,c=nf(this.ha,a);return yf.n?yf.n(b,c,a,0):yf.call(null,b,c,a,0)}return J};k.F=function(a,b){var c=this.ha,d=this.Ja,e=this.m,f=this.V;return yf.r?yf.r(c,d,e,f,b):yf.call(null,c,d,e,f,b)};k.G=function(a,b){return M(b,this)};k.Bb=function(){var a=this.m+this.Ja.length;if(a<Ma(this.ha)){var b=this.ha,c=nf(this.ha,a);return yf.n?yf.n(b,c,a,0):yf.call(null,b,c,a,0)}return null};
+Bf.prototype[Ea]=function(){return uc(this)};var yf=function(){function a(a,b,c,d,l){return new Bf(a,b,c,d,l,null)}function b(a,b,c,d){return new Bf(a,b,c,d,null,null)}function c(a,b,c){return new Bf(a,of(a,b),b,c,null,null)}var d=null,d=function(d,f,g,h,l){switch(arguments.length){case 3:return c.call(this,d,f,g);case 4:return b.call(this,d,f,g,h);case 5:return a.call(this,d,f,g,h,l)}throw Error("Invalid arity: "+arguments.length);};d.c=c;d.n=b;d.r=a;return d}();
+function Df(a,b,c,d,e){this.k=a;this.oa=b;this.start=c;this.end=d;this.p=e;this.j=166617887;this.q=8192}k=Df.prototype;k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){return"number"===typeof b?C.c(this,b,c):c};k.Q=function(a,b){return 0>b||this.end<=this.start+b?mf(b,this.end-this.start):C.a(this.oa,this.start+b)};k.$=function(a,b,c){return 0>b||this.end<=this.start+b?c:C.c(this.oa,this.start+b,c)};
+k.Ua=function(a,b,c){var d=this.start+b;a=this.k;c=Rc.c(this.oa,d,c);b=this.start;var e=this.end,d=d+1,d=e>d?e:d;return Ef.r?Ef.r(a,c,b,d,null):Ef.call(null,a,c,b,d,null)};k.H=function(){return this.k};k.L=function(){return this.end-this.start};k.La=function(){return C.a(this.oa,this.end-1)};k.Ma=function(){if(this.start===this.end)throw Error("Can't pop empty vector");var a=this.k,b=this.oa,c=this.start,d=this.end-1;return Ef.r?Ef.r(a,b,c,d,null):Ef.call(null,a,b,c,d,null)};
+k.ab=function(){return this.start!==this.end?new Hc(this,this.end-this.start-1,null):null};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(Mc,this.k)};k.R=function(a,b){return Cc.a(this,b)};k.O=function(a,b,c){return Cc.c(this,b,c)};k.Ka=function(a,b,c){if("number"===typeof b)return pb(this,b,c);throw Error("Subvec's key for assoc must be a number.");};
+k.D=function(){var a=this;return function(b){return function d(e){return e===a.end?null:M(C.a(a.oa,e),new V(null,function(){return function(){return d(e+1)}}(b),null,null))}}(this)(a.start)};k.F=function(a,b){var c=this.oa,d=this.start,e=this.end,f=this.p;return Ef.r?Ef.r(b,c,d,e,f):Ef.call(null,b,c,d,e,f)};k.G=function(a,b){var c=this.k,d=pb(this.oa,this.end,b),e=this.start,f=this.end+1;return Ef.r?Ef.r(c,d,e,f,null):Ef.call(null,c,d,e,f,null)};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.Q(null,c);case 3:return this.$(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.Q(null,c)};a.c=function(a,c,d){return this.$(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.Q(null,a)};k.a=function(a,b){return this.$(null,a,b)};Df.prototype[Ea]=function(){return uc(this)};
+function Ef(a,b,c,d,e){for(;;)if(b instanceof Df)c=b.start+c,d=b.start+d,b=b.oa;else{var f=Q(b);if(0>c||0>d||c>f||d>f)throw Error("Index out of bounds");return new Df(a,b,c,d,e)}}var Cf=function(){function a(a,b,c){return Ef(null,a,b,c,null)}function b(a,b){return c.c(a,b,Q(a))}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();
+function Ff(a,b){return a===b.u?b:new ef(a,Fa(b.e))}function wf(a){return new ef({},Fa(a.e))}function xf(a){var b=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];hd(a,0,b,0,a.length);return b}
+var Hf=function Gf(b,c,d,e){d=Ff(b.root.u,d);var f=b.g-1>>>c&31;if(5===c)b=e;else{var g=d.e[f];b=null!=g?Gf(b,c-5,g,e):jf(b.root.u,c-5,e)}d.e[f]=b;return d},Jf=function If(b,c,d){d=Ff(b.root.u,d);var e=b.g-2>>>c&31;if(5<c){b=If(b,c-5,d.e[e]);if(null==b&&0===e)return null;d.e[e]=b;return d}if(0===e)return null;d.e[e]=null;return d};function vf(a,b,c,d){this.g=a;this.shift=b;this.root=c;this.W=d;this.j=275;this.q=88}k=vf.prototype;
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};k.t=function(a,b){return $a.c(this,b,null)};
+k.s=function(a,b,c){return"number"===typeof b?C.c(this,b,c):c};k.Q=function(a,b){if(this.root.u)return of(this,b)[b&31];throw Error("nth after persistent!");};k.$=function(a,b,c){return 0<=b&&b<this.g?C.a(this,b):c};k.L=function(){if(this.root.u)return this.g;throw Error("count after persistent!");};
+k.Ub=function(a,b,c){var d=this;if(d.root.u){if(0<=b&&b<d.g)return hf(this)<=b?d.W[b&31]=c:(a=function(){return function f(a,h){var l=Ff(d.root.u,h);if(0===a)l.e[b&31]=c;else{var m=b>>>a&31,p=f(a-5,l.e[m]);l.e[m]=p}return l}}(this).call(null,d.shift,d.root),d.root=a),this;if(b===d.g)return Pb(this,c);throw Error([z("Index "),z(b),z(" out of bounds for TransientVector of length"),z(d.g)].join(""));}throw Error("assoc! after persistent!");};
+k.Vb=function(){if(this.root.u){if(0===this.g)throw Error("Can't pop empty vector");if(1===this.g)this.g=0;else if(0<(this.g-1&31))this.g-=1;else{var a;a:if(a=this.g-2,a>=hf(this))a=this.W;else{for(var b=this.root,c=b,d=this.shift;;)if(0<d)c=Ff(b.u,c.e[a>>>d&31]),d-=5;else{a=c.e;break a}a=void 0}b=Jf(this,this.shift,this.root);b=null!=b?b:new ef(this.root.u,[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+null,null,null,null]);5<this.shift&&null==b.e[1]?(this.root=Ff(this.root.u,b.e[0]),this.shift-=5):this.root=b;this.g-=1;this.W=a}return this}throw Error("pop! after persistent!");};k.kb=function(a,b,c){if("number"===typeof b)return Tb(this,b,c);throw Error("TransientVector's key for assoc! must be a number.");};
+k.Sa=function(a,b){if(this.root.u){if(32>this.g-hf(this))this.W[this.g&31]=b;else{var c=new ef(this.root.u,this.W),d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];d[0]=b;this.W=d;if(this.g>>>5>1<<this.shift){var d=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],e=this.shift+
+5;d[0]=this.root;d[1]=jf(this.root.u,this.shift,c);this.root=new ef(this.root.u,d);this.shift=e}else this.root=Hf(this,this.shift,this.root,c)}this.g+=1;return this}throw Error("conj! after persistent!");};k.Ta=function(){if(this.root.u){this.root.u=null;var a=this.g-hf(this),b=Array(a);hd(this.W,0,b,0,a);return new W(null,this.g,this.shift,this.root,b,null)}throw Error("persistent! called twice");};function Kf(a,b,c,d){this.k=a;this.ea=b;this.sa=c;this.p=d;this.q=0;this.j=31850572}k=Kf.prototype;
+k.toString=function(){return ec(this)};k.H=function(){return this.k};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.N=function(){return G(this.ea)};k.S=function(){var a=K(this.ea);return a?new Kf(this.k,a,this.sa,null):null==this.sa?Na(this):new Kf(this.k,this.sa,null,null)};k.D=function(){return this};k.F=function(a,b){return new Kf(b,this.ea,this.sa,this.p)};k.G=function(a,b){return M(b,this)};
+Kf.prototype[Ea]=function(){return uc(this)};function Lf(a,b,c,d,e){this.k=a;this.count=b;this.ea=c;this.sa=d;this.p=e;this.j=31858766;this.q=8192}k=Lf.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.L=function(){return this.count};k.La=function(){return G(this.ea)};k.Ma=function(){if(t(this.ea)){var a=K(this.ea);return a?new Lf(this.k,this.count-1,a,this.sa,null):new Lf(this.k,this.count-1,D(this.sa),Mc,null)}return this};
+k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(Mf,this.k)};k.N=function(){return G(this.ea)};k.S=function(){return H(D(this))};k.D=function(){var a=D(this.sa),b=this.ea;return t(t(b)?b:a)?new Kf(null,this.ea,D(a),null):null};k.F=function(a,b){return new Lf(b,this.count,this.ea,this.sa,this.p)};
+k.G=function(a,b){var c;t(this.ea)?(c=this.sa,c=new Lf(this.k,this.count+1,this.ea,Nc.a(t(c)?c:Mc,b),null)):c=new Lf(this.k,this.count+1,Nc.a(this.ea,b),Mc,null);return c};var Mf=new Lf(null,0,null,Mc,0);Lf.prototype[Ea]=function(){return uc(this)};function Nf(){this.q=0;this.j=2097152}Nf.prototype.A=function(){return!1};var Of=new Nf;function Pf(a,b){return md(dd(b)?Q(a)===Q(b)?Ee(ud,Oe.a(function(a){return sc.a(S.c(b,G(a),Of),Lc(a))},a)):null:null)}
+function Qf(a,b){var c=a.e;if(b instanceof U)a:{for(var d=c.length,e=b.pa,f=0;;){if(d<=f){c=-1;break a}var g=c[f];if(g instanceof U&&e===g.pa){c=f;break a}f+=2}c=void 0}else if(d="string"==typeof b,t(t(d)?d:"number"===typeof b))a:{d=c.length;for(e=0;;){if(d<=e){c=-1;break a}if(b===c[e]){c=e;break a}e+=2}c=void 0}else if(b instanceof qc)a:{d=c.length;e=b.ta;for(f=0;;){if(d<=f){c=-1;break a}g=c[f];if(g instanceof qc&&e===g.ta){c=f;break a}f+=2}c=void 0}else if(null==b)a:{d=c.length;for(e=0;;){if(d<=
+e){c=-1;break a}if(null==c[e]){c=e;break a}e+=2}c=void 0}else a:{d=c.length;for(e=0;;){if(d<=e){c=-1;break a}if(sc.a(b,c[e])){c=e;break a}e+=2}c=void 0}return c}function Rf(a,b,c){this.e=a;this.m=b;this.Z=c;this.q=0;this.j=32374990}k=Rf.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.Z};k.T=function(){return this.m<this.e.length-2?new Rf(this.e,this.m+2,this.Z):null};k.L=function(){return(this.e.length-this.m)/2};k.B=function(){return wc(this)};
+k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.Z)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return new W(null,2,5,uf,[this.e[this.m],this.e[this.m+1]],null)};k.S=function(){return this.m<this.e.length-2?new Rf(this.e,this.m+2,this.Z):J};k.D=function(){return this};k.F=function(a,b){return new Rf(this.e,this.m,b)};k.G=function(a,b){return M(b,this)};Rf.prototype[Ea]=function(){return uc(this)};
+function Sf(a,b,c){this.e=a;this.m=b;this.g=c}Sf.prototype.ga=function(){return this.m<this.g};Sf.prototype.next=function(){var a=new W(null,2,5,uf,[this.e[this.m],this.e[this.m+1]],null);this.m+=2;return a};function pa(a,b,c,d){this.k=a;this.g=b;this.e=c;this.p=d;this.j=16647951;this.q=8196}k=pa.prototype;k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){a=Qf(this,b);return-1===a?c:this.e[a+1]};
+k.gb=function(a,b,c){a=this.e.length;for(var d=0;;)if(d<a){var e=this.e[d],f=this.e[d+1];c=b.c?b.c(c,e,f):b.call(null,c,e,f);if(Ac(c))return b=c,L.b?L.b(b):L.call(null,b);d+=2}else return c};k.vb=!0;k.fb=function(){return new Sf(this.e,0,2*this.g)};k.H=function(){return this.k};k.L=function(){return this.g};k.B=function(){var a=this.p;return null!=a?a:this.p=a=xc(this)};
+k.A=function(a,b){if(b&&(b.j&1024||b.ic)){var c=this.e.length;if(this.g===b.L(null))for(var d=0;;)if(d<c){var e=b.s(null,this.e[d],jd);if(e!==jd)if(sc.a(this.e[d+1],e))d+=2;else return!1;else return!1}else return!0;else return!1}else return Pf(this,b)};k.$a=function(){return new Tf({},this.e.length,Fa(this.e))};k.J=function(){return ub(Uf,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};
+k.wb=function(a,b){if(0<=Qf(this,b)){var c=this.e.length,d=c-2;if(0===d)return Na(this);for(var d=Array(d),e=0,f=0;;){if(e>=c)return new pa(this.k,this.g-1,d,null);sc.a(b,this.e[e])||(d[f]=this.e[e],d[f+1]=this.e[e+1],f+=2);e+=2}}else return this};
+k.Ka=function(a,b,c){a=Qf(this,b);if(-1===a){if(this.g<Vf){a=this.e;for(var d=a.length,e=Array(d+2),f=0;;)if(f<d)e[f]=a[f],f+=1;else break;e[d]=b;e[d+1]=c;return new pa(this.k,this.g+1,e,null)}return ub(cb(af.a(Qc,this),b,c),this.k)}if(c===this.e[a+1])return this;b=Fa(this.e);b[a+1]=c;return new pa(this.k,this.g,b,null)};k.rb=function(a,b){return-1!==Qf(this,b)};k.D=function(){var a=this.e;return 0<=a.length-2?new Rf(a,0,null):null};k.F=function(a,b){return new pa(b,this.g,this.e,this.p)};
+k.G=function(a,b){if(ed(b))return cb(this,C.a(b,0),C.a(b,1));for(var c=this,d=D(b);;){if(null==d)return c;var e=G(d);if(ed(e))c=cb(c,C.a(e,0),C.a(e,1)),d=K(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};var Uf=new pa(null,0,[],null),Vf=8;pa.prototype[Ea]=function(){return uc(this)};
+function Tf(a,b,c){this.Va=a;this.qa=b;this.e=c;this.q=56;this.j=258}k=Tf.prototype;k.Jb=function(a,b){if(t(this.Va)){var c=Qf(this,b);0<=c&&(this.e[c]=this.e[this.qa-2],this.e[c+1]=this.e[this.qa-1],c=this.e,c.pop(),c.pop(),this.qa-=2);return this}throw Error("dissoc! after persistent!");};
+k.kb=function(a,b,c){var d=this;if(t(d.Va)){a=Qf(this,b);if(-1===a)return d.qa+2<=2*Vf?(d.qa+=2,d.e.push(b),d.e.push(c),this):ee.c(function(){var a=d.qa,b=d.e;return Xf.a?Xf.a(a,b):Xf.call(null,a,b)}(),b,c);c!==d.e[a+1]&&(d.e[a+1]=c);return this}throw Error("assoc! after persistent!");};
+k.Sa=function(a,b){if(t(this.Va)){if(b?b.j&2048||b.jc||(b.j?0:w(fb,b)):w(fb,b))return Rb(this,Yf.b?Yf.b(b):Yf.call(null,b),Zf.b?Zf.b(b):Zf.call(null,b));for(var c=D(b),d=this;;){var e=G(c);if(t(e))var f=e,c=K(c),d=Rb(d,function(){var a=f;return Yf.b?Yf.b(a):Yf.call(null,a)}(),function(){var a=f;return Zf.b?Zf.b(a):Zf.call(null,a)}());else return d}}else throw Error("conj! after persistent!");};
+k.Ta=function(){if(t(this.Va))return this.Va=!1,new pa(null,Cd(this.qa,2),this.e,null);throw Error("persistent! called twice");};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){if(t(this.Va))return a=Qf(this,b),-1===a?c:this.e[a+1];throw Error("lookup after persistent!");};k.L=function(){if(t(this.Va))return Cd(this.qa,2);throw Error("count after persistent!");};function Xf(a,b){for(var c=Ob(Qc),d=0;;)if(d<a)c=ee.c(c,b[d],b[d+1]),d+=2;else return c}function $f(){this.o=!1}
+function ag(a,b){return a===b?!0:Nd(a,b)?!0:sc.a(a,b)}var bg=function(){function a(a,b,c,g,h){a=Fa(a);a[b]=c;a[g]=h;return a}function b(a,b,c){a=Fa(a);a[b]=c;return a}var c=null,c=function(c,e,f,g,h){switch(arguments.length){case 3:return b.call(this,c,e,f);case 5:return a.call(this,c,e,f,g,h)}throw Error("Invalid arity: "+arguments.length);};c.c=b;c.r=a;return c}();function cg(a,b){var c=Array(a.length-2);hd(a,0,c,0,2*b);hd(a,2*(b+1),c,2*b,c.length-2*b);return c}
+var dg=function(){function a(a,b,c,g,h,l){a=a.Na(b);a.e[c]=g;a.e[h]=l;return a}function b(a,b,c,g){a=a.Na(b);a.e[c]=g;return a}var c=null,c=function(c,e,f,g,h,l){switch(arguments.length){case 4:return b.call(this,c,e,f,g);case 6:return a.call(this,c,e,f,g,h,l)}throw Error("Invalid arity: "+arguments.length);};c.n=b;c.P=a;return c}();
+function eg(a,b,c){for(var d=a.length,e=0,f=c;;)if(e<d){c=a[e];if(null!=c){var g=a[e+1];c=b.c?b.c(f,c,g):b.call(null,f,c,g)}else c=a[e+1],c=null!=c?c.Xa(b,f):f;if(Ac(c))return a=c,L.b?L.b(a):L.call(null,a);e+=2;f=c}else return f}function fg(a,b,c){this.u=a;this.w=b;this.e=c}k=fg.prototype;k.Na=function(a){if(a===this.u)return this;var b=Dd(this.w),c=Array(0>b?4:2*(b+1));hd(this.e,0,c,0,2*b);return new fg(a,this.w,c)};
+k.nb=function(a,b,c,d,e){var f=1<<(c>>>b&31);if(0===(this.w&f))return this;var g=Dd(this.w&f-1),h=this.e[2*g],l=this.e[2*g+1];return null==h?(b=l.nb(a,b+5,c,d,e),b===l?this:null!=b?dg.n(this,a,2*g+1,b):this.w===f?null:gg(this,a,f,g)):ag(d,h)?(e[0]=!0,gg(this,a,f,g)):this};function gg(a,b,c,d){if(a.w===c)return null;a=a.Na(b);b=a.e;var e=b.length;a.w^=c;hd(b,2*(d+1),b,2*d,e-2*(d+1));b[e-2]=null;b[e-1]=null;return a}k.lb=function(){var a=this.e;return hg.b?hg.b(a):hg.call(null,a)};
+k.Xa=function(a,b){return eg(this.e,a,b)};k.Oa=function(a,b,c,d){var e=1<<(b>>>a&31);if(0===(this.w&e))return d;var f=Dd(this.w&e-1),e=this.e[2*f],f=this.e[2*f+1];return null==e?f.Oa(a+5,b,c,d):ag(c,e)?f:d};
+k.la=function(a,b,c,d,e,f){var g=1<<(c>>>b&31),h=Dd(this.w&g-1);if(0===(this.w&g)){var l=Dd(this.w);if(2*l<this.e.length){var m=this.Na(a),p=m.e;f.o=!0;id(p,2*h,p,2*(h+1),2*(l-h));p[2*h]=d;p[2*h+1]=e;m.w|=g;return m}if(16<=l){g=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];g[c>>>b&31]=ig.la(a,b+5,c,d,e,f);for(m=h=0;;)if(32>h)0!==(this.w>>>h&1)&&(g[h]=null!=this.e[m]?ig.la(a,b+5,nc(this.e[m]),
+this.e[m],this.e[m+1],f):this.e[m+1],m+=2),h+=1;else break;return new jg(a,l+1,g)}p=Array(2*(l+4));hd(this.e,0,p,0,2*h);p[2*h]=d;p[2*h+1]=e;hd(this.e,2*h,p,2*(h+1),2*(l-h));f.o=!0;m=this.Na(a);m.e=p;m.w|=g;return m}var q=this.e[2*h],s=this.e[2*h+1];if(null==q)return l=s.la(a,b+5,c,d,e,f),l===s?this:dg.n(this,a,2*h+1,l);if(ag(d,q))return e===s?this:dg.n(this,a,2*h+1,e);f.o=!0;return dg.P(this,a,2*h,null,2*h+1,function(){var f=b+5;return kg.ia?kg.ia(a,f,q,s,c,d,e):kg.call(null,a,f,q,s,c,d,e)}())};
+k.ka=function(a,b,c,d,e){var f=1<<(b>>>a&31),g=Dd(this.w&f-1);if(0===(this.w&f)){var h=Dd(this.w);if(16<=h){f=[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null];f[b>>>a&31]=ig.ka(a+5,b,c,d,e);for(var l=g=0;;)if(32>g)0!==(this.w>>>g&1)&&(f[g]=null!=this.e[l]?ig.ka(a+5,nc(this.e[l]),this.e[l],this.e[l+1],e):this.e[l+1],l+=2),g+=1;else break;return new jg(null,h+1,f)}l=Array(2*(h+1));hd(this.e,
+0,l,0,2*g);l[2*g]=c;l[2*g+1]=d;hd(this.e,2*g,l,2*(g+1),2*(h-g));e.o=!0;return new fg(null,this.w|f,l)}var m=this.e[2*g],p=this.e[2*g+1];if(null==m)return h=p.ka(a+5,b,c,d,e),h===p?this:new fg(null,this.w,bg.c(this.e,2*g+1,h));if(ag(c,m))return d===p?this:new fg(null,this.w,bg.c(this.e,2*g+1,d));e.o=!0;return new fg(null,this.w,bg.r(this.e,2*g,null,2*g+1,function(){var e=a+5;return kg.P?kg.P(e,m,p,b,c,d):kg.call(null,e,m,p,b,c,d)}()))};
+k.mb=function(a,b,c){var d=1<<(b>>>a&31);if(0===(this.w&d))return this;var e=Dd(this.w&d-1),f=this.e[2*e],g=this.e[2*e+1];return null==f?(a=g.mb(a+5,b,c),a===g?this:null!=a?new fg(null,this.w,bg.c(this.e,2*e+1,a)):this.w===d?null:new fg(null,this.w^d,cg(this.e,e))):ag(c,f)?new fg(null,this.w^d,cg(this.e,e)):this};var ig=new fg(null,0,[]);
+function lg(a,b,c){var d=a.e,e=d.length;a=Array(2*(a.g-1));for(var f=0,g=1,h=0;;)if(f<e)f!==c&&null!=d[f]&&(a[g]=d[f],g+=2,h|=1<<f),f+=1;else return new fg(b,h,a)}function jg(a,b,c){this.u=a;this.g=b;this.e=c}k=jg.prototype;k.Na=function(a){return a===this.u?this:new jg(a,this.g,Fa(this.e))};
+k.nb=function(a,b,c,d,e){var f=c>>>b&31,g=this.e[f];if(null==g)return this;b=g.nb(a,b+5,c,d,e);if(b===g)return this;if(null==b){if(8>=this.g)return lg(this,a,f);a=dg.n(this,a,f,b);a.g-=1;return a}return dg.n(this,a,f,b)};k.lb=function(){var a=this.e;return mg.b?mg.b(a):mg.call(null,a)};k.Xa=function(a,b){for(var c=this.e.length,d=0,e=b;;)if(d<c){var f=this.e[d];if(null!=f&&(e=f.Xa(a,e),Ac(e)))return c=e,L.b?L.b(c):L.call(null,c);d+=1}else return e};
+k.Oa=function(a,b,c,d){var e=this.e[b>>>a&31];return null!=e?e.Oa(a+5,b,c,d):d};k.la=function(a,b,c,d,e,f){var g=c>>>b&31,h=this.e[g];if(null==h)return a=dg.n(this,a,g,ig.la(a,b+5,c,d,e,f)),a.g+=1,a;b=h.la(a,b+5,c,d,e,f);return b===h?this:dg.n(this,a,g,b)};k.ka=function(a,b,c,d,e){var f=b>>>a&31,g=this.e[f];if(null==g)return new jg(null,this.g+1,bg.c(this.e,f,ig.ka(a+5,b,c,d,e)));a=g.ka(a+5,b,c,d,e);return a===g?this:new jg(null,this.g,bg.c(this.e,f,a))};
+k.mb=function(a,b,c){var d=b>>>a&31,e=this.e[d];return null!=e?(a=e.mb(a+5,b,c),a===e?this:null==a?8>=this.g?lg(this,null,d):new jg(null,this.g-1,bg.c(this.e,d,a)):new jg(null,this.g,bg.c(this.e,d,a))):this};function ng(a,b,c){b*=2;for(var d=0;;)if(d<b){if(ag(c,a[d]))return d;d+=2}else return-1}function og(a,b,c,d){this.u=a;this.Ia=b;this.g=c;this.e=d}k=og.prototype;k.Na=function(a){if(a===this.u)return this;var b=Array(2*(this.g+1));hd(this.e,0,b,0,2*this.g);return new og(a,this.Ia,this.g,b)};
+k.nb=function(a,b,c,d,e){b=ng(this.e,this.g,d);if(-1===b)return this;e[0]=!0;if(1===this.g)return null;a=this.Na(a);e=a.e;e[b]=e[2*this.g-2];e[b+1]=e[2*this.g-1];e[2*this.g-1]=null;e[2*this.g-2]=null;a.g-=1;return a};k.lb=function(){var a=this.e;return hg.b?hg.b(a):hg.call(null,a)};k.Xa=function(a,b){return eg(this.e,a,b)};k.Oa=function(a,b,c,d){a=ng(this.e,this.g,c);return 0>a?d:ag(c,this.e[a])?this.e[a+1]:d};
+k.la=function(a,b,c,d,e,f){if(c===this.Ia){b=ng(this.e,this.g,d);if(-1===b){if(this.e.length>2*this.g)return a=dg.P(this,a,2*this.g,d,2*this.g+1,e),f.o=!0,a.g+=1,a;c=this.e.length;b=Array(c+2);hd(this.e,0,b,0,c);b[c]=d;b[c+1]=e;f.o=!0;f=this.g+1;a===this.u?(this.e=b,this.g=f,a=this):a=new og(this.u,this.Ia,f,b);return a}return this.e[b+1]===e?this:dg.n(this,a,b+1,e)}return(new fg(a,1<<(this.Ia>>>b&31),[null,this,null,null])).la(a,b,c,d,e,f)};
+k.ka=function(a,b,c,d,e){return b===this.Ia?(a=ng(this.e,this.g,c),-1===a?(a=2*this.g,b=Array(a+2),hd(this.e,0,b,0,a),b[a]=c,b[a+1]=d,e.o=!0,new og(null,this.Ia,this.g+1,b)):sc.a(this.e[a],d)?this:new og(null,this.Ia,this.g,bg.c(this.e,a+1,d))):(new fg(null,1<<(this.Ia>>>a&31),[null,this])).ka(a,b,c,d,e)};k.mb=function(a,b,c){a=ng(this.e,this.g,c);return-1===a?this:1===this.g?null:new og(null,this.Ia,this.g-1,cg(this.e,Cd(a,2)))};
+var kg=function(){function a(a,b,c,g,h,l,m){var p=nc(c);if(p===h)return new og(null,p,2,[c,g,l,m]);var q=new $f;return ig.la(a,b,p,c,g,q).la(a,b,h,l,m,q)}function b(a,b,c,g,h,l){var m=nc(b);if(m===g)return new og(null,m,2,[b,c,h,l]);var p=new $f;return ig.ka(a,m,b,c,p).ka(a,g,h,l,p)}var c=null,c=function(c,e,f,g,h,l,m){switch(arguments.length){case 6:return b.call(this,c,e,f,g,h,l);case 7:return a.call(this,c,e,f,g,h,l,m)}throw Error("Invalid arity: "+arguments.length);};c.P=b;c.ia=a;return c}();
+function pg(a,b,c,d,e){this.k=a;this.Pa=b;this.m=c;this.C=d;this.p=e;this.q=0;this.j=32374860}k=pg.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return null==this.C?new W(null,2,5,uf,[this.Pa[this.m],this.Pa[this.m+1]],null):G(this.C)};
+k.S=function(){if(null==this.C){var a=this.Pa,b=this.m+2;return hg.c?hg.c(a,b,null):hg.call(null,a,b,null)}var a=this.Pa,b=this.m,c=K(this.C);return hg.c?hg.c(a,b,c):hg.call(null,a,b,c)};k.D=function(){return this};k.F=function(a,b){return new pg(b,this.Pa,this.m,this.C,this.p)};k.G=function(a,b){return M(b,this)};pg.prototype[Ea]=function(){return uc(this)};
+var hg=function(){function a(a,b,c){if(null==c)for(c=a.length;;)if(b<c){if(null!=a[b])return new pg(null,a,b,null,null);var g=a[b+1];if(t(g)&&(g=g.lb(),t(g)))return new pg(null,a,b+2,g,null);b+=2}else return null;else return new pg(null,a,b,c,null)}function b(a){return c.c(a,0,null)}var c=null,c=function(c,e,f){switch(arguments.length){case 1:return b.call(this,c);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.c=a;return c}();
+function qg(a,b,c,d,e){this.k=a;this.Pa=b;this.m=c;this.C=d;this.p=e;this.q=0;this.j=32374860}k=qg.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return G(this.C)};
+k.S=function(){var a=this.Pa,b=this.m,c=K(this.C);return mg.n?mg.n(null,a,b,c):mg.call(null,null,a,b,c)};k.D=function(){return this};k.F=function(a,b){return new qg(b,this.Pa,this.m,this.C,this.p)};k.G=function(a,b){return M(b,this)};qg.prototype[Ea]=function(){return uc(this)};
+var mg=function(){function a(a,b,c,g){if(null==g)for(g=b.length;;)if(c<g){var h=b[c];if(t(h)&&(h=h.lb(),t(h)))return new qg(a,b,c+1,h,null);c+=1}else return null;else return new qg(a,b,c,g,null)}function b(a){return c.n(null,a,0,null)}var c=null,c=function(c,e,f,g){switch(arguments.length){case 1:return b.call(this,c);case 4:return a.call(this,c,e,f,g)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.n=a;return c}();
+function rg(a,b,c,d,e,f){this.k=a;this.g=b;this.root=c;this.U=d;this.da=e;this.p=f;this.j=16123663;this.q=8196}k=rg.prototype;k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){return null==b?this.U?this.da:c:null==this.root?c:this.root.Oa(0,nc(b),b,c)};k.gb=function(a,b,c){this.U&&(a=this.da,c=b.c?b.c(c,null,a):b.call(null,c,null,a));return Ac(c)?L.b?L.b(c):L.call(null,c):null!=this.root?this.root.Xa(b,c):c};k.H=function(){return this.k};k.L=function(){return this.g};
+k.B=function(){var a=this.p;return null!=a?a:this.p=a=xc(this)};k.A=function(a,b){return Pf(this,b)};k.$a=function(){return new sg({},this.root,this.g,this.U,this.da)};k.J=function(){return ub(Qc,this.k)};k.wb=function(a,b){if(null==b)return this.U?new rg(this.k,this.g-1,this.root,!1,null,null):this;if(null==this.root)return this;var c=this.root.mb(0,nc(b),b);return c===this.root?this:new rg(this.k,this.g-1,c,this.U,this.da,null)};
+k.Ka=function(a,b,c){if(null==b)return this.U&&c===this.da?this:new rg(this.k,this.U?this.g:this.g+1,this.root,!0,c,null);a=new $f;b=(null==this.root?ig:this.root).ka(0,nc(b),b,c,a);return b===this.root?this:new rg(this.k,a.o?this.g+1:this.g,b,this.U,this.da,null)};k.rb=function(a,b){return null==b?this.U:null==this.root?!1:this.root.Oa(0,nc(b),b,jd)!==jd};k.D=function(){if(0<this.g){var a=null!=this.root?this.root.lb():null;return this.U?M(new W(null,2,5,uf,[null,this.da],null),a):a}return null};
+k.F=function(a,b){return new rg(b,this.g,this.root,this.U,this.da,this.p)};k.G=function(a,b){if(ed(b))return cb(this,C.a(b,0),C.a(b,1));for(var c=this,d=D(b);;){if(null==d)return c;var e=G(d);if(ed(e))c=cb(c,C.a(e,0),C.a(e,1)),d=K(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};var Qc=new rg(null,0,null,!1,null,0);rg.prototype[Ea]=function(){return uc(this)};
+function sg(a,b,c,d,e){this.u=a;this.root=b;this.count=c;this.U=d;this.da=e;this.q=56;this.j=258}k=sg.prototype;k.Jb=function(a,b){if(this.u)if(null==b)this.U&&(this.U=!1,this.da=null,this.count-=1);else{if(null!=this.root){var c=new $f,d=this.root.nb(this.u,0,nc(b),b,c);d!==this.root&&(this.root=d);t(c[0])&&(this.count-=1)}}else throw Error("dissoc! after persistent!");return this};k.kb=function(a,b,c){return tg(this,b,c)};k.Sa=function(a,b){return ug(this,b)};
+k.Ta=function(){var a;if(this.u)this.u=null,a=new rg(null,this.count,this.root,this.U,this.da,null);else throw Error("persistent! called twice");return a};k.t=function(a,b){return null==b?this.U?this.da:null:null==this.root?null:this.root.Oa(0,nc(b),b)};k.s=function(a,b,c){return null==b?this.U?this.da:c:null==this.root?c:this.root.Oa(0,nc(b),b,c)};k.L=function(){if(this.u)return this.count;throw Error("count after persistent!");};
+function ug(a,b){if(a.u){if(b?b.j&2048||b.jc||(b.j?0:w(fb,b)):w(fb,b))return tg(a,Yf.b?Yf.b(b):Yf.call(null,b),Zf.b?Zf.b(b):Zf.call(null,b));for(var c=D(b),d=a;;){var e=G(c);if(t(e))var f=e,c=K(c),d=tg(d,function(){var a=f;return Yf.b?Yf.b(a):Yf.call(null,a)}(),function(){var a=f;return Zf.b?Zf.b(a):Zf.call(null,a)}());else return d}}else throw Error("conj! after persistent");}
+function tg(a,b,c){if(a.u){if(null==b)a.da!==c&&(a.da=c),a.U||(a.count+=1,a.U=!0);else{var d=new $f;b=(null==a.root?ig:a.root).la(a.u,0,nc(b),b,c,d);b!==a.root&&(a.root=b);d.o&&(a.count+=1)}return a}throw Error("assoc! after persistent!");}function vg(a,b,c){for(var d=b;;)if(null!=a)b=c?a.left:a.right,d=Nc.a(d,a),a=b;else return d}function wg(a,b,c,d,e){this.k=a;this.stack=b;this.pb=c;this.g=d;this.p=e;this.q=0;this.j=32374862}k=wg.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.k};
+k.L=function(){return 0>this.g?Q(K(this))+1:this.g};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return Wc(this.stack)};k.S=function(){var a=G(this.stack),a=vg(this.pb?a.right:a.left,K(this.stack),this.pb);return null!=a?new wg(null,a,this.pb,this.g-1,null):J};k.D=function(){return this};
+k.F=function(a,b){return new wg(b,this.stack,this.pb,this.g,this.p)};k.G=function(a,b){return M(b,this)};wg.prototype[Ea]=function(){return uc(this)};function xg(a,b,c){return new wg(null,vg(a,null,b),b,c,null)}
+function yg(a,b,c,d){return c instanceof X?c.left instanceof X?new X(c.key,c.o,c.left.ua(),new Z(a,b,c.right,d,null),null):c.right instanceof X?new X(c.right.key,c.right.o,new Z(c.key,c.o,c.left,c.right.left,null),new Z(a,b,c.right.right,d,null),null):new Z(a,b,c,d,null):new Z(a,b,c,d,null)}
+function zg(a,b,c,d){return d instanceof X?d.right instanceof X?new X(d.key,d.o,new Z(a,b,c,d.left,null),d.right.ua(),null):d.left instanceof X?new X(d.left.key,d.left.o,new Z(a,b,c,d.left.left,null),new Z(d.key,d.o,d.left.right,d.right,null),null):new Z(a,b,c,d,null):new Z(a,b,c,d,null)}
+function Ag(a,b,c,d){if(c instanceof X)return new X(a,b,c.ua(),d,null);if(d instanceof Z)return zg(a,b,c,d.ob());if(d instanceof X&&d.left instanceof Z)return new X(d.left.key,d.left.o,new Z(a,b,c,d.left.left,null),zg(d.key,d.o,d.left.right,d.right.ob()),null);throw Error("red-black tree invariant violation");}
+var Cg=function Bg(b,c,d){d=null!=b.left?Bg(b.left,c,d):d;if(Ac(d))return L.b?L.b(d):L.call(null,d);var e=b.key,f=b.o;d=c.c?c.c(d,e,f):c.call(null,d,e,f);if(Ac(d))return L.b?L.b(d):L.call(null,d);b=null!=b.right?Bg(b.right,c,d):d;return Ac(b)?L.b?L.b(b):L.call(null,b):b};function Z(a,b,c,d,e){this.key=a;this.o=b;this.left=c;this.right=d;this.p=e;this.q=0;this.j=32402207}k=Z.prototype;k.Mb=function(a){return a.Ob(this)};k.ob=function(){return new X(this.key,this.o,this.left,this.right,null)};
+k.ua=function(){return this};k.Lb=function(a){return a.Nb(this)};k.replace=function(a,b,c,d){return new Z(a,b,c,d,null)};k.Nb=function(a){return new Z(a.key,a.o,this,a.right,null)};k.Ob=function(a){return new Z(a.key,a.o,a.left,this,null)};k.Xa=function(a,b){return Cg(this,a,b)};k.t=function(a,b){return C.c(this,b,null)};k.s=function(a,b,c){return C.c(this,b,c)};k.Q=function(a,b){return 0===b?this.key:1===b?this.o:null};k.$=function(a,b,c){return 0===b?this.key:1===b?this.o:c};
+k.Ua=function(a,b,c){return(new W(null,2,5,uf,[this.key,this.o],null)).Ua(null,b,c)};k.H=function(){return null};k.L=function(){return 2};k.hb=function(){return this.key};k.ib=function(){return this.o};k.La=function(){return this.o};k.Ma=function(){return new W(null,1,5,uf,[this.key],null)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return Mc};k.R=function(a,b){return Cc.a(this,b)};k.O=function(a,b,c){return Cc.c(this,b,c)};
+k.Ka=function(a,b,c){return Rc.c(new W(null,2,5,uf,[this.key,this.o],null),b,c)};k.D=function(){return Ra(Ra(J,this.o),this.key)};k.F=function(a,b){return O(new W(null,2,5,uf,[this.key,this.o],null),b)};k.G=function(a,b){return new W(null,3,5,uf,[this.key,this.o,b],null)};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};Z.prototype[Ea]=function(){return uc(this)};
+function X(a,b,c,d,e){this.key=a;this.o=b;this.left=c;this.right=d;this.p=e;this.q=0;this.j=32402207}k=X.prototype;k.Mb=function(a){return new X(this.key,this.o,this.left,a,null)};k.ob=function(){throw Error("red-black tree invariant violation");};k.ua=function(){return new Z(this.key,this.o,this.left,this.right,null)};k.Lb=function(a){return new X(this.key,this.o,a,this.right,null)};k.replace=function(a,b,c,d){return new X(a,b,c,d,null)};
+k.Nb=function(a){return this.left instanceof X?new X(this.key,this.o,this.left.ua(),new Z(a.key,a.o,this.right,a.right,null),null):this.right instanceof X?new X(this.right.key,this.right.o,new Z(this.key,this.o,this.left,this.right.left,null),new Z(a.key,a.o,this.right.right,a.right,null),null):new Z(a.key,a.o,this,a.right,null)};
+k.Ob=function(a){return this.right instanceof X?new X(this.key,this.o,new Z(a.key,a.o,a.left,this.left,null),this.right.ua(),null):this.left instanceof X?new X(this.left.key,this.left.o,new Z(a.key,a.o,a.left,this.left.left,null),new Z(this.key,this.o,this.left.right,this.right,null),null):new Z(a.key,a.o,a.left,this,null)};k.Xa=function(a,b){return Cg(this,a,b)};k.t=function(a,b){return C.c(this,b,null)};k.s=function(a,b,c){return C.c(this,b,c)};
+k.Q=function(a,b){return 0===b?this.key:1===b?this.o:null};k.$=function(a,b,c){return 0===b?this.key:1===b?this.o:c};k.Ua=function(a,b,c){return(new W(null,2,5,uf,[this.key,this.o],null)).Ua(null,b,c)};k.H=function(){return null};k.L=function(){return 2};k.hb=function(){return this.key};k.ib=function(){return this.o};k.La=function(){return this.o};k.Ma=function(){return new W(null,1,5,uf,[this.key],null)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};
+k.A=function(a,b){return Ic(this,b)};k.J=function(){return Mc};k.R=function(a,b){return Cc.a(this,b)};k.O=function(a,b,c){return Cc.c(this,b,c)};k.Ka=function(a,b,c){return Rc.c(new W(null,2,5,uf,[this.key,this.o],null),b,c)};k.D=function(){return Ra(Ra(J,this.o),this.key)};k.F=function(a,b){return O(new W(null,2,5,uf,[this.key,this.o],null),b)};k.G=function(a,b){return new W(null,3,5,uf,[this.key,this.o,b],null)};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};X.prototype[Ea]=function(){return uc(this)};
+var Eg=function Dg(b,c,d,e,f){if(null==c)return new X(d,e,null,null,null);var g;g=c.key;g=b.a?b.a(d,g):b.call(null,d,g);if(0===g)return f[0]=c,null;if(0>g)return b=Dg(b,c.left,d,e,f),null!=b?c.Lb(b):null;b=Dg(b,c.right,d,e,f);return null!=b?c.Mb(b):null},Gg=function Fg(b,c){if(null==b)return c;if(null==c)return b;if(b instanceof X){if(c instanceof X){var d=Fg(b.right,c.left);return d instanceof X?new X(d.key,d.o,new X(b.key,b.o,b.left,d.left,null),new X(c.key,c.o,d.right,c.right,null),null):new X(b.key,
+b.o,b.left,new X(c.key,c.o,d,c.right,null),null)}return new X(b.key,b.o,b.left,Fg(b.right,c),null)}if(c instanceof X)return new X(c.key,c.o,Fg(b,c.left),c.right,null);d=Fg(b.right,c.left);return d instanceof X?new X(d.key,d.o,new Z(b.key,b.o,b.left,d.left,null),new Z(c.key,c.o,d.right,c.right,null),null):Ag(b.key,b.o,b.left,new Z(c.key,c.o,d,c.right,null))},Ig=function Hg(b,c,d,e){if(null!=c){var f;f=c.key;f=b.a?b.a(d,f):b.call(null,d,f);if(0===f)return e[0]=c,Gg(c.left,c.right);if(0>f)return b=Hg(b,
+c.left,d,e),null!=b||null!=e[0]?c.left instanceof Z?Ag(c.key,c.o,b,c.right):new X(c.key,c.o,b,c.right,null):null;b=Hg(b,c.right,d,e);if(null!=b||null!=e[0])if(c.right instanceof Z)if(e=c.key,d=c.o,c=c.left,b instanceof X)c=new X(e,d,c,b.ua(),null);else if(c instanceof Z)c=yg(e,d,c.ob(),b);else if(c instanceof X&&c.right instanceof Z)c=new X(c.right.key,c.right.o,yg(c.key,c.o,c.left.ob(),c.right.left),new Z(e,d,c.right.right,b,null),null);else throw Error("red-black tree invariant violation");else c=
+new X(c.key,c.o,c.left,b,null);else c=null;return c}return null},Kg=function Jg(b,c,d,e){var f=c.key,g=b.a?b.a(d,f):b.call(null,d,f);return 0===g?c.replace(f,e,c.left,c.right):0>g?c.replace(f,c.o,Jg(b,c.left,d,e),c.right):c.replace(f,c.o,c.left,Jg(b,c.right,d,e))};function Lg(a,b,c,d,e){this.aa=a;this.na=b;this.g=c;this.k=d;this.p=e;this.j=418776847;this.q=8192}k=Lg.prototype;k.toString=function(){return ec(this)};
+function Mg(a,b){for(var c=a.na;;)if(null!=c){var d;d=c.key;d=a.aa.a?a.aa.a(b,d):a.aa.call(null,b,d);if(0===d)return c;c=0>d?c.left:c.right}else return null}k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){a=Mg(this,b);return null!=a?a.o:c};k.gb=function(a,b,c){return null!=this.na?Cg(this.na,b,c):c};k.H=function(){return this.k};k.L=function(){return this.g};k.ab=function(){return 0<this.g?xg(this.na,!1,this.g):null};k.B=function(){var a=this.p;return null!=a?a:this.p=a=xc(this)};
+k.A=function(a,b){return Pf(this,b)};k.J=function(){return new Lg(this.aa,null,0,this.k,0)};k.wb=function(a,b){var c=[null],d=Ig(this.aa,this.na,b,c);return null==d?null==R.a(c,0)?this:new Lg(this.aa,null,0,this.k,null):new Lg(this.aa,d.ua(),this.g-1,this.k,null)};k.Ka=function(a,b,c){a=[null];var d=Eg(this.aa,this.na,b,c,a);return null==d?(a=R.a(a,0),sc.a(c,a.o)?this:new Lg(this.aa,Kg(this.aa,this.na,b,c),this.g,this.k,null)):new Lg(this.aa,d.ua(),this.g+1,this.k,null)};
+k.rb=function(a,b){return null!=Mg(this,b)};k.D=function(){return 0<this.g?xg(this.na,!0,this.g):null};k.F=function(a,b){return new Lg(this.aa,this.na,this.g,b,this.p)};k.G=function(a,b){if(ed(b))return cb(this,C.a(b,0),C.a(b,1));for(var c=this,d=D(b);;){if(null==d)return c;var e=G(d);if(ed(e))c=cb(c,C.a(e,0),C.a(e,1)),d=K(d);else throw Error("conj on a map takes map entries or seqables of map entries");}};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};k.Hb=function(a,b){return 0<this.g?xg(this.na,b,this.g):null};
+k.Ib=function(a,b,c){if(0<this.g){a=null;for(var d=this.na;;)if(null!=d){var e;e=d.key;e=this.aa.a?this.aa.a(b,e):this.aa.call(null,b,e);if(0===e)return new wg(null,Nc.a(a,d),c,-1,null);t(c)?0>e?(a=Nc.a(a,d),d=d.left):d=d.right:0<e?(a=Nc.a(a,d),d=d.right):d=d.left}else return null==a?null:new wg(null,a,c,-1,null)}else return null};k.Gb=function(a,b){return Yf.b?Yf.b(b):Yf.call(null,b)};k.Fb=function(){return this.aa};var Ng=new Lg(od,null,0,null,0);Lg.prototype[Ea]=function(){return uc(this)};
+var Og=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){a=D(a);for(var b=Ob(Qc);;)if(a){var e=K(K(a)),b=ee.c(b,G(a),Lc(a));a=e}else return Qb(b)}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}(),Pg=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,
+d)}function b(a){a:{a=T.a(Ha,a);for(var b=a.length,e=0,f=Ob(Uf);;)if(e<b)var g=e+2,f=Rb(f,a[e],a[e+1]),e=g;else{a=Qb(f);break a}a=void 0}return a}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}(),Qg=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){a=D(a);for(var b=Ng;;)if(a){var e=K(K(a)),b=Rc.c(b,G(a),Lc(a));a=e}else return b}a.i=0;a.f=function(a){a=D(a);
+return b(a)};a.d=b;return a}(),Rg=function(){function a(a,d){var e=null;if(1<arguments.length){for(var e=0,f=Array(arguments.length-1);e<f.length;)f[e]=arguments[e+1],++e;e=new F(f,0)}return b.call(this,a,e)}function b(a,b){for(var e=D(b),f=new Lg(qd(a),null,0,null,0);;)if(e)var g=K(K(e)),f=Rc.c(f,G(e),Lc(e)),e=g;else return f}a.i=1;a.f=function(a){var d=G(a);a=H(a);return b(d,a)};a.d=b;return a}();function Sg(a,b){this.Y=a;this.Z=b;this.q=0;this.j=32374988}k=Sg.prototype;k.toString=function(){return ec(this)};
+k.H=function(){return this.Z};k.T=function(){var a=this.Y,a=(a?a.j&128||a.xb||(a.j?0:w(Xa,a)):w(Xa,a))?this.Y.T(null):K(this.Y);return null==a?null:new Sg(a,this.Z)};k.B=function(){return wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.Z)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return this.Y.N(null).hb(null)};
+k.S=function(){var a=this.Y,a=(a?a.j&128||a.xb||(a.j?0:w(Xa,a)):w(Xa,a))?this.Y.T(null):K(this.Y);return null!=a?new Sg(a,this.Z):J};k.D=function(){return this};k.F=function(a,b){return new Sg(this.Y,b)};k.G=function(a,b){return M(b,this)};Sg.prototype[Ea]=function(){return uc(this)};function Tg(a){return(a=D(a))?new Sg(a,null):null}function Yf(a){return hb(a)}function Ug(a,b){this.Y=a;this.Z=b;this.q=0;this.j=32374988}k=Ug.prototype;k.toString=function(){return ec(this)};k.H=function(){return this.Z};
+k.T=function(){var a=this.Y,a=(a?a.j&128||a.xb||(a.j?0:w(Xa,a)):w(Xa,a))?this.Y.T(null):K(this.Y);return null==a?null:new Ug(a,this.Z)};k.B=function(){return wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.Z)};k.R=function(a,b){return P.a(b,this)};k.O=function(a,b,c){return P.c(b,c,this)};k.N=function(){return this.Y.N(null).ib(null)};k.S=function(){var a=this.Y,a=(a?a.j&128||a.xb||(a.j?0:w(Xa,a)):w(Xa,a))?this.Y.T(null):K(this.Y);return null!=a?new Ug(a,this.Z):J};
+k.D=function(){return this};k.F=function(a,b){return new Ug(this.Y,b)};k.G=function(a,b){return M(b,this)};Ug.prototype[Ea]=function(){return uc(this)};function Vg(a){return(a=D(a))?new Ug(a,null):null}function Zf(a){return ib(a)}
+var Wg=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){return t(Fe(ud,a))?A.a(function(a,b){return Nc.a(t(a)?a:Uf,b)},a):null}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}(),Xg=function(){function a(a,d){var e=null;if(1<arguments.length){for(var e=0,f=Array(arguments.length-1);e<f.length;)f[e]=arguments[e+1],++e;e=new F(f,0)}return b.call(this,a,e)}function b(a,
+b){return t(Fe(ud,b))?A.a(function(a){return function(b,c){return A.c(a,t(b)?b:Uf,D(c))}}(function(b,d){var g=G(d),h=Lc(d);return nd(b,g)?Rc.c(b,g,function(){var d=S.a(b,g);return a.a?a.a(d,h):a.call(null,d,h)}()):Rc.c(b,g,h)}),b):null}a.i=1;a.f=function(a){var d=G(a);a=H(a);return b(d,a)};a.d=b;return a}();function Yg(a,b){for(var c=Uf,d=D(b);;)if(d)var e=G(d),f=S.c(a,e,Zg),c=je.a(f,Zg)?Rc.c(c,e,f):c,d=K(d);else return O(c,Vc(a))}
+function $g(a,b,c){this.k=a;this.Wa=b;this.p=c;this.j=15077647;this.q=8196}k=$g.prototype;k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){return bb(this.Wa,b)?b:c};k.H=function(){return this.k};k.L=function(){return Ma(this.Wa)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=xc(this)};k.A=function(a,b){return ad(b)&&Q(this)===Q(b)&&Ee(function(a){return function(b){return nd(a,b)}}(this),b)};k.$a=function(){return new ah(Ob(this.Wa))};
+k.J=function(){return O(bh,this.k)};k.Eb=function(a,b){return new $g(this.k,eb(this.Wa,b),null)};k.D=function(){return Tg(this.Wa)};k.F=function(a,b){return new $g(b,this.Wa,this.p)};k.G=function(a,b){return new $g(this.k,Rc.c(this.Wa,b,null),null)};
+k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};var bh=new $g(null,Uf,0);$g.prototype[Ea]=function(){return uc(this)};
+function ah(a){this.ma=a;this.j=259;this.q=136}k=ah.prototype;k.call=function(){function a(a,b,c){return $a.c(this.ma,b,jd)===jd?c:b}function b(a,b){return $a.c(this.ma,b,jd)===jd?null:b}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+arguments.length);};c.a=b;c.c=a;return c}();k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};
+k.b=function(a){return $a.c(this.ma,a,jd)===jd?null:a};k.a=function(a,b){return $a.c(this.ma,a,jd)===jd?b:a};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){return $a.c(this.ma,b,jd)===jd?c:b};k.L=function(){return Q(this.ma)};k.Tb=function(a,b){this.ma=fe.a(this.ma,b);return this};k.Sa=function(a,b){this.ma=ee.c(this.ma,b,null);return this};k.Ta=function(){return new $g(null,Qb(this.ma),null)};function ch(a,b,c){this.k=a;this.ja=b;this.p=c;this.j=417730831;this.q=8192}k=ch.prototype;
+k.toString=function(){return ec(this)};k.t=function(a,b){return $a.c(this,b,null)};k.s=function(a,b,c){a=Mg(this.ja,b);return null!=a?a.key:c};k.H=function(){return this.k};k.L=function(){return Q(this.ja)};k.ab=function(){return 0<Q(this.ja)?Oe.a(Yf,Gb(this.ja)):null};k.B=function(){var a=this.p;return null!=a?a:this.p=a=xc(this)};k.A=function(a,b){return ad(b)&&Q(this)===Q(b)&&Ee(function(a){return function(b){return nd(a,b)}}(this),b)};k.J=function(){return new ch(this.k,Na(this.ja),0)};
+k.Eb=function(a,b){return new ch(this.k,Sc.a(this.ja,b),null)};k.D=function(){return Tg(this.ja)};k.F=function(a,b){return new ch(b,this.ja,this.p)};k.G=function(a,b){return new ch(this.k,Rc.c(this.ja,b,null),null)};k.call=function(){var a=null,a=function(a,c,d){switch(arguments.length){case 2:return this.t(null,c);case 3:return this.s(null,c,d)}throw Error("Invalid arity: "+arguments.length);};a.a=function(a,c){return this.t(null,c)};a.c=function(a,c,d){return this.s(null,c,d)};return a}();
+k.apply=function(a,b){return this.call.apply(this,[this].concat(Fa(b)))};k.b=function(a){return this.t(null,a)};k.a=function(a,b){return this.s(null,a,b)};k.Hb=function(a,b){return Oe.a(Yf,Hb(this.ja,b))};k.Ib=function(a,b,c){return Oe.a(Yf,Ib(this.ja,b,c))};k.Gb=function(a,b){return b};k.Fb=function(){return Kb(this.ja)};var eh=new ch(null,Ng,0);ch.prototype[Ea]=function(){return uc(this)};
+function fh(a){a=D(a);if(null==a)return bh;if(a instanceof F&&0===a.m){a=a.e;a:{for(var b=0,c=Ob(bh);;)if(b<a.length)var d=b+1,c=c.Sa(null,a[b]),b=d;else{a=c;break a}a=void 0}return a.Ta(null)}for(d=Ob(bh);;)if(null!=a)b=a.T(null),d=d.Sa(null,a.N(null)),a=b;else return d.Ta(null)}
+var gh=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){return A.c(Ra,eh,a)}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}(),hh=function(){function a(a,d){var e=null;if(1<arguments.length){for(var e=0,f=Array(arguments.length-1);e<f.length;)f[e]=arguments[e+1],++e;e=new F(f,0)}return b.call(this,a,e)}function b(a,b){return A.c(Ra,new ch(null,Rg(a),0),b)}
+a.i=1;a.f=function(a){var d=G(a);a=H(a);return b(d,a)};a.d=b;return a}();function Od(a){if(a&&(a.q&4096||a.lc))return a.name;if("string"===typeof a)return a;throw Error([z("Doesn't support name: "),z(a)].join(""));}
+var ih=function(){function a(a,b,c){return(a.b?a.b(b):a.call(null,b))>(a.b?a.b(c):a.call(null,c))?b:c}var b=null,c=function(){function a(b,d,h,l){var m=null;if(3<arguments.length){for(var m=0,p=Array(arguments.length-3);m<p.length;)p[m]=arguments[m+3],++m;m=new F(p,0)}return c.call(this,b,d,h,m)}function c(a,d,e,l){return A.c(function(c,d){return b.c(a,c,d)},b.c(a,d,e),l)}a.i=3;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=K(a);var l=G(a);a=H(a);return c(b,d,l,a)};a.d=c;return a}(),b=function(b,
+e,f,g){switch(arguments.length){case 2:return e;case 3:return a.call(this,b,e,f);default:var h=null;if(3<arguments.length){for(var h=0,l=Array(arguments.length-3);h<l.length;)l[h]=arguments[h+3],++h;h=new F(l,0)}return c.d(b,e,f,h)}throw Error("Invalid arity: "+arguments.length);};b.i=3;b.f=c.f;b.a=function(a,b){return b};b.c=a;b.d=c.d;return b}();function jh(a){this.e=a}jh.prototype.add=function(a){return this.e.push(a)};jh.prototype.size=function(){return this.e.length};
+jh.prototype.clear=function(){return this.e=[]};
+var kh=function(){function a(a,b,c){return new V(null,function(){var h=D(c);return h?M(Pe.a(a,h),d.c(a,b,Qe.a(b,h))):null},null,null)}function b(a,b){return d.c(a,a,b)}function c(a){return function(b){return function(c){return function(){function d(h,l){c.add(l);if(a===c.size()){var m=zf(c.e);c.clear();return b.a?b.a(h,m):b.call(null,h,m)}return h}function l(a){if(!t(0===c.e.length)){var d=zf(c.e);c.clear();a=Bc(b.a?b.a(a,d):b.call(null,a,d))}return b.b?b.b(a):b.call(null,a)}function m(){return b.l?
+b.l():b.call(null)}var p=null,p=function(a,b){switch(arguments.length){case 0:return m.call(this);case 1:return l.call(this,a);case 2:return d.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};p.l=m;p.b=l;p.a=d;return p}()}(new jh([]))}}var d=null,d=function(d,f,g){switch(arguments.length){case 1:return c.call(this,d);case 2:return b.call(this,d,f);case 3:return a.call(this,d,f,g)}throw Error("Invalid arity: "+arguments.length);};d.b=c;d.a=b;d.c=a;return d}(),lh=function(){function a(a,
+b){return new V(null,function(){var f=D(b);if(f){var g;g=G(f);g=a.b?a.b(g):a.call(null,g);f=t(g)?M(G(f),c.a(a,H(f))):null}else f=null;return f},null,null)}function b(a){return function(b){return function(){function c(f,g){return t(a.b?a.b(g):a.call(null,g))?b.a?b.a(f,g):b.call(null,f,g):new yc(f)}function g(a){return b.b?b.b(a):b.call(null,a)}function h(){return b.l?b.l():b.call(null)}var l=null,l=function(a,b){switch(arguments.length){case 0:return h.call(this);case 1:return g.call(this,a);case 2:return c.call(this,
+a,b)}throw Error("Invalid arity: "+arguments.length);};l.l=h;l.b=g;l.a=c;return l}()}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();function mh(a,b,c){return function(d){var e=Kb(a);d=Jb(a,d);e=e.a?e.a(d,c):e.call(null,d,c);return b.a?b.a(e,0):b.call(null,e,0)}}
+var nh=function(){function a(a,b,c,g,h){var l=Ib(a,c,!0);if(t(l)){var m=R.c(l,0,null);return lh.a(mh(a,g,h),t(mh(a,b,c).call(null,m))?l:K(l))}return null}function b(a,b,c){var g=mh(a,b,c),h;a:{h=[Ad,Bd];var l=h.length;if(l<=Vf)for(var m=0,p=Ob(Uf);;)if(m<l)var q=m+1,p=Rb(p,h[m],null),m=q;else{h=new $g(null,Qb(p),null);break a}else for(m=0,p=Ob(bh);;)if(m<l)q=m+1,p=Pb(p,h[m]),m=q;else{h=Qb(p);break a}h=void 0}return t(h.call(null,b))?(a=Ib(a,c,!0),t(a)?(b=R.c(a,0,null),t(g.b?g.b(b):g.call(null,b))?
+a:K(a)):null):lh.a(g,Hb(a,!0))}var c=null,c=function(c,e,f,g,h){switch(arguments.length){case 3:return b.call(this,c,e,f);case 5:return a.call(this,c,e,f,g,h)}throw Error("Invalid arity: "+arguments.length);};c.c=b;c.r=a;return c}();function oh(a,b,c){this.m=a;this.end=b;this.step=c}oh.prototype.ga=function(){return 0<this.step?this.m<this.end:this.m>this.end};oh.prototype.next=function(){var a=this.m;this.m+=this.step;return a};
+function ph(a,b,c,d,e){this.k=a;this.start=b;this.end=c;this.step=d;this.p=e;this.j=32375006;this.q=8192}k=ph.prototype;k.toString=function(){return ec(this)};k.Q=function(a,b){if(b<Ma(this))return this.start+b*this.step;if(this.start>this.end&&0===this.step)return this.start;throw Error("Index out of bounds");};k.$=function(a,b,c){return b<Ma(this)?this.start+b*this.step:this.start>this.end&&0===this.step?this.start:c};k.vb=!0;k.fb=function(){return new oh(this.start,this.end,this.step)};k.H=function(){return this.k};
+k.T=function(){return 0<this.step?this.start+this.step<this.end?new ph(this.k,this.start+this.step,this.end,this.step,null):null:this.start+this.step>this.end?new ph(this.k,this.start+this.step,this.end,this.step,null):null};k.L=function(){if(Aa(Cb(this)))return 0;var a=(this.end-this.start)/this.step;return Math.ceil.b?Math.ceil.b(a):Math.ceil.call(null,a)};k.B=function(){var a=this.p;return null!=a?a:this.p=a=wc(this)};k.A=function(a,b){return Ic(this,b)};k.J=function(){return O(J,this.k)};
+k.R=function(a,b){return Cc.a(this,b)};k.O=function(a,b,c){for(a=this.start;;)if(0<this.step?a<this.end:a>this.end){var d=a;c=b.a?b.a(c,d):b.call(null,c,d);if(Ac(c))return b=c,L.b?L.b(b):L.call(null,b);a+=this.step}else return c};k.N=function(){return null==Cb(this)?null:this.start};k.S=function(){return null!=Cb(this)?new ph(this.k,this.start+this.step,this.end,this.step,null):J};k.D=function(){return 0<this.step?this.start<this.end?this:null:this.start>this.end?this:null};
+k.F=function(a,b){return new ph(b,this.start,this.end,this.step,this.p)};k.G=function(a,b){return M(b,this)};ph.prototype[Ea]=function(){return uc(this)};
+var qh=function(){function a(a,b,c){return new ph(null,a,b,c,null)}function b(a,b){return e.c(a,b,1)}function c(a){return e.c(0,a,1)}function d(){return e.c(0,Number.MAX_VALUE,1)}var e=null,e=function(e,g,h){switch(arguments.length){case 0:return d.call(this);case 1:return c.call(this,e);case 2:return b.call(this,e,g);case 3:return a.call(this,e,g,h)}throw Error("Invalid arity: "+arguments.length);};e.l=d;e.b=c;e.a=b;e.c=a;return e}(),rh=function(){function a(a,b){return new V(null,function(){var f=
+D(b);return f?M(G(f),c.a(a,Qe.a(a,f))):null},null,null)}function b(a){return function(b){return function(c){return function(){function g(g,h){var l=c.bb(0,c.Ra(null)+1),m=Cd(l,a);return 0===l-a*m?b.a?b.a(g,h):b.call(null,g,h):g}function h(a){return b.b?b.b(a):b.call(null,a)}function l(){return b.l?b.l():b.call(null)}var m=null,m=function(a,b){switch(arguments.length){case 0:return l.call(this);case 1:return h.call(this,a);case 2:return g.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);
+};m.l=l;m.b=h;m.a=g;return m}()}(new Me(-1))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),th=function(){function a(a,b){return new V(null,function(){var f=D(b);if(f){var g=G(f),h=a.b?a.b(g):a.call(null,g),g=M(g,lh.a(function(b,c){return function(b){return sc.a(c,a.b?a.b(b):a.call(null,b))}}(g,h,f,f),K(f)));return M(g,c.a(a,D(Qe.a(Q(g),f))))}return null},null,
+null)}function b(a){return function(b){return function(c,g){return function(){function h(h,l){var m=L.b?L.b(g):L.call(null,g),p=a.b?a.b(l):a.call(null,l);ac(g,p);if(Nd(m,sh)||sc.a(p,m))return c.add(l),h;m=zf(c.e);c.clear();m=b.a?b.a(h,m):b.call(null,h,m);Ac(m)||c.add(l);return m}function l(a){if(!t(0===c.e.length)){var d=zf(c.e);c.clear();a=Bc(b.a?b.a(a,d):b.call(null,a,d))}return b.b?b.b(a):b.call(null,a)}function m(){return b.l?b.l():b.call(null)}var p=null,p=function(a,b){switch(arguments.length){case 0:return m.call(this);
+case 1:return l.call(this,a);case 2:return h.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};p.l=m;p.b=l;p.a=h;return p}()}(new jh([]),new Me(sh))}}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),uh=function(){function a(a,b){for(;;)if(D(b)&&0<a){var c=a-1,g=K(b);a=c;b=g}else return null}function b(a){for(;;)if(D(a))a=K(a);else return null}var c=
+null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}(),vh=function(){function a(a,b){uh.a(a,b);return b}function b(a){uh.b(a);return a}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}();
+function wh(a,b,c,d,e,f,g){var h=ma;try{ma=null==ma?null:ma-1;if(null!=ma&&0>ma)return Lb(a,"#");Lb(a,c);if(D(g)){var l=G(g);b.c?b.c(l,a,f):b.call(null,l,a,f)}for(var m=K(g),p=za.b(f)-1;;)if(!m||null!=p&&0===p){D(m)&&0===p&&(Lb(a,d),Lb(a,"..."));break}else{Lb(a,d);var q=G(m);c=a;g=f;b.c?b.c(q,c,g):b.call(null,q,c,g);var s=K(m);c=p-1;m=s;p=c}return Lb(a,e)}finally{ma=h}}
+var xh=function(){function a(a,d){var e=null;if(1<arguments.length){for(var e=0,f=Array(arguments.length-1);e<f.length;)f[e]=arguments[e+1],++e;e=new F(f,0)}return b.call(this,a,e)}function b(a,b){for(var e=D(b),f=null,g=0,h=0;;)if(h<g){var l=f.Q(null,h);Lb(a,l);h+=1}else if(e=D(e))f=e,fd(f)?(e=Yb(f),g=Zb(f),f=e,l=Q(e),e=g,g=l):(l=G(f),Lb(a,l),e=K(f),f=null,g=0),h=0;else return null}a.i=1;a.f=function(a){var d=G(a);a=H(a);return b(d,a)};a.d=b;return a}(),yh={'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f",
+"\n":"\\n","\r":"\\r","\t":"\\t"};function zh(a){return[z('"'),z(a.replace(RegExp('[\\\\"\b\f\n\r\t]',"g"),function(a){return yh[a]})),z('"')].join("")}
+var $=function Ah(b,c,d){if(null==b)return Lb(c,"nil");if(void 0===b)return Lb(c,"#\x3cundefined\x3e");t(function(){var c=S.a(d,wa);return t(c)?(c=b?b.j&131072||b.kc?!0:b.j?!1:w(rb,b):w(rb,b))?Vc(b):c:c}())&&(Lb(c,"^"),Ah(Vc(b),c,d),Lb(c," "));if(null==b)return Lb(c,"nil");if(b.Yb)return b.nc(c);if(b&&(b.j&2147483648||b.I))return b.v(null,c,d);if(Ba(b)===Boolean||"number"===typeof b)return Lb(c,""+z(b));if(null!=b&&b.constructor===Object){Lb(c,"#js ");var e=Oe.a(function(c){return new W(null,2,5,
+uf,[Pd.b(c),b[c]],null)},gd(b));return Bh.n?Bh.n(e,Ah,c,d):Bh.call(null,e,Ah,c,d)}return b instanceof Array?wh(c,Ah,"#js ["," ","]",d,b):t("string"==typeof b)?t(ua.b(d))?Lb(c,zh(b)):Lb(c,b):Tc(b)?xh.d(c,Kc(["#\x3c",""+z(b),"\x3e"],0)):b instanceof Date?(e=function(b,c){for(var d=""+z(b);;)if(Q(d)<c)d=[z("0"),z(d)].join("");else return d},xh.d(c,Kc(['#inst "',""+z(b.getUTCFullYear()),"-",e(b.getUTCMonth()+1,2),"-",e(b.getUTCDate(),2),"T",e(b.getUTCHours(),2),":",e(b.getUTCMinutes(),2),":",e(b.getUTCSeconds(),
+2),".",e(b.getUTCMilliseconds(),3),"-",'00:00"'],0))):b instanceof RegExp?xh.d(c,Kc(['#"',b.source,'"'],0)):(b?b.j&2147483648||b.I||(b.j?0:w(Mb,b)):w(Mb,b))?Nb(b,c,d):xh.d(c,Kc(["#\x3c",""+z(b),"\x3e"],0))},Ch=function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){var b=oa();if(Yc(a))b="";else{var e=z,f=new fa;a:{var g=new dc(f);$(G(a),g,b);a=D(K(a));for(var h=null,l=0,
+m=0;;)if(m<l){var p=h.Q(null,m);Lb(g," ");$(p,g,b);m+=1}else if(a=D(a))h=a,fd(h)?(a=Yb(h),l=Zb(h),h=a,p=Q(a),a=l,l=p):(p=G(h),Lb(g," "),$(p,g,b),a=K(h),h=null,l=0),m=0;else break a}b=""+e(f)}return b}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}();function Bh(a,b,c,d){return wh(c,function(a,c,d){var h=hb(a);b.c?b.c(h,c,d):b.call(null,h,c,d);Lb(c," ");a=ib(a);return b.c?b.c(a,c,d):b.call(null,a,c,d)},"{",", ","}",d,D(a))}Me.prototype.I=!0;
+Me.prototype.v=function(a,b,c){Lb(b,"#\x3cVolatile: ");$(this.state,b,c);return Lb(b,"\x3e")};F.prototype.I=!0;F.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};V.prototype.I=!0;V.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};wg.prototype.I=!0;wg.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};pg.prototype.I=!0;pg.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Z.prototype.I=!0;
+Z.prototype.v=function(a,b,c){return wh(b,$,"["," ","]",c,this)};Rf.prototype.I=!0;Rf.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};ch.prototype.I=!0;ch.prototype.v=function(a,b,c){return wh(b,$,"#{"," ","}",c,this)};Bf.prototype.I=!0;Bf.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Ld.prototype.I=!0;Ld.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Hc.prototype.I=!0;Hc.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};
+rg.prototype.I=!0;rg.prototype.v=function(a,b,c){return Bh(this,$,b,c)};qg.prototype.I=!0;qg.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Df.prototype.I=!0;Df.prototype.v=function(a,b,c){return wh(b,$,"["," ","]",c,this)};Lg.prototype.I=!0;Lg.prototype.v=function(a,b,c){return Bh(this,$,b,c)};$g.prototype.I=!0;$g.prototype.v=function(a,b,c){return wh(b,$,"#{"," ","}",c,this)};Vd.prototype.I=!0;Vd.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Ug.prototype.I=!0;
+Ug.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};X.prototype.I=!0;X.prototype.v=function(a,b,c){return wh(b,$,"["," ","]",c,this)};W.prototype.I=!0;W.prototype.v=function(a,b,c){return wh(b,$,"["," ","]",c,this)};Kf.prototype.I=!0;Kf.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Hd.prototype.I=!0;Hd.prototype.v=function(a,b){return Lb(b,"()")};ze.prototype.I=!0;ze.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Lf.prototype.I=!0;
+Lf.prototype.v=function(a,b,c){return wh(b,$,"#queue ["," ","]",c,D(this))};pa.prototype.I=!0;pa.prototype.v=function(a,b,c){return Bh(this,$,b,c)};ph.prototype.I=!0;ph.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Sg.prototype.I=!0;Sg.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Fd.prototype.I=!0;Fd.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};W.prototype.sb=!0;W.prototype.tb=function(a,b){return pd.a(this,b)};Df.prototype.sb=!0;
+Df.prototype.tb=function(a,b){return pd.a(this,b)};U.prototype.sb=!0;U.prototype.tb=function(a,b){return Md(this,b)};qc.prototype.sb=!0;qc.prototype.tb=function(a,b){return pc(this,b)};var Dh=function(){function a(a,d,e){var f=null;if(2<arguments.length){for(var f=0,g=Array(arguments.length-2);f<g.length;)g[f]=arguments[f+2],++f;f=new F(g,0)}return b.call(this,a,d,f)}function b(a,b,e){return a.k=T.c(b,a.k,e)}a.i=2;a.f=function(a){var d=G(a);a=K(a);var e=G(a);a=H(a);return b(d,e,a)};a.d=b;return a}();
+function Eh(a){return function(b,c){var d=a.a?a.a(b,c):a.call(null,b,c);return Ac(d)?new yc(d):d}}
+function Ve(a){return function(b){return function(){function c(a,c){return A.c(b,a,c)}function d(b){return a.b?a.b(b):a.call(null,b)}function e(){return a.l?a.l():a.call(null)}var f=null,f=function(a,b){switch(arguments.length){case 0:return e.call(this);case 1:return d.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);};f.l=e;f.b=d;f.a=c;return f}()}(Eh(a))}
+var Fh=function(){function a(a){return Ce.a(c.l(),a)}function b(){return function(a){return function(b){return function(){function c(f,g){var h=L.b?L.b(b):L.call(null,b);ac(b,g);return sc.a(h,g)?f:a.a?a.a(f,g):a.call(null,f,g)}function g(b){return a.b?a.b(b):a.call(null,b)}function h(){return a.l?a.l():a.call(null)}var l=null,l=function(a,b){switch(arguments.length){case 0:return h.call(this);case 1:return g.call(this,a);case 2:return c.call(this,a,b)}throw Error("Invalid arity: "+arguments.length);
+};l.l=h;l.b=g;l.a=c;return l}()}(new Me(sh))}}var c=null,c=function(c){switch(arguments.length){case 0:return b.call(this);case 1:return a.call(this,c)}throw Error("Invalid arity: "+arguments.length);};c.l=b;c.b=a;return c}();function Gh(a,b){this.fa=a;this.Zb=b;this.q=0;this.j=2173173760}Gh.prototype.v=function(a,b,c){return wh(b,$,"("," ",")",c,this)};Gh.prototype.O=function(a,b,c){return wd.n(this.fa,b,c,this.Zb)};Gh.prototype.D=function(){return D(Ce.a(this.fa,this.Zb))};Gh.prototype[Ea]=function(){return uc(this)};
+var Hh={};function Ih(a){if(a?a.gc:a)return a.gc(a);var b;b=Ih[n(null==a?null:a)];if(!b&&(b=Ih._,!b))throw x("IEncodeJS.-clj-\x3ejs",a);return b.call(null,a)}function Jh(a){return(a?t(t(null)?null:a.fc)||(a.yb?0:w(Hh,a)):w(Hh,a))?Ih(a):"string"===typeof a||"number"===typeof a||a instanceof U||a instanceof qc?Kh.b?Kh.b(a):Kh.call(null,a):Ch.d(Kc([a],0))}
+var Kh=function Lh(b){if(null==b)return null;if(b?t(t(null)?null:b.fc)||(b.yb?0:w(Hh,b)):w(Hh,b))return Ih(b);if(b instanceof U)return Od(b);if(b instanceof qc)return""+z(b);if(dd(b)){var c={};b=D(b);for(var d=null,e=0,f=0;;)if(f<e){var g=d.Q(null,f),h=R.c(g,0,null),g=R.c(g,1,null);c[Jh(h)]=Lh(g);f+=1}else if(b=D(b))fd(b)?(e=Yb(b),b=Zb(b),d=e,e=Q(e)):(e=G(b),d=R.c(e,0,null),e=R.c(e,1,null),c[Jh(d)]=Lh(e),b=K(b),d=null,e=0),f=0;else break;return c}if($c(b)){c=[];b=D(Oe.a(Lh,b));d=null;for(f=e=0;;)if(f<
+e)h=d.Q(null,f),c.push(h),f+=1;else if(b=D(b))d=b,fd(d)?(b=Yb(d),f=Zb(d),d=b,e=Q(b),b=f):(b=G(d),c.push(b),b=K(d),d=null,e=0),f=0;else break;return c}return b},Mh={};function Nh(a,b){if(a?a.ec:a)return a.ec(a,b);var c;c=Nh[n(null==a?null:a)];if(!c&&(c=Nh._,!c))throw x("IEncodeClojure.-js-\x3eclj",a);return c.call(null,a,b)}
+var Ph=function(){function a(a){return b.d(a,Kc([new pa(null,1,[Oh,!1],null)],0))}var b=null,c=function(){function a(c,d){var h=null;if(1<arguments.length){for(var h=0,l=Array(arguments.length-1);h<l.length;)l[h]=arguments[h+1],++h;h=new F(l,0)}return b.call(this,c,h)}function b(a,c){var d=kd(c)?T.a(Og,c):c,e=S.a(d,Oh);return function(a,b,d,e){return function v(f){return(f?t(t(null)?null:f.uc)||(f.yb?0:w(Mh,f)):w(Mh,f))?Nh(f,T.a(Pg,c)):kd(f)?vh.b(Oe.a(v,f)):$c(f)?af.a(Oc(f),Oe.a(v,f)):f instanceof
+Array?zf(Oe.a(v,f)):Ba(f)===Object?af.a(Uf,function(){return function(a,b,c,d){return function Pa(e){return new V(null,function(a,b,c,d){return function(){for(;;){var a=D(e);if(a){if(fd(a)){var b=Yb(a),c=Q(b),g=Td(c);return function(){for(var a=0;;)if(a<c){var e=C.a(b,a),h=g,l=uf,m;m=e;m=d.b?d.b(m):d.call(null,m);e=new W(null,2,5,l,[m,v(f[e])],null);h.add(e);a+=1}else return!0}()?Wd(g.ca(),Pa(Zb(a))):Wd(g.ca(),null)}var h=G(a);return M(new W(null,2,5,uf,[function(){var a=h;return d.b?d.b(a):d.call(null,
+a)}(),v(f[h])],null),Pa(H(a)))}return null}}}(a,b,c,d),null,null)}}(a,b,d,e)(gd(f))}()):f}}(c,d,e,t(e)?Pd:z)(a)}a.i=1;a.f=function(a){var c=G(a);a=H(a);return b(c,a)};a.d=b;return a}(),b=function(b,e){switch(arguments.length){case 1:return a.call(this,b);default:var f=null;if(1<arguments.length){for(var f=0,g=Array(arguments.length-1);f<g.length;)g[f]=arguments[f+1],++f;f=new F(g,0)}return c.d(b,f)}throw Error("Invalid arity: "+arguments.length);};b.i=1;b.f=c.f;b.b=a;b.d=c.d;return b}();var wa=new U(null,"meta","meta",1499536964),ya=new U(null,"dup","dup",556298533),sh=new U("cljs.core","none","cljs.core/none",926646439),pe=new U(null,"file","file",-1269645878),le=new U(null,"end-column","end-column",1425389514),sa=new U(null,"flush-on-newline","flush-on-newline",-151457939),ne=new U(null,"column","column",2078222095),ua=new U(null,"readably","readably",1129599760),oe=new U(null,"line","line",212345235),za=new U(null,"print-length","print-length",1931866356),me=new U(null,"end-line",
+"end-line",1837326455),Oh=new U(null,"keywordize-keys","keywordize-keys",1310784252),Zg=new U("cljs.core","not-found","cljs.core/not-found",-1572889185);function Qh(a,b){var c=T.c(ih,a,b);return M(c,Ye.a(function(a){return function(b){return a===b}}(c),b))}
+var Rh=function(){function a(a,b){return Q(a)<Q(b)?A.c(Nc,b,a):A.c(Nc,a,b)}var b=null,c=function(){function a(c,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return b.call(this,c,d,l)}function b(a,c,d){a=Qh(Q,Nc.d(d,c,Kc([a],0)));return A.c(af,G(a),H(a))}a.i=2;a.f=function(a){var c=G(a);a=K(a);var d=G(a);a=H(a);return b(c,d,a)};a.d=b;return a}(),b=function(b,e,f){switch(arguments.length){case 0:return bh;case 1:return b;
+case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.l=function(){return bh};b.b=function(a){return a};b.a=a;b.d=c.d;return b}(),Sh=function(){function a(a,b){for(;;)if(Q(b)<Q(a)){var c=a;a=b;b=c}else return A.c(function(a,b){return function(a,c){return nd(b,c)?a:Xc.a(a,c)}}(a,b),a,a)}var b=null,c=function(){function a(b,
+d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,e){a=Qh(function(a){return-Q(a)},Nc.d(e,d,Kc([a],0)));return A.c(b,G(a),H(a))}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return b;case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-
+2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(a){return a};b.a=a;b.d=c.d;return b}(),Th=function(){function a(a,b){return Q(a)<Q(b)?A.c(function(a,c){return nd(b,c)?Xc.a(a,c):a},a,a):A.c(Xc,a,b)}var b=null,c=function(){function a(b,d,h){var l=null;if(2<arguments.length){for(var l=0,m=Array(arguments.length-2);l<m.length;)m[l]=arguments[l+2],++l;l=new F(m,0)}return c.call(this,b,d,l)}function c(a,d,
+e){return A.c(b,a,Nc.a(e,d))}a.i=2;a.f=function(a){var b=G(a);a=K(a);var d=G(a);a=H(a);return c(b,d,a)};a.d=c;return a}(),b=function(b,e,f){switch(arguments.length){case 1:return b;case 2:return a.call(this,b,e);default:var g=null;if(2<arguments.length){for(var g=0,h=Array(arguments.length-2);g<h.length;)h[g]=arguments[g+2],++g;g=new F(h,0)}return c.d(b,e,g)}throw Error("Invalid arity: "+arguments.length);};b.i=2;b.f=c.f;b.b=function(a){return a};b.a=a;b.d=c.d;return b}();
+function Uh(a,b){return A.c(function(b,d){var e=R.c(d,0,null),f=R.c(d,1,null);return nd(a,e)?Rc.c(b,f,S.a(a,e)):b},T.c(Sc,a,Tg(b)),b)}function Vh(a,b){return A.c(function(a,d){var e=Yg(d,b);return Rc.c(a,e,Nc.a(S.c(a,e,bh),d))},Uf,a)}function Wh(a){return A.c(function(a,c){var d=R.c(c,0,null),e=R.c(c,1,null);return Rc.c(a,e,d)},Uf,a)}
+var Xh=function(){function a(a,b,c){a=Q(a)<=Q(b)?new W(null,3,5,uf,[a,b,Wh(c)],null):new W(null,3,5,uf,[b,a,c],null);b=R.c(a,0,null);c=R.c(a,1,null);var g=R.c(a,2,null),h=Vh(b,Vg(g));return A.c(function(a,b,c,d,e){return function(f,g){var h=function(){var a=Uh(Yg(g,Tg(d)),d);return e.b?e.b(a):e.call(null,a)}();return t(h)?A.c(function(){return function(a,b){return Nc.a(a,Wg.d(Kc([b,g],0)))}}(h,a,b,c,d,e),f,h):f}}(a,b,c,g,h),bh,c)}function b(a,b){if(D(a)&&D(b)){var c=Sh.a(fh(Tg(G(a))),fh(Tg(G(b)))),
+g=Q(a)<=Q(b)?new W(null,2,5,uf,[a,b],null):new W(null,2,5,uf,[b,a],null),h=R.c(g,0,null),l=R.c(g,1,null),m=Vh(h,c);return A.c(function(a,b,c,d,e){return function(f,g){var h=function(){var b=Yg(g,a);return e.b?e.b(b):e.call(null,b)}();return t(h)?A.c(function(){return function(a,b){return Nc.a(a,Wg.d(Kc([b,g],0)))}}(h,a,b,c,d,e),f,h):f}}(c,g,h,l,m),bh,l)}return bh}var c=null,c=function(c,e,f){switch(arguments.length){case 2:return b.call(this,c,e);case 3:return a.call(this,c,e,f)}throw Error("Invalid arity: "+
+arguments.length);};c.a=b;c.c=a;return c}();r("mori.apply",T);r("mori.apply.f2",T.a);r("mori.apply.f3",T.c);r("mori.apply.f4",T.n);r("mori.apply.f5",T.r);r("mori.apply.fn",T.K);r("mori.count",Q);r("mori.distinct",function(a){return function c(a,e){return new V(null,function(){return function(a,d){for(;;){var e=a,l=R.c(e,0,null);if(e=D(e))if(nd(d,l))l=H(e),e=d,a=l,d=e;else return M(l,c(H(e),Nc.a(d,l)));else return null}}.call(null,a,e)},null,null)}(a,bh)});r("mori.empty",Oc);r("mori.first",G);r("mori.second",Lc);r("mori.next",K);
+r("mori.rest",H);r("mori.seq",D);r("mori.conj",Nc);r("mori.conj.f0",Nc.l);r("mori.conj.f1",Nc.b);r("mori.conj.f2",Nc.a);r("mori.conj.fn",Nc.K);r("mori.cons",M);r("mori.find",function(a,b){return null!=a&&bd(a)&&nd(a,b)?new W(null,2,5,uf,[b,S.a(a,b)],null):null});r("mori.nth",R);r("mori.nth.f2",R.a);r("mori.nth.f3",R.c);r("mori.last",function(a){for(;;){var b=K(a);if(null!=b)a=b;else return G(a)}});r("mori.assoc",Rc);r("mori.assoc.f3",Rc.c);r("mori.assoc.fn",Rc.K);r("mori.dissoc",Sc);
+r("mori.dissoc.f1",Sc.b);r("mori.dissoc.f2",Sc.a);r("mori.dissoc.fn",Sc.K);r("mori.getIn",cf);r("mori.getIn.f2",cf.a);r("mori.getIn.f3",cf.c);r("mori.updateIn",df);r("mori.updateIn.f3",df.c);r("mori.updateIn.f4",df.n);r("mori.updateIn.f5",df.r);r("mori.updateIn.f6",df.P);r("mori.updateIn.fn",df.K);r("mori.assocIn",function Yh(b,c,d){var e=R.c(c,0,null);return(c=Ed(c))?Rc.c(b,e,Yh(S.a(b,e),c,d)):Rc.c(b,e,d)});r("mori.fnil",Ke);r("mori.fnil.f2",Ke.a);r("mori.fnil.f3",Ke.c);r("mori.fnil.f4",Ke.n);
+r("mori.disj",Xc);r("mori.disj.f1",Xc.b);r("mori.disj.f2",Xc.a);r("mori.disj.fn",Xc.K);r("mori.pop",function(a){return null==a?null:mb(a)});r("mori.peek",Wc);r("mori.hash",nc);r("mori.get",S);r("mori.get.f2",S.a);r("mori.get.f3",S.c);r("mori.hasKey",nd);r("mori.isEmpty",Yc);r("mori.reverse",Jd);r("mori.take",Pe);r("mori.take.f1",Pe.b);r("mori.take.f2",Pe.a);r("mori.drop",Qe);r("mori.drop.f1",Qe.b);r("mori.drop.f2",Qe.a);r("mori.takeNth",rh);r("mori.takeNth.f1",rh.b);r("mori.takeNth.f2",rh.a);
+r("mori.partition",bf);r("mori.partition.f2",bf.a);r("mori.partition.f3",bf.c);r("mori.partition.f4",bf.n);r("mori.partitionAll",kh);r("mori.partitionAll.f1",kh.b);r("mori.partitionAll.f2",kh.a);r("mori.partitionAll.f3",kh.c);r("mori.partitionBy",th);r("mori.partitionBy.f1",th.b);r("mori.partitionBy.f2",th.a);r("mori.iterate",function Zh(b,c){return M(c,new V(null,function(){return Zh(b,b.b?b.b(c):b.call(null,c))},null,null))});r("mori.into",af);r("mori.into.f2",af.a);r("mori.into.f3",af.c);
+r("mori.merge",Wg);r("mori.mergeWith",Xg);r("mori.subvec",Cf);r("mori.subvec.f2",Cf.a);r("mori.subvec.f3",Cf.c);r("mori.takeWhile",lh);r("mori.takeWhile.f1",lh.b);r("mori.takeWhile.f2",lh.a);r("mori.dropWhile",Re);r("mori.dropWhile.f1",Re.b);r("mori.dropWhile.f2",Re.a);r("mori.groupBy",function(a,b){return ce(A.c(function(b,d){var e=a.b?a.b(d):a.call(null,d);return ee.c(b,e,Nc.a(S.c(b,e,Mc),d))},Ob(Uf),b))});r("mori.interpose",function(a,b){return Qe.a(1,Ue.a(Se.b(a),b))});r("mori.interleave",Ue);
+r("mori.interleave.f2",Ue.a);r("mori.interleave.fn",Ue.K);r("mori.concat",ae);r("mori.concat.f0",ae.l);r("mori.concat.f1",ae.b);r("mori.concat.f2",ae.a);r("mori.concat.fn",ae.K);function $e(a){return a instanceof Array||cd(a)}r("mori.flatten",function(a){return Xe.a(function(a){return!$e(a)},H(Ze(a)))});r("mori.lazySeq",function(a){return new V(null,a,null,null)});r("mori.keys",Tg);r("mori.selectKeys",Yg);r("mori.vals",Vg);r("mori.primSeq",Jc);r("mori.primSeq.f1",Jc.b);r("mori.primSeq.f2",Jc.a);
+r("mori.map",Oe);r("mori.map.f1",Oe.b);r("mori.map.f2",Oe.a);r("mori.map.f3",Oe.c);r("mori.map.f4",Oe.n);r("mori.map.fn",Oe.K);
+r("mori.mapIndexed",function(a,b){return function d(b,f){return new V(null,function(){var g=D(f);if(g){if(fd(g)){for(var h=Yb(g),l=Q(h),m=Td(l),p=0;;)if(p<l)Xd(m,function(){var d=b+p,f=C.a(h,p);return a.a?a.a(d,f):a.call(null,d,f)}()),p+=1;else break;return Wd(m.ca(),d(b+l,Zb(g)))}return M(function(){var d=G(g);return a.a?a.a(b,d):a.call(null,b,d)}(),d(b+1,H(g)))}return null},null,null)}(0,b)});r("mori.mapcat",We);r("mori.mapcat.f1",We.b);r("mori.mapcat.fn",We.K);r("mori.reduce",A);
+r("mori.reduce.f2",A.a);r("mori.reduce.f3",A.c);r("mori.reduceKV",function(a,b,c){return null!=c?xb(c,a,b):b});r("mori.keep",Le);r("mori.keep.f1",Le.b);r("mori.keep.f2",Le.a);r("mori.keepIndexed",Ne);r("mori.keepIndexed.f1",Ne.b);r("mori.keepIndexed.f2",Ne.a);r("mori.filter",Xe);r("mori.filter.f1",Xe.b);r("mori.filter.f2",Xe.a);r("mori.remove",Ye);r("mori.remove.f1",Ye.b);r("mori.remove.f2",Ye.a);r("mori.some",Fe);r("mori.every",Ee);r("mori.equals",sc);r("mori.equals.f1",sc.b);
+r("mori.equals.f2",sc.a);r("mori.equals.fn",sc.K);r("mori.range",qh);r("mori.range.f0",qh.l);r("mori.range.f1",qh.b);r("mori.range.f2",qh.a);r("mori.range.f3",qh.c);r("mori.repeat",Se);r("mori.repeat.f1",Se.b);r("mori.repeat.f2",Se.a);r("mori.repeatedly",Te);r("mori.repeatedly.f1",Te.b);r("mori.repeatedly.f2",Te.a);r("mori.sort",sd);r("mori.sort.f1",sd.b);r("mori.sort.f2",sd.a);r("mori.sortBy",td);r("mori.sortBy.f2",td.a);r("mori.sortBy.f3",td.c);r("mori.intoArray",Ia);r("mori.intoArray.f1",Ia.b);
+r("mori.intoArray.f2",Ia.a);r("mori.subseq",nh);r("mori.subseq.f3",nh.c);r("mori.subseq.f5",nh.r);r("mori.dedupe",Fh);r("mori.dedupe.f0",Fh.l);r("mori.dedupe.f1",Fh.b);r("mori.transduce",wd);r("mori.transduce.f3",wd.c);r("mori.transduce.f4",wd.n);r("mori.eduction",function(a,b){return new Gh(a,b)});r("mori.sequence",Ce);r("mori.sequence.f1",Ce.b);r("mori.sequence.f2",Ce.a);r("mori.sequence.fn",Ce.K);r("mori.completing",vd);r("mori.completing.f1",vd.b);r("mori.completing.f2",vd.a);r("mori.list",Kd);
+r("mori.vector",Af);r("mori.hashMap",Pg);r("mori.set",fh);r("mori.sortedSet",gh);r("mori.sortedSetBy",hh);r("mori.sortedMap",Qg);r("mori.sortedMapBy",Rg);r("mori.queue",function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){return af.a?af.a(Mf,a):af.call(null,Mf,a)}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}());r("mori.keyword",Pd);r("mori.keyword.f1",Pd.b);
+r("mori.keyword.f2",Pd.a);r("mori.symbol",rc);r("mori.symbol.f1",rc.b);r("mori.symbol.f2",rc.a);r("mori.zipmap",function(a,b){for(var c=Ob(Uf),d=D(a),e=D(b);;)if(d&&e)c=ee.c(c,G(d),G(e)),d=K(d),e=K(e);else return Qb(c)});r("mori.isList",function(a){return a?a.j&33554432||a.wc?!0:a.j?!1:w(Eb,a):w(Eb,a)});r("mori.isSeq",kd);r("mori.isVector",ed);r("mori.isMap",dd);r("mori.isSet",ad);r("mori.isKeyword",function(a){return a instanceof U});r("mori.isSymbol",function(a){return a instanceof qc});
+r("mori.isCollection",$c);r("mori.isSequential",cd);r("mori.isAssociative",bd);r("mori.isCounted",Ec);r("mori.isIndexed",Fc);r("mori.isReduceable",function(a){return a?a.j&524288||a.Sb?!0:a.j?!1:w(vb,a):w(vb,a)});r("mori.isSeqable",ld);r("mori.isReversible",Id);r("mori.union",Rh);r("mori.union.f0",Rh.l);r("mori.union.f1",Rh.b);r("mori.union.f2",Rh.a);r("mori.union.fn",Rh.K);r("mori.intersection",Sh);r("mori.intersection.f1",Sh.b);r("mori.intersection.f2",Sh.a);r("mori.intersection.fn",Sh.K);
+r("mori.difference",Th);r("mori.difference.f1",Th.b);r("mori.difference.f2",Th.a);r("mori.difference.fn",Th.K);r("mori.join",Xh);r("mori.join.f2",Xh.a);r("mori.join.f3",Xh.c);r("mori.index",Vh);r("mori.project",function(a,b){return fh(Oe.a(function(a){return Yg(a,b)},a))});r("mori.mapInvert",Wh);r("mori.rename",function(a,b){return fh(Oe.a(function(a){return Uh(a,b)},a))});r("mori.renameKeys",Uh);r("mori.isSubset",function(a,b){return Q(a)<=Q(b)&&Ee(function(a){return nd(b,a)},a)});
+r("mori.isSuperset",function(a,b){return Q(a)>=Q(b)&&Ee(function(b){return nd(a,b)},b)});r("mori.notEquals",je);r("mori.notEquals.f1",je.b);r("mori.notEquals.f2",je.a);r("mori.notEquals.fn",je.K);r("mori.gt",Ad);r("mori.gt.f1",Ad.b);r("mori.gt.f2",Ad.a);r("mori.gt.fn",Ad.K);r("mori.gte",Bd);r("mori.gte.f1",Bd.b);r("mori.gte.f2",Bd.a);r("mori.gte.fn",Bd.K);r("mori.lt",yd);r("mori.lt.f1",yd.b);r("mori.lt.f2",yd.a);r("mori.lt.fn",yd.K);r("mori.lte",zd);r("mori.lte.f1",zd.b);r("mori.lte.f2",zd.a);
+r("mori.lte.fn",zd.K);r("mori.compare",od);r("mori.partial",Je);r("mori.partial.f1",Je.b);r("mori.partial.f2",Je.a);r("mori.partial.f3",Je.c);r("mori.partial.f4",Je.n);r("mori.partial.fn",Je.K);r("mori.comp",Ie);r("mori.comp.f0",Ie.l);r("mori.comp.f1",Ie.b);r("mori.comp.f2",Ie.a);r("mori.comp.f3",Ie.c);r("mori.comp.fn",Ie.K);
+r("mori.pipeline",function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){function b(a,c){return c.b?c.b(a):c.call(null,a)}return A.a?A.a(b,a):A.call(null,b,a)}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}());
+r("mori.curry",function(){function a(a,d){var e=null;if(1<arguments.length){for(var e=0,f=Array(arguments.length-1);e<f.length;)f[e]=arguments[e+1],++e;e=new F(f,0)}return b.call(this,a,e)}function b(a,b){return function(e){return T.a(a,M.a?M.a(e,b):M.call(null,e,b))}}a.i=1;a.f=function(a){var d=G(a);a=H(a);return b(d,a)};a.d=b;return a}());
+r("mori.juxt",function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){return function(){function b(a){var c=null;if(0<arguments.length){for(var c=0,d=Array(arguments.length-0);c<d.length;)d[c]=arguments[c+0],++c;c=new F(d,0)}return e.call(this,c)}function e(b){var d=function(){function d(a){return T.a(a,b)}return Oe.a?Oe.a(d,a):Oe.call(null,d,a)}();return Ia.b?Ia.b(d):Ia.call(null,
+d)}b.i=0;b.f=function(a){a=D(a);return e(a)};b.d=e;return b}()}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}());
+r("mori.knit",function(){function a(a){var d=null;if(0<arguments.length){for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;d=new F(e,0)}return b.call(this,d)}function b(a){return function(b){var e=function(){function e(a,b){return a.b?a.b(b):a.call(null,b)}return Oe.c?Oe.c(e,a,b):Oe.call(null,e,a,b)}();return Ia.b?Ia.b(e):Ia.call(null,e)}}a.i=0;a.f=function(a){a=D(a);return b(a)};a.d=b;return a}());r("mori.sum",xd);r("mori.sum.f0",xd.l);r("mori.sum.f1",xd.b);
+r("mori.sum.f2",xd.a);r("mori.sum.fn",xd.K);r("mori.inc",function(a){return a+1});r("mori.dec",function(a){return a-1});r("mori.isEven",Ge);r("mori.isOdd",function(a){return!Ge(a)});r("mori.each",function(a,b){for(var c=D(a),d=null,e=0,f=0;;)if(f<e){var g=d.Q(null,f);b.b?b.b(g):b.call(null,g);f+=1}else if(c=D(c))fd(c)?(e=Yb(c),c=Zb(c),d=e,e=Q(e)):(d=g=G(c),b.b?b.b(d):b.call(null,d),c=K(c),d=null,e=0),f=0;else return null});r("mori.identity",ud);
+r("mori.constantly",function(a){return function(){function b(b){if(0<arguments.length)for(var d=0,e=Array(arguments.length-0);d<e.length;)e[d]=arguments[d+0],++d;return a}b.i=0;b.f=function(b){D(b);return a};b.d=function(){return a};return b}()});r("mori.toJs",Kh);
+r("mori.toClj",function(){function a(a,b){return Ph.d(a,Kc([Oh,b],0))}function b(a){return Ph.b(a)}var c=null,c=function(c,e){switch(arguments.length){case 1:return b.call(this,c);case 2:return a.call(this,c,e)}throw Error("Invalid arity: "+arguments.length);};c.b=b;c.a=a;return c}());r("mori.configure",function(a,b){switch(a){case "print-length":return la=b;case "print-level":return ma=b;default:throw Error([z("No matching clause: "),z(a)].join(""));}});r("mori.meta",Vc);r("mori.withMeta",O);
+r("mori.varyMeta",ie);r("mori.varyMeta.f2",ie.a);r("mori.varyMeta.f3",ie.c);r("mori.varyMeta.f4",ie.n);r("mori.varyMeta.f5",ie.r);r("mori.varyMeta.f6",ie.P);r("mori.varyMeta.fn",ie.K);r("mori.alterMeta",Dh);r("mori.resetMeta",function(a,b){return a.k=b});V.prototype.inspect=function(){return this.toString()};F.prototype.inspect=function(){return this.toString()};Hc.prototype.inspect=function(){return this.toString()};wg.prototype.inspect=function(){return this.toString()};pg.prototype.inspect=function(){return this.toString()};
+qg.prototype.inspect=function(){return this.toString()};Fd.prototype.inspect=function(){return this.toString()};Ld.prototype.inspect=function(){return this.toString()};Hd.prototype.inspect=function(){return this.toString()};W.prototype.inspect=function(){return this.toString()};Vd.prototype.inspect=function(){return this.toString()};Bf.prototype.inspect=function(){return this.toString()};Df.prototype.inspect=function(){return this.toString()};Z.prototype.inspect=function(){return this.toString()};
+X.prototype.inspect=function(){return this.toString()};pa.prototype.inspect=function(){return this.toString()};rg.prototype.inspect=function(){return this.toString()};Lg.prototype.inspect=function(){return this.toString()};$g.prototype.inspect=function(){return this.toString()};ch.prototype.inspect=function(){return this.toString()};ph.prototype.inspect=function(){return this.toString()};U.prototype.inspect=function(){return this.toString()};qc.prototype.inspect=function(){return this.toString()};
+Lf.prototype.inspect=function(){return this.toString()};Kf.prototype.inspect=function(){return this.toString()};r("mori.mutable.thaw",function(a){return Ob(a)});r("mori.mutable.freeze",ce);r("mori.mutable.conj",de);r("mori.mutable.conj.f0",de.l);r("mori.mutable.conj.f1",de.b);r("mori.mutable.conj.f2",de.a);r("mori.mutable.conj.fn",de.K);r("mori.mutable.assoc",ee);r("mori.mutable.assoc.f3",ee.c);r("mori.mutable.assoc.fn",ee.K);r("mori.mutable.dissoc",fe);r("mori.mutable.dissoc.f2",fe.a);r("mori.mutable.dissoc.fn",fe.K);r("mori.mutable.pop",function(a){return Ub(a)});r("mori.mutable.disj",ge);
+r("mori.mutable.disj.f2",ge.a);r("mori.mutable.disj.fn",ge.K);;return this.mori;}.call({});});
diff --git a/third_party/immer/extra/js/lib/platform.js b/third_party/immer/extra/js/lib/platform.js
new file mode 100644
index 0000000000..a998e99537
--- /dev/null
+++ b/third_party/immer/extra/js/lib/platform.js
@@ -0,0 +1,1135 @@
+/*!
+ * Platform.js v1.3.1 <http://mths.be/platform>
+ * Copyright 2014-2016 Benjamin Tan <https://d10.github.io/>
+ * Copyright 2011-2013 John-David Dalton <http://allyoucanleet.com/>
+ * Available under MIT license <http://mths.be/mit>
+ */
+;(function() {
+  'use strict';
+
+  /** Used to determine if values are of the language type `Object` */
+  var objectTypes = {
+    'function': true,
+    'object': true
+  };
+
+  /** Used as a reference to the global object */
+  var root = (objectTypes[typeof window] && window) || this;
+
+  /** Backup possible global object */
+  var oldRoot = root;
+
+  /** Detect free variable `exports` */
+  var freeExports = objectTypes[typeof exports] && exports;
+
+  /** Detect free variable `module` */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  /**
+   * Used as the maximum length of an array-like object.
+   * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
+   * for more details.
+   */
+  var maxSafeInteger = Math.pow(2, 53) - 1;
+
+  /** Opera regexp */
+  var reOpera = /\bOpera/;
+
+  /** Possible global object */
+  var thisBinding = this;
+
+  /** Used for native method references */
+  var objectProto = Object.prototype;
+
+  /** Used to check for own properties of an object */
+  var hasOwnProperty = objectProto.hasOwnProperty;
+
+  /** Used to resolve the internal `[[Class]]` of values */
+  var toString = objectProto.toString;
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Capitalizes a string value.
+   *
+   * @private
+   * @param {string} string The string to capitalize.
+   * @returns {string} The capitalized string.
+   */
+  function capitalize(string) {
+    string = String(string);
+    return string.charAt(0).toUpperCase() + string.slice(1);
+  }
+
+  /**
+   * A utility function to clean up the OS name.
+   *
+   * @private
+   * @param {string} os The OS name to clean up.
+   * @param {string} [pattern] A `RegExp` pattern matching the OS name.
+   * @param {string} [label] A label for the OS.
+   */
+  function cleanupOS(os, pattern, label) {
+    // platform tokens defined at
+    // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+    // http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
+    var data = {
+      '6.4':  '10',
+      '6.3':  '8.1',
+      '6.2':  '8',
+      '6.1':  'Server 2008 R2 / 7',
+      '6.0':  'Server 2008 / Vista',
+      '5.2':  'Server 2003 / XP 64-bit',
+      '5.1':  'XP',
+      '5.01': '2000 SP1',
+      '5.0':  '2000',
+      '4.0':  'NT',
+      '4.90': 'ME'
+    };
+    // detect Windows version from platform tokens
+    if (pattern && label && /^Win/i.test(os) &&
+        (data = data[0/*Opera 9.25 fix*/, /[\d.]+$/.exec(os)])) {
+      os = 'Windows ' + data;
+    }
+    // correct character case and cleanup
+    os = String(os);
+
+    if (pattern && label) {
+      os = os.replace(RegExp(pattern, 'i'), label);
+    }
+
+    os = format(
+      os.replace(/ ce$/i, ' CE')
+        .replace(/\bhpw/i, 'web')
+        .replace(/\bMacintosh\b/, 'Mac OS')
+        .replace(/_PowerPC\b/i, ' OS')
+        .replace(/\b(OS X) [^ \d]+/i, '$1')
+        .replace(/\bMac (OS X)\b/, '$1')
+        .replace(/\/(\d)/, ' $1')
+        .replace(/_/g, '.')
+        .replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
+        .replace(/\bx86\.64\b/gi, 'x86_64')
+        .replace(/\b(Windows Phone) OS\b/, '$1')
+        .split(' on ')[0]
+    );
+
+    return os;
+  }
+
+  /**
+   * An iteration utility for arrays and objects.
+   *
+   * @private
+   * @param {Array|Object} object The object to iterate over.
+   * @param {Function} callback The function called per iteration.
+   */
+  function each(object, callback) {
+    var index = -1,
+        length = object ? object.length : 0;
+
+    if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
+      while (++index < length) {
+        callback(object[index], index, object);
+      }
+    } else {
+      forOwn(object, callback);
+    }
+  }
+
+  /**
+   * Trim and conditionally capitalize string values.
+   *
+   * @private
+   * @param {string} string The string to format.
+   * @returns {string} The formatted string.
+   */
+  function format(string) {
+    string = trim(string);
+    return /^(?:webOS|i(?:OS|P))/.test(string)
+      ? string
+      : capitalize(string);
+  }
+
+  /**
+   * Iterates over an object's own properties, executing the `callback` for each.
+   *
+   * @private
+   * @param {Object} object The object to iterate over.
+   * @param {Function} callback The function executed per own property.
+   */
+  function forOwn(object, callback) {
+    for (var key in object) {
+      if (hasOwnProperty.call(object, key)) {
+        callback(object[key], key, object);
+      }
+    }
+  }
+
+  /**
+   * Gets the internal `[[Class]]` of a value.
+   *
+   * @private
+   * @param {*} value The value.
+   * @returns {string} The `[[Class]]`.
+   */
+  function getClassOf(value) {
+    return value == null
+      ? capitalize(value)
+      : toString.call(value).slice(8, -1);
+  }
+
+  /**
+   * Host objects can return type values that are different from their actual
+   * data type. The objects we are concerned with usually return non-primitive
+   * types of "object", "function", or "unknown".
+   *
+   * @private
+   * @param {*} object The owner of the property.
+   * @param {string} property The property to check.
+   * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.
+   */
+  function isHostType(object, property) {
+    var type = object != null ? typeof object[property] : 'number';
+    return !/^(?:boolean|number|string|undefined)$/.test(type) &&
+      (type == 'object' ? !!object[property] : true);
+  }
+
+  /**
+   * Prepares a string for use in a `RegExp` by making hyphens and spaces optional.
+   *
+   * @private
+   * @param {string} string The string to qualify.
+   * @returns {string} The qualified string.
+   */
+  function qualify(string) {
+    return String(string).replace(/([ -])(?!$)/g, '$1?');
+  }
+
+  /**
+   * A bare-bones `Array#reduce` like utility function.
+   *
+   * @private
+   * @param {Array} array The array to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @returns {*} The accumulated result.
+   */
+  function reduce(array, callback) {
+    var accumulator = null;
+    each(array, function(value, index) {
+      accumulator = callback(accumulator, value, index, array);
+    });
+    return accumulator;
+  }
+
+  /**
+   * Removes leading and trailing whitespace from a string.
+   *
+   * @private
+   * @param {string} string The string to trim.
+   * @returns {string} The trimmed string.
+   */
+  function trim(string) {
+    return String(string).replace(/^ +| +$/g, '');
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Creates a new platform object.
+   *
+   * @memberOf platform
+   * @param {Object|string} [ua=navigator.userAgent] The user agent string or
+   *  context object.
+   * @returns {Object} A platform object.
+   */
+  function parse(ua) {
+
+    /** The environment context object */
+    var context = root;
+
+    /** Used to flag when a custom context is provided */
+    var isCustomContext = ua && typeof ua == 'object' && getClassOf(ua) != 'String';
+
+    // juggle arguments
+    if (isCustomContext) {
+      context = ua;
+      ua = null;
+    }
+
+    /** Browser navigator object */
+    var nav = context.navigator || {};
+
+    /** Browser user agent string */
+    var userAgent = nav.userAgent || '';
+
+    ua || (ua = userAgent);
+
+    /** Used to flag when `thisBinding` is the [ModuleScope] */
+    var isModuleScope = isCustomContext || thisBinding == oldRoot;
+
+    /** Used to detect if browser is like Chrome */
+    var likeChrome = isCustomContext
+      ? !!nav.likeChrome
+      : /\bChrome\b/.test(ua) && !/internal|\n/i.test(toString.toString());
+
+    /** Internal `[[Class]]` value shortcuts */
+    var objectClass = 'Object',
+        airRuntimeClass = isCustomContext ? objectClass : 'ScriptBridgingProxyObject',
+        enviroClass = isCustomContext ? objectClass : 'Environment',
+        javaClass = (isCustomContext && context.java) ? 'JavaPackage' : getClassOf(context.java),
+        phantomClass = isCustomContext ? objectClass : 'RuntimeObject';
+
+    /** Detect Java environment */
+    var java = /\bJava/.test(javaClass) && context.java;
+
+    /** Detect Rhino */
+    var rhino = java && getClassOf(context.environment) == enviroClass;
+
+    /** A character to represent alpha */
+    var alpha = java ? 'a' : '\u03b1';
+
+    /** A character to represent beta */
+    var beta = java ? 'b' : '\u03b2';
+
+    /** Browser document object */
+    var doc = context.document || {};
+
+    /**
+     * Detect Opera browser (Presto-based)
+     * http://www.howtocreate.co.uk/operaStuff/operaObject.html
+     * http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini
+     */
+    var opera = context.operamini || context.opera;
+
+    /** Opera `[[Class]]` */
+    var operaClass = reOpera.test(operaClass = (isCustomContext && opera) ? opera['[[Class]]'] : getClassOf(opera))
+      ? operaClass
+      : (opera = null);
+
+    /*------------------------------------------------------------------------*/
+
+    /** Temporary variable used over the script's lifetime */
+    var data;
+
+    /** The CPU architecture */
+    var arch = ua;
+
+    /** Platform description array */
+    var description = [];
+
+    /** Platform alpha/beta indicator */
+    var prerelease = null;
+
+    /** A flag to indicate that environment features should be used to resolve the platform */
+    var useFeatures = ua == userAgent;
+
+    /** The browser/environment version */
+    var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();
+
+    /** A flag to indicate if the OS ends with "/ Version" */
+    var isSpecialCasedOS;
+
+    /* Detectable layout engines (order is important) */
+    var layout = getLayout([
+      'Trident',
+      { 'label': 'WebKit', 'pattern': 'AppleWebKit' },
+      'iCab',
+      'Presto',
+      'NetFront',
+      'Tasman',
+      'KHTML',
+      'Gecko'
+    ]);
+
+    /* Detectable browser names (order is important) */
+    var name = getName([
+      'Adobe AIR',
+      'Arora',
+      'Avant Browser',
+      'Breach',
+      'Camino',
+      'Epiphany',
+      'Fennec',
+      'Flock',
+      'Galeon',
+      'GreenBrowser',
+      'iCab',
+      'Iceweasel',
+      { 'label': 'SRWare Iron', 'pattern': 'Iron' },
+      'K-Meleon',
+      'Konqueror',
+      'Lunascape',
+      'Maxthon',
+      'Midori',
+      'Nook Browser',
+      'PhantomJS',
+      'Raven',
+      'Rekonq',
+      'RockMelt',
+      'SeaMonkey',
+      { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+      'Sleipnir',
+      'SlimBrowser',
+      'Sunrise',
+      'Swiftfox',
+      'WebPositive',
+      'Opera Mini',
+      { 'label': 'Opera Mini', 'pattern': 'OPiOS' },
+      'Opera',
+      { 'label': 'Opera', 'pattern': 'OPR' },
+      'Chrome',
+      { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' },
+      { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },
+      { 'label': 'IE', 'pattern': 'IEMobile' },
+      { 'label': 'IE', 'pattern': 'MSIE' },
+      'Safari'
+    ]);
+
+    /* Detectable products (order is important) */
+    var product = getProduct([
+      { 'label': 'BlackBerry', 'pattern': 'BB10' },
+      'BlackBerry',
+      { 'label': 'Galaxy S', 'pattern': 'GT-I9000' },
+      { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },
+      { 'label': 'Galaxy S3', 'pattern': 'GT-I9300' },
+      { 'label': 'Galaxy S4', 'pattern': 'GT-I9500' },
+      'Google TV',
+      'Lumia',
+      'iPad',
+      'iPod',
+      'iPhone',
+      'Kindle',
+      { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
+      'Nook',
+      'PlayBook',
+      'PlayStation 4',
+      'PlayStation 3',
+      'PlayStation Vita',
+      'TouchPad',
+      'Transformer',
+      { 'label': 'Wii U', 'pattern': 'WiiU' },
+      'Wii',
+      'Xbox One',
+      { 'label': 'Xbox 360', 'pattern': 'Xbox' },
+      'Xoom'
+    ]);
+
+    /* Detectable manufacturers */
+    var manufacturer = getManufacturer({
+      'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },
+      'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },
+      'Asus': { 'Transformer': 1 },
+      'Barnes & Noble': { 'Nook': 1 },
+      'BlackBerry': { 'PlayBook': 1 },
+      'Google': { 'Google TV': 1 },
+      'HP': { 'TouchPad': 1 },
+      'HTC': {},
+      'LG': {},
+      'Microsoft': { 'Xbox': 1, 'Xbox One': 1 },
+      'Motorola': { 'Xoom': 1 },
+      'Nintendo': { 'Wii U': 1,  'Wii': 1 },
+      'Nokia': { 'Lumia': 1 },
+      'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 },
+      'Sony': { 'PlayStation 4': 1, 'PlayStation 3': 1, 'PlayStation Vita': 1 }
+    });
+
+    /* Detectable OSes (order is important) */
+    var os = getOS([
+      'Windows Phone ',
+      'Android',
+      'CentOS',
+      'Debian',
+      'Fedora',
+      'FreeBSD',
+      'Gentoo',
+      'Haiku',
+      'Kubuntu',
+      'Linux Mint',
+      'Red Hat',
+      'SuSE',
+      'Ubuntu',
+      'Xubuntu',
+      'Cygwin',
+      'Symbian OS',
+      'hpwOS',
+      'webOS ',
+      'webOS',
+      'Tablet OS',
+      'Linux',
+      'Mac OS X',
+      'Macintosh',
+      'Mac',
+      'Windows 98;',
+      'Windows '
+    ]);
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Picks the layout engine from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected layout engine.
+     */
+    function getLayout(guesses) {
+      return reduce(guesses, function(result, guess) {
+        return result || RegExp('\\b' + (
+          guess.pattern || qualify(guess)
+        ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+      });
+    }
+
+    /**
+     * Picks the manufacturer from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An object of guesses.
+     * @returns {null|string} The detected manufacturer.
+     */
+    function getManufacturer(guesses) {
+      return reduce(guesses, function(result, value, key) {
+        // lookup the manufacturer by product or scan the UA for the manufacturer
+        return result || (
+          value[product] ||
+          value[0/*Opera 9.25 fix*/, /^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] ||
+          RegExp('\\b' + qualify(key) + '(?:\\b|\\w*\\d)', 'i').exec(ua)
+        ) && key;
+      });
+    }
+
+    /**
+     * Picks the browser name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected browser name.
+     */
+    function getName(guesses) {
+      return reduce(guesses, function(result, guess) {
+        return result || RegExp('\\b' + (
+          guess.pattern || qualify(guess)
+        ) + '\\b', 'i').exec(ua) && (guess.label || guess);
+      });
+    }
+
+    /**
+     * Picks the OS name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected OS name.
+     */
+    function getOS(guesses) {
+      return reduce(guesses, function(result, guess) {
+        var pattern = guess.pattern || qualify(guess);
+        if (!result && (result =
+              RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua)
+            )) {
+          result = cleanupOS(result, pattern, guess.label || guess);
+        }
+        return result;
+      });
+    }
+
+    /**
+     * Picks the product name from an array of guesses.
+     *
+     * @private
+     * @param {Array} guesses An array of guesses.
+     * @returns {null|string} The detected product name.
+     */
+    function getProduct(guesses) {
+      return reduce(guesses, function(result, guess) {
+        var pattern = guess.pattern || qualify(guess);
+        if (!result && (result =
+              RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) ||
+              RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua)
+            )) {
+          // split by forward slash and append product version if needed
+          if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\d.]+/.test(result[0])) {
+            result[0] += ' ' + result[1];
+          }
+          // correct character case and cleanup
+          guess = guess.label || guess;
+          result = format(result[0]
+            .replace(RegExp(pattern, 'i'), guess)
+            .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')
+            .replace(RegExp('(' + guess + ')[-_.]?(\\w)', 'i'), '$1 $2'));
+        }
+        return result;
+      });
+    }
+
+    /**
+     * Resolves the version using an array of UA patterns.
+     *
+     * @private
+     * @param {Array} patterns An array of UA patterns.
+     * @returns {null|string} The detected version.
+     */
+    function getVersion(patterns) {
+      return reduce(patterns, function(result, pattern) {
+        return result || (RegExp(pattern +
+          '(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;
+      });
+    }
+
+    /**
+     * Returns `platform.description` when the platform object is coerced to a string.
+     *
+     * @name toString
+     * @memberOf platform
+     * @returns {string} Returns `platform.description` if available, else an empty string.
+     */
+    function toStringPlatform() {
+      return this.description || '';
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // convert layout to an array so we can add extra details
+    layout && (layout = [layout]);
+
+    // detect product names that contain their manufacturer's name
+    if (manufacturer && !product) {
+      product = getProduct([manufacturer]);
+    }
+    // clean up Google TV
+    if ((data = /\bGoogle TV\b/.exec(product))) {
+      product = data[0];
+    }
+    // detect simulators
+    if (/\bSimulator\b/i.test(ua)) {
+      product = (product ? product + ' ' : '') + 'Simulator';
+    }
+    // detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS
+    if (name == 'Opera Mini' && /\bOPiOS\b/.test(ua)) {
+      description.push('running in Turbo/Uncompressed mode');
+    }
+    // detect iOS
+    if (/^iP/.test(product)) {
+      name || (name = 'Safari');
+      os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua))
+        ? ' ' + data[1].replace(/_/g, '.')
+        : '');
+    }
+    // detect Kubuntu
+    else if (name == 'Konqueror' && !/buntu/i.test(os)) {
+      os = 'Kubuntu';
+    }
+    // detect Android browsers
+    else if (manufacturer && manufacturer != 'Google' &&
+        ((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) {
+      name = 'Android Browser';
+      os = /\bAndroid\b/.test(os) ? os : 'Android';
+    }
+    // detect false positives for Firefox/Safari
+    else if (!name || (data = !/\bMinefield\b|\(Android;/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) {
+      // escape the `/` for Firefox 1
+      if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
+        // clear name of false positives
+        name = null;
+      }
+      // reassign a generic name
+      if ((data = product || manufacturer || os) &&
+          (product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) {
+        name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser';
+      }
+    }
+    // detect Firefox OS
+    if ((data = /\((Mobile|Tablet).*?Firefox\b/i.exec(ua)) && data[1]) {
+      os = 'Firefox OS';
+      if (!product) {
+        product = data[1];
+      }
+    }
+    // detect non-Opera versions (order is important)
+    if (!version) {
+      version = getVersion([
+        '(?:Cloud9|CriOS|CrMo|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|Silk(?!/[\\d.]+$))',
+        'Version',
+        qualify(name),
+        '(?:Firefox|Minefield|NetFront)'
+      ]);
+    }
+    // detect stubborn layout engines
+    if (layout == 'iCab' && parseFloat(version) > 3) {
+      layout = ['WebKit'];
+    } else if (
+        layout != 'Trident' &&
+        (data =
+          /\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') ||
+          /\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
+          !layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident')
+        )
+    ) {
+      layout = [data];
+    }
+    // detect NetFront on PlayStation
+    else if (/\bPlayStation\b(?! Vita\b)/i.test(name) && layout == 'WebKit') {
+      layout = ['NetFront'];
+    }
+    // detect Windows Phone 7 desktop mode
+    if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) {
+      name += ' Mobile';
+      os = 'Windows Phone ' + (/\+$/.test(data) ? data : data + '.x');
+      description.unshift('desktop mode');
+    }
+    // detect Windows Phone 8+ desktop mode
+    else if (/\bWPDesktop\b/i.test(ua)) {
+      name = 'IE Mobile';
+      os = 'Windows Phone 8+';
+      description.unshift('desktop mode');
+      version || (version = (/\brv:([\d.]+)/.exec(ua) || 0)[1]);
+    }
+    // detect IE 11 and above
+    else if (name != 'IE' && layout == 'Trident' && (data = /\brv:([\d.]+)/.exec(ua))) {
+      if (!/\bWPDesktop\b/i.test(ua)) {
+        if (name) {
+          description.push('identifying as ' + name + (version ? ' ' + version : ''));
+        }
+        name = 'IE';
+      }
+      version = data[1];
+    }
+    // detect Microsoft Edge
+    else if ((name == 'Chrome' || name != 'IE') && (data = /\bEdge\/([\d.]+)/.exec(ua))) {
+      name = 'Microsoft Edge';
+      version = data[1];
+      layout = ['Trident'];
+    }
+    // leverage environment features
+    if (useFeatures) {
+      // detect server-side environments
+      // Rhino has a global function while others have a global object
+      if (isHostType(context, 'global')) {
+        if (java) {
+          data = java.lang.System;
+          arch = data.getProperty('os.arch');
+          os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
+        }
+        if (isModuleScope && isHostType(context, 'system') && (data = [context.system])[0]) {
+          os || (os = data[0].os || null);
+          try {
+            data[1] = context.require('ringo/engine').version;
+            version = data[1].join('.');
+            name = 'RingoJS';
+          } catch(e) {
+            if (data[0].global.system == context.system) {
+              name = 'Narwhal';
+            }
+          }
+        }
+        else if (typeof context.process == 'object' && (data = context.process)) {
+          name = 'Node.js';
+          arch = data.arch;
+          os = data.platform;
+          version = /[\d.]+/.exec(data.version)[0];
+        }
+        else if (rhino) {
+          name = 'Rhino';
+        }
+      }
+      // detect Adobe AIR
+      else if (getClassOf((data = context.runtime)) == airRuntimeClass) {
+        name = 'Adobe AIR';
+        os = data.flash.system.Capabilities.os;
+      }
+      // detect PhantomJS
+      else if (getClassOf((data = context.phantom)) == phantomClass) {
+        name = 'PhantomJS';
+        version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
+      }
+      // detect IE compatibility modes
+      else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) {
+        // we're in compatibility mode when the Trident version + 4 doesn't
+        // equal the document mode
+        version = [version, doc.documentMode];
+        if ((data = +data[1] + 4) != version[1]) {
+          description.push('IE ' + version[1] + ' mode');
+          layout && (layout[1] = '');
+          version[1] = data;
+        }
+        version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
+      }
+      os = os && format(os);
+    }
+    // detect prerelease phases
+    if (version && (data =
+          /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
+          /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
+          /\bMinefield\b/i.test(ua) && 'a'
+        )) {
+      prerelease = /b/i.test(data) ? 'beta' : 'alpha';
+      version = version.replace(RegExp(data + '\\+?$'), '') +
+        (prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
+    }
+    // detect Firefox Mobile
+    if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS)\b/.test(os)) {
+      name = 'Firefox Mobile';
+    }
+    // obscure Maxthon's unreliable version
+    else if (name == 'Maxthon' && version) {
+      version = version.replace(/\.[\d.]+/, '.x');
+    }
+    // detect Silk desktop/accelerated modes
+    else if (name == 'Silk') {
+      if (!/\bMobi/i.test(ua)) {
+        os = 'Android';
+        description.unshift('desktop mode');
+      }
+      if (/Accelerated *= *true/i.test(ua)) {
+        description.unshift('accelerated');
+      }
+    }
+    // detect Xbox 360 and Xbox One
+    else if (/\bXbox\b/i.test(product)) {
+      os = null;
+      if (product == 'Xbox 360' && /\bIEMobile\b/.test(ua)) {
+        description.unshift('mobile mode');
+      }
+    }
+    // add mobile postfix
+    else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) &&
+        (os == 'Windows CE' || /Mobi/i.test(ua))) {
+      name += ' Mobile';
+    }
+    // detect IE platform preview
+    else if (name == 'IE' && useFeatures && context.external === null) {
+      description.unshift('platform preview');
+    }
+    // detect BlackBerry OS version
+    // http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
+    else if ((/\bBlackBerry\b/.test(product) || /\bBB10\b/.test(ua)) && (data =
+          (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
+          version
+        )) {
+      data = [data, /BB10/.test(ua)];
+      os = (data[1] ? (product = null, manufacturer = 'BlackBerry') : 'Device Software') + ' ' + data[0];
+      version = null;
+    }
+    // detect Opera identifying/masking itself as another browser
+    // http://www.opera.com/support/kb/view/843/
+    else if (this != forOwn && (
+          product != 'Wii' && (
+            (useFeatures && opera) ||
+            (/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
+            (name == 'Firefox' && /\bOS X (?:\d+\.){2,}/.test(os)) ||
+            (name == 'IE' && (
+              (os && !/^Win/.test(os) && version > 5.5) ||
+              /\bWindows XP\b/.test(os) && version > 8 ||
+              version == 8 && !/\bTrident\b/.test(ua)
+            ))
+          )
+        ) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {
+
+      // when "indentifying", the UA contains both Opera and the other browser's name
+      data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
+      if (reOpera.test(name)) {
+        if (/\bIE\b/.test(data) && os == 'Mac OS') {
+          os = null;
+        }
+        data = 'identify' + data;
+      }
+      // when "masking", the UA contains only the other browser's name
+      else {
+        data = 'mask' + data;
+        if (operaClass) {
+          name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));
+        } else {
+          name = 'Opera';
+        }
+        if (/\bIE\b/.test(data)) {
+          os = null;
+        }
+        if (!useFeatures) {
+          version = null;
+        }
+      }
+      layout = ['Presto'];
+      description.push(data);
+    }
+    // detect WebKit Nightly and approximate Chrome/Safari versions
+    if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
+      // correct build for numeric comparison
+      // (e.g. "532.5" becomes "532.05")
+      data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data];
+      // nightly builds are postfixed with a `+`
+      if (name == 'Safari' && data[1].slice(-1) == '+') {
+        name = 'WebKit Nightly';
+        prerelease = 'alpha';
+        version = data[1].slice(0, -1);
+      }
+      // clear incorrect browser versions
+      else if (version == data[1] ||
+          version == (data[2] = (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
+        version = null;
+      }
+      // use the full Chrome version when available
+      data[1] = (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1];
+      // detect Blink layout engine
+      if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && name != 'IE' && name != 'Microsoft Edge') {
+        layout = ['Blink'];
+      }
+      // detect JavaScriptCore
+      // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi
+      if (!useFeatures || (!likeChrome && !data[1])) {
+        layout && (layout[1] = 'like Safari');
+        data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : '8');
+      } else {
+        layout && (layout[1] = 'like Chrome');
+        data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28');
+      }
+      // add the postfix of ".x" or "+" for approximate versions
+      layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+'));
+      // obscure version for some Safari 1-2 releases
+      if (name == 'Safari' && (!version || parseInt(version) > 45)) {
+        version = data;
+      }
+    }
+    // detect Opera desktop modes
+    if (name == 'Opera' &&  (data = /\bzbov|zvav$/.exec(os))) {
+      name += ' ';
+      description.unshift('desktop mode');
+      if (data == 'zvav') {
+        name += 'Mini';
+        version = null;
+      } else {
+        name += 'Mobile';
+      }
+      os = os.replace(RegExp(' *' + data + '$'), '');
+    }
+    // detect Chrome desktop mode
+    else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) {
+      description.unshift('desktop mode');
+      name = 'Chrome Mobile';
+      version = null;
+
+      if (/\bOS X\b/.test(os)) {
+        manufacturer = 'Apple';
+        os = 'iOS 4.3+';
+      } else {
+        os = null;
+      }
+    }
+    // strip incorrect OS versions
+    if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 &&
+        ua.indexOf('/' + data + '-') > -1) {
+      os = trim(os.replace(data, ''));
+    }
+    // add layout engine
+    if (layout && !/\b(?:Avant|Nook)\b/.test(name) && (
+        /Browser|Lunascape|Maxthon/.test(name) ||
+        /^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Sleipnir|Web)/.test(name) && layout[1])) {
+      // don't add layout details to description if they are falsey
+      (data = layout[layout.length - 1]) && description.push(data);
+    }
+    // combine contextual information
+    if (description.length) {
+      description = ['(' + description.join('; ') + ')'];
+    }
+    // append manufacturer
+    if (manufacturer && product && product.indexOf(manufacturer) < 0) {
+      description.push('on ' + manufacturer);
+    }
+    // append product
+    if (product) {
+      description.push((/^on /.test(description[description.length -1]) ? '' : 'on ') + product);
+    }
+    // parse OS into an object
+    if (os) {
+      data = / ([\d.+]+)$/.exec(os);
+      isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/';
+      os = {
+        'architecture': 32,
+        'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os,
+        'version': data ? data[1] : null,
+        'toString': function() {
+          var version = this.version;
+          return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : '');
+        }
+      };
+    }
+    // add browser/OS architecture
+    if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) {
+      if (os) {
+        os.architecture = 64;
+        os.family = os.family.replace(RegExp(' *' + data), '');
+      }
+      if (
+          name && (/\bWOW64\b/i.test(ua) ||
+          (useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua)))
+      ) {
+        description.unshift('32-bit');
+      }
+    }
+
+    ua || (ua = null);
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The platform object.
+     *
+     * @name platform
+     * @type Object
+     */
+    var platform = {};
+
+    /**
+     * The platform description.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.description = ua;
+
+    /**
+     * The name of the browser's layout engine.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.layout = layout && layout[0];
+
+    /**
+     * The name of the product's manufacturer.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.manufacturer = manufacturer;
+
+    /**
+     * The name of the browser/environment.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.name = name;
+
+    /**
+     * The alpha/beta release indicator.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.prerelease = prerelease;
+
+    /**
+     * The name of the product hosting the browser.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.product = product;
+
+    /**
+     * The browser's user agent string.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.ua = ua;
+
+    /**
+     * The browser/environment version.
+     *
+     * @memberOf platform
+     * @type string|null
+     */
+    platform.version = name && version;
+
+    /**
+     * The name of the operating system.
+     *
+     * @memberOf platform
+     * @type Object
+     */
+    platform.os = os || {
+
+      /**
+       * The CPU architecture the OS is built for.
+       *
+       * @memberOf platform.os
+       * @type number|null
+       */
+      'architecture': null,
+
+      /**
+       * The family of the OS.
+       *
+       * Common values include:
+       * "Windows", "Windows Server 2008 R2 / 7", "Windows Server 2008 / Vista",
+       * "Windows XP", "OS X", "Ubuntu", "Debian", "Fedora", "Red Hat", "SuSE",
+       * "Android", "iOS" and "Windows Phone"
+       *
+       * @memberOf platform.os
+       * @type string|null
+       */
+      'family': null,
+
+      /**
+       * The version of the OS.
+       *
+       * @memberOf platform.os
+       * @type string|null
+       */
+      'version': null,
+
+      /**
+       * Returns the OS string.
+       *
+       * @memberOf platform.os
+       * @returns {string} The OS string.
+       */
+      'toString': function() { return 'null'; }
+    };
+
+    platform.parse = parse;
+    platform.toString = toStringPlatform;
+
+    if (platform.version) {
+      description.unshift(version);
+    }
+    if (platform.name) {
+      description.unshift(name);
+    }
+    if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) {
+      description.push(product ? '(' + os + ')' : 'on ' + os);
+    }
+    if (description.length) {
+      platform.description = description.join(' ');
+    }
+    return platform;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // export platform
+  // some AMD build optimizers, like r.js, check for condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // define as an anonymous module so, through path mapping, it can be aliased
+    define(function() {
+      return parse();
+    });
+  }
+  // check for `exports` after `define` in case a build optimizer adds an `exports` object
+  else if (freeExports && freeModule) {
+    // in Narwhal, Node.js, Rhino -require, or RingoJS
+    forOwn(parse(), function(value, key) {
+      freeExports[key] = value;
+    });
+  }
+  // in a browser or Rhino
+  else {
+    root.platform = parse();
+  }
+}.call(this));
diff --git a/third_party/immer/extra/js/makefile b/third_party/immer/extra/js/makefile
new file mode 100644
index 0000000000..fc6075bfe8
--- /dev/null
+++ b/third_party/immer/extra/js/makefile
@@ -0,0 +1,29 @@
+
+BOOST=.
+EMCXX=em++
+EMCXXFLAGS=-O3 -std=c++14 --bind \
+	-I ../.. \
+	-I $(BOOST)
+WASM_BACKEND=0
+
+ALL= \
+	out/immer.asmjs.js \
+	out/immer.asmjs.html \
+	out/immer.wasm.js \
+	out/immer.wasm.html
+
+all: $(ALL)
+
+out/immer.asmjs.js: immer.cpp
+	@mkdir -p $(@D)
+	$(EMCXX) $(EMCXXFLAGS) $< -o $@
+
+out/immer.wasm.js: immer.cpp
+	@mkdir -p $(@D)
+	EMCC_WASM_BACKEND=$(WASM_BACKEND) $(EMCXX) -s WASM=1 $(EMCXXFLAGS) $< -o $@
+
+%.html: %.js index.tpl.html
+	sed s/%IMMER_JS%/`basename $<`/ < index.tpl.html > $@
+
+clean:
+	rm -f $(ALL)
diff --git a/third_party/immer/extra/python/CMakeLists.txt b/third_party/immer/extra/python/CMakeLists.txt
new file mode 100644
index 0000000000..11d5fff209
--- /dev/null
+++ b/third_party/immer/extra/python/CMakeLists.txt
@@ -0,0 +1,46 @@
+
+option(USE_PYBIND "bind with pybind1" off)
+option(USE_BOOST_PYTHON  "bind with boost::python" off)
+
+if (USE_PYBIND)
+  set(PYBIND11_CPP_STANDARD -std=c++14)
+  find_package(Boost 1.56 REQUIRED)
+  add_subdirectory(lib/pybind11)
+  pybind11_add_module(immer_python_module src/immer-pybind.cpp)
+  target_link_libraries(immer_python_module PUBLIC
+    immer)
+
+elseif(USE_BOOST_PYTHON)
+  find_package(PythonInterp)
+  find_package(PythonLibs)
+  find_package(Boost 1.56 COMPONENTS python)
+  python_add_module(immer_python_module src/immer-boost.cpp)
+  include_directories(immer_python_module PUBLIC
+    ${immer_include_dir}
+    ${Boost_INCLUDE_DIRS}
+    ${PYTHON_INCLUDE_DIRS})
+  target_link_libraries(immer_python_module PUBLIC
+    immer
+    ${Boost_LIBRARIES}
+    ${PYTHON_LIBRARIES})
+
+else()
+  find_package(PythonInterp)
+  find_package(PythonLibs)
+
+  if (NOT PYTHONLIBS_FOUND)
+    message(STATUS "Disabling Python modules")
+    return()
+  endif()
+
+  python_add_module(immer_python_module EXCLUDE_FROM_ALL
+    src/immer-raw.cpp)
+  target_include_directories(immer_python_module PUBLIC
+    ${PYTHON_INCLUDE_DIRS})
+  target_link_libraries(immer_python_module PUBLIC
+    immer
+    ${PYTHON_LIBRARIES})
+
+endif()
+
+add_custom_target(python DEPENDS immer_python_module)
diff --git a/third_party/immer/extra/python/README.rst b/third_party/immer/extra/python/README.rst
new file mode 100644
index 0000000000..7447e99e62
--- /dev/null
+++ b/third_party/immer/extra/python/README.rst
@@ -0,0 +1,42 @@
+
+Python bindings
+===============
+
+This library includes experimental bindings bring efficient immutable
+vectors for the Python language.  They were developed as part of the
+research for the `ICFP'17 paper`_.  The interface is quite
+**incomplete**, yet you can already do some things like:
+
+.. literalinclude:: ../extra/python/example.py
+   :language: python
+   :start-after: intro/start
+   :end-before:  intro/end
+..
+
+    **Do you want to help** making these bindings complete and production
+    ready?  Drop a line at `immer@sinusoid.al
+    <mailto:immer@sinusoid.al>`_ or `open an issue on Github
+    <https://github.com/arximboldi/immer>`_
+
+Installation
+------------
+::
+
+    pip install --user git+https://github.com/arximboldi/immer.git
+
+Benchmarks
+----------
+
+The library includes a set of benchmarks that compare it to
+`pyrsistent <https://github.com/tobgu/pyrsistent>`_.  You can see the
+results in the `ICFP'17 paper`_.  If you want to run them yourself,
+you need to install some dependencies::
+
+     pip install --user pytest-benchmark pyrsistent
+
+Then you need to clone the `project repository
+<https://github.com/arximboldi/immer>`_ and from its root, run::
+
+     pytest extra/python/benchmark
+
+.. _ICFP'17 paper: https://public.sinusoid.es/misc/immer/immer-icfp17.pdf
diff --git a/third_party/immer/extra/python/benchmark/test_benchmarks.py b/third_party/immer/extra/python/benchmark/test_benchmarks.py
new file mode 100644
index 0000000000..11e4817c32
--- /dev/null
+++ b/third_party/immer/extra/python/benchmark/test_benchmarks.py
@@ -0,0 +1,45 @@
+
+# immer: immutable data structures for C++
+# Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+#
+# This software is distributed under the Boost Software License, Version 1.0.
+# See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+
+##
+
+import immer
+import pyrsistent
+
+BENCHMARK_SIZE = 1000
+
+def push(v, n=BENCHMARK_SIZE):
+    for x in xrange(n):
+        v = v.append(x)
+    return v
+
+def assoc(v):
+    for i in xrange(len(v)):
+        v = v.set(i, i+1)
+    return v
+
+def index(v):
+    for i in xrange(len(v)):
+        v[i]
+
+def test_push_immer(benchmark):
+    benchmark(push, immer.Vector())
+
+def test_push_pyrsistent(benchmark):
+    benchmark(push, pyrsistent.pvector())
+
+def test_assoc_immer(benchmark):
+    benchmark(assoc, push(immer.Vector()))
+
+def test_assoc_pyrsistent(benchmark):
+    benchmark(assoc, push(pyrsistent.pvector()))
+
+def test_index_immer(benchmark):
+    benchmark(index, push(immer.Vector()))
+
+def test_index_pyrsistent(benchmark):
+    benchmark(index, push(pyrsistent.pvector()))
diff --git a/third_party/immer/extra/python/example.py b/third_party/immer/extra/python/example.py
new file mode 100755
index 0000000000..36f7333c4f
--- /dev/null
+++ b/third_party/immer/extra/python/example.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python##
+
+# immer: immutable data structures for C++
+# Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+#
+# This software is distributed under the Boost Software License, Version 1.0.
+# See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+
+
+# include:intro/start
+import immer
+
+v0 = immer.Vector().append(13).append(42)
+assert v0[0] == 13
+assert v0[1] == 42
+assert len(v0) == 2
+
+v1 = v0.set(0, 12)
+assert v0.tolist() == [13, 42]
+assert v1.tolist() == [12, 42]
+# include:intro/end
diff --git a/third_party/immer/extra/python/immer/__init__.py b/third_party/immer/extra/python/immer/__init__.py
new file mode 100644
index 0000000000..e4702b40e4
--- /dev/null
+++ b/third_party/immer/extra/python/immer/__init__.py
@@ -0,0 +1,2 @@
+
+from immer_python_module import *
diff --git a/third_party/immer/extra/python/lib/pybind11 b/third_party/immer/extra/python/lib/pybind11
new file mode 160000
+Subproject 1eaacd19f6de9a053570c21de6d173efc2304bc
diff --git a/third_party/immer/extra/python/src/immer-boost.cpp b/third_party/immer/extra/python/src/immer-boost.cpp
new file mode 100644
index 0000000000..0ea42354b6
--- /dev/null
+++ b/third_party/immer/extra/python/src/immer-boost.cpp
@@ -0,0 +1,80 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <boost/python.hpp>
+#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
+
+#include <immer/vector.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+template <typename Vector>
+struct immer_vector_indexing_suite : boost::python::vector_indexing_suite<
+    Vector, true, immer_vector_indexing_suite<Vector>>
+{
+    using value_t = typename Vector::value_type;
+    using index_t = typename Vector::size_type;
+    using object_t = boost::python::object;
+
+    static void forbidden() { throw std::runtime_error{"immutable!"}; }
+    static void todo() { throw std::runtime_error{"TODO!"}; }
+
+    static const value_t& get_item(const Vector& v, index_t i) { return v[i]; }
+    static object_t get_slice(const Vector&, index_t, index_t) { todo(); }
+
+    static void set_item(const Vector&, index_t, const value_t&) { forbidden(); }
+    static void delete_item(const Vector&, index_t) { forbidden(); }
+    static void set_slice(const Vector&, index_t, index_t, const value_t&) { forbidden(); }
+    static void delete_slice(const Vector&, index_t, index_t) { forbidden(); }
+    template <typename Iter>
+    static void set_slice(const Vector&, index_t, index_t, Iter, Iter) { forbidden(); }
+    template <class Iter>
+    static void extend(const Vector& container, Iter, Iter) { forbidden(); }
+};
+
+} // anonymous namespace
+
+
+BOOST_PYTHON_MODULE(immer_python_module)
+{
+    using namespace boost::python;
+
+    using vector_t = immer::vector<object, memory_t>;
+
+    class_<vector_t>("Vector")
+        .def(immer_vector_indexing_suite<vector_t>())
+        .def("append",
+             +[] (const vector_t& v, object x) {
+                 return v.push_back(std::move(x));
+              })
+        .def("set",
+             +[] (const vector_t& v, std::size_t i, object x) {
+                 return v.set(i, std::move(x));
+              })
+        ;
+}
diff --git a/third_party/immer/extra/python/src/immer-pybind.cpp b/third_party/immer/extra/python/src/immer-pybind.cpp
new file mode 100644
index 0000000000..8f8aab1231
--- /dev/null
+++ b/third_party/immer/extra/python/src/immer-pybind.cpp
@@ -0,0 +1,77 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <pybind11/pybind11.h>
+
+#include <immer/vector.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+} // anonymous namespace
+
+namespace py = pybind11;
+
+PYBIND11_PLUGIN(immer_python_module)
+{
+    py::module m("immer", R"pbdoc(
+        Immer
+        -----
+        .. currentmodule:: immer
+        .. autosummary::
+           :toctree: _generate
+           Vector
+    )pbdoc");
+
+    using vector_t = immer::vector<py::object, memory_t>;
+
+    py::class_<vector_t>(m, "Vector")
+        .def(py::init<>())
+        .def("__len__", &vector_t::size)
+        .def("__getitem__",
+             [] (const vector_t& v, std::size_t i) {
+                 if (i > v.size())
+                     throw py::index_error{"Index out of range"};
+                 return v[i];
+             })
+        .def("append",
+             [] (const vector_t& v, py::object x) {
+                 return v.push_back(std::move(x));
+             })
+        .def("set",
+             [] (const vector_t& v, std::size_t i, py::object x) {
+                 return v.set(i, std::move(x));
+             });
+
+#ifdef VERSION_INFO
+    m.attr("__version__") = py::str(VERSION_INFO);
+#else
+    m.attr("__version__") = py::str("dev");
+#endif
+
+    return m.ptr();
+}
diff --git a/third_party/immer/extra/python/src/immer-raw.cpp b/third_party/immer/extra/python/src/immer-raw.cpp
new file mode 100644
index 0000000000..283f973c8d
--- /dev/null
+++ b/third_party/immer/extra/python/src/immer-raw.cpp
@@ -0,0 +1,402 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+extern "C" {
+#include <Python.h>
+#include <structmember.h>
+}
+
+#include <immer/vector.hpp>
+#include <immer/algorithm.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#include <iostream>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+struct object_t
+{
+    struct wrap_t {};
+    struct adopt_t {};
+
+    PyObject* ptr_ = nullptr;
+
+    object_t() = delete;
+    ~object_t() { Py_XDECREF(ptr_); }
+
+    explicit object_t(PyObject* p, wrap_t)  : ptr_{p} {}
+    explicit object_t(PyObject* p, adopt_t) : ptr_{p}
+    {
+        assert(p);
+        Py_INCREF(p);
+    }
+
+    static object_t wrap(PyObject* p)  { return object_t{p, wrap_t{}}; }
+    static object_t adopt(PyObject* p) { return object_t{p, adopt_t{}}; }
+
+    object_t(const object_t& o) : ptr_(o.ptr_) { Py_INCREF(ptr_); }
+    object_t(object_t&& o) { std::swap(ptr_, o.ptr_); }
+
+    object_t& operator=(const object_t& o)
+    {
+        Py_XINCREF(o.ptr_);
+        Py_XDECREF(ptr_);
+        ptr_ = o.ptr_;
+        return *this;
+    }
+    object_t& operator=(object_t&& o)
+    {
+        std::swap(ptr_, o.ptr_);
+        return *this;
+    }
+
+    PyObject* release()
+    {
+        auto p = ptr_;
+        ptr_ = nullptr;
+        return p;
+    }
+
+    PyObject* get() const { return ptr_; }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+using vector_impl_t = immer::vector<object_t, memory_t>;
+
+struct vector_t
+{
+    PyObject_HEAD
+    vector_impl_t impl;
+    PyObject*     in_weakreflist;
+
+    static PyTypeObject type;
+};
+
+vector_t* empty_vector = nullptr;
+
+vector_t* make_vector()
+{
+    auto* v = PyObject_GC_New(vector_t, &vector_t::type);
+    new (&v->impl) vector_impl_t{};
+    v->in_weakreflist = nullptr;
+    PyObject_GC_Track((PyObject*)v);
+    return v;
+}
+
+vector_t* make_vector(vector_impl_t&& impl)
+{
+    auto v = PyObject_GC_New(vector_t, &vector_t::type);
+    new (&v->impl) vector_impl_t{std::move(impl)};
+    v->in_weakreflist = nullptr;
+    PyObject_GC_Track((PyObject*)v);
+    return v;
+}
+
+auto todo()
+{
+    PyErr_SetString(PyExc_RuntimeError, "immer: todo!");
+    return nullptr;
+}
+
+void vector_dealloc(vector_t* self)
+{
+    if (self->in_weakreflist != nullptr)
+        PyObject_ClearWeakRefs((PyObject*)self);
+
+    PyObject_GC_UnTrack((PyObject*)self);
+    Py_TRASHCAN_SAFE_BEGIN(self);
+
+    self->impl.~vector_impl_t();
+
+    PyObject_GC_Del(self);
+    Py_TRASHCAN_SAFE_END(self);
+}
+
+PyObject* vector_to_list(vector_t* self)
+{
+    auto list = PyList_New(self->impl.size());
+    auto idx  = 0;
+    immer::for_each(self->impl, [&] (auto&& obj) {
+            auto o = obj.get();
+            Py_INCREF(o);
+            PyList_SET_ITEM(list, idx, o);
+            ++idx;
+        });
+    return list;
+}
+
+PyObject* vector_repr(vector_t *self)
+{
+    auto list = vector_to_list(self);
+    auto list_repr = PyObject_Repr(list);
+    Py_DECREF(list);
+
+    if (!list_repr) return nullptr;
+
+#if PY_MAJOR_VERSION >= 3
+    auto s = PyUnicode_FromFormat("%s%U%s", "immer.vector(", list_repr, ")");
+    Py_DECREF(list_repr);
+#else
+    auto s = PyString_FromString("immer.vector(");
+    PyString_ConcatAndDel(&s, list_repr);
+    PyString_ConcatAndDel(&s, PyString_FromString(")"));
+#endif
+    return s;
+}
+
+Py_ssize_t vector_len(vector_t* self)
+{
+    return self->impl.size();
+}
+
+PyObject* vector_extend(vector_t* self, PyObject* iterable)
+{
+    return todo();
+}
+
+vector_t* vector_append(vector_t* self, PyObject *obj)
+{
+    assert(obj != nullptr);
+    return make_vector(self->impl.push_back(object_t::adopt(obj)));
+}
+
+vector_t* vector_set(vector_t* self, PyObject* args)
+{
+    PyObject* obj = nullptr;
+    Py_ssize_t pos;
+    // the n parses for size, the O parses for a Python object
+    if(!PyArg_ParseTuple(args, "nO", &pos, &obj)) {
+        return nullptr;
+    }
+    if (pos < 0)
+        pos += self->impl.size();
+    if (pos < 0 || pos > (Py_ssize_t)self->impl.size()) {
+        PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
+        return nullptr;
+    }
+    return make_vector(self->impl.set(pos, object_t::adopt(obj)));
+}
+
+PyObject* vector_new(PyTypeObject* subtype, PyObject *args, PyObject *kwds)
+{
+    Py_INCREF(empty_vector);
+    return (PyObject*)empty_vector;
+}
+
+long vector_hash(vector_t* self)
+{
+    todo();
+    return 0;
+}
+
+PyObject* vector_get_item(vector_t* self, Py_ssize_t pos)
+{
+    if (pos < 0)
+        pos += self->impl.size();
+    if (pos < 0 || pos >= (Py_ssize_t)self->impl.size()) {
+        PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
+        return nullptr;
+    }
+    auto r = self->impl[pos];
+    return r.release();
+}
+
+PyObject* vector_subscript(vector_t* self, PyObject* item)
+{
+    if (PyIndex_Check(item)) {
+        auto i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+        if (i == -1 && PyErr_Occurred())
+            return nullptr;
+        return vector_get_item(self, i);
+    } else if (PySlice_Check(item)) {
+        return todo();
+    } else {
+        PyErr_Format(PyExc_TypeError,
+                     "vector indices must be integers, not %.200s",
+                     Py_TYPE(item)->tp_name);
+        return nullptr;
+    }
+}
+
+PyObject* vector_repeat(vector_t* self, Py_ssize_t n)
+{
+    return todo();
+}
+
+int vector_traverse(vector_t* self, visitproc visit, void* arg)
+{
+    auto result = 0;
+    immer::all_of(self->impl, [&] (auto&& o) {
+            return 0 == (result = [&] {
+                Py_VISIT(o.get());
+                return 0;
+            }());
+        });
+    return result;
+}
+
+PyObject* vector_richcompare(PyObject* v, PyObject* w, int op)
+{
+    return todo();
+}
+
+PyObject* vector_iter(PyObject* self)
+{
+    return todo();
+}
+
+PyMethodDef vector_methods[] =
+{
+    {"append",      (PyCFunction)vector_append, METH_O, "Appends an element"},
+    {"set",         (PyCFunction)vector_set, METH_VARARGS, "Inserts an element at the specified position"},
+    {"extend",      (PyCFunction)vector_extend, METH_O|METH_COEXIST, "Extend"},
+    {"tolist",      (PyCFunction)vector_to_list, METH_NOARGS, "Convert to list"},
+    {0}
+};
+
+PyMemberDef vector_members[] =
+{
+    {0}  /* sentinel */
+};
+
+PySequenceMethods vector_sequence_methods =
+{
+    (lenfunc)vector_len,             /* sq_length */
+    (binaryfunc)vector_extend,       /* sq_concat */
+    (ssizeargfunc)vector_repeat,     /* sq_repeat */
+    (ssizeargfunc)vector_get_item,   /* sq_item */
+    0,                               /* sq_slice */
+    0,                               /* sq_ass_item */
+    0,                               /* sq_ass_slice */
+    0,                               /* sq_contains */
+    0,                               /* sq_inplace_concat */
+    0,                               /* sq_inplace_repeat */
+};
+
+PyMappingMethods vector_mapping_methods =
+{
+    (lenfunc)vector_len,
+    (binaryfunc)vector_subscript,
+    0
+};
+
+PyTypeObject vector_t::type =
+{
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "immer.Vector",                             /* tp_name        */
+    sizeof(vector_t),                           /* tp_basicsize   */
+    0,		                                /* tp_itemsize    */
+    (destructor)vector_dealloc,                 /* tp_dealloc     */
+    0,                                          /* tp_print       */
+    0,                                          /* tp_getattr     */
+    0,                                          /* tp_setattr     */
+    0,                                          /* tp_compare     */
+    (reprfunc)vector_repr,                      /* tp_repr        */
+    0,                                          /* tp_as_number   */
+    &vector_sequence_methods,                   /* tp_as_sequence */
+    &vector_mapping_methods,                    /* tp_as_mapping  */
+    (hashfunc)vector_hash,                      /* tp_hash        */
+    0,                                          /* tp_call        */
+    0,                                          /* tp_str         */
+    0,                                          /* tp_getattro    */
+    0,                                          /* tp_setattro    */
+    0,                                          /* tp_as_buffer   */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags       */
+    "",                                         /* tp_doc         */
+    (traverseproc)vector_traverse,              /* tp_traverse       */
+    0,                                          /* tp_clear          */
+    vector_richcompare,                         /* tp_richcompare    */
+    offsetof(vector_t, in_weakreflist),         /* tp_weaklistoffset */
+    vector_iter,                                /* tp_iter           */
+    0,                                          /* tp_iternext       */
+    vector_methods,                             /* tp_methods        */
+    vector_members,                             /* tp_members        */
+    0,                                          /* tp_getset         */
+    0,                                          /* tp_base           */
+    0,                                          /* tp_dict           */
+    0,                                          /* tp_descr_get      */
+    0,                                          /* tp_descr_set      */
+    0,                                          /* tp_dictoffset     */
+    0,                                          /* tp_init           */
+    0,                                          /* tp_alloc */
+    vector_new,                                 /* tp_new   */
+};
+
+#if PY_MAJOR_VERSION >= 3
+struct PyModuleDef module_def =
+{
+    PyModuleDef_HEAD_INIT,
+    "immer_python_module", /* m_name */
+    "",                  /* m_doc */
+    -1,                  /* m_size */
+    /module_methods,     /* m_methods */
+    0,                   /* m_reload */
+    0,                   /* m_traverse */
+    0,                   /* m_clear */
+    0,                   /* m_free */
+};
+#endif
+
+PyMethodDef module_methods[] = {
+    {0, 0, 0, 0}
+};
+
+PyObject* module_init()
+{
+    if (PyType_Ready(&vector_t::type) < 0)
+        return nullptr;
+
+#if PY_MAJOR_VERSION >= 3
+    auto m = PyModule_Create(&module_def);
+#else
+    auto m = Py_InitModule3("immer_python_module", module_methods, "");
+#endif
+    if (!m)
+        return nullptr;
+
+    if (!empty_vector)
+        empty_vector = make_vector();
+
+    Py_INCREF(&vector_t::type);
+    PyModule_AddObject(m, "Vector", (PyObject*) &vector_t::type);
+    return m;
+}
+
+} // anonymous namespace
+
+extern "C" {
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC PyInit_immer_python_module()
+{
+    return module_init();
+}
+#else
+PyMODINIT_FUNC initimmer_python_module()
+{
+    module_init();
+}
+#endif
+}
diff --git a/third_party/immer/immer/algorithm.hpp b/third_party/immer/immer/algorithm.hpp
new file mode 100644
index 0000000000..ecdc417e2b
--- /dev/null
+++ b/third_party/immer/immer/algorithm.hpp
@@ -0,0 +1,212 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <algorithm>
+#include <numeric>
+#include <type_traits>
+
+namespace immer {
+
+/**
+ * @defgroup algorithm
+ * @{
+ */
+
+/*@{*/
+// Right now these algorithms dispatch directly to the vector
+// implementations unconditionally.  This will be changed in the
+// future to support other kinds of containers.
+
+/*!
+ * Apply operation `fn` for every contiguous *chunk* of data in the
+ * range sequentially.  Each time, `Fn` is passed two `value_type`
+ * pointers describing a range over a part of the vector.  This allows
+ * iterating over the elements in the most efficient way.
+ *
+ * @rst
+ *
+ * .. tip:: This is a low level method. Most of the time, :doc:`other
+ *    wrapper algorithms <algorithms>` should be used instead.
+ *
+ * @endrst
+ */
+template <typename Range, typename Fn>
+void for_each_chunk(const Range& r, Fn&& fn)
+{
+    r.impl().for_each_chunk(std::forward<Fn>(fn));
+}
+
+template <typename Iterator, typename Fn>
+void for_each_chunk(const Iterator& first, const Iterator& last, Fn&& fn)
+{
+    assert(&first.impl() == &last.impl());
+    first.impl().for_each_chunk(
+        first.index(), last.index(), std::forward<Fn>(fn));
+}
+
+template <typename T, typename Fn>
+void for_each_chunk(const T* first, const T* last, Fn&& fn)
+{
+    std::forward<Fn>(fn)(first, last);
+}
+
+/*!
+ * Apply operation `fn` for every contiguous *chunk* of data in the
+ * range sequentially, until `fn` returns `false`.  Each time, `Fn` is
+ * passed two `value_type` pointers describing a range over a part of
+ * the vector.  This allows iterating over the elements in the most
+ * efficient way.
+ *
+ * @rst
+ *
+ * .. tip:: This is a low level method. Most of the time, :doc:`other
+ *    wrapper algorithms <algorithms>` should be used instead.
+ *
+ * @endrst
+ */
+template <typename Range, typename Fn>
+bool for_each_chunk_p(const Range& r, Fn&& fn)
+{
+    return r.impl().for_each_chunk_p(std::forward<Fn>(fn));
+}
+
+template <typename Iterator, typename Fn>
+bool for_each_chunk_p(const Iterator& first, const Iterator& last, Fn&& fn)
+{
+    assert(&first.impl() == &last.impl());
+    return first.impl().for_each_chunk_p(
+        first.index(), last.index(), std::forward<Fn>(fn));
+}
+
+template <typename T, typename Fn>
+bool for_each_chunk_p(const T* first, const T* last, Fn&& fn)
+{
+    return std::forward<Fn>(fn)(first, last);
+}
+
+/*!
+ * Equivalent of `std::accumulate` applied to the range `r`.
+ */
+template <typename Range, typename T>
+T accumulate(Range&& r, T init)
+{
+    for_each_chunk(r, [&](auto first, auto last) {
+        init = std::accumulate(first, last, init);
+    });
+    return init;
+}
+
+template <typename Range, typename T, typename Fn>
+T accumulate(Range&& r, T init, Fn fn)
+{
+    for_each_chunk(r, [&](auto first, auto last) {
+        init = std::accumulate(first, last, init, fn);
+    });
+    return init;
+}
+
+/*!
+ * Equivalent of `std::accumulate` applied to the range @f$ [first,
+ * last) @f$.
+ */
+template <typename Iterator, typename T>
+T accumulate(Iterator first, Iterator last, T init)
+{
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        init = std::accumulate(first, last, init);
+    });
+    return init;
+}
+
+template <typename Iterator, typename T, typename Fn>
+T accumulate(Iterator first, Iterator last, T init, Fn fn)
+{
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        init = std::accumulate(first, last, init, fn);
+    });
+    return init;
+}
+
+/*!
+ * Equivalent of `std::for_each` applied to the range `r`.
+ */
+template <typename Range, typename Fn>
+Fn&& for_each(Range&& r, Fn&& fn)
+{
+    for_each_chunk(r, [&](auto first, auto last) {
+        for (; first != last; ++first)
+            fn(*first);
+    });
+    return std::forward<Fn>(fn);
+}
+
+/*!
+ * Equivalent of `std::for_each` applied to the range @f$ [first,
+ * last) @f$.
+ */
+template <typename Iterator, typename Fn>
+Fn&& for_each(Iterator first, Iterator last, Fn&& fn)
+{
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        for (; first != last; ++first)
+            fn(*first);
+    });
+    return std::forward<Fn>(fn);
+}
+
+/*!
+ * Equivalent of `std::copy` applied to the range `r`.
+ */
+template <typename Range, typename OutIter>
+OutIter copy(Range&& r, OutIter out)
+{
+    for_each_chunk(
+        r, [&](auto first, auto last) { out = std::copy(first, last, out); });
+    return out;
+}
+
+/*!
+ * Equivalent of `std::copy` applied to the range @f$ [first,
+ * last) @f$.
+ */
+template <typename InIter, typename OutIter>
+OutIter copy(InIter first, InIter last, OutIter out)
+{
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        out = std::copy(first, last, out);
+    });
+    return out;
+}
+
+/*!
+ * Equivalent of `std::all_of` applied to the range `r`.
+ */
+template <typename Range, typename Pred>
+bool all_of(Range&& r, Pred p)
+{
+    return for_each_chunk_p(
+        r, [&](auto first, auto last) { return std::all_of(first, last, p); });
+}
+
+/*!
+ * Equivalent of `std::all_of` applied to the range @f$ [first, last)
+ * @f$.
+ */
+template <typename Iter, typename Pred>
+bool all_of(Iter first, Iter last, Pred p)
+{
+    return for_each_chunk_p(first, last, [&](auto first, auto last) {
+        return std::all_of(first, last, p);
+    });
+}
+
+/** @} */ // group: algorithm
+
+} // namespace immer
diff --git a/third_party/immer/immer/array.hpp b/third_party/immer/immer/array.hpp
new file mode 100644
index 0000000000..24c8f84c8b
--- /dev/null
+++ b/third_party/immer/immer/array.hpp
@@ -0,0 +1,364 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/arrays/with_capacity.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T, typename MemoryPolicy>
+class array_transient;
+
+/*!
+ * Immutable container that stores a sequence of elements in
+ * contiguous memory.
+ *
+ * @tparam T The type of the values to be stored in the container.
+ *
+ * @rst
+ *
+ * It supports the most efficient iteration and random access,
+ * equivalent to a ``std::vector`` or ``std::array``, but all
+ * manipulations are :math:`O(size)`.
+ *
+ * .. tip:: Don't be fooled by the bad complexity of this data
+ *    structure.  It is a great choice for short sequence or when it
+ *    is seldom or never changed.  This depends on the ``sizeof(T)``
+ *    and the expensiveness of its ``T``'s copy constructor, in case
+ *    of doubt, measure.  For basic types, using an `array` when
+ *    :math:`n < 100` is a good heuristic.
+ *
+ * @endrst
+ */
+template <typename T, typename MemoryPolicy = default_memory_policy>
+class array
+{
+    using impl_t =
+        std::conditional_t<MemoryPolicy::use_transient_rvalues,
+                           detail::arrays::with_capacity<T, MemoryPolicy>,
+                           detail::arrays::no_capacity<T, MemoryPolicy>>;
+
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
+public:
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator         = const T*;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using memory_policy  = MemoryPolicy;
+    using transient_type = array_transient<T, MemoryPolicy>;
+
+    /*!
+     * Default constructor.  It creates an array of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    array() = default;
+
+    /*!
+     * Constructs an array containing the elements in `values`.
+     */
+    array(std::initializer_list<T> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
+
+    /*!
+     * Constructs a array containing the elements in the range
+     * defined by the forward iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent> &&
+                                   detail::is_forward_iterator_v<Iter>,
+                               bool> = true>
+    array(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
+
+    /*!
+     * Constructs a array containing the element `val` repeated `n`
+     * times.
+     */
+    array(size_type n, T v = {})
+        : impl_{impl_t::from_fill(n, v)}
+    {}
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return impl_.data(); }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD std::size_t size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Access the raw data.
+     */
+    IMMER_NODISCARD const T* data() const { return impl_.data(); }
+
+    /*!
+     * Access the last element.
+     */
+    IMMER_NODISCARD const T& back() const { return data()[size() - 1]; }
+
+    /*!
+     * Access the first element.
+     */
+    IMMER_NODISCARD const T& front() const { return data()[0]; }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Returns whether the vectors are equal.
+     */
+    IMMER_NODISCARD bool operator==(const array& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const array& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns an array with `value` inserted at the end.  It may
+     * allocate memory and its complexity is @f$ O(size) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/array/array.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: push-back/start
+     *      :end-before:  push-back/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD array push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
+
+    /*!
+     * Returns an array containing value `value` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is @f$ O(size) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/array/array.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: set/start
+     *      :end-before:  set/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD array set(std::size_t index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
+
+    /*!
+     * Returns an array containing the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is @f$ O(size) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/array/array.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: update/start
+     *      :end-before:  update/end
+     *
+     * @endrst
+     */
+    template <typename FnT>
+    IMMER_NODISCARD array update(std::size_t index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
+
+    template <typename FnT>
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Returns a array containing only the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/array/array.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: take/start
+     *      :end-before:  take/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD array take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
+
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
+
+    /*!
+     * Returns an @a transient form of this container, an
+     * `immer::array_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+private:
+    friend transient_type;
+
+    array(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    array&& push_back_move(std::true_type, value_type value)
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    array push_back_move(std::false_type, value_type value)
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    array&& set_move(std::true_type, size_type index, value_type value)
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
+    array set_move(std::false_type, size_type index, value_type value)
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    template <typename Fn>
+    array&& update_move(std::true_type, size_type index, Fn&& fn)
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    array update_move(std::false_type, size_type index, Fn&& fn)
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
+
+    array&& take_move(std::true_type, size_type elems)
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
+    array take_move(std::false_type, size_type elems)
+    {
+        return impl_.take(elems);
+    }
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} /* namespace immer */
diff --git a/third_party/immer/immer/array_transient.hpp b/third_party/immer/immer/array_transient.hpp
new file mode 100644
index 0000000000..bc2d1a5b7b
--- /dev/null
+++ b/third_party/immer/immer/array_transient.hpp
@@ -0,0 +1,202 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/arrays/with_capacity.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T, typename MemoryPolicy>
+class array;
+
+/*!
+ * Mutable version of `immer::array`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
+ */
+template <typename T, typename MemoryPolicy = default_memory_policy>
+class array_transient : MemoryPolicy::transience_t::owner
+{
+    using impl_t             = detail::arrays::with_capacity<T, MemoryPolicy>;
+    using impl_no_capacity_t = detail::arrays::no_capacity<T, MemoryPolicy>;
+    using owner_t            = typename MemoryPolicy::transience_t::owner;
+
+public:
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator         = const T*;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using memory_policy   = MemoryPolicy;
+    using persistent_type = array<T, MemoryPolicy>;
+
+    /*!
+     * Default constructor.  It creates a mutable array of `size() ==
+     * 0`.  It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    array_transient() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return impl_.data(); }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD std::size_t size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Access the raw data.
+     */
+    IMMER_NODISCARD const T* data() const { return impl_.data(); }
+
+    /*!
+     * Provide mutable access to the raw underlaying data.
+     */
+    IMMER_NODISCARD T* data_mut() { return impl_.data_mut(*this); }
+
+    /*!
+     * Access the last element.
+     */
+    IMMER_NODISCARD const T& back() const { return data()[size() - 1]; }
+
+    /*!
+     * Access the first element.
+     */
+    IMMER_NODISCARD const T& front() const { return data()[0]; }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    reference operator[](size_type index) const { return impl_.get(index); }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Inserts `value` at the end.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    void push_back(value_type value)
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
+
+    /*!
+     * Sets to the value `value` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void set(size_type index, value_type value)
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
+
+    /*!
+     * Updates the array to contain the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `0 >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    template <typename FnT>
+    void update(size_type index, FnT&& fn)
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Resizes the array to only contain the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::array`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return persistent_type{impl_};
+    }
+    IMMER_NODISCARD persistent_type persistent() &&
+    {
+        return persistent_type{std::move(impl_)};
+    }
+
+private:
+    friend persistent_type;
+
+    array_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/atom.hpp b/third_party/immer/immer/atom.hpp
new file mode 100644
index 0000000000..f3ebb5aa1c
--- /dev/null
+++ b/third_party/immer/immer/atom.hpp
@@ -0,0 +1,254 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/box.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <atomic>
+#include <type_traits>
+
+namespace immer {
+
+namespace detail {
+
+template <typename T, typename MemoryPolicy>
+struct refcount_atom_impl
+{
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
+    using memory_policy = MemoryPolicy;
+    using spinlock_t    = typename MemoryPolicy::refcount::spinlock_type;
+    using scoped_lock_t = typename spinlock_t::scoped_lock;
+
+    refcount_atom_impl(const refcount_atom_impl&) = delete;
+    refcount_atom_impl(refcount_atom_impl&&)      = delete;
+    refcount_atom_impl& operator=(const refcount_atom_impl&) = delete;
+    refcount_atom_impl& operator=(refcount_atom_impl&&) = delete;
+
+    refcount_atom_impl(box_type b)
+        : impl_{std::move(b)}
+    {}
+
+    box_type load() const
+    {
+        scoped_lock_t lock{lock_};
+        return impl_;
+    }
+
+    void store(box_type b)
+    {
+        scoped_lock_t lock{lock_};
+        impl_ = std::move(b);
+    }
+
+    box_type exchange(box_type b)
+    {
+        {
+            scoped_lock_t lock{lock_};
+            swap(b, impl_);
+        }
+        return b;
+    }
+
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    {
+        while (true) {
+            auto oldv = load();
+            auto newv = oldv.update(fn);
+            {
+                scoped_lock_t lock{lock_};
+                if (oldv.impl_ == impl_.impl_) {
+                    impl_ = newv;
+                    return {newv};
+                }
+            }
+        }
+    }
+
+private:
+    mutable spinlock_t lock_;
+    box_type impl_;
+};
+
+template <typename T, typename MemoryPolicy>
+struct gc_atom_impl
+{
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
+    using memory_policy = MemoryPolicy;
+
+    static_assert(std::is_same<typename MemoryPolicy::refcount,
+                               no_refcount_policy>::value,
+                  "gc_atom_impl can only be used when there is no refcount!");
+
+    gc_atom_impl(const gc_atom_impl&) = delete;
+    gc_atom_impl(gc_atom_impl&&)      = delete;
+    gc_atom_impl& operator=(const gc_atom_impl&) = delete;
+    gc_atom_impl& operator=(gc_atom_impl&&) = delete;
+
+    gc_atom_impl(box_type b)
+        : impl_{b.impl_}
+    {}
+
+    box_type load() const { return {impl_.load()}; }
+
+    void store(box_type b) { impl_.store(b.impl_); }
+
+    box_type exchange(box_type b) { return {impl_.exchange(b.impl_)}; }
+
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    {
+        while (true) {
+            auto oldv = box_type{impl_.load()};
+            auto newv = oldv.update(fn);
+            if (impl_.compare_exchange_weak(oldv.impl_, newv.impl_))
+                return {newv};
+        }
+    }
+
+private:
+    std::atomic<typename box_type::holder*> impl_;
+};
+
+} // namespace detail
+
+/*!
+ * Stores for boxed values of type `T` in a thread-safe manner.
+ *
+ * @see box
+ *
+ * @rst
+ *
+ * .. warning:: If memory policy used includes thread unsafe reference counting,
+ *    no no thread safety is assumed, and the atom becomes thread unsafe too!
+ *
+ * .. note:: ``box<T>`` provides a value based box of type ``T``, this is, we
+ * can think about it as a value-based version of ``std::shared_ptr``.  In a
+ *    similar fashion, ``atom<T>`` is in spirit the value-based equivalent of
+ *    C++20 ``std::atomic_shared_ptr``.  However, the API does not follow
+ *    ``std::atomic`` interface closely, since it attempts to be a higher level
+ *    construction, most similar to Clojure's ``(atom)``.  It is remarkable in
+ *    particular that, since ``box<T>`` underlying object is immutable, using
+ *    ``atom<T>`` is fully thread-safe in ways that ``std::atmic_shared_ptr`` is
+ *    not. This is so because dereferencing the underlying pointer in a
+ *    ``std::atomic_share_ptr`` may require further synchronization, in
+ * particular when invoking non-const methods.
+ *
+ * @endrst
+ */
+template <typename T, typename MemoryPolicy = default_memory_policy>
+class atom
+{
+public:
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
+    using memory_policy = MemoryPolicy;
+
+    atom(const atom&) = delete;
+    atom(atom&&)      = delete;
+    void operator=(const atom&) = delete;
+    void operator=(atom&&) = delete;
+
+    /*!
+     * Constructs an atom holding a value `b`;
+     */
+    atom(box_type v = {})
+        : impl_{std::move(v)}
+    {}
+
+    /*!
+     * Sets a new value in the atom.
+     */
+    atom& operator=(box_type b)
+    {
+        impl_.store(std::move(b));
+        return *this;
+    }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    operator box_type() const { return impl_.load(); }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    operator value_type() const { return *impl_.load(); }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    IMMER_NODISCARD box_type load() const { return impl_.load(); }
+
+    /*!
+     * Stores a new value in a thread-safe manner.
+     */
+    void store(box_type b) { impl_.store(std::move(b)); }
+
+    /*!
+     * Stores a new value and returns the old value, in a thread-safe manner.
+     */
+    IMMER_NODISCARD box_type exchange(box_type b)
+    {
+        return impl_.exchange(std::move(b));
+    }
+
+    /*!
+     * Stores the result of applying `fn` to the current value atomically and
+     * returns the new resulting value.
+     *
+     * @rst
+     *
+     * .. warning:: ``fn`` must be a pure function and have no side effects! The
+     *    function might be evaluated multiple times when multiple threads
+     *    content to update the value.
+     *
+     * @endrst
+     */
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    {
+        return impl_.update(std::forward<Fn>(fn));
+    }
+
+private:
+    struct get_refcount_atom_impl
+    {
+        template <typename U, typename MP>
+        struct apply
+        {
+            using type = detail::refcount_atom_impl<U, MP>;
+        };
+    };
+
+    struct get_gc_atom_impl
+    {
+        template <typename U, typename MP>
+        struct apply
+        {
+            using type = detail::gc_atom_impl<U, MP>;
+        };
+    };
+
+    // If we are using "real" garbage collection (we assume this when we use
+    // `no_refcount_policy`), we just store the pointer in an atomic.  If we use
+    // reference counting, we rely on the reference counting spinlock.
+    using impl_t = typename std::conditional_t<
+        std::is_same<typename MemoryPolicy::refcount,
+                     no_refcount_policy>::value,
+        get_gc_atom_impl,
+        get_refcount_atom_impl>::template apply<T, MemoryPolicy>::type;
+
+    impl_t impl_;
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/box.hpp b/third_party/immer/immer/box.hpp
new file mode 100644
index 0000000000..0fd2961272
--- /dev/null
+++ b/third_party/immer/immer/box.hpp
@@ -0,0 +1,194 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/util.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+namespace detail {
+
+template <typename U, typename MP>
+struct gc_atom_impl;
+
+template <typename U, typename MP>
+struct refcount_atom_impl;
+
+} // namespace detail
+
+/*!
+ * Immutable box for a single value of type `T`.
+ *
+ * The box is always copiable and movable. The `T` copy or move
+ * operations are never called.  Since a box is immutable, copying or
+ * moving just copy the underlying pointers.
+ */
+template <typename T, typename MemoryPolicy = default_memory_policy>
+class box
+{
+    friend struct detail::gc_atom_impl<T, MemoryPolicy>;
+    friend struct detail::refcount_atom_impl<T, MemoryPolicy>;
+
+    struct holder : MemoryPolicy::refcount
+    {
+        T value;
+
+        template <typename... Args>
+        holder(Args&&... args)
+            : value{std::forward<Args>(args)...}
+        {}
+    };
+
+    using heap = typename MemoryPolicy::heap::type;
+
+    holder* impl_ = nullptr;
+
+    box(holder* impl)
+        : impl_{impl}
+    {}
+
+public:
+    using value_type    = T;
+    using memory_policy = MemoryPolicy;
+
+    /*!
+     * Constructs a box holding `T{}`.
+     */
+    box()
+        : impl_{detail::make<heap, holder>()}
+    {}
+
+    /*!
+     * Constructs a box holding `T{arg}`
+     */
+    template <typename Arg,
+              typename Enable = std::enable_if_t<
+                  !std::is_same<box, std::decay_t<Arg>>::value &&
+                  std::is_constructible<T, Arg>::value>>
+    box(Arg&& arg)
+        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
+    {}
+
+    /*!
+     * Constructs a box holding `T{arg1, arg2, args...}`
+     */
+    template <typename Arg1, typename Arg2, typename... Args>
+    box(Arg1&& arg1, Arg2&& arg2, Args&&... args)
+        : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1),
+                                           std::forward<Arg2>(arg2),
+                                           std::forward<Args>(args)...)}
+    {}
+
+    friend void swap(box& a, box& b)
+    {
+        using std::swap;
+        swap(a.impl_, b.impl_);
+    }
+
+    box(box&& other) { swap(*this, other); }
+    box(const box& other)
+        : impl_(other.impl_)
+    {
+        impl_->inc();
+    }
+    box& operator=(box&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+    box& operator=(const box& other)
+    {
+        auto aux = other;
+        swap(*this, aux);
+        return *this;
+    }
+    ~box()
+    {
+        if (impl_ && impl_->dec()) {
+            impl_->~holder();
+            heap::deallocate(sizeof(holder), impl_);
+        }
+    }
+
+    /*! Query the current value. */
+    IMMER_NODISCARD const T& get() const { return impl_->value; }
+
+    /*! Conversion to the boxed type. */
+    operator const T&() const { return get(); }
+
+    /*! Access via dereference */
+    const T& operator*() const { return get(); }
+
+    /*! Access via pointer member access */
+    const T* operator->() const { return &get(); }
+
+    /*! Comparison. */
+    IMMER_NODISCARD bool operator==(detail::exact_t<const box&> other) const
+    {
+        return impl_ == other.value.impl_ || get() == other.value.get();
+    }
+    // Note that the `exact_t` disambiguates comparisons against `T{}`
+    // directly.  In that case we want to use `operator T&` and
+    // compare directly.  We definitely never want to convert a value
+    // to a box (which causes an allocation) just to compare it.
+    IMMER_NODISCARD bool operator!=(detail::exact_t<const box&> other) const
+    {
+        return !(*this == other.value);
+    }
+    IMMER_NODISCARD bool operator<(detail::exact_t<const box&> other) const
+    {
+        return get() < other.value.get();
+    }
+
+    /*!
+     * Returns a new box built by applying the `fn` to the underlying
+     * value.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/box/box.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: update/start
+     *      :end-before:  update/end
+     *
+     * @endrst
+     */
+    template <typename Fn>
+    IMMER_NODISCARD box update(Fn&& fn) const&
+    {
+        return std::forward<Fn>(fn)(get());
+    }
+    template <typename Fn>
+    IMMER_NODISCARD box&& update(Fn&& fn) &&
+    {
+        if (impl_->unique())
+            impl_->value = std::forward<Fn>(fn)(std::move(impl_->value));
+        else
+            *this = std::forward<Fn>(fn)(impl_->value);
+        return std::move(*this);
+    }
+};
+
+} // namespace immer
+
+namespace std {
+
+template <typename T, typename MP>
+struct hash<immer::box<T, MP>>
+{
+    std::size_t operator()(const immer::box<T, MP>& x) const
+    {
+        return std::hash<T>{}(*x);
+    }
+};
+
+} // namespace std
diff --git a/third_party/immer/immer/config.hpp b/third_party/immer/immer/config.hpp
new file mode 100644
index 0000000000..581e905a4d
--- /dev/null
+++ b/third_party/immer/immer/config.hpp
@@ -0,0 +1,93 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#if defined(__has_cpp_attribute)
+#if __has_cpp_attribute(nodiscard)
+#define IMMER_NODISCARD [[nodiscard]]
+#endif
+#else
+#if _MSVC_LANG >= 201703L
+#define IMMER_NODISCARD [[nodiscard]]
+#endif
+#endif
+
+#ifndef IMMER_NODISCARD
+#define IMMER_NODISCARD
+#endif
+
+#ifndef IMMER_TAGGED_NODE
+#ifdef NDEBUG
+#define IMMER_TAGGED_NODE 0
+#else
+#define IMMER_TAGGED_NODE 1
+#endif
+#endif
+
+#if IMMER_TAGGED_NODE
+#define IMMER_ASSERT_TAGGED(assertion) assert(assertion)
+#else
+#define IMMER_ASSERT_TAGGED(assertion)
+#endif
+
+#ifndef IMMER_DEBUG_TRACES
+#define IMMER_DEBUG_TRACES 0
+#endif
+
+#ifndef IMMER_DEBUG_PRINT
+#define IMMER_DEBUG_PRINT 0
+#endif
+
+#ifndef IMMER_DEBUG_DEEP_CHECK
+#define IMMER_DEBUG_DEEP_CHECK 0
+#endif
+
+#if IMMER_DEBUG_TRACES || IMMER_DEBUG_PRINT
+#include <iostream>
+#include <prettyprint.hpp>
+#endif
+
+#if IMMER_DEBUG_TRACES
+#define IMMER_TRACE(...) std::cout << __VA_ARGS__ << std::endl
+#else
+#define IMMER_TRACE(...)
+#endif
+#define IMMER_TRACE_F(...)                                                     \
+    IMMER_TRACE(__FILE__ << ":" << __LINE__ << ": " << __VA_ARGS__)
+#define IMMER_TRACE_E(expr) IMMER_TRACE("    " << #expr << " = " << (expr))
+
+#if defined(_MSC_VER)
+#define IMMER_UNREACHABLE __assume(false)
+#define IMMER_LIKELY(cond) cond
+#define IMMER_UNLIKELY(cond) cond
+#define IMMER_FORCEINLINE __forceinline
+#define IMMER_PREFETCH(p)
+#else
+#define IMMER_UNREACHABLE __builtin_unreachable()
+#define IMMER_LIKELY(cond) __builtin_expect(!!(cond), 1)
+#define IMMER_UNLIKELY(cond) __builtin_expect(!!(cond), 0)
+#define IMMER_FORCEINLINE inline __attribute__((always_inline))
+#define IMMER_PREFETCH(p)
+// #define IMMER_PREFETCH(p)    __builtin_prefetch(p)
+#endif
+
+#define IMMER_DESCENT_DEEP 0
+
+#ifdef NDEBUG
+#define IMMER_ENABLE_DEBUG_SIZE_HEAP 0
+#else
+#define IMMER_ENABLE_DEBUG_SIZE_HEAP 1
+#endif
+
+namespace immer {
+
+const auto default_bits           = 5;
+const auto default_free_list_size = 1 << 10;
+
+} // namespace immer
diff --git a/third_party/immer/immer/detail/arrays/no_capacity.hpp b/third_party/immer/immer/detail/arrays/no_capacity.hpp
new file mode 100644
index 0000000000..9cb561e14b
--- /dev/null
+++ b/third_party/immer/immer/detail/arrays/no_capacity.hpp
@@ -0,0 +1,203 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/algorithm.hpp>
+#include <immer/detail/arrays/node.hpp>
+
+namespace immer {
+namespace detail {
+namespace arrays {
+
+template <typename T, typename MemoryPolicy>
+struct no_capacity
+{
+    using node_t = node<T, MemoryPolicy>;
+    using edit_t = typename MemoryPolicy::transience_t::edit;
+    using size_t = std::size_t;
+
+    node_t* ptr;
+    size_t size;
+
+    static const no_capacity& empty()
+    {
+        static const no_capacity empty_{
+            node_t::make_n(0),
+            0,
+        };
+        return empty_;
+    }
+
+    no_capacity(node_t* p, size_t s)
+        : ptr{p}
+        , size{s}
+    {}
+
+    no_capacity(const no_capacity& other)
+        : no_capacity{other.ptr, other.size}
+    {
+        inc();
+    }
+
+    no_capacity(no_capacity&& other)
+        : no_capacity{empty()}
+    {
+        swap(*this, other);
+    }
+
+    no_capacity& operator=(const no_capacity& other)
+    {
+        auto next = other;
+        swap(*this, next);
+        return *this;
+    }
+
+    no_capacity& operator=(no_capacity&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+
+    friend void swap(no_capacity& x, no_capacity& y)
+    {
+        using std::swap;
+        swap(x.ptr, y.ptr);
+        swap(x.size, y.size);
+    }
+
+    ~no_capacity() { dec(); }
+
+    void inc()
+    {
+        using immer::detail::get;
+        ptr->refs().inc();
+    }
+
+    void dec()
+    {
+        using immer::detail::get;
+        if (ptr->refs().dec())
+            node_t::delete_n(ptr, size, size);
+    }
+
+    T* data() { return ptr->data(); }
+    const T* data() const { return ptr->data(); }
+
+    T* data_mut(edit_t e)
+    {
+        if (!ptr->can_mutate(e))
+            ptr = node_t::copy_e(e, size, ptr, size);
+        return data();
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<is_forward_iterator_v<Iter> &&
+                                   compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    static no_capacity from_range(Iter first, Sent last)
+    {
+        auto count = static_cast<size_t>(distance(first, last));
+        if (count == 0)
+            return empty();
+        else
+            return {
+                node_t::copy_n(count, first, last),
+                count,
+            };
+    }
+
+    static no_capacity from_fill(size_t n, T v)
+    {
+        return {node_t::fill_n(n, v), n};
+    }
+
+    template <typename U>
+    static no_capacity from_initializer_list(std::initializer_list<U> values)
+    {
+        using namespace std;
+        return from_range(begin(values), end(values));
+    }
+
+    template <typename Fn>
+    void for_each_chunk(Fn&& fn) const
+    {
+        std::forward<Fn>(fn)(data(), data() + size);
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(Fn&& fn) const
+    {
+        return std::forward<Fn>(fn)(data(), data() + size);
+    }
+
+    const T& get(std::size_t index) const { return data()[index]; }
+
+    const T& get_check(std::size_t index) const
+    {
+        if (index >= size)
+            throw std::out_of_range{"out of range"};
+        return data()[index];
+    }
+
+    bool equals(const no_capacity& other) const
+    {
+        return ptr == other.ptr ||
+               (size == other.size &&
+                std::equal(data(), data() + size, other.data()));
+    }
+
+    no_capacity push_back(T value) const
+    {
+        auto p = node_t::copy_n(size + 1, ptr, size);
+        try {
+            new (p->data() + size) T{std::move(value)};
+            return {p, size + 1};
+        } catch (...) {
+            node_t::delete_n(p, size, size + 1);
+            throw;
+        }
+    }
+
+    no_capacity assoc(std::size_t idx, T value) const
+    {
+        auto p = node_t::copy_n(size, ptr, size);
+        try {
+            p->data()[idx] = std::move(value);
+            return {p, size};
+        } catch (...) {
+            node_t::delete_n(p, size, size);
+            throw;
+        }
+    }
+
+    template <typename Fn>
+    no_capacity update(std::size_t idx, Fn&& op) const
+    {
+        auto p = node_t::copy_n(size, ptr, size);
+        try {
+            auto& elem = p->data()[idx];
+            elem       = std::forward<Fn>(op)(std::move(elem));
+            return {p, size};
+        } catch (...) {
+            node_t::delete_n(p, size, size);
+            throw;
+        }
+    }
+
+    no_capacity take(std::size_t sz) const
+    {
+        auto p = node_t::copy_n(sz, ptr, sz);
+        return {p, sz};
+    }
+};
+
+} // namespace arrays
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/arrays/node.hpp b/third_party/immer/immer/detail/arrays/node.hpp
new file mode 100644
index 0000000000..f96a63a9f4
--- /dev/null
+++ b/third_party/immer/immer/detail/arrays/node.hpp
@@ -0,0 +1,127 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/combine_standard_layout.hpp>
+#include <immer/detail/type_traits.hpp>
+#include <immer/detail/util.hpp>
+
+#include <limits>
+
+namespace immer {
+namespace detail {
+namespace arrays {
+
+template <typename T, typename MemoryPolicy>
+struct node
+{
+    using memory     = MemoryPolicy;
+    using heap       = typename MemoryPolicy::heap::type;
+    using transience = typename memory::transience_t;
+    using refs_t     = typename memory::refcount;
+    using ownee_t    = typename transience::ownee;
+    using node_t     = node;
+    using edit_t     = typename transience::edit;
+
+    struct data_t
+    {
+        aligned_storage_for<T> buffer;
+    };
+
+    using impl_t = combine_standard_layout_t<data_t, refs_t, ownee_t>;
+
+    impl_t impl;
+
+    constexpr static std::size_t sizeof_n(size_t count)
+    {
+        return immer_offsetof(impl_t, d.buffer) +
+               sizeof(T) * (count == 0 ? 1 : count);
+    }
+
+    refs_t& refs() const { return auto_const_cast(get<refs_t>(impl)); }
+
+    const ownee_t& ownee() const { return get<ownee_t>(impl); }
+    ownee_t& ownee() { return get<ownee_t>(impl); }
+
+    const T* data() const { return reinterpret_cast<const T*>(&impl.d.buffer); }
+    T* data() { return reinterpret_cast<T*>(&impl.d.buffer); }
+
+    bool can_mutate(edit_t e) const
+    {
+        return refs().unique() || ownee().can_mutate(e);
+    }
+
+    static void delete_n(node_t* p, size_t sz, size_t cap)
+    {
+        destroy_n(p->data(), sz);
+        heap::deallocate(sizeof_n(cap), p);
+    }
+
+    static node_t* make_n(size_t n)
+    {
+        return new (heap::allocate(sizeof_n(n))) node_t{};
+    }
+
+    static node_t* make_e(edit_t e, size_t n)
+    {
+        auto p     = make_n(n);
+        p->ownee() = e;
+        return p;
+    }
+
+    static node_t* fill_n(size_t n, T v)
+    {
+        auto p = make_n(n);
+        try {
+            std::uninitialized_fill_n(p->data(), n, v);
+            return p;
+        } catch (...) {
+            heap::deallocate(sizeof_n(n), p);
+            throw;
+        }
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    static node_t* copy_n(size_t n, Iter first, Sent last)
+    {
+        auto p = make_n(n);
+        try {
+            uninitialized_copy(first, last, p->data());
+            return p;
+        } catch (...) {
+            heap::deallocate(sizeof_n(n), p);
+            throw;
+        }
+    }
+
+    static node_t* copy_n(size_t n, node_t* p, size_t count)
+    {
+        return copy_n(n, p->data(), p->data() + count);
+    }
+
+    template <typename Iter>
+    static node_t* copy_e(edit_t e, size_t n, Iter first, Iter last)
+    {
+        auto p     = copy_n(n, first, last);
+        p->ownee() = e;
+        return p;
+    }
+
+    static node_t* copy_e(edit_t e, size_t n, node_t* p, size_t count)
+    {
+        return copy_e(e, n, p->data(), p->data() + count);
+    }
+};
+
+} // namespace arrays
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/arrays/with_capacity.hpp b/third_party/immer/immer/detail/arrays/with_capacity.hpp
new file mode 100644
index 0000000000..290809e4b6
--- /dev/null
+++ b/third_party/immer/immer/detail/arrays/with_capacity.hpp
@@ -0,0 +1,303 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/arrays/no_capacity.hpp>
+
+namespace immer {
+namespace detail {
+namespace arrays {
+
+template <typename T, typename MemoryPolicy>
+struct with_capacity
+{
+    using no_capacity_t = no_capacity<T, MemoryPolicy>;
+
+    using node_t = node<T, MemoryPolicy>;
+    using edit_t = typename MemoryPolicy::transience_t::edit;
+    using size_t = std::size_t;
+
+    node_t* ptr;
+    size_t size;
+    size_t capacity;
+
+    static const with_capacity& empty()
+    {
+        static const with_capacity empty_{node_t::make_n(1), 0, 1};
+        return empty_;
+    }
+
+    with_capacity(node_t* p, size_t s, size_t c)
+        : ptr{p}
+        , size{s}
+        , capacity{c}
+    {}
+
+    with_capacity(const with_capacity& other)
+        : with_capacity{other.ptr, other.size, other.capacity}
+    {
+        inc();
+    }
+
+    with_capacity(const no_capacity_t& other)
+        : with_capacity{other.ptr, other.size, other.size}
+    {
+        inc();
+    }
+
+    with_capacity(with_capacity&& other)
+        : with_capacity{empty()}
+    {
+        swap(*this, other);
+    }
+
+    with_capacity& operator=(const with_capacity& other)
+    {
+        auto next = other;
+        swap(*this, next);
+        return *this;
+    }
+
+    with_capacity& operator=(with_capacity&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+
+    friend void swap(with_capacity& x, with_capacity& y)
+    {
+        using std::swap;
+        swap(x.ptr, y.ptr);
+        swap(x.size, y.size);
+        swap(x.capacity, y.capacity);
+    }
+
+    ~with_capacity() { dec(); }
+
+    void inc()
+    {
+        using immer::detail::get;
+        ptr->refs().inc();
+    }
+
+    void dec()
+    {
+        using immer::detail::get;
+        if (ptr->refs().dec())
+            node_t::delete_n(ptr, size, capacity);
+    }
+
+    const T* data() const { return ptr->data(); }
+    T* data() { return ptr->data(); }
+
+    T* data_mut(edit_t e)
+    {
+        if (!ptr->can_mutate(e)) {
+            auto p = node_t::copy_e(e, capacity, ptr, size);
+            dec();
+            ptr = p;
+        }
+        return data();
+    }
+
+    operator no_capacity_t() const
+    {
+        if (size == capacity) {
+            ptr->refs().inc();
+            return {ptr, size};
+        } else {
+            return {node_t::copy_n(size, ptr, size), size};
+        }
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<is_forward_iterator_v<Iter> &&
+                                   compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    static with_capacity from_range(Iter first, Sent last)
+    {
+        auto count = static_cast<size_t>(distance(first, last));
+        if (count == 0)
+            return empty();
+        else
+            return {node_t::copy_n(count, first, last), count, count};
+    }
+
+    template <typename U>
+    static with_capacity from_initializer_list(std::initializer_list<U> values)
+    {
+        using namespace std;
+        return from_range(begin(values), end(values));
+    }
+
+    static with_capacity from_fill(size_t n, T v)
+    {
+        return {node_t::fill_n(n, v), n, n};
+    }
+
+    template <typename Fn>
+    void for_each_chunk(Fn&& fn) const
+    {
+        std::forward<Fn>(fn)(data(), data() + size);
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(Fn&& fn) const
+    {
+        return std::forward<Fn>(fn)(data(), data() + size);
+    }
+
+    const T& get(std::size_t index) const { return data()[index]; }
+
+    const T& get_check(std::size_t index) const
+    {
+        if (index >= size)
+            throw std::out_of_range{"out of range"};
+        return data()[index];
+    }
+
+    bool equals(const with_capacity& other) const
+    {
+        return ptr == other.ptr ||
+               (size == other.size &&
+                std::equal(data(), data() + size, other.data()));
+    }
+
+    static size_t recommend_up(size_t sz, size_t cap)
+    {
+        auto max = std::numeric_limits<size_t>::max();
+        return sz <= cap ? cap
+                         : cap >= max / 2 ? max
+                                          /* otherwise */
+                                          : std::max(2 * cap, sz);
+    }
+
+    static size_t recommend_down(size_t sz, size_t cap)
+    {
+        return sz == 0 ? 1
+                       : sz < cap / 2 ? sz * 2 :
+                                      /* otherwise */ cap;
+    }
+
+    with_capacity push_back(T value) const
+    {
+        auto cap = recommend_up(size + 1, capacity);
+        auto p   = node_t::copy_n(cap, ptr, size);
+        try {
+            new (p->data() + size) T{std::move(value)};
+            return {p, size + 1, cap};
+        } catch (...) {
+            node_t::delete_n(p, size, cap);
+            throw;
+        }
+    }
+
+    void push_back_mut(edit_t e, T value)
+    {
+        if (ptr->can_mutate(e) && capacity > size) {
+            new (data() + size) T{std::move(value)};
+            ++size;
+        } else {
+            auto cap = recommend_up(size + 1, capacity);
+            auto p   = node_t::copy_e(e, cap, ptr, size);
+            try {
+                new (p->data() + size) T{std::move(value)};
+                *this = {p, size + 1, cap};
+            } catch (...) {
+                node_t::delete_n(p, size, cap);
+                throw;
+            }
+        }
+    }
+
+    with_capacity assoc(std::size_t idx, T value) const
+    {
+        auto p = node_t::copy_n(capacity, ptr, size);
+        try {
+            p->data()[idx] = std::move(value);
+            return {p, size, capacity};
+        } catch (...) {
+            node_t::delete_n(p, size, capacity);
+            throw;
+        }
+    }
+
+    void assoc_mut(edit_t e, std::size_t idx, T value)
+    {
+        if (ptr->can_mutate(e)) {
+            data()[idx] = std::move(value);
+        } else {
+            auto p = node_t::copy_n(capacity, ptr, size);
+            try {
+                p->data()[idx] = std::move(value);
+                *this          = {p, size, capacity};
+            } catch (...) {
+                node_t::delete_n(p, size, capacity);
+                throw;
+            }
+        }
+    }
+
+    template <typename Fn>
+    with_capacity update(std::size_t idx, Fn&& op) const
+    {
+        auto p = node_t::copy_n(capacity, ptr, size);
+        try {
+            auto& elem = p->data()[idx];
+            elem       = std::forward<Fn>(op)(std::move(elem));
+            return {p, size, capacity};
+        } catch (...) {
+            node_t::delete_n(p, size, capacity);
+            throw;
+        }
+    }
+
+    template <typename Fn>
+    void update_mut(edit_t e, std::size_t idx, Fn&& op)
+    {
+        if (ptr->can_mutate(e)) {
+            auto& elem = data()[idx];
+            elem       = std::forward<Fn>(op)(std::move(elem));
+        } else {
+            auto p = node_t::copy_e(e, capacity, ptr, size);
+            try {
+                auto& elem = p->data()[idx];
+                elem       = std::forward<Fn>(op)(std::move(elem));
+                *this      = {p, size, capacity};
+            } catch (...) {
+                node_t::delete_n(p, size, capacity);
+                throw;
+            }
+        }
+    }
+
+    with_capacity take(std::size_t sz) const
+    {
+        auto cap = recommend_down(sz, capacity);
+        auto p   = node_t::copy_n(cap, ptr, sz);
+        return {p, sz, cap};
+    }
+
+    void take_mut(edit_t e, std::size_t sz)
+    {
+        if (ptr->can_mutate(e)) {
+            destroy_n(data() + size, size - sz);
+            size = sz;
+        } else {
+            auto cap = recommend_down(sz, capacity);
+            auto p   = node_t::copy_e(e, cap, ptr, sz);
+            *this    = {p, sz, cap};
+        }
+    }
+};
+
+} // namespace arrays
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/combine_standard_layout.hpp b/third_party/immer/immer/detail/combine_standard_layout.hpp
new file mode 100644
index 0000000000..55c69bc916
--- /dev/null
+++ b/third_party/immer/immer/detail/combine_standard_layout.hpp
@@ -0,0 +1,235 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <type_traits>
+
+#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ == 1
+#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1
+#define immer_offsetof(st, m) ((std::size_t) & (((st*) 0)->m))
+#else
+#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0
+#define immer_offsetof offsetof
+#endif
+
+namespace immer {
+namespace detail {
+
+//
+// Metafunction that returns a standard layout struct that combines
+// all the standard layout types in `Ts...`, while making sure that
+// empty base optimizations are used.
+//
+// To query a part of the type do `get<my_part>(x)`;
+//
+// This is useful when putting together a type that merges various
+// types coming from different policies.  Some of them might be empty,
+// so we shall enable empty base optimizations.  But if we just
+// inherit from all of them, we would break the "standard layout"
+// rules, preventing us from using `offseof(...)`.  So metafunction
+// will generate the type by sometimes inheriting, sometimes adding as
+// member.
+//
+// Note that the types are added to the combined type from right to
+// left!
+//
+template <typename... Ts>
+struct combine_standard_layout;
+
+template <typename... Ts>
+using combine_standard_layout_t = typename combine_standard_layout<Ts...>::type;
+
+namespace csl {
+
+template <typename T>
+struct type_t
+{};
+
+template <typename U, typename T>
+U& get(T& x);
+
+template <typename U, typename T>
+const U& get(const T& x);
+
+template <typename T, typename Next = void>
+struct inherit
+{
+    struct type
+        : T
+        , Next
+    {
+        using Next::get_;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return *this; }
+        const T& get_(type_t<T>) const { return *this; }
+    };
+};
+
+template <typename T>
+struct inherit<T, void>
+{
+    struct type : T
+    {
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return *this; }
+        const T& get_(type_t<T>) const { return *this; }
+    };
+};
+
+template <typename T, typename Next = void>
+struct member
+{
+    struct type : Next
+    {
+        T d;
+
+        using Next::get_;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+    };
+};
+
+template <typename T>
+struct member<T, void>
+{
+    struct type
+    {
+        T d;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+    };
+};
+
+template <typename T, typename Next>
+struct member_two
+{
+    struct type
+    {
+        Next n;
+        T d;
+
+        template <typename U>
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+        template <typename U>
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
+
+        T& get_(type_t<T>) { return d; }
+        const T& get_(type_t<T>) const { return d; }
+
+        template <typename U>
+        auto get_(type_t<U> t) -> decltype(auto)
+        {
+            return n.get_(t);
+        }
+        template <typename U>
+        auto get_(type_t<U> t) const -> decltype(auto)
+        {
+            return n.get_(t);
+        }
+    };
+};
+
+template <typename... Ts>
+struct combine_standard_layout_aux;
+
+template <typename T>
+struct combine_standard_layout_aux<T>
+{
+    static_assert(std::is_standard_layout<T>::value, "");
+
+    using type = typename std::conditional_t<std::is_empty<T>::value,
+                                             csl::inherit<T>,
+                                             csl::member<T>>::type;
+};
+
+template <typename T, typename... Ts>
+struct combine_standard_layout_aux<T, Ts...>
+{
+    static_assert(std::is_standard_layout<T>::value, "");
+
+    using this_t = T;
+    using next_t = typename combine_standard_layout_aux<Ts...>::type;
+
+    static constexpr auto empty_this = std::is_empty<this_t>::value;
+    static constexpr auto empty_next = std::is_empty<next_t>::value;
+
+    using type = typename std::conditional_t<
+        empty_this,
+        inherit<this_t, next_t>,
+        std::conditional_t<empty_next,
+                           member<this_t, next_t>,
+                           member_two<this_t, next_t>>>::type;
+};
+
+} // namespace csl
+
+using csl::get;
+
+template <typename... Ts>
+struct combine_standard_layout
+{
+    using type = typename csl::combine_standard_layout_aux<Ts...>::type;
+#if !IMMER_BROKEN_STANDARD_LAYOUT_DETECTION
+    static_assert(std::is_standard_layout<type>::value, "");
+#endif
+};
+
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/hamts/bits.hpp b/third_party/immer/immer/detail/hamts/bits.hpp
new file mode 100644
index 0000000000..b02caf7706
--- /dev/null
+++ b/third_party/immer/immer/detail/hamts/bits.hpp
@@ -0,0 +1,108 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstdint>
+
+#if defined(_MSC_VER)
+#include <intrin.h> // __popcnt
+#endif
+
+namespace immer {
+namespace detail {
+namespace hamts {
+
+using size_t  = std::size_t;
+using hash_t  = std::size_t;
+using bits_t  = std::uint32_t;
+using count_t = std::uint32_t;
+using shift_t = std::uint32_t;
+
+template <bits_t B>
+struct get_bitmap_type
+{
+    static_assert(B < 6u, "B > 6 is not supported.");
+
+    using type = std::uint32_t;
+};
+
+template <>
+struct get_bitmap_type<6u>
+{
+    using type = std::uint64_t;
+};
+
+template <bits_t B, typename T = count_t>
+constexpr T branches = T{1u} << B;
+
+template <bits_t B, typename T = size_t>
+constexpr T mask = branches<B, T> - 1u;
+
+template <bits_t B, typename T = count_t>
+constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B;
+
+template <bits_t B, typename T = count_t>
+constexpr T max_shift = max_depth<B, count_t>* B;
+
+#define IMMER_HAS_BUILTIN_POPCOUNT 1
+
+inline auto popcount_fallback(std::uint32_t x)
+{
+    // More alternatives:
+    // https://en.wikipedia.org/wiki/Hamming_weight
+    // http://wm.ite.pl/articles/sse-popcount.html
+    // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+    x = x - ((x >> 1) & 0x55555555u);
+    x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u);
+    return (((x + (x >> 4u)) & 0xF0F0F0Fu) * 0x1010101u) >> 24u;
+}
+
+inline auto popcount_fallback(std::uint64_t x)
+{
+    x = x - ((x >> 1) & 0x5555555555555555u);
+    x = (x & 0x3333333333333333u) + ((x >> 2u) & 0x3333333333333333u);
+    return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >>
+           56u;
+}
+
+inline count_t popcount(std::uint32_t x)
+{
+#if IMMER_HAS_BUILTIN_POPCOUNT
+#if defined(_MSC_VER)
+    return __popcnt(x);
+#else
+    return __builtin_popcount(x);
+#endif
+#else
+    return popcount_fallback(x);
+#endif
+}
+
+inline count_t popcount(std::uint64_t x)
+{
+#if IMMER_HAS_BUILTIN_POPCOUNT
+#if defined(_MSC_VER)
+#if defined(_WIN64)
+    return __popcnt64(x);
+#else
+    // TODO: benchmark against popcount_fallback(std::uint64_t x)
+    return popcount(static_cast<std::uint32_t>(x >> 32)) +
+           popcount(static_cast<std::uint32_t>(x));
+#endif
+#else
+    return __builtin_popcountll(x);
+#endif
+#else
+    return popcount_fallback(x);
+#endif
+}
+
+} // namespace hamts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/hamts/champ.hpp b/third_party/immer/immer/detail/hamts/champ.hpp
new file mode 100644
index 0000000000..e3b55d397c
--- /dev/null
+++ b/third_party/immer/immer/detail/hamts/champ.hpp
@@ -0,0 +1,473 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/detail/hamts/node.hpp>
+
+#include <algorithm>
+
+namespace immer {
+namespace detail {
+namespace hamts {
+
+template <typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          bits_t B>
+struct champ
+{
+    static constexpr auto bits = B;
+
+    using node_t   = node<T, Hash, Equal, MemoryPolicy, B>;
+    using bitmap_t = typename get_bitmap_type<B>::type;
+
+    static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
+
+    node_t* root;
+    size_t size;
+
+    static const champ& empty()
+    {
+        static const champ empty_{
+            node_t::make_inner_n(0),
+            0,
+        };
+        return empty_;
+    }
+
+    champ(node_t* r, size_t sz)
+        : root{r}
+        , size{sz}
+    {}
+
+    champ(const champ& other)
+        : champ{other.root, other.size}
+    {
+        inc();
+    }
+
+    champ(champ&& other)
+        : champ{empty()}
+    {
+        swap(*this, other);
+    }
+
+    champ& operator=(const champ& other)
+    {
+        auto next = other;
+        swap(*this, next);
+        return *this;
+    }
+
+    champ& operator=(champ&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+
+    friend void swap(champ& x, champ& y)
+    {
+        using std::swap;
+        swap(x.root, y.root);
+        swap(x.size, y.size);
+    }
+
+    ~champ() { dec(); }
+
+    void inc() const { root->inc(); }
+
+    void dec() const
+    {
+        if (root->dec())
+            node_t::delete_deep(root, 0);
+    }
+
+    template <typename Fn>
+    void for_each_chunk(Fn&& fn) const
+    {
+        for_each_chunk_traversal(root, 0, fn);
+    }
+
+    template <typename Fn>
+    void for_each_chunk_traversal(node_t* node, count_t depth, Fn&& fn) const
+    {
+        if (depth < max_depth<B>) {
+            auto datamap = node->datamap();
+            if (datamap)
+                fn(node->values(), node->values() + popcount(datamap));
+            auto nodemap = node->nodemap();
+            if (nodemap) {
+                auto fst = node->children();
+                auto lst = fst + popcount(nodemap);
+                for (; fst != lst; ++fst)
+                    for_each_chunk_traversal(*fst, depth + 1, fn);
+            }
+        } else {
+            fn(node->collisions(),
+               node->collisions() + node->collision_count());
+        }
+    }
+
+    template <typename Project, typename Default, typename K>
+    decltype(auto) get(const K& k) const
+    {
+        auto node = root;
+        auto hash = Hash{}(k);
+        for (auto i = count_t{}; i < max_depth<B>; ++i) {
+            auto bit = bitmap_t{1u} << (hash & mask<B>);
+            if (node->nodemap() & bit) {
+                auto offset = popcount(node->nodemap() & (bit - 1));
+                node        = node->children()[offset];
+                hash        = hash >> B;
+            } else if (node->datamap() & bit) {
+                auto offset = popcount(node->datamap() & (bit - 1));
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k))
+                    return Project{}(*val);
+                else
+                    return Default{}();
+            } else {
+                return Default{}();
+            }
+        }
+        auto fst = node->collisions();
+        auto lst = fst + node->collision_count();
+        for (; fst != lst; ++fst)
+            if (Equal{}(*fst, k))
+                return Project{}(*fst);
+        return Default{}();
+    }
+
+    std::pair<node_t*, bool>
+    do_add(node_t* node, T v, hash_t hash, shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, v))
+                    return {
+                        node_t::copy_collision_replace(node, fst, std::move(v)),
+                        false};
+            return {node_t::copy_collision_insert(node, std::move(v)), true};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = popcount(node->nodemap() & (bit - 1));
+                auto result = do_add(
+                    node->children()[offset], std::move(v), hash, shift + B);
+                try {
+                    result.first =
+                        node_t::copy_inner_replace(node, offset, result.first);
+                    return result;
+                } catch (...) {
+                    node_t::delete_deep_shift(result.first, shift + B);
+                    throw;
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = popcount(node->datamap() & (bit - 1));
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, v))
+                    return {node_t::copy_inner_replace_value(
+                                node, offset, std::move(v)),
+                            false};
+                else {
+                    auto child = node_t::make_merged(
+                        shift + B, std::move(v), hash, *val, Hash{}(*val));
+                    try {
+                        return {node_t::copy_inner_replace_merged(
+                                    node, bit, offset, child),
+                                true};
+                    } catch (...) {
+                        node_t::delete_deep_shift(child, shift + B);
+                        throw;
+                    }
+                }
+            } else {
+                return {
+                    node_t::copy_inner_insert_value(node, bit, std::move(v)),
+                    true};
+            }
+        }
+    }
+
+    champ add(T v) const
+    {
+        auto hash     = Hash{}(v);
+        auto res      = do_add(root, std::move(v), hash, 0);
+        auto new_size = size + (res.second ? 1 : 0);
+        return {res.first, new_size};
+    }
+
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
+    std::pair<node_t*, bool>
+    do_update(node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, k))
+                    return {
+                        node_t::copy_collision_replace(
+                            node,
+                            fst,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(Project{}(*fst)))),
+                        false};
+            return {node_t::copy_collision_insert(
+                        node,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(Default{}()))),
+                    true};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = popcount(node->nodemap() & (bit - 1));
+                auto result = do_update<Project, Default, Combine>(
+                    node->children()[offset],
+                    k,
+                    std::forward<Fn>(fn),
+                    hash,
+                    shift + B);
+                try {
+                    result.first =
+                        node_t::copy_inner_replace(node, offset, result.first);
+                    return result;
+                } catch (...) {
+                    node_t::delete_deep_shift(result.first, shift + B);
+                    throw;
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = popcount(node->datamap() & (bit - 1));
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k))
+                    return {
+                        node_t::copy_inner_replace_value(
+                            node,
+                            offset,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(Project{}(*val)))),
+                        false};
+                else {
+                    auto child = node_t::make_merged(
+                        shift + B,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(Default{}())),
+                        hash,
+                        *val,
+                        Hash{}(*val));
+                    try {
+                        return {node_t::copy_inner_replace_merged(
+                                    node, bit, offset, child),
+                                true};
+                    } catch (...) {
+                        node_t::delete_deep_shift(child, shift + B);
+                        throw;
+                    }
+                }
+            } else {
+                return {node_t::copy_inner_insert_value(
+                            node,
+                            bit,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(Default{}()))),
+                        true};
+            }
+        }
+    }
+
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
+    champ update(const K& k, Fn&& fn) const
+    {
+        auto hash = Hash{}(k);
+        auto res  = do_update<Project, Default, Combine>(
+            root, k, std::forward<Fn>(fn), hash, 0);
+        auto new_size = size + (res.second ? 1 : 0);
+        return {res.first, new_size};
+    }
+
+    // basically:
+    //      variant<monostate_t, T*, node_t*>
+    // boo bad we are not using... C++17 :'(
+    struct sub_result
+    {
+        enum kind_t
+        {
+            nothing,
+            singleton,
+            tree
+        };
+
+        union data_t
+        {
+            T* singleton;
+            node_t* tree;
+        };
+
+        kind_t kind;
+        data_t data;
+
+        sub_result()
+            : kind{nothing} {};
+        sub_result(T* x)
+            : kind{singleton}
+        {
+            data.singleton = x;
+        };
+        sub_result(node_t* x)
+            : kind{tree}
+        {
+            data.tree = x;
+        };
+    };
+
+    template <typename K>
+    sub_result
+    do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (auto cur = fst; cur != lst; ++cur)
+                if (Equal{}(*cur, k))
+                    return node->collision_count() > 2
+                               ? node_t::copy_collision_remove(node, cur)
+                               : sub_result{fst + (cur == fst)};
+            return {};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = popcount(node->nodemap() & (bit - 1));
+                auto result =
+                    do_sub(node->children()[offset], k, hash, shift + B);
+                switch (result.kind) {
+                case sub_result::nothing:
+                    return {};
+                case sub_result::singleton:
+                    return node->datamap() == 0 &&
+                                   popcount(node->nodemap()) == 1 && shift > 0
+                               ? result
+                               : node_t::copy_inner_replace_inline(
+                                     node, bit, offset, *result.data.singleton);
+                case sub_result::tree:
+                    try {
+                        return node_t::copy_inner_replace(
+                            node, offset, result.data.tree);
+                    } catch (...) {
+                        node_t::delete_deep_shift(result.data.tree, shift + B);
+                        throw;
+                    }
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = popcount(node->datamap() & (bit - 1));
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k)) {
+                    auto nv = popcount(node->datamap());
+                    if (node->nodemap() || nv > 2)
+                        return node_t::copy_inner_remove_value(
+                            node, bit, offset);
+                    else if (nv == 2) {
+                        return shift > 0 ? sub_result{node->values() + !offset}
+                                         : node_t::make_inner_n(
+                                               0,
+                                               node->datamap() & ~bit,
+                                               node->values()[!offset]);
+                    } else {
+                        assert(shift == 0);
+                        return empty().root->inc();
+                    }
+                }
+            }
+            return {};
+        }
+    }
+
+    template <typename K>
+    champ sub(const K& k) const
+    {
+        auto hash = Hash{}(k);
+        auto res  = do_sub(root, k, hash, 0);
+        switch (res.kind) {
+        case sub_result::nothing:
+            return *this;
+        case sub_result::tree:
+            return {res.data.tree, size - 1};
+        default:
+            IMMER_UNREACHABLE;
+        }
+    }
+
+    template <typename Eq = Equal>
+    bool equals(const champ& other) const
+    {
+        return size == other.size && equals_tree<Eq>(root, other.root, 0);
+    }
+
+    template <typename Eq>
+    static bool equals_tree(const node_t* a, const node_t* b, count_t depth)
+    {
+        if (a == b)
+            return true;
+        else if (depth == max_depth<B>) {
+            auto nv = a->collision_count();
+            return nv == b->collision_count() &&
+                   equals_collisions<Eq>(a->collisions(), b->collisions(), nv);
+        } else {
+            if (a->nodemap() != b->nodemap() || a->datamap() != b->datamap())
+                return false;
+            auto n = popcount(a->nodemap());
+            for (auto i = count_t{}; i < n; ++i)
+                if (!equals_tree<Eq>(
+                        a->children()[i], b->children()[i], depth + 1))
+                    return false;
+            auto nv = popcount(a->datamap());
+            return !nv || equals_values<Eq>(a->values(), b->values(), nv);
+        }
+    }
+
+    template <typename Eq>
+    static bool equals_values(const T* a, const T* b, count_t n)
+    {
+        return std::equal(a, a + n, b, Eq{});
+    }
+
+    template <typename Eq>
+    static bool equals_collisions(const T* a, const T* b, count_t n)
+    {
+        auto ae = a + n;
+        auto be = b + n;
+        for (; a != ae; ++a) {
+            for (auto fst = b; fst != be; ++fst)
+                if (Eq{}(*a, *fst))
+                    goto good;
+            return false;
+        good:
+            continue;
+        }
+        return true;
+    }
+};
+
+} // namespace hamts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/hamts/champ_iterator.hpp b/third_party/immer/immer/detail/hamts/champ_iterator.hpp
new file mode 100644
index 0000000000..72673b41be
--- /dev/null
+++ b/third_party/immer/immer/detail/hamts/champ_iterator.hpp
@@ -0,0 +1,148 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/detail/iterator_facade.hpp>
+
+namespace immer {
+namespace detail {
+namespace hamts {
+
+template <typename T, typename Hash, typename Eq, typename MP, bits_t B>
+struct champ_iterator
+    : iterator_facade<champ_iterator<T, Hash, Eq, MP, B>,
+                      std::forward_iterator_tag,
+                      T,
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
+{
+    using tree_t = champ<T, Hash, Eq, MP, B>;
+    using node_t = typename tree_t::node_t;
+
+    struct end_t
+    {};
+
+    champ_iterator() = default;
+
+    champ_iterator(const tree_t& v)
+        : depth_{0}
+    {
+        if (v.root->datamap()) {
+            cur_ = v.root->values();
+            end_ = v.root->values() + popcount(v.root->datamap());
+        } else {
+            cur_ = end_ = nullptr;
+        }
+        path_[0] = &v.root;
+        ensure_valid_();
+    }
+
+    champ_iterator(const tree_t& v, end_t)
+        : cur_{nullptr}
+        , end_{nullptr}
+        , depth_{0}
+    {
+        path_[0] = &v.root;
+    }
+
+    champ_iterator(const champ_iterator& other)
+        : cur_{other.cur_}
+        , end_{other.end_}
+        , depth_{other.depth_}
+    {
+        std::copy(other.path_, other.path_ + depth_ + 1, path_);
+    }
+
+private:
+    friend iterator_core_access;
+
+    T* cur_;
+    T* end_;
+    count_t depth_;
+    node_t* const* path_[max_depth<B> + 1];
+
+    void increment()
+    {
+        ++cur_;
+        ensure_valid_();
+    }
+
+    bool step_down()
+    {
+        if (depth_ < max_depth<B>) {
+            auto parent = *path_[depth_];
+            if (parent->nodemap()) {
+                ++depth_;
+                path_[depth_] = parent->children();
+                auto child    = *path_[depth_];
+                if (depth_ < max_depth<B>) {
+                    if (child->datamap()) {
+                        cur_ = child->values();
+                        end_ = cur_ + popcount(child->datamap());
+                    }
+                } else {
+                    cur_ = child->collisions();
+                    end_ = cur_ + child->collision_count();
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool step_right()
+    {
+        while (depth_ > 0) {
+            auto parent = *path_[depth_ - 1];
+            auto last   = parent->children() + popcount(parent->nodemap());
+            auto next   = path_[depth_] + 1;
+            if (next < last) {
+                path_[depth_] = next;
+                auto child    = *path_[depth_];
+                if (depth_ < max_depth<B>) {
+                    if (child->datamap()) {
+                        cur_ = child->values();
+                        end_ = cur_ + popcount(child->datamap());
+                    }
+                } else {
+                    cur_ = child->collisions();
+                    end_ = cur_ + child->collision_count();
+                }
+                return true;
+            }
+            --depth_;
+        }
+        return false;
+    }
+
+    void ensure_valid_()
+    {
+        while (cur_ == end_) {
+            while (step_down())
+                if (cur_ != end_)
+                    return;
+            if (!step_right()) {
+                // end of sequence
+                assert(depth_ == 0);
+                cur_ = end_ = nullptr;
+                return;
+            }
+        }
+    }
+
+    bool equal(const champ_iterator& other) const { return cur_ == other.cur_; }
+
+    const T& dereference() const { return *cur_; }
+};
+
+} // namespace hamts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/hamts/node.hpp b/third_party/immer/immer/detail/hamts/node.hpp
new file mode 100644
index 0000000000..216e82b787
--- /dev/null
+++ b/third_party/immer/immer/detail/hamts/node.hpp
@@ -0,0 +1,717 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/combine_standard_layout.hpp>
+#include <immer/detail/hamts/bits.hpp>
+#include <immer/detail/util.hpp>
+
+#include <cassert>
+
+namespace immer {
+namespace detail {
+namespace hamts {
+
+template <typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          bits_t B>
+struct node
+{
+    using node_t = node;
+
+    using memory      = MemoryPolicy;
+    using heap_policy = typename memory::heap;
+    using heap        = typename heap_policy::type;
+    using transience  = typename memory::transience_t;
+    using refs_t      = typename memory::refcount;
+    using ownee_t     = typename transience::ownee;
+    using edit_t      = typename transience::edit;
+    using value_t     = T;
+    using bitmap_t    = typename get_bitmap_type<B>::type;
+
+    enum class kind_t
+    {
+        collision,
+        inner
+    };
+
+    struct collision_t
+    {
+        count_t count;
+        aligned_storage_for<T> buffer;
+    };
+
+    struct values_data_t
+    {
+        aligned_storage_for<T> buffer;
+    };
+
+    using values_t = combine_standard_layout_t<values_data_t, refs_t>;
+
+    struct inner_t
+    {
+        bitmap_t nodemap;
+        bitmap_t datamap;
+        values_t* values;
+        aligned_storage_for<node_t*> buffer;
+    };
+
+    union data_t
+    {
+        inner_t inner;
+        collision_t collision;
+    };
+
+    struct impl_data_t
+    {
+#if IMMER_TAGGED_NODE
+        kind_t kind;
+#endif
+        data_t data;
+    };
+
+    using impl_t = combine_standard_layout_t<impl_data_t, refs_t>;
+
+    impl_t impl;
+
+    constexpr static std::size_t sizeof_values_n(count_t count)
+    {
+        return std::max(sizeof(values_t),
+                        immer_offsetof(values_t, d.buffer) +
+                            sizeof(values_data_t::buffer) * count);
+    }
+
+    constexpr static std::size_t sizeof_collision_n(count_t count)
+    {
+        return immer_offsetof(impl_t, d.data.collision.buffer) +
+               sizeof(collision_t::buffer) * count;
+    }
+
+    constexpr static std::size_t sizeof_inner_n(count_t count)
+    {
+        return immer_offsetof(impl_t, d.data.inner.buffer) +
+               sizeof(inner_t::buffer) * count;
+    }
+
+#if IMMER_TAGGED_NODE
+    kind_t kind() const { return impl.d.kind; }
+#endif
+
+    auto values()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        assert(impl.d.data.inner.values);
+        return (T*) &impl.d.data.inner.values->d.buffer;
+    }
+
+    auto values() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        assert(impl.d.data.inner.values);
+        return (const T*) &impl.d.data.inner.values->d.buffer;
+    }
+
+    auto children()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return (node_t**) &impl.d.data.inner.buffer;
+    }
+
+    auto children() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return (const node_t* const*) &impl.d.data.inner.buffer;
+    }
+
+    auto datamap() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return impl.d.data.inner.datamap;
+    }
+
+    auto nodemap() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return impl.d.data.inner.nodemap;
+    }
+
+    auto collision_count() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
+        return impl.d.data.collision.count;
+    }
+
+    T* collisions()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
+        return (T*) &impl.d.data.collision.buffer;
+    }
+
+    const T* collisions() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
+        return (const T*) &impl.d.data.collision.buffer;
+    }
+
+    static refs_t& refs(const values_t* x)
+    {
+        return auto_const_cast(get<refs_t>(*x));
+    }
+    static const ownee_t& ownee(const values_t* x) { return get<ownee_t>(*x); }
+    static ownee_t& ownee(values_t* x) { return get<ownee_t>(*x); }
+
+    static refs_t& refs(const node_t* x)
+    {
+        return auto_const_cast(get<refs_t>(x->impl));
+    }
+    static const ownee_t& ownee(const node_t* x)
+    {
+        return get<ownee_t>(x->impl);
+    }
+    static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
+
+    static node_t* make_inner_n(count_t n)
+    {
+        assert(n <= branches<B>);
+        auto m = heap::allocate(sizeof_inner_n(n));
+        auto p = new (m) node_t;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::inner;
+#endif
+        p->impl.d.data.inner.nodemap = 0;
+        p->impl.d.data.inner.datamap = 0;
+        p->impl.d.data.inner.values  = nullptr;
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, values_t* values)
+    {
+        auto p = make_inner_n(n);
+        if (values) {
+            p->impl.d.data.inner.values = values;
+            refs(values).inc();
+        }
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, count_t nv)
+    {
+        assert(nv <= branches<B>);
+        auto p = make_inner_n(n);
+        if (nv) {
+            try {
+                p->impl.d.data.inner.values =
+                    new (heap::allocate(sizeof_values_n(nv))) values_t{};
+            } catch (...) {
+                deallocate_inner(p, n);
+                throw;
+            }
+        }
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, count_t idx, node_t* child)
+    {
+        assert(n >= 1);
+        auto p                       = make_inner_n(n);
+        p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx;
+        p->children()[0]             = child;
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, bitmap_t bitmap, T x)
+    {
+        auto p                       = make_inner_n(n, 1);
+        p->impl.d.data.inner.datamap = bitmap;
+        try {
+            new (p->values()) T{std::move(x)};
+        } catch (...) {
+            deallocate_inner(p, n, 1);
+            throw;
+        }
+        return p;
+    }
+
+    static node_t*
+    make_inner_n(count_t n, count_t idx1, T x1, count_t idx2, T x2)
+    {
+        assert(idx1 != idx2);
+        auto p = make_inner_n(n, 2);
+        p->impl.d.data.inner.datamap =
+            (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
+        auto assign = [&](auto&& x1, auto&& x2) {
+            auto vp = p->values();
+            try {
+                new (vp) T{std::move(x1)};
+                try {
+                    new (vp + 1) T{std::move(x2)};
+                } catch (...) {
+                    vp->~T();
+                    throw;
+                }
+            } catch (...) {
+                deallocate_inner(p, n, 2);
+                throw;
+            }
+        };
+        if (idx1 < idx2)
+            assign(x1, x2);
+        else
+            assign(x2, x1);
+        return p;
+    }
+
+    static node_t* make_collision_n(count_t n)
+    {
+        auto m = heap::allocate(sizeof_collision_n(n));
+        auto p = new (m) node_t;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::collision;
+#endif
+        p->impl.d.data.collision.count = n;
+        return p;
+    }
+
+    static node_t* make_collision(T v1, T v2)
+    {
+        auto m = heap::allocate(sizeof_collision_n(2));
+        auto p = new (m) node_t;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::collision;
+#endif
+        p->impl.d.data.collision.count = 2;
+        auto cols                      = p->collisions();
+        try {
+            new (cols) T{std::move(v1)};
+            try {
+                new (cols + 1) T{std::move(v2)};
+            } catch (...) {
+                cols->~T();
+                throw;
+            }
+        } catch (...) {
+            deallocate_collision(p, 2);
+            throw;
+        }
+        return p;
+    }
+
+    static node_t* copy_collision_insert(node_t* src, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
+        auto n    = src->collision_count();
+        auto dst  = make_collision_n(n + 1);
+        auto srcp = src->collisions();
+        auto dstp = dst->collisions();
+        try {
+            new (dstp) T{std::move(v)};
+            try {
+                std::uninitialized_copy(srcp, srcp + n, dstp + 1);
+            } catch (...) {
+                dstp->~T();
+                throw;
+            }
+        } catch (...) {
+            deallocate_collision(dst, n + 1);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_collision_remove(node_t* src, T* v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
+        assert(src->collision_count() > 1);
+        auto n    = src->collision_count();
+        auto dst  = make_collision_n(n - 1);
+        auto srcp = src->collisions();
+        auto dstp = dst->collisions();
+        try {
+            dstp = std::uninitialized_copy(srcp, v, dstp);
+            try {
+                std::uninitialized_copy(v + 1, srcp + n, dstp);
+            } catch (...) {
+                destroy(dst->collisions(), dstp);
+                throw;
+            }
+        } catch (...) {
+            deallocate_collision(dst, n - 1);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_collision_replace(node_t* src, T* pos, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
+        auto n    = src->collision_count();
+        auto dst  = make_collision_n(n);
+        auto srcp = src->collisions();
+        auto dstp = dst->collisions();
+        assert(pos >= srcp && pos < srcp + n);
+        try {
+            new (dstp) T{std::move(v)};
+            try {
+                dstp = std::uninitialized_copy(srcp, pos, dstp + 1);
+                try {
+                    std::uninitialized_copy(pos + 1, srcp + n, dstp);
+                } catch (...) {
+                    destroy(dst->collisions(), dstp);
+                    throw;
+                }
+            } catch (...) {
+                dst->collisions()->~T();
+                throw;
+            }
+        } catch (...) {
+            deallocate_collision(dst, n);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t*
+    copy_inner_replace(node_t* src, count_t offset, node_t* child)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto n    = popcount(src->nodemap());
+        auto dst  = make_inner_n(n, src->impl.d.data.inner.values);
+        auto srcp = src->children();
+        auto dstp = dst->children();
+        dst->impl.d.data.inner.datamap = src->datamap();
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        std::uninitialized_copy(srcp, srcp + n, dstp);
+        inc_nodes(srcp, n);
+        srcp[offset]->dec_unsafe();
+        dstp[offset] = child;
+        return dst;
+    }
+
+    static node_t* copy_inner_replace_value(node_t* src, count_t offset, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(offset < popcount(src->datamap()));
+        auto n                         = popcount(src->nodemap());
+        auto nv                        = popcount(src->datamap());
+        auto dst                       = make_inner_n(n, nv);
+        dst->impl.d.data.inner.datamap = src->datamap();
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        try {
+            std::uninitialized_copy(
+                src->values(), src->values() + nv, dst->values());
+            try {
+                dst->values()[offset] = std::move(v);
+            } catch (...) {
+                destroy_n(dst->values(), nv);
+                throw;
+            }
+        } catch (...) {
+            deallocate_inner(dst, n, nv);
+            throw;
+        }
+        inc_nodes(src->children(), n);
+        std::uninitialized_copy(
+            src->children(), src->children() + n, dst->children());
+        return dst;
+    }
+
+    static node_t* copy_inner_replace_merged(node_t* src,
+                                             bitmap_t bit,
+                                             count_t voffset,
+                                             node_t* node)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->nodemap() & bit));
+        assert(src->datamap() & bit);
+        assert(voffset == popcount(src->datamap() & (bit - 1)));
+        auto n                         = popcount(src->nodemap());
+        auto nv                        = popcount(src->datamap());
+        auto dst                       = make_inner_n(n + 1, nv - 1);
+        auto noffset                   = popcount(src->nodemap() & (bit - 1));
+        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
+        if (nv > 1) {
+            try {
+                std::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+                try {
+                    std::uninitialized_copy(src->values() + voffset + 1,
+                                            src->values() + nv,
+                                            dst->values() + voffset);
+                } catch (...) {
+                    destroy_n(dst->values(), voffset);
+                    throw;
+                }
+            } catch (...) {
+                deallocate_inner(dst, n + 1, nv - 1);
+                throw;
+            }
+        }
+        inc_nodes(src->children(), n);
+        std::uninitialized_copy(
+            src->children(), src->children() + noffset, dst->children());
+        std::uninitialized_copy(src->children() + noffset,
+                                src->children() + n,
+                                dst->children() + noffset + 1);
+        dst->children()[noffset] = node;
+        return dst;
+    }
+
+    static node_t* copy_inner_replace_inline(node_t* src,
+                                             bitmap_t bit,
+                                             count_t noffset,
+                                             T value)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->datamap() & bit));
+        assert(src->nodemap() & bit);
+        assert(noffset == popcount(src->nodemap() & (bit - 1)));
+        auto n                         = popcount(src->nodemap());
+        auto nv                        = popcount(src->datamap());
+        auto dst                       = make_inner_n(n - 1, nv + 1);
+        auto voffset                   = popcount(src->datamap() & (bit - 1));
+        dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
+        dst->impl.d.data.inner.datamap = src->datamap() | bit;
+        try {
+            if (nv)
+                std::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+            try {
+                new (dst->values() + voffset) T{std::move(value)};
+                try {
+                    if (nv)
+                        std::uninitialized_copy(src->values() + voffset,
+                                                src->values() + nv,
+                                                dst->values() + voffset + 1);
+                } catch (...) {
+                    dst->values()[voffset].~T();
+                    throw;
+                }
+            } catch (...) {
+                destroy_n(dst->values(), voffset);
+                throw;
+            }
+        } catch (...) {
+            deallocate_inner(dst, n - 1, nv + 1);
+            throw;
+        }
+        inc_nodes(src->children(), n);
+        src->children()[noffset]->dec_unsafe();
+        std::uninitialized_copy(
+            src->children(), src->children() + noffset, dst->children());
+        std::uninitialized_copy(src->children() + noffset + 1,
+                                src->children() + n,
+                                dst->children() + noffset);
+        return dst;
+    }
+
+    static node_t*
+    copy_inner_remove_value(node_t* src, bitmap_t bit, count_t voffset)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->nodemap() & bit));
+        assert(src->datamap() & bit);
+        assert(voffset == popcount(src->datamap() & (bit - 1)));
+        auto n                         = popcount(src->nodemap());
+        auto nv                        = popcount(src->datamap());
+        auto dst                       = make_inner_n(n, nv - 1);
+        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        if (nv > 1) {
+            try {
+                std::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+                try {
+                    std::uninitialized_copy(src->values() + voffset + 1,
+                                            src->values() + nv,
+                                            dst->values() + voffset);
+                } catch (...) {
+                    destroy_n(dst->values(), voffset);
+                    throw;
+                }
+            } catch (...) {
+                deallocate_inner(dst, n, nv - 1);
+                throw;
+            }
+        }
+        inc_nodes(src->children(), n);
+        std::uninitialized_copy(
+            src->children(), src->children() + n, dst->children());
+        return dst;
+    }
+
+    static node_t* copy_inner_insert_value(node_t* src, bitmap_t bit, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto n                         = popcount(src->nodemap());
+        auto nv                        = popcount(src->datamap());
+        auto offset                    = popcount(src->datamap() & (bit - 1));
+        auto dst                       = make_inner_n(n, nv + 1);
+        dst->impl.d.data.inner.datamap = src->datamap() | bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        try {
+            if (nv)
+                std::uninitialized_copy(
+                    src->values(), src->values() + offset, dst->values());
+            try {
+                new (dst->values() + offset) T{std::move(v)};
+                try {
+                    if (nv)
+                        std::uninitialized_copy(src->values() + offset,
+                                                src->values() + nv,
+                                                dst->values() + offset + 1);
+                } catch (...) {
+                    dst->values()[offset].~T();
+                    throw;
+                }
+            } catch (...) {
+                destroy_n(dst->values(), offset);
+                throw;
+            }
+        } catch (...) {
+            deallocate_inner(dst, n, nv + 1);
+            throw;
+        }
+        inc_nodes(src->children(), n);
+        std::uninitialized_copy(
+            src->children(), src->children() + n, dst->children());
+        return dst;
+    }
+
+    static node_t*
+    make_merged(shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2)
+    {
+        if (shift < max_shift<B>) {
+            auto idx1 = hash1 & (mask<B> << shift);
+            auto idx2 = hash2 & (mask<B> << shift);
+            if (idx1 == idx2) {
+                auto merged = make_merged(
+                    shift + B, std::move(v1), hash1, std::move(v2), hash2);
+                try {
+                    return make_inner_n(1, idx1 >> shift, merged);
+                } catch (...) {
+                    delete_deep_shift(merged, shift + B);
+                    throw;
+                }
+            } else {
+                return make_inner_n(0,
+                                    idx1 >> shift,
+                                    std::move(v1),
+                                    idx2 >> shift,
+                                    std::move(v2));
+            }
+        } else {
+            return make_collision(std::move(v1), std::move(v2));
+        }
+    }
+
+    node_t* inc()
+    {
+        refs(this).inc();
+        return this;
+    }
+
+    const node_t* inc() const
+    {
+        refs(this).inc();
+        return this;
+    }
+
+    bool dec() const { return refs(this).dec(); }
+    void dec_unsafe() const { refs(this).dec_unsafe(); }
+
+    static void inc_nodes(node_t** p, count_t n)
+    {
+        for (auto i = p, e = i + n; i != e; ++i)
+            refs(*i).inc();
+    }
+
+    static void delete_values(values_t* p, count_t n)
+    {
+        assert(p);
+        deallocate_values(p, n);
+    }
+
+    static void delete_inner(node_t* p)
+    {
+        assert(p);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
+        auto vp = p->impl.d.data.inner.values;
+        if (vp && refs(vp).dec())
+            delete_values(vp, popcount(p->datamap()));
+        deallocate_inner(p, popcount(p->nodemap()));
+    }
+
+    static void delete_collision(node_t* p)
+    {
+        assert(p);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::collision);
+        auto n = p->collision_count();
+        deallocate_collision(p, n);
+    }
+
+    static void delete_deep(node_t* p, shift_t s)
+    {
+        if (s == max_depth<B>)
+            delete_collision(p);
+        else {
+            auto fst = p->children();
+            auto lst = fst + popcount(p->nodemap());
+            for (; fst != lst; ++fst)
+                if ((*fst)->dec())
+                    delete_deep(*fst, s + 1);
+            delete_inner(p);
+        }
+    }
+
+    static void delete_deep_shift(node_t* p, shift_t s)
+    {
+        if (s == max_shift<B>)
+            delete_collision(p);
+        else {
+            auto fst = p->children();
+            auto lst = fst + popcount(p->nodemap());
+            for (; fst != lst; ++fst)
+                if ((*fst)->dec())
+                    delete_deep_shift(*fst, s + B);
+            delete_inner(p);
+        }
+    }
+
+    static void deallocate_values(values_t* p, count_t n)
+    {
+        destroy_n((T*) &p->d.buffer, n);
+        heap::deallocate(node_t::sizeof_values_n(n), p);
+    }
+
+    static void deallocate_collision(node_t* p, count_t n)
+    {
+        destroy_n(p->collisions(), n);
+        heap::deallocate(node_t::sizeof_collision_n(n), p);
+    }
+
+    static void deallocate_inner(node_t* p, count_t n)
+    {
+        heap::deallocate(node_t::sizeof_inner_n(n), p);
+    }
+
+    static void deallocate_inner(node_t* p, count_t n, count_t nv)
+    {
+        assert(nv);
+        heap::deallocate(node_t::sizeof_values_n(nv),
+                         p->impl.d.data.inner.values);
+        heap::deallocate(node_t::sizeof_inner_n(n), p);
+    }
+};
+
+} // namespace hamts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/iterator_facade.hpp b/third_party/immer/immer/detail/iterator_facade.hpp
new file mode 100644
index 0000000000..ffc237943e
--- /dev/null
+++ b/third_party/immer/immer/detail/iterator_facade.hpp
@@ -0,0 +1,212 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+namespace immer {
+namespace detail {
+
+struct iterator_core_access
+{
+    template <typename T>
+    static decltype(auto) dereference(T&& x)
+    {
+        return x.dereference();
+    }
+
+    template <typename T>
+    static decltype(auto) increment(T&& x)
+    {
+        return x.increment();
+    }
+
+    template <typename T>
+    static decltype(auto) decrement(T&& x)
+    {
+        return x.decrement();
+    }
+
+    template <typename T1, typename T2>
+    static decltype(auto) equal(T1&& x1, T2&& x2)
+    {
+        return x1.equal(x2);
+    }
+
+    template <typename T, typename D>
+    static decltype(auto) advance(T&& x, D d)
+    {
+        return x.advance(d);
+    }
+
+    template <typename T1, typename T2>
+    static decltype(auto) distance_to(T1&& x1, T2&& x2)
+    {
+        return x1.distance_to(x2);
+    }
+};
+
+/*!
+ * Minimalistic reimplementation of boost::iterator_facade
+ */
+template <typename DerivedT,
+          typename IteratorCategoryT,
+          typename T,
+          typename ReferenceT      = T&,
+          typename DifferenceTypeT = std::ptrdiff_t,
+          typename PointerT        = T*>
+class iterator_facade
+{
+public:
+    using iterator_category = IteratorCategoryT;
+    using value_type        = T;
+    using difference_type   = DifferenceTypeT;
+    using pointer           = PointerT;
+    using reference         = ReferenceT;
+
+protected:
+    using access_t = iterator_core_access;
+
+    constexpr static auto is_random_access =
+        std::is_base_of<std::random_access_iterator_tag,
+                        IteratorCategoryT>::value;
+    constexpr static auto is_bidirectional =
+        std::is_base_of<std::bidirectional_iterator_tag,
+                        IteratorCategoryT>::value;
+
+    class reference_proxy
+    {
+        friend iterator_facade;
+        DerivedT iter_;
+
+        reference_proxy(DerivedT iter)
+            : iter_{std::move(iter)}
+        {}
+
+    public:
+        operator ReferenceT() const { return *iter_; }
+    };
+
+    const DerivedT& derived() const
+    {
+        static_assert(std::is_base_of<iterator_facade, DerivedT>::value,
+                      "must pass a derived thing");
+        return *static_cast<const DerivedT*>(this);
+    }
+    DerivedT& derived()
+    {
+        static_assert(std::is_base_of<iterator_facade, DerivedT>::value,
+                      "must pass a derived thing");
+        return *static_cast<DerivedT*>(this);
+    }
+
+public:
+    ReferenceT operator*() const { return access_t::dereference(derived()); }
+    PointerT operator->() const { return &access_t::dereference(derived()); }
+    reference_proxy operator[](DifferenceTypeT n) const
+    {
+        static_assert(is_random_access, "");
+        return derived() + n;
+    }
+
+    bool operator==(const DerivedT& rhs) const
+    {
+        return access_t::equal(derived(), rhs);
+    }
+    bool operator!=(const DerivedT& rhs) const
+    {
+        return !access_t::equal(derived(), rhs);
+    }
+
+    DerivedT& operator++()
+    {
+        access_t::increment(derived());
+        return derived();
+    }
+    DerivedT operator++(int)
+    {
+        auto tmp = derived();
+        access_t::increment(derived());
+        return tmp;
+    }
+
+    DerivedT& operator--()
+    {
+        static_assert(is_bidirectional || is_random_access, "");
+        access_t::decrement(derived());
+        return derived();
+    }
+    DerivedT operator--(int)
+    {
+        static_assert(is_bidirectional || is_random_access, "");
+        auto tmp = derived();
+        access_t::decrement(derived());
+        return tmp;
+    }
+
+    DerivedT& operator+=(DifferenceTypeT n)
+    {
+        access_t::advance(derived(), n);
+        return derived();
+    }
+    DerivedT& operator-=(DifferenceTypeT n)
+    {
+        access_t::advance(derived(), -n);
+        return derived();
+    }
+
+    DerivedT operator+(DifferenceTypeT n) const
+    {
+        static_assert(is_random_access, "");
+        auto tmp = derived();
+        return tmp += n;
+    }
+    friend DerivedT operator+(DifferenceTypeT n, const DerivedT& i)
+    {
+        static_assert(is_random_access, "");
+        return i + n;
+    }
+    DerivedT operator-(DifferenceTypeT n) const
+    {
+        static_assert(is_random_access, "");
+        auto tmp = derived();
+        return tmp -= n;
+    }
+    DifferenceTypeT operator-(const DerivedT& rhs) const
+    {
+        static_assert(is_random_access, "");
+        return access_t::distance_to(rhs, derived());
+    }
+
+    bool operator<(const DerivedT& rhs) const
+    {
+        static_assert(is_random_access, "");
+        return access_t::distance_to(derived(), rhs) > 0;
+    }
+    bool operator<=(const DerivedT& rhs) const
+    {
+        static_assert(is_random_access, "");
+        return access_t::distance_to(derived(), rhs) >= 0;
+    }
+    bool operator>(const DerivedT& rhs) const
+    {
+        static_assert(is_random_access, "");
+        return access_t::distance_to(derived(), rhs) < 0;
+    }
+    bool operator>=(const DerivedT& rhs) const
+    {
+        static_assert(is_random_access, "");
+        return access_t::distance_to(derived(), rhs) <= 0;
+    }
+};
+
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/bits.hpp b/third_party/immer/immer/detail/rbts/bits.hpp
new file mode 100644
index 0000000000..58d4e3c9c9
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/bits.hpp
@@ -0,0 +1,33 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstdint>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+using bits_t  = std::uint32_t;
+using shift_t = std::uint32_t;
+using count_t = std::uint32_t;
+using size_t  = std::size_t;
+
+template <bits_t B, typename T = count_t>
+constexpr T branches = T{1} << B;
+
+template <bits_t B, typename T = size_t>
+constexpr T mask = branches<B, T> - 1;
+
+template <bits_t B, bits_t BL>
+constexpr shift_t endshift = shift_t{BL} - shift_t{B};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/node.hpp b/third_party/immer/immer/detail/rbts/node.hpp
new file mode 100644
index 0000000000..e54e569636
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/node.hpp
@@ -0,0 +1,932 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/combine_standard_layout.hpp>
+#include <immer/detail/rbts/bits.hpp>
+#include <immer/detail/util.hpp>
+#include <immer/heap/tags.hpp>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
+struct node
+{
+    static constexpr auto bits      = B;
+    static constexpr auto bits_leaf = BL;
+
+    using node_t      = node;
+    using memory      = MemoryPolicy;
+    using heap_policy = typename memory::heap;
+    using transience  = typename memory::transience_t;
+    using refs_t      = typename memory::refcount;
+    using ownee_t     = typename transience::ownee;
+    using edit_t      = typename transience::edit;
+    using value_t     = T;
+
+    static constexpr bool embed_relaxed = memory::prefer_fewer_bigger_objects;
+
+    enum class kind_t
+    {
+        leaf,
+        inner
+    };
+
+    struct relaxed_data_t
+    {
+        count_t count;
+        size_t sizes[branches<B>];
+    };
+
+    using relaxed_data_with_meta_t =
+        combine_standard_layout_t<relaxed_data_t, refs_t, ownee_t>;
+
+    using relaxed_data_no_meta_t = combine_standard_layout_t<relaxed_data_t>;
+
+    using relaxed_t = std::conditional_t<embed_relaxed,
+                                         relaxed_data_no_meta_t,
+                                         relaxed_data_with_meta_t>;
+
+    struct leaf_t
+    {
+        aligned_storage_for<T> buffer;
+    };
+
+    struct inner_t
+    {
+        relaxed_t* relaxed;
+        aligned_storage_for<node_t*> buffer;
+    };
+
+    union data_t
+    {
+        inner_t inner;
+        leaf_t leaf;
+    };
+
+    struct impl_data_t
+    {
+#if IMMER_TAGGED_NODE
+        kind_t kind;
+#endif
+        data_t data;
+    };
+
+    using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>;
+
+    impl_t impl;
+
+    // assume that we need to keep headroom space in the node when we
+    // are doing reference counting, since any node may become
+    // transient when it has only one reference
+    constexpr static bool keep_headroom = !std::is_empty<refs_t>{};
+
+    constexpr static std::size_t sizeof_packed_leaf_n(count_t count)
+    {
+        return immer_offsetof(impl_t, d.data.leaf.buffer) +
+               sizeof(leaf_t::buffer) * count;
+    }
+
+    constexpr static std::size_t sizeof_packed_inner_n(count_t count)
+    {
+        return immer_offsetof(impl_t, d.data.inner.buffer) +
+               sizeof(inner_t::buffer) * count;
+    }
+
+    constexpr static std::size_t sizeof_packed_relaxed_n(count_t count)
+    {
+        return immer_offsetof(relaxed_t, d.sizes) + sizeof(size_t) * count;
+    }
+
+    constexpr static std::size_t sizeof_packed_inner_r_n(count_t count)
+    {
+        return embed_relaxed ? sizeof_packed_inner_n(count) +
+                                   sizeof_packed_relaxed_n(count)
+                             : sizeof_packed_inner_n(count);
+    }
+
+    constexpr static std::size_t max_sizeof_leaf =
+        sizeof_packed_leaf_n(branches<BL>);
+
+    constexpr static std::size_t max_sizeof_inner =
+        sizeof_packed_inner_n(branches<B>);
+
+    constexpr static std::size_t max_sizeof_relaxed =
+        sizeof_packed_relaxed_n(branches<B>);
+
+    constexpr static std::size_t max_sizeof_inner_r =
+        sizeof_packed_inner_r_n(branches<B>);
+
+    constexpr static std::size_t sizeof_inner_n(count_t n)
+    {
+        return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n);
+    }
+
+    constexpr static std::size_t sizeof_inner_r_n(count_t n)
+    {
+        return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n);
+    }
+
+    constexpr static std::size_t sizeof_relaxed_n(count_t n)
+    {
+        return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n);
+    }
+
+    constexpr static std::size_t sizeof_leaf_n(count_t n)
+    {
+        return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n);
+    }
+
+    using heap =
+        typename heap_policy::template optimized<max_sizeof_inner>::type;
+
+#if IMMER_TAGGED_NODE
+    kind_t kind() const { return impl.d.kind; }
+#endif
+
+    relaxed_t* relaxed()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return impl.d.data.inner.relaxed;
+    }
+
+    const relaxed_t* relaxed() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return impl.d.data.inner.relaxed;
+    }
+
+    node_t** inner()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return reinterpret_cast<node_t**>(&impl.d.data.inner.buffer);
+    }
+
+    T* leaf()
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
+        return reinterpret_cast<T*>(&impl.d.data.leaf.buffer);
+    }
+
+    static refs_t& refs(const relaxed_t* x)
+    {
+        return auto_const_cast(get<refs_t>(*x));
+    }
+    static const ownee_t& ownee(const relaxed_t* x) { return get<ownee_t>(*x); }
+    static ownee_t& ownee(relaxed_t* x) { return get<ownee_t>(*x); }
+
+    static refs_t& refs(const node_t* x)
+    {
+        return auto_const_cast(get<refs_t>(x->impl));
+    }
+    static const ownee_t& ownee(const node_t* x)
+    {
+        return get<ownee_t>(x->impl);
+    }
+    static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
+
+    static node_t* make_inner_n(count_t n)
+    {
+        assert(n <= branches<B>);
+        auto m                       = heap::allocate(sizeof_inner_n(n));
+        auto p                       = new (m) node_t;
+        p->impl.d.data.inner.relaxed = nullptr;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::inner;
+#endif
+        return p;
+    }
+
+    static node_t* make_inner_e(edit_t e)
+    {
+        auto m                       = heap::allocate(max_sizeof_inner);
+        auto p                       = new (m) node_t;
+        ownee(p)                     = e;
+        p->impl.d.data.inner.relaxed = nullptr;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::inner;
+#endif
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n)
+    {
+        assert(n <= branches<B>);
+        auto mp = heap::allocate(sizeof_inner_r_n(n));
+        auto mr = static_cast<void*>(nullptr);
+        if (embed_relaxed) {
+            mr = reinterpret_cast<unsigned char*>(mp) + sizeof_inner_n(n);
+        } else {
+            try {
+                mr = heap::allocate(sizeof_relaxed_n(n), norefs_tag{});
+            } catch (...) {
+                heap::deallocate(sizeof_inner_r_n(n), mp);
+                throw;
+            }
+        }
+        auto p                       = new (mp) node_t;
+        auto r                       = new (mr) relaxed_t;
+        r->d.count                   = 0;
+        p->impl.d.data.inner.relaxed = r;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::inner;
+#endif
+        return p;
+    }
+
+    static node_t* make_inner_sr_n(count_t n, relaxed_t* r)
+    {
+        return static_if<embed_relaxed, node_t*>(
+            [&](auto) { return node_t::make_inner_r_n(n); },
+            [&](auto) {
+                auto p =
+                    new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t;
+                assert(r->d.count >= n);
+                node_t::refs(r).inc();
+                p->impl.d.data.inner.relaxed = r;
+#if IMMER_TAGGED_NODE
+                p->impl.d.kind = node_t::kind_t::inner;
+#endif
+                return p;
+            });
+    }
+
+    static node_t* make_inner_r_e(edit_t e)
+    {
+        auto mp = heap::allocate(max_sizeof_inner_r);
+        auto mr = static_cast<void*>(nullptr);
+        if (embed_relaxed) {
+            mr = reinterpret_cast<unsigned char*>(mp) + max_sizeof_inner;
+        } else {
+            try {
+                mr = heap::allocate(max_sizeof_relaxed, norefs_tag{});
+            } catch (...) {
+                heap::deallocate(max_sizeof_inner_r, mp);
+                throw;
+            }
+        }
+        auto p   = new (mp) node_t;
+        auto r   = new (mr) relaxed_t;
+        ownee(p) = e;
+        static_if<!embed_relaxed>([&](auto) { node_t::ownee(r) = e; });
+        r->d.count                   = 0;
+        p->impl.d.data.inner.relaxed = r;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::inner;
+#endif
+        return p;
+    }
+
+    static node_t* make_inner_sr_e(edit_t e, relaxed_t* r)
+    {
+        return static_if<embed_relaxed, node_t*>(
+            [&](auto) { return node_t::make_inner_r_e(e); },
+            [&](auto) {
+                auto p =
+                    new (heap::allocate(node_t::max_sizeof_inner_r)) node_t;
+                node_t::refs(r).inc();
+                p->impl.d.data.inner.relaxed = r;
+                node_t::ownee(p)             = e;
+#if IMMER_TAGGED_NODE
+                p->impl.d.kind = node_t::kind_t::inner;
+#endif
+                return p;
+            });
+    }
+
+    static node_t* make_leaf_n(count_t n)
+    {
+        assert(n <= branches<BL>);
+        auto p = new (heap::allocate(sizeof_leaf_n(n))) node_t;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::leaf;
+#endif
+        return p;
+    }
+
+    static node_t* make_leaf_e(edit_t e)
+    {
+        auto p   = new (heap::allocate(max_sizeof_leaf)) node_t;
+        ownee(p) = e;
+#if IMMER_TAGGED_NODE
+        p->impl.d.kind = node_t::kind_t::leaf;
+#endif
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, node_t* x)
+    {
+        assert(n >= 1);
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
+        return p;
+    }
+
+    static node_t* make_inner_n(edit_t n, node_t* x)
+    {
+        assert(n >= 1);
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
+        return p;
+    }
+
+    static node_t* make_inner_n(count_t n, node_t* x, node_t* y)
+    {
+        assert(n >= 2);
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n, node_t* x)
+    {
+        assert(n >= 1);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        r->d.count    = 1;
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs)
+    {
+        assert(n >= 1);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        r->d.sizes[0] = xs;
+        r->d.count    = 1;
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n, node_t* x, node_t* y)
+    {
+        assert(n >= 2);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.count    = 2;
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y)
+    {
+        assert(n >= 2);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.sizes[0] = xs;
+        r->d.count    = 2;
+        return p;
+    }
+
+    static node_t*
+    make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y, size_t ys)
+    {
+        assert(n >= 2);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.sizes[0] = xs;
+        r->d.sizes[1] = xs + ys;
+        r->d.count    = 2;
+        return p;
+    }
+
+    static node_t* make_inner_r_n(count_t n,
+                                  node_t* x,
+                                  size_t xs,
+                                  node_t* y,
+                                  size_t ys,
+                                  node_t* z,
+                                  size_t zs)
+    {
+        assert(n >= 3);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        p->inner()[2] = z;
+        r->d.sizes[0] = xs;
+        r->d.sizes[1] = xs + ys;
+        r->d.sizes[2] = xs + ys + zs;
+        r->d.count    = 3;
+        return p;
+    }
+
+    template <typename U>
+    static node_t* make_leaf_n(count_t n, U&& x)
+    {
+        assert(n >= 1);
+        auto p = make_leaf_n(n);
+        try {
+            new (p->leaf()) T{std::forward<U>(x)};
+        } catch (...) {
+            heap::deallocate(node_t::sizeof_leaf_n(n), p);
+            throw;
+        }
+        return p;
+    }
+
+    template <typename U>
+    static node_t* make_leaf_e(edit_t e, U&& x)
+    {
+        auto p = make_leaf_e(e);
+        try {
+            new (p->leaf()) T{std::forward<U>(x)};
+        } catch (...) {
+            heap::deallocate(node_t::max_sizeof_leaf, p);
+            throw;
+        }
+        return p;
+    }
+
+    static node_t* make_path(shift_t shift, node_t* node)
+    {
+        IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
+        if (shift == endshift<B, BL>)
+            return node;
+        else {
+            auto n = node_t::make_inner_n(1);
+            try {
+                n->inner()[0] = make_path(shift - B, node);
+            } catch (...) {
+                heap::deallocate(node_t::sizeof_inner_n(1), n);
+                throw;
+            }
+            return n;
+        }
+    }
+
+    static node_t* make_path_e(edit_t e, shift_t shift, node_t* node)
+    {
+        IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
+        if (shift == endshift<B, BL>)
+            return node;
+        else {
+            auto n = node_t::make_inner_e(e);
+            try {
+                n->inner()[0] = make_path_e(e, shift - B, node);
+            } catch (...) {
+                heap::deallocate(node_t::max_sizeof_inner, n);
+                throw;
+            }
+            return n;
+        }
+    }
+
+    static node_t* copy_inner(node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_n(n);
+        inc_nodes(src->inner(), n);
+        std::uninitialized_copy(src->inner(), src->inner() + n, dst->inner());
+        return dst;
+    }
+
+    static node_t* copy_inner_n(count_t allocn, node_t* src, count_t n)
+    {
+        assert(allocn >= n);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_n(allocn);
+        return do_copy_inner(dst, src, n);
+    }
+
+    static node_t* copy_inner_e(edit_t e, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_e(e);
+        return do_copy_inner(dst, src, n);
+    }
+
+    static node_t* do_copy_inner(node_t* dst, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto p = src->inner();
+        inc_nodes(p, n);
+        std::uninitialized_copy(p, p + n, dst->inner());
+        return dst;
+    }
+
+    static node_t* copy_inner_r(node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_r_n(n);
+        return do_copy_inner_r(dst, src, n);
+    }
+
+    static node_t* copy_inner_r_n(count_t allocn, node_t* src, count_t n)
+    {
+        assert(allocn >= n);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_r_n(allocn);
+        return do_copy_inner_r(dst, src, n);
+    }
+
+    static node_t* copy_inner_r_e(edit_t e, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_r_e(e);
+        return do_copy_inner_r(dst, src, n);
+    }
+
+    static node_t* copy_inner_sr_e(edit_t e, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto dst = make_inner_sr_e(e, src->relaxed());
+        return do_copy_inner_sr(dst, src, n);
+    }
+
+    static node_t* do_copy_inner_r(node_t* dst, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto src_r = src->relaxed();
+        auto dst_r = dst->relaxed();
+        inc_nodes(src->inner(), n);
+        std::copy(src->inner(), src->inner() + n, dst->inner());
+        std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+        dst_r->d.count = n;
+        return dst;
+    }
+
+    static node_t* do_copy_inner_sr(node_t* dst, node_t* src, count_t n)
+    {
+        if (embed_relaxed)
+            return do_copy_inner_r(dst, src, n);
+        else {
+            inc_nodes(src->inner(), n);
+            std::copy(src->inner(), src->inner() + n, dst->inner());
+            return dst;
+        }
+    }
+
+    static node_t* copy_leaf(node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
+        auto dst = make_leaf_n(n);
+        try {
+            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
+        } catch (...) {
+            heap::deallocate(node_t::sizeof_leaf_n(n), dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_leaf_e(edit_t e, node_t* src, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
+        auto dst = make_leaf_e(e);
+        try {
+            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
+        } catch (...) {
+            heap::deallocate(node_t::max_sizeof_leaf, dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_leaf_n(count_t allocn, node_t* src, count_t n)
+    {
+        assert(allocn >= n);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
+        auto dst = make_leaf_n(allocn);
+        try {
+            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
+        } catch (...) {
+            heap::deallocate(node_t::sizeof_leaf_n(allocn), dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_leaf(node_t* src1, count_t n1, node_t* src2, count_t n2)
+    {
+        IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
+        auto dst = make_leaf_n(n1 + n2);
+        try {
+            std::uninitialized_copy(
+                src1->leaf(), src1->leaf() + n1, dst->leaf());
+        } catch (...) {
+            heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
+            throw;
+        }
+        try {
+            std::uninitialized_copy(
+                src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
+        } catch (...) {
+            destroy_n(dst->leaf(), n1);
+            heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t*
+    copy_leaf_e(edit_t e, node_t* src1, count_t n1, node_t* src2, count_t n2)
+    {
+        IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
+        auto dst = make_leaf_e(e);
+        try {
+            std::uninitialized_copy(
+                src1->leaf(), src1->leaf() + n1, dst->leaf());
+        } catch (...) {
+            heap::deallocate(max_sizeof_leaf, dst);
+            throw;
+        }
+        try {
+            std::uninitialized_copy(
+                src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
+        } catch (...) {
+            destroy_n(dst->leaf(), n1);
+            heap::deallocate(max_sizeof_leaf, dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_leaf_e(edit_t e, node_t* src, count_t idx, count_t last)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
+        auto dst = make_leaf_e(e);
+        try {
+            std::uninitialized_copy(
+                src->leaf() + idx, src->leaf() + last, dst->leaf());
+        } catch (...) {
+            heap::deallocate(max_sizeof_leaf, dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static node_t* copy_leaf(node_t* src, count_t idx, count_t last)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
+        auto dst = make_leaf_n(last - idx);
+        try {
+            std::uninitialized_copy(
+                src->leaf() + idx, src->leaf() + last, dst->leaf());
+        } catch (...) {
+            heap::deallocate(node_t::sizeof_leaf_n(last - idx), dst);
+            throw;
+        }
+        return dst;
+    }
+
+    template <typename U>
+    static node_t* copy_leaf_emplace(node_t* src, count_t n, U&& x)
+    {
+        auto dst = copy_leaf_n(n + 1, src, n);
+        try {
+            new (dst->leaf() + n) T{std::forward<U>(x)};
+        } catch (...) {
+            destroy_n(dst->leaf(), n);
+            heap::deallocate(node_t::sizeof_leaf_n(n + 1), dst);
+            throw;
+        }
+        return dst;
+    }
+
+    static void delete_inner(node_t* p, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
+        assert(!p->relaxed());
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner
+                                          : node_t::sizeof_inner_n(n),
+                         p);
+    }
+
+    static void delete_inner_e(node_t* p)
+    {
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
+        assert(!p->relaxed());
+        heap::deallocate(node_t::max_sizeof_inner, p);
+    }
+
+    static void delete_inner_any(node_t* p, count_t n)
+    {
+        if (p->relaxed())
+            delete_inner_r(p, n);
+        else
+            delete_inner(p, n);
+    }
+
+    static void delete_inner_r(node_t* p, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
+        auto r = p->relaxed();
+        assert(r);
+        static_if<!embed_relaxed>([&](auto) {
+            if (node_t::refs(r).dec())
+                heap::deallocate(node_t::ownee(r).owned()
+                                     ? node_t::max_sizeof_relaxed
+                                     : node_t::sizeof_relaxed_n(n),
+                                 r);
+        });
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner_r
+                                          : node_t::sizeof_inner_r_n(n),
+                         p);
+    }
+
+    static void delete_inner_r_e(node_t* p)
+    {
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
+        auto r = p->relaxed();
+        assert(r);
+        static_if<!embed_relaxed>([&](auto) {
+            if (node_t::refs(r).dec())
+                heap::deallocate(node_t::max_sizeof_relaxed, r);
+        });
+        heap::deallocate(node_t::max_sizeof_inner_r, p);
+    }
+
+    static void delete_leaf(node_t* p, count_t n)
+    {
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::leaf);
+        destroy_n(p->leaf(), n);
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_leaf
+                                          : node_t::sizeof_leaf_n(n),
+                         p);
+    }
+
+    bool can_mutate(edit_t e) const
+    {
+        return refs(this).unique() || ownee(this).can_mutate(e);
+    }
+
+    bool can_relax() const { return !embed_relaxed || relaxed(); }
+
+    relaxed_t* ensure_mutable_relaxed(edit_t e)
+    {
+        auto src_r = relaxed();
+        return static_if<embed_relaxed, relaxed_t*>(
+            [&](auto) { return src_r; },
+            [&](auto) {
+                if (node_t::refs(src_r).unique() ||
+                    node_t::ownee(src_r).can_mutate(e))
+                    return src_r;
+                else {
+                    if (src_r)
+                        node_t::refs(src_r).dec_unsafe();
+                    auto dst_r = impl.d.data.inner.relaxed =
+                        new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
+                    node_t::ownee(dst_r) = e;
+                    return dst_r;
+                }
+            });
+    }
+
+    relaxed_t* ensure_mutable_relaxed_e(edit_t e, edit_t ec)
+    {
+        auto src_r = relaxed();
+        return static_if<embed_relaxed, relaxed_t*>(
+            [&](auto) { return src_r; },
+            [&](auto) {
+                if (src_r && (node_t::refs(src_r).unique() ||
+                              node_t::ownee(src_r).can_mutate(e))) {
+                    node_t::ownee(src_r) = ec;
+                    return src_r;
+                } else {
+                    if (src_r)
+                        node_t::refs(src_r).dec_unsafe();
+                    auto dst_r = impl.d.data.inner.relaxed =
+                        new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
+                    node_t::ownee(dst_r) = ec;
+                    return dst_r;
+                }
+            });
+    }
+
+    relaxed_t* ensure_mutable_relaxed_n(edit_t e, count_t n)
+    {
+        auto src_r = relaxed();
+        return static_if<embed_relaxed, relaxed_t*>(
+            [&](auto) { return src_r; },
+            [&](auto) {
+                if (node_t::refs(src_r).unique() ||
+                    node_t::ownee(src_r).can_mutate(e))
+                    return src_r;
+                else {
+                    if (src_r)
+                        node_t::refs(src_r).dec_unsafe();
+                    auto dst_r =
+                        new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
+                    std::copy(
+                        src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+                    node_t::ownee(dst_r)             = e;
+                    return impl.d.data.inner.relaxed = dst_r;
+                }
+            });
+    }
+
+    node_t* inc()
+    {
+        refs(this).inc();
+        return this;
+    }
+
+    const node_t* inc() const
+    {
+        refs(this).inc();
+        return this;
+    }
+
+    bool dec() const { return refs(this).dec(); }
+    void dec_unsafe() const { refs(this).dec_unsafe(); }
+
+    static void inc_nodes(node_t** p, count_t n)
+    {
+        for (auto i = p, e = i + n; i != e; ++i)
+            refs(*i).inc();
+    }
+
+#if IMMER_TAGGED_NODE
+    shift_t compute_shift()
+    {
+        if (kind() == kind_t::leaf)
+            return endshift<B, BL>;
+        else
+            return B + inner()[0]->compute_shift();
+    }
+#endif
+
+    bool check(shift_t shift, size_t size)
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        assert(size > 0);
+        if (shift == endshift<B, BL>) {
+            IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
+            assert(size <= branches<BL>);
+        } else if (auto r = relaxed()) {
+            auto count = r->d.count;
+            assert(count > 0);
+            assert(count <= branches<B>);
+            if (r->d.sizes[count - 1] != size) {
+                IMMER_TRACE_F("check");
+                IMMER_TRACE_E(r->d.sizes[count - 1]);
+                IMMER_TRACE_E(size);
+            }
+            assert(r->d.sizes[count - 1] == size);
+            for (auto i = 1; i < count; ++i)
+                assert(r->d.sizes[i - 1] < r->d.sizes[i]);
+            auto last_size = size_t{};
+            for (auto i = 0; i < count; ++i) {
+                assert(inner()[i]->check(shift - B, r->d.sizes[i] - last_size));
+                last_size = r->d.sizes[i];
+            }
+        } else {
+            assert(size <= branches<B> << shift);
+            auto count =
+                (size >> shift) + (size - ((size >> shift) << shift) > 0);
+            assert(count <= branches<B>);
+            if (count) {
+                for (auto i = 1; i < count - 1; ++i)
+                    assert(inner()[i]->check(shift - B, 1 << shift));
+                assert(inner()[count - 1]->check(
+                    shift - B, size - ((count - 1) << shift)));
+            }
+        }
+#endif // IMMER_DEBUG_DEEP_CHECK
+        return true;
+    }
+};
+
+template <typename T, typename MP, bits_t B>
+constexpr bits_t derive_bits_leaf_aux()
+{
+    using node_t               = node<T, MP, B, B>;
+    constexpr auto sizeof_elem = sizeof(T);
+    constexpr auto space =
+        node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0);
+    constexpr auto full_elems = space / sizeof_elem;
+    constexpr auto BL         = log2(full_elems);
+    return BL;
+}
+
+template <typename T, typename MP, bits_t B>
+constexpr bits_t derive_bits_leaf = derive_bits_leaf_aux<T, MP, B>();
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/operations.hpp b/third_party/immer/immer/detail/rbts/operations.hpp
new file mode 100644
index 0000000000..ff703e892b
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/operations.hpp
@@ -0,0 +1,2461 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <algorithm>
+#include <memory>
+#include <numeric>
+#include <utility>
+
+#include <immer/config.hpp>
+#include <immer/detail/rbts/position.hpp>
+#include <immer/detail/rbts/visitor.hpp>
+#include <immer/detail/util.hpp>
+#include <immer/heap/tags.hpp>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T>
+struct array_for_visitor : visitor_base<array_for_visitor<T>>
+{
+    using this_t = array_for_visitor;
+
+    template <typename PosT>
+    static T* visit_inner(PosT&& pos, size_t idx)
+    {
+        return pos.descend(this_t{}, idx);
+    }
+
+    template <typename PosT>
+    static T* visit_leaf(PosT&& pos, size_t)
+    {
+        return pos.node()->leaf();
+    }
+};
+
+template <typename T>
+struct region_for_visitor : visitor_base<region_for_visitor<T>>
+{
+    using this_t   = region_for_visitor;
+    using result_t = std::tuple<T*, size_t, size_t>;
+
+    template <typename PosT>
+    static result_t visit_inner(PosT&& pos, size_t idx)
+    {
+        return pos.towards(this_t{}, idx);
+    }
+
+    template <typename PosT>
+    static result_t visit_leaf(PosT&& pos, size_t idx)
+    {
+        return std::make_tuple(pos.node()->leaf(), pos.index(idx), pos.count());
+    }
+};
+
+template <typename T>
+struct get_visitor : visitor_base<get_visitor<T>>
+{
+    using this_t = get_visitor;
+
+    template <typename PosT>
+    static const T& visit_inner(PosT&& pos, size_t idx)
+    {
+        return pos.descend(this_t{}, idx);
+    }
+
+    template <typename PosT>
+    static const T& visit_leaf(PosT&& pos, size_t idx)
+    {
+        return pos.node()->leaf()[pos.index(idx)];
+    }
+};
+
+struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor>
+{
+    using this_t = for_each_chunk_visitor;
+
+    template <typename Pos, typename Fn>
+    static void visit_inner(Pos&& pos, Fn&& fn)
+    {
+        pos.each(this_t{}, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static void visit_leaf(Pos&& pos, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        fn(data, data + pos.count());
+    }
+};
+
+struct for_each_chunk_p_visitor : visitor_base<for_each_chunk_p_visitor>
+{
+    using this_t = for_each_chunk_p_visitor;
+
+    template <typename Pos, typename Fn>
+    static bool visit_inner(Pos&& pos, Fn&& fn)
+    {
+        return pos.each_pred(this_t{}, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static bool visit_leaf(Pos&& pos, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        return fn(data, data + pos.count());
+    }
+};
+
+struct for_each_chunk_left_visitor : visitor_base<for_each_chunk_left_visitor>
+{
+    using this_t = for_each_chunk_left_visitor;
+
+    template <typename Pos, typename Fn>
+    static void visit_inner(Pos&& pos, size_t last, Fn&& fn)
+    {
+        auto l = pos.index(last);
+        pos.each_left(for_each_chunk_visitor{}, l, fn);
+        pos.towards_oh(this_t{}, last, l, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static void visit_leaf(Pos&& pos, size_t last, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        auto l    = pos.index(last);
+        fn(data, data + l + 1);
+    }
+};
+
+struct for_each_chunk_right_visitor : visitor_base<for_each_chunk_right_visitor>
+{
+    using this_t = for_each_chunk_right_visitor;
+
+    template <typename Pos, typename Fn>
+    static void visit_inner(Pos&& pos, size_t first, Fn&& fn)
+    {
+        auto f = pos.index(first);
+        pos.towards_oh(this_t{}, first, f, fn);
+        pos.each_right(for_each_chunk_visitor{}, f + 1, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static void visit_leaf(Pos&& pos, size_t first, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        auto f    = pos.index(first);
+        fn(data + f, data + pos.count());
+    }
+};
+
+struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor>
+{
+    using this_t = for_each_chunk_i_visitor;
+
+    template <typename Pos, typename Fn>
+    static void visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        // we are going towards *two* indices, so we need to do the
+        // relaxed as a special case to correct the second index
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            if (f == l) {
+                auto sbh = pos.size_before(f);
+                pos.towards_oh_sbh(this_t{}, first, f, sbh, last - sbh, fn);
+            } else {
+                assert(f < l);
+                pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn);
+                pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn);
+                pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn);
+            }
+        }
+    }
+
+    template <typename Pos, typename Fn>
+    static void visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            if (f == l)
+                pos.towards_oh(this_t{}, first, f, last, fn);
+            else {
+                assert(f < l);
+                pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn);
+                pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn);
+                pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn);
+            }
+        }
+    }
+
+    template <typename Pos, typename Fn>
+    static void visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            fn(data + f, data + l + 1);
+        }
+    }
+};
+
+struct for_each_chunk_p_left_visitor
+    : visitor_base<for_each_chunk_p_left_visitor>
+{
+    using this_t = for_each_chunk_p_left_visitor;
+
+    template <typename Pos, typename Fn>
+    static bool visit_inner(Pos&& pos, size_t last, Fn&& fn)
+    {
+        auto l = pos.index(last);
+        return pos.each_pred_left(for_each_chunk_p_visitor{}, l, fn) &&
+               pos.towards_oh(this_t{}, last, l, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static bool visit_leaf(Pos&& pos, size_t last, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        auto l    = pos.index(last);
+        return fn(data, data + l + 1);
+    }
+};
+
+struct for_each_chunk_p_right_visitor
+    : visitor_base<for_each_chunk_p_right_visitor>
+{
+    using this_t = for_each_chunk_p_right_visitor;
+
+    template <typename Pos, typename Fn>
+    static bool visit_inner(Pos&& pos, size_t first, Fn&& fn)
+    {
+        auto f = pos.index(first);
+        return pos.towards_oh(this_t{}, first, f, fn) &&
+               pos.each_pred_right(for_each_chunk_p_visitor{}, f + 1, fn);
+    }
+
+    template <typename Pos, typename Fn>
+    static bool visit_leaf(Pos&& pos, size_t first, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        auto f    = pos.index(first);
+        return fn(data + f, data + pos.count());
+    }
+};
+
+struct for_each_chunk_p_i_visitor : visitor_base<for_each_chunk_p_i_visitor>
+{
+    using this_t = for_each_chunk_p_i_visitor;
+
+    template <typename Pos, typename Fn>
+    static bool visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        // we are going towards *two* indices, so we need to do the
+        // relaxed as a special case to correct the second index
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            if (f == l) {
+                auto sbh = pos.size_before(f);
+                return pos.towards_oh_sbh(
+                    this_t{}, first, f, sbh, last - sbh, fn);
+            } else {
+                assert(f < l);
+                return pos.towards_oh(
+                           for_each_chunk_p_right_visitor{}, first, f, fn) &&
+                       pos.each_pred_i(
+                           for_each_chunk_p_visitor{}, f + 1, l, fn) &&
+                       pos.towards_oh(
+                           for_each_chunk_p_left_visitor{}, last - 1, l, fn);
+            }
+        }
+        return true;
+    }
+
+    template <typename Pos, typename Fn>
+    static bool visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            if (f == l)
+                return pos.towards_oh(this_t{}, first, f, last, fn);
+            else {
+                assert(f < l);
+                return pos.towards_oh(
+                           for_each_chunk_p_right_visitor{}, first, f, fn) &&
+                       pos.each_pred_i(
+                           for_each_chunk_p_visitor{}, f + 1, l, fn) &&
+                       pos.towards_oh(
+                           for_each_chunk_p_left_visitor{}, last - 1, l, fn);
+            }
+        }
+        return true;
+    }
+
+    template <typename Pos, typename Fn>
+    static bool visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn)
+    {
+        auto data = pos.node()->leaf();
+        if (first < last) {
+            auto f = pos.index(first);
+            auto l = pos.index(last - 1);
+            return fn(data + f, data + l + 1);
+        }
+        return true;
+    }
+};
+
+struct equals_visitor : visitor_base<equals_visitor>
+{
+    using this_t = equals_visitor;
+
+    struct this_aux_t : visitor_base<this_aux_t>
+    {
+        template <typename PosR, typename PosL, typename Iter>
+        static bool visit_inner(
+            PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx)
+        {
+            return posl.nth_sub(i, this_t{}, posr, first, idx);
+        }
+
+        template <typename PosR, typename PosL, typename Iter>
+        static bool visit_leaf(
+            PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx)
+        {
+            return posl.nth_sub_leaf(i, this_t{}, posr, first, idx);
+        }
+    };
+
+    struct rrb : visitor_base<rrb>
+    {
+        template <typename PosR, typename Iter, typename Node>
+        static bool visit_node(PosR&& posr,
+                               Iter&& first,
+                               Node* rootl,
+                               shift_t shiftl,
+                               size_t sizel)
+        {
+            assert(shiftl <= posr.shift());
+            return shiftl == posr.shift()
+                       ? visit_maybe_relaxed_sub(rootl,
+                                                 shiftl,
+                                                 sizel,
+                                                 this_t{},
+                                                 posr,
+                                                 first,
+                                                 size_t{})
+                       : posr.first_sub_inner(
+                             rrb{}, first, rootl, shiftl, sizel);
+        }
+    };
+
+    template <typename Iter>
+    static auto equal_chunk_p(Iter&& iter)
+    {
+        return [iter](auto f, auto e) mutable {
+            if (f == &*iter) {
+                iter += e - f;
+                return true;
+            }
+            for (; f != e; ++f, ++iter)
+                if (*f != *iter)
+                    return false;
+            return true;
+        };
+    }
+
+    template <typename PosL, typename PosR, typename Iter>
+    static bool
+    visit_relaxed(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    {
+        auto nl = posl.node();
+        auto nr = posr.node();
+        if (nl == nr)
+            return true;
+        auto cl = posl.count();
+        auto cr = posr.count();
+        assert(cr > 0);
+        auto sbr = size_t{};
+        auto i   = count_t{};
+        auto j   = count_t{};
+        for (; i < cl; ++i) {
+            auto sbl = posl.size_before(i);
+            for (; j + 1 < cr && (sbr = posr.size_before(j)) < sbl; ++j)
+                ;
+            auto res =
+                sbl == sbr
+                    ? posr.nth_sub(j, this_aux_t{}, i, posl, first, idx + sbl)
+                    : posl.nth_sub(i,
+                                   for_each_chunk_p_visitor{},
+                                   this_t::equal_chunk_p(first + (idx + sbl)));
+            if (!res)
+                return false;
+        }
+        return true;
+    }
+
+    template <typename PosL, typename PosR, typename Iter>
+    static std::enable_if_t<is_relaxed_v<PosR>, bool>
+    visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    {
+        return this_t::visit_relaxed(posl, posr, first, idx);
+    }
+
+    template <typename PosL, typename PosR, typename Iter>
+    static std::enable_if_t<!is_relaxed_v<PosR>, bool>
+    visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    {
+        return posl.count() >= posr.count()
+                   ? this_t::visit_regular(posl, posr.node())
+                   : this_t::visit_regular(posr, posl.node());
+    }
+
+    template <typename PosL, typename PosR, typename Iter>
+    static bool visit_leaf(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    {
+        if (posl.node() == posr.node())
+            return true;
+        auto cl = posl.count();
+        auto cr = posr.count();
+        auto mp = std::min(cl, cr);
+        return std::equal(posl.node()->leaf(),
+                          posl.node()->leaf() + mp,
+                          posr.node()->leaf()) &&
+               std::equal(posl.node()->leaf() + mp,
+                          posl.node()->leaf() + posl.count(),
+                          first + (idx + mp));
+    }
+
+    template <typename Pos, typename NodeT>
+    static bool visit_regular(Pos&& pos, NodeT* other)
+    {
+        auto node = pos.node();
+        return node == other || pos.each_pred_zip(this_t{}, other);
+    }
+
+    template <typename Pos, typename NodeT>
+    static bool visit_leaf(Pos&& pos, NodeT* other)
+    {
+        auto node = pos.node();
+        return node == other || std::equal(node->leaf(),
+                                           node->leaf() + pos.count(),
+                                           other->leaf());
+    }
+};
+
+template <typename NodeT>
+struct update_visitor : visitor_base<update_visitor<NodeT>>
+{
+    using node_t = NodeT;
+    using this_t = update_visitor;
+
+    template <typename Pos, typename Fn>
+    static node_t* visit_relaxed(Pos&& pos, size_t idx, Fn&& fn)
+    {
+        auto offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = node_t::make_inner_sr_n(count, pos.relaxed());
+        try {
+            auto child = pos.towards_oh(this_t{}, idx, offset, fn);
+            node_t::do_copy_inner_sr(node, pos.node(), count);
+            node->inner()[offset]->dec_unsafe();
+            node->inner()[offset] = child;
+            return node;
+        } catch (...) {
+            node_t::delete_inner_r(node, count);
+            throw;
+        }
+    }
+
+    template <typename Pos, typename Fn>
+    static node_t* visit_regular(Pos&& pos, size_t idx, Fn&& fn)
+    {
+        auto offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = node_t::make_inner_n(count);
+        try {
+            auto child = pos.towards_oh_ch(this_t{}, idx, offset, count, fn);
+            node_t::do_copy_inner(node, pos.node(), count);
+            node->inner()[offset]->dec_unsafe();
+            node->inner()[offset] = child;
+            return node;
+        } catch (...) {
+            node_t::delete_inner(node, count);
+            throw;
+        }
+    }
+
+    template <typename Pos, typename Fn>
+    static node_t* visit_leaf(Pos&& pos, size_t idx, Fn&& fn)
+    {
+        auto offset = pos.index(idx);
+        auto node   = node_t::copy_leaf(pos.node(), pos.count());
+        try {
+            node->leaf()[offset] =
+                std::forward<Fn>(fn)(std::move(node->leaf()[offset]));
+            return node;
+        } catch (...) {
+            node_t::delete_leaf(node, pos.count());
+            throw;
+        }
+    }
+};
+
+struct dec_visitor : visitor_base<dec_visitor>
+{
+    using this_t = dec_visitor;
+
+    template <typename Pos>
+    static void visit_relaxed(Pos&& p)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each(this_t{});
+            node_t::delete_inner_r(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_regular(Pos&& p)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each(this_t{});
+            node_t::delete_inner(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_leaf(Pos&& p)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            node_t::delete_leaf(node, p.count());
+        }
+    }
+};
+
+template <typename NodeT>
+void dec_leaf(NodeT* node, count_t n)
+{
+    make_leaf_sub_pos(node, n).visit(dec_visitor{});
+}
+
+template <typename NodeT>
+void dec_inner(NodeT* node, shift_t shift, size_t size)
+{
+    visit_maybe_relaxed_sub(node, shift, size, dec_visitor());
+}
+
+template <typename NodeT>
+void dec_relaxed(NodeT* node, shift_t shift)
+{
+    make_relaxed_pos(node, shift, node->relaxed()).visit(dec_visitor());
+}
+
+template <typename NodeT>
+void dec_regular(NodeT* node, shift_t shift, size_t size)
+{
+    make_regular_pos(node, shift, size).visit(dec_visitor());
+}
+
+template <typename NodeT>
+void dec_empty_regular(NodeT* node)
+{
+    make_empty_regular_pos(node).visit(dec_visitor());
+}
+
+template <typename NodeT>
+struct get_mut_visitor : visitor_base<get_mut_visitor<NodeT>>
+{
+    using node_t  = NodeT;
+    using this_t  = get_mut_visitor;
+    using value_t = typename NodeT::value_t;
+    using edit_t  = typename NodeT::edit_t;
+
+    template <typename Pos>
+    static value_t&
+    visit_relaxed(Pos&& pos, size_t idx, edit_t e, node_t** location)
+    {
+        auto offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = pos.node();
+        if (node->can_mutate(e)) {
+            return pos.towards_oh(
+                this_t{}, idx, offset, e, &node->inner()[offset]);
+        } else {
+            auto new_node = node_t::copy_inner_sr_e(e, node, count);
+            try {
+                auto& res = pos.towards_oh(
+                    this_t{}, idx, offset, e, &new_node->inner()[offset]);
+                pos.visit(dec_visitor{});
+                *location = new_node;
+                return res;
+            } catch (...) {
+                dec_relaxed(new_node, pos.shift());
+                throw;
+            }
+        }
+    }
+
+    template <typename Pos>
+    static value_t&
+    visit_regular(Pos&& pos, size_t idx, edit_t e, node_t** location)
+    {
+        assert(pos.node() == *location);
+        auto offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = pos.node();
+        if (node->can_mutate(e)) {
+            return pos.towards_oh_ch(
+                this_t{}, idx, offset, count, e, &node->inner()[offset]);
+        } else {
+            auto new_node = node_t::copy_inner_e(e, node, count);
+            try {
+                auto& res = pos.towards_oh_ch(this_t{},
+                                              idx,
+                                              offset,
+                                              count,
+                                              e,
+                                              &new_node->inner()[offset]);
+                pos.visit(dec_visitor{});
+                *location = new_node;
+                return res;
+            } catch (...) {
+                dec_regular(new_node, pos.shift(), pos.size());
+                throw;
+            }
+        }
+    }
+
+    template <typename Pos>
+    static value_t&
+    visit_leaf(Pos&& pos, size_t idx, edit_t e, node_t** location)
+    {
+        assert(pos.node() == *location);
+        auto node = pos.node();
+        if (node->can_mutate(e)) {
+            return node->leaf()[pos.index(idx)];
+        } else {
+            auto new_node = node_t::copy_leaf_e(e, pos.node(), pos.count());
+            pos.visit(dec_visitor{});
+            *location = new_node;
+            return new_node->leaf()[pos.index(idx)];
+        }
+    }
+};
+
+template <typename NodeT, bool Mutating = true>
+struct push_tail_mut_visitor
+    : visitor_base<push_tail_mut_visitor<NodeT, Mutating>>
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using this_t        = push_tail_mut_visitor;
+    using this_no_mut_t = push_tail_mut_visitor<NodeT, false>;
+    using node_t        = NodeT;
+    using edit_t        = typename NodeT::edit_t;
+
+    template <typename Pos>
+    static node_t* visit_relaxed(Pos&& pos, edit_t e, node_t* tail, count_t ts)
+    {
+        auto node     = pos.node();
+        auto level    = pos.shift();
+        auto idx      = pos.count() - 1;
+        auto children = pos.size(idx);
+        auto new_idx =
+            children == size_t{1} << level || level == BL ? idx + 1 : idx;
+        auto new_child = static_cast<node_t*>(nullptr);
+        auto mutate    = Mutating && node->can_mutate(e);
+
+        if (new_idx >= branches<B>)
+            return nullptr;
+        else if (idx == new_idx) {
+            new_child =
+                mutate ? pos.last_oh_csh(this_t{}, idx, children, e, tail, ts)
+                       : pos.last_oh_csh(
+                             this_no_mut_t{}, idx, children, e, tail, ts);
+            if (!new_child) {
+                if (++new_idx < branches<B>)
+                    new_child = node_t::make_path_e(e, level - B, tail);
+                else
+                    return nullptr;
+            }
+        } else
+            new_child = node_t::make_path_e(e, level - B, tail);
+
+        if (mutate) {
+            auto count             = new_idx + 1;
+            auto relaxed           = node->ensure_mutable_relaxed_n(e, new_idx);
+            node->inner()[new_idx] = new_child;
+            relaxed->d.sizes[new_idx] = pos.size() + ts;
+            relaxed->d.count          = count;
+            return node;
+        } else {
+            try {
+                auto count    = new_idx + 1;
+                auto new_node = node_t::copy_inner_r_e(e, pos.node(), new_idx);
+                auto relaxed  = new_node->relaxed();
+                new_node->inner()[new_idx] = new_child;
+                relaxed->d.sizes[new_idx]  = pos.size() + ts;
+                relaxed->d.count           = count;
+                if (Mutating)
+                    pos.visit(dec_visitor{});
+                return new_node;
+            } catch (...) {
+                auto shift = pos.shift();
+                auto size  = new_idx == idx ? children + ts : ts;
+                if (shift > BL) {
+                    tail->inc();
+                    dec_inner(new_child, shift - B, size);
+                }
+                throw;
+            }
+        }
+    }
+
+    template <typename Pos, typename... Args>
+    static node_t* visit_regular(Pos&& pos, edit_t e, node_t* tail, Args&&...)
+    {
+        assert((pos.size() & mask<BL>) == 0);
+        auto node    = pos.node();
+        auto idx     = pos.index(pos.size() - 1);
+        auto new_idx = pos.index(pos.size() + branches<BL> - 1);
+        auto mutate  = Mutating && node->can_mutate(e);
+        if (mutate) {
+            node->inner()[new_idx] =
+                idx == new_idx ? pos.last_oh(this_t{}, idx, e, tail)
+                               /* otherwise */
+                               : node_t::make_path_e(e, pos.shift() - B, tail);
+            return node;
+        } else {
+            auto new_parent = node_t::make_inner_e(e);
+            try {
+                new_parent->inner()[new_idx] =
+                    idx == new_idx
+                        ? pos.last_oh(this_no_mut_t{}, idx, e, tail)
+                        /* otherwise */
+                        : node_t::make_path_e(e, pos.shift() - B, tail);
+                node_t::do_copy_inner(new_parent, node, new_idx);
+                if (Mutating)
+                    pos.visit(dec_visitor{});
+                return new_parent;
+            } catch (...) {
+                node_t::delete_inner_e(new_parent);
+                throw;
+            }
+        }
+    }
+
+    template <typename Pos, typename... Args>
+    static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+template <typename NodeT>
+struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>>
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using this_t = push_tail_visitor;
+    using node_t = NodeT;
+
+    template <typename Pos>
+    static node_t* visit_relaxed(Pos&& pos, node_t* tail, count_t ts)
+    {
+        auto level    = pos.shift();
+        auto idx      = pos.count() - 1;
+        auto children = pos.size(idx);
+        auto new_idx =
+            children == size_t{1} << level || level == BL ? idx + 1 : idx;
+        auto new_child = static_cast<node_t*>(nullptr);
+        if (new_idx >= branches<B>)
+            return nullptr;
+        else if (idx == new_idx) {
+            new_child = pos.last_oh_csh(this_t{}, idx, children, tail, ts);
+            if (!new_child) {
+                if (++new_idx < branches<B>)
+                    new_child = node_t::make_path(level - B, tail);
+                else
+                    return nullptr;
+            }
+        } else
+            new_child = node_t::make_path(level - B, tail);
+        try {
+            auto count = new_idx + 1;
+            auto new_parent =
+                node_t::copy_inner_r_n(count, pos.node(), new_idx);
+            auto new_relaxed              = new_parent->relaxed();
+            new_parent->inner()[new_idx]  = new_child;
+            new_relaxed->d.sizes[new_idx] = pos.size() + ts;
+            new_relaxed->d.count          = count;
+            return new_parent;
+        } catch (...) {
+            auto shift = pos.shift();
+            auto size  = new_idx == idx ? children + ts : ts;
+            if (shift > BL) {
+                tail->inc();
+                dec_inner(new_child, shift - B, size);
+            }
+            throw;
+        }
+    }
+
+    template <typename Pos, typename... Args>
+    static node_t* visit_regular(Pos&& pos, node_t* tail, Args&&...)
+    {
+        assert((pos.size() & mask<BL>) == 0);
+        auto idx        = pos.index(pos.size() - 1);
+        auto new_idx    = pos.index(pos.size() + branches<BL> - 1);
+        auto count      = new_idx + 1;
+        auto new_parent = node_t::make_inner_n(count);
+        try {
+            new_parent->inner()[new_idx] =
+                idx == new_idx ? pos.last_oh(this_t{}, idx, tail)
+                               /* otherwise */
+                               : node_t::make_path(pos.shift() - B, tail);
+        } catch (...) {
+            node_t::delete_inner(new_parent, count);
+            throw;
+        }
+        return node_t::do_copy_inner(new_parent, pos.node(), new_idx);
+    }
+
+    template <typename Pos, typename... Args>
+    static node_t* visit_leaf(Pos&& pos, node_t* tail, Args&&...)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+struct dec_right_visitor : visitor_base<dec_right_visitor>
+{
+    using this_t = dec_right_visitor;
+    using dec_t  = dec_visitor;
+
+    template <typename Pos>
+    static void visit_relaxed(Pos&& p, count_t idx)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each_right(dec_t{}, idx);
+            node_t::delete_inner_r(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_regular(Pos&& p, count_t idx)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each_right(dec_t{}, idx);
+            node_t::delete_inner(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_leaf(Pos&& p, count_t idx)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+template <typename NodeT, bool Collapse = true, bool Mutating = true>
+struct slice_right_mut_visitor
+    : visitor_base<slice_right_mut_visitor<NodeT, Collapse, Mutating>>
+{
+    using node_t = NodeT;
+    using this_t = slice_right_mut_visitor;
+    using edit_t = typename NodeT::edit_t;
+
+    // returns a new shift, new root, the new tail size and the new tail
+    using result_t             = std::tuple<shift_t, NodeT*, count_t, NodeT*>;
+    using no_collapse_t        = slice_right_mut_visitor<NodeT, false, true>;
+    using no_collapse_no_mut_t = slice_right_mut_visitor<NodeT, false, false>;
+    using no_mut_t = slice_right_mut_visitor<NodeT, Collapse, false>;
+
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    template <typename PosT>
+    static result_t visit_relaxed(PosT&& pos, size_t last, edit_t e)
+    {
+        auto idx    = pos.index(last);
+        auto node   = pos.node();
+        auto mutate = Mutating && node->can_mutate(e);
+        if (Collapse && idx == 0) {
+            auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e)
+                              : pos.towards_oh(no_mut_t{}, last, idx, e);
+            if (Mutating)
+                pos.visit(dec_right_visitor{}, count_t{1});
+            return res;
+        } else {
+            using std::get;
+            auto subs =
+                mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e)
+                       : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e);
+            auto next = get<1>(subs);
+            auto ts   = get<2>(subs);
+            auto tail = get<3>(subs);
+            try {
+                if (next) {
+                    if (mutate) {
+                        auto nodr = node->ensure_mutable_relaxed_n(e, idx);
+                        pos.each_right(dec_visitor{}, idx + 1);
+                        node->inner()[idx] = next;
+                        nodr->d.sizes[idx] = last + 1 - ts;
+                        nodr->d.count      = idx + 1;
+                        return std::make_tuple(pos.shift(), node, ts, tail);
+                    } else {
+                        auto newn = node_t::copy_inner_r_e(e, node, idx);
+                        auto newr = newn->relaxed();
+                        newn->inner()[idx] = next;
+                        newr->d.sizes[idx] = last + 1 - ts;
+                        newr->d.count      = idx + 1;
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
+                    }
+                } else if (idx == 0) {
+                    if (Mutating)
+                        pos.visit(dec_right_visitor{}, count_t{1});
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
+                } else if (Collapse && idx == 1 && pos.shift() > BL) {
+                    auto newn = pos.node()->inner()[0];
+                    if (!mutate)
+                        newn->inc();
+                    if (Mutating)
+                        pos.visit(dec_right_visitor{}, count_t{2});
+                    return std::make_tuple(pos.shift() - B, newn, ts, tail);
+                } else {
+                    if (mutate) {
+                        pos.each_right(dec_visitor{}, idx + 1);
+                        node->ensure_mutable_relaxed_n(e, idx)->d.count = idx;
+                        return std::make_tuple(pos.shift(), node, ts, tail);
+                    } else {
+                        auto newn = node_t::copy_inner_r_e(e, node, idx);
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
+                    }
+                }
+            } catch (...) {
+                assert(!mutate);
+                assert(!next || pos.shift() > BL);
+                if (next)
+                    dec_inner(next,
+                              pos.shift() - B,
+                              last + 1 - ts - pos.size_before(idx));
+                dec_leaf(tail, ts);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_regular(PosT&& pos, size_t last, edit_t e)
+    {
+        auto idx    = pos.index(last);
+        auto node   = pos.node();
+        auto mutate = Mutating && node->can_mutate(e);
+        if (Collapse && idx == 0) {
+            auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e)
+                              : pos.towards_oh(no_mut_t{}, last, idx, e);
+            if (Mutating)
+                pos.visit(dec_right_visitor{}, count_t{1});
+            return res;
+        } else {
+            using std::get;
+            auto subs =
+                mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e)
+                       : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e);
+            auto next = get<1>(subs);
+            auto ts   = get<2>(subs);
+            auto tail = get<3>(subs);
+            try {
+                if (next) {
+                    if (mutate) {
+                        node->inner()[idx] = next;
+                        pos.each_right(dec_visitor{}, idx + 1);
+                        return std::make_tuple(pos.shift(), node, ts, tail);
+                    } else {
+                        auto newn          = node_t::copy_inner_e(e, node, idx);
+                        newn->inner()[idx] = next;
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
+                    }
+                } else if (idx == 0) {
+                    if (Mutating)
+                        pos.visit(dec_right_visitor{}, count_t{1});
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
+                } else if (Collapse && idx == 1 && pos.shift() > BL) {
+                    auto newn = pos.node()->inner()[0];
+                    if (!mutate)
+                        newn->inc();
+                    if (Mutating)
+                        pos.visit(dec_right_visitor{}, count_t{2});
+                    return std::make_tuple(pos.shift() - B, newn, ts, tail);
+                } else {
+                    if (mutate) {
+                        pos.each_right(dec_visitor{}, idx + 1);
+                        return std::make_tuple(pos.shift(), node, ts, tail);
+                    } else {
+                        auto newn = node_t::copy_inner_e(e, node, idx);
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
+                    }
+                }
+            } catch (...) {
+                assert(!mutate);
+                assert(!next || pos.shift() > BL);
+                assert(tail);
+                if (next)
+                    dec_regular(next, pos.shift() - B, last + 1 - ts);
+                dec_leaf(tail, ts);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_leaf(PosT&& pos, size_t last, edit_t e)
+    {
+        auto old_tail_size = pos.count();
+        auto new_tail_size = pos.index(last) + 1;
+        auto node          = pos.node();
+        auto mutate        = Mutating && node->can_mutate(e);
+        if (new_tail_size == old_tail_size) {
+            if (!Mutating)
+                node->inc();
+            return std::make_tuple(0, nullptr, new_tail_size, node);
+        } else if (mutate) {
+            destroy_n(node->leaf() + new_tail_size,
+                      old_tail_size - new_tail_size);
+            return std::make_tuple(0, nullptr, new_tail_size, node);
+        } else {
+            auto new_tail = node_t::copy_leaf_e(e, node, new_tail_size);
+            if (Mutating)
+                pos.visit(dec_visitor{});
+            return std::make_tuple(0, nullptr, new_tail_size, new_tail);
+        }
+    }
+};
+
+template <typename NodeT, bool Collapse = true>
+struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>>
+{
+    using node_t = NodeT;
+    using this_t = slice_right_visitor;
+
+    // returns a new shift, new root, the new tail size and the new tail
+    using result_t      = std::tuple<shift_t, NodeT*, count_t, NodeT*>;
+    using no_collapse_t = slice_right_visitor<NodeT, false>;
+
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    template <typename PosT>
+    static result_t visit_relaxed(PosT&& pos, size_t last)
+    {
+        auto idx = pos.index(last);
+        if (Collapse && idx == 0) {
+            return pos.towards_oh(this_t{}, last, idx);
+        } else {
+            using std::get;
+            auto subs = pos.towards_oh(no_collapse_t{}, last, idx);
+            auto next = get<1>(subs);
+            auto ts   = get<2>(subs);
+            auto tail = get<3>(subs);
+            try {
+                if (next) {
+                    auto count = idx + 1;
+                    auto newn  = node_t::copy_inner_r_n(count, pos.node(), idx);
+                    auto newr  = newn->relaxed();
+                    newn->inner()[idx] = next;
+                    newr->d.sizes[idx] = last + 1 - ts;
+                    newr->d.count      = count;
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
+                } else if (idx == 0) {
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
+                } else if (Collapse && idx == 1 && pos.shift() > BL) {
+                    auto newn = pos.node()->inner()[0];
+                    return std::make_tuple(
+                        pos.shift() - B, newn->inc(), ts, tail);
+                } else {
+                    auto newn = node_t::copy_inner_r(pos.node(), idx);
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
+                }
+            } catch (...) {
+                assert(!next || pos.shift() > BL);
+                if (next)
+                    dec_inner(next,
+                              pos.shift() - B,
+                              last + 1 - ts - pos.size_before(idx));
+                if (tail)
+                    dec_leaf(tail, ts);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_regular(PosT&& pos, size_t last)
+    {
+        auto idx = pos.index(last);
+        if (Collapse && idx == 0) {
+            return pos.towards_oh(this_t{}, last, idx);
+        } else {
+            using std::get;
+            auto subs = pos.towards_oh(no_collapse_t{}, last, idx);
+            auto next = get<1>(subs);
+            auto ts   = get<2>(subs);
+            auto tail = get<3>(subs);
+            try {
+                if (next) {
+                    auto newn = node_t::copy_inner_n(idx + 1, pos.node(), idx);
+                    newn->inner()[idx] = next;
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
+                } else if (idx == 0) {
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
+                } else if (Collapse && idx == 1 && pos.shift() > BL) {
+                    auto newn = pos.node()->inner()[0];
+                    return std::make_tuple(
+                        pos.shift() - B, newn->inc(), ts, tail);
+                } else {
+                    auto newn = node_t::copy_inner_n(idx, pos.node(), idx);
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
+                }
+            } catch (...) {
+                assert(!next || pos.shift() > BL);
+                assert(tail);
+                if (next)
+                    dec_regular(next, pos.shift() - B, last + 1 - ts);
+                dec_leaf(tail, ts);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_leaf(PosT&& pos, size_t last)
+    {
+        auto old_tail_size = pos.count();
+        auto new_tail_size = pos.index(last) + 1;
+        auto new_tail      = new_tail_size == old_tail_size
+                            ? pos.node()->inc()
+                            : node_t::copy_leaf(pos.node(), new_tail_size);
+        return std::make_tuple(0, nullptr, new_tail_size, new_tail);
+    }
+};
+
+struct dec_left_visitor : visitor_base<dec_left_visitor>
+{
+    using this_t = dec_left_visitor;
+    using dec_t  = dec_visitor;
+
+    template <typename Pos>
+    static void visit_relaxed(Pos&& p, count_t idx)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each_left(dec_t{}, idx);
+            node_t::delete_inner_r(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_regular(Pos&& p, count_t idx)
+    {
+        using node_t = node_type<Pos>;
+        auto node    = p.node();
+        if (node->dec()) {
+            p.each_left(dec_t{}, idx);
+            node_t::delete_inner(node, p.count());
+        }
+    }
+
+    template <typename Pos>
+    static void visit_leaf(Pos&& p, count_t idx)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+template <typename NodeT, bool Collapse = true, bool Mutating = true>
+struct slice_left_mut_visitor
+    : visitor_base<slice_left_mut_visitor<NodeT, Collapse, Mutating>>
+{
+    using node_t    = NodeT;
+    using this_t    = slice_left_mut_visitor;
+    using edit_t    = typename NodeT::edit_t;
+    using value_t   = typename NodeT::value_t;
+    using relaxed_t = typename NodeT::relaxed_t;
+    // returns a new shift and new root
+    using result_t = std::tuple<shift_t, NodeT*>;
+
+    using no_collapse_t        = slice_left_mut_visitor<NodeT, false, true>;
+    using no_collapse_no_mut_t = slice_left_mut_visitor<NodeT, false, false>;
+    using no_mut_t             = slice_left_mut_visitor<NodeT, Collapse, false>;
+
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    template <typename PosT>
+    static result_t visit_relaxed(PosT&& pos, size_t first, edit_t e)
+    {
+        auto idx                = pos.subindex(first);
+        auto count              = pos.count();
+        auto node               = pos.node();
+        auto mutate             = Mutating && node->can_mutate(e);
+        auto left_size          = pos.size_before(idx);
+        auto child_size         = pos.size_sbh(idx, left_size);
+        auto dropped_size       = first;
+        auto child_dropped_size = dropped_size - left_size;
+        if (Collapse && pos.shift() > BL && idx == pos.count() - 1) {
+            auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e)
+                            : pos.towards_sub_oh(no_mut_t{}, first, idx, e);
+            if (Mutating)
+                pos.visit(dec_left_visitor{}, idx);
+            return r;
+        } else {
+            using std::get;
+            auto newn = mutate ? (node->ensure_mutable_relaxed(e), node)
+                               : node_t::make_inner_r_e(e);
+            auto newr           = newn->relaxed();
+            auto newcount       = count - idx;
+            auto new_child_size = child_size - child_dropped_size;
+            try {
+                auto subs =
+                    mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e)
+                           : pos.towards_sub_oh(
+                                 no_collapse_no_mut_t{}, first, idx, e);
+                if (mutate)
+                    pos.each_left(dec_visitor{}, idx);
+                pos.copy_sizes(
+                    idx + 1, newcount - 1, new_child_size, newr->d.sizes + 1);
+                std::uninitialized_copy(node->inner() + idx + 1,
+                                        node->inner() + count,
+                                        newn->inner() + 1);
+                newn->inner()[0] = get<1>(subs);
+                newr->d.sizes[0] = new_child_size;
+                newr->d.count    = newcount;
+                if (!mutate) {
+                    node_t::inc_nodes(newn->inner() + 1, newcount - 1);
+                    if (Mutating)
+                        pos.visit(dec_visitor{});
+                }
+                return std::make_tuple(pos.shift(), newn);
+            } catch (...) {
+                if (!mutate)
+                    node_t::delete_inner_r_e(newn);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_regular(PosT&& pos, size_t first, edit_t e)
+    {
+        auto idx    = pos.subindex(first);
+        auto count  = pos.count();
+        auto node   = pos.node();
+        auto mutate = Mutating
+                      // this is more restrictive than actually needed because
+                      // it causes the algorithm to also avoid mutating the leaf
+                      // in place
+                      && !node_t::embed_relaxed && node->can_mutate(e);
+        auto left_size          = pos.size_before(idx);
+        auto child_size         = pos.size_sbh(idx, left_size);
+        auto dropped_size       = first;
+        auto child_dropped_size = dropped_size - left_size;
+        if (Collapse && pos.shift() > BL && idx == pos.count() - 1) {
+            auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e)
+                            : pos.towards_sub_oh(no_mut_t{}, first, idx, e);
+            if (Mutating)
+                pos.visit(dec_left_visitor{}, idx);
+            return r;
+        } else {
+            using std::get;
+            // if possible, we convert the node to a relaxed one
+            // simply by allocating a `relaxed_t` size table for
+            // it... maybe some of this magic should be moved as a
+            // `node<...>` static method...
+            auto newcount = count - idx;
+            auto newn =
+                mutate ? (node->impl.d.data.inner.relaxed = new (
+                              node_t::heap::allocate(node_t::max_sizeof_relaxed,
+                                                     norefs_tag{})) relaxed_t,
+                          node)
+                       : node_t::make_inner_r_e(e);
+            auto newr = newn->relaxed();
+            try {
+                auto subs =
+                    mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e)
+                           : pos.towards_sub_oh(
+                                 no_collapse_no_mut_t{}, first, idx, e);
+                if (mutate)
+                    pos.each_left(dec_visitor{}, idx);
+                newr->d.sizes[0] = child_size - child_dropped_size;
+                pos.copy_sizes(
+                    idx + 1, newcount - 1, newr->d.sizes[0], newr->d.sizes + 1);
+                newr->d.count    = newcount;
+                newn->inner()[0] = get<1>(subs);
+                std::uninitialized_copy(node->inner() + idx + 1,
+                                        node->inner() + count,
+                                        newn->inner() + 1);
+                if (!mutate) {
+                    node_t::inc_nodes(newn->inner() + 1, newcount - 1);
+                    if (Mutating)
+                        pos.visit(dec_visitor{});
+                }
+                return std::make_tuple(pos.shift(), newn);
+            } catch (...) {
+                if (!mutate)
+                    node_t::delete_inner_r_e(newn);
+                else {
+                    // restore the regular node that we were
+                    // attempting to relax...
+                    node_t::heap::deallocate(node_t::max_sizeof_relaxed,
+                                             node->impl.d.data.inner.relaxed);
+                    node->impl.d.data.inner.relaxed = nullptr;
+                }
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_leaf(PosT&& pos, size_t first, edit_t e)
+    {
+        auto node   = pos.node();
+        auto idx    = pos.index(first);
+        auto count  = pos.count();
+        auto mutate = Mutating &&
+                      std::is_nothrow_move_constructible<value_t>::value &&
+                      node->can_mutate(e);
+        if (mutate) {
+            auto data     = node->leaf();
+            auto newcount = count - idx;
+            std::move(data + idx, data + count, data);
+            destroy_n(data + newcount, idx);
+            return std::make_tuple(0, node);
+        } else {
+            auto newn = node_t::copy_leaf_e(e, node, idx, count);
+            if (Mutating)
+                pos.visit(dec_visitor{});
+            return std::make_tuple(0, newn);
+        }
+    }
+};
+
+template <typename NodeT, bool Collapse = true>
+struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>>
+{
+    using node_t = NodeT;
+    using this_t = slice_left_visitor;
+
+    // returns a new shift and new root
+    using result_t      = std::tuple<shift_t, NodeT*>;
+    using no_collapse_t = slice_left_visitor<NodeT, false>;
+
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    template <typename PosT>
+    static result_t visit_inner(PosT&& pos, size_t first)
+    {
+        auto idx                = pos.subindex(first);
+        auto count              = pos.count();
+        auto left_size          = pos.size_before(idx);
+        auto child_size         = pos.size_sbh(idx, left_size);
+        auto dropped_size       = first;
+        auto child_dropped_size = dropped_size - left_size;
+        if (Collapse && pos.shift() > BL && idx == pos.count() - 1) {
+            return pos.towards_sub_oh(this_t{}, first, idx);
+        } else {
+            using std::get;
+            auto n    = pos.node();
+            auto newc = count - idx;
+            auto newn = node_t::make_inner_r_n(newc);
+            try {
+                auto subs     = pos.towards_sub_oh(no_collapse_t{}, first, idx);
+                auto newr     = newn->relaxed();
+                newr->d.count = count - idx;
+                newr->d.sizes[0] = child_size - child_dropped_size;
+                pos.copy_sizes(idx + 1,
+                               newr->d.count - 1,
+                               newr->d.sizes[0],
+                               newr->d.sizes + 1);
+                assert(newr->d.sizes[newr->d.count - 1] ==
+                       pos.size() - dropped_size);
+                newn->inner()[0] = get<1>(subs);
+                std::uninitialized_copy(n->inner() + idx + 1,
+                                        n->inner() + count,
+                                        newn->inner() + 1);
+                node_t::inc_nodes(newn->inner() + 1, newr->d.count - 1);
+                return std::make_tuple(pos.shift(), newn);
+            } catch (...) {
+                node_t::delete_inner_r(newn, newc);
+                throw;
+            }
+        }
+    }
+
+    template <typename PosT>
+    static result_t visit_leaf(PosT&& pos, size_t first)
+    {
+        auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count());
+        return std::make_tuple(0, n);
+    }
+};
+
+template <typename Node>
+struct concat_center_pos
+{
+    static constexpr auto B  = Node::bits;
+    static constexpr auto BL = Node::bits_leaf;
+
+    static constexpr count_t max_children = 3;
+
+    using node_t = Node;
+    using edit_t = typename Node::edit_t;
+
+    shift_t shift_ = 0u;
+    count_t count_ = 0u;
+    node_t* nodes_[max_children];
+    size_t sizes_[max_children];
+
+    auto shift() const { return shift_; }
+
+    concat_center_pos(shift_t s, Node* n0, size_t s0)
+        : shift_{s}
+        , count_{1}
+        , nodes_{n0}
+        , sizes_{s0}
+    {}
+
+    concat_center_pos(shift_t s, Node* n0, size_t s0, Node* n1, size_t s1)
+        : shift_{s}
+        , count_{2}
+        , nodes_{n0, n1}
+        , sizes_{s0, s1}
+    {}
+
+    concat_center_pos(shift_t s,
+                      Node* n0,
+                      size_t s0,
+                      Node* n1,
+                      size_t s1,
+                      Node* n2,
+                      size_t s2)
+        : shift_{s}
+        , count_{3}
+        , nodes_{n0, n1, n2}
+        , sizes_{s0, s1, s2}
+    {}
+
+    template <typename Visitor, typename... Args>
+    void each_sub(Visitor v, Args&&... args)
+    {
+        if (shift_ == BL) {
+            for (auto i = count_t{0}; i < count_; ++i)
+                make_leaf_sub_pos(nodes_[i], sizes_[i]).visit(v, args...);
+        } else {
+            for (auto i = count_t{0}; i < count_; ++i)
+                make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed())
+                    .visit(v, args...);
+        }
+    }
+
+    relaxed_pos<Node> realize() &&
+    {
+        if (count_ > 1) {
+            try {
+                auto result = node_t::make_inner_r_n(count_);
+                auto r      = result->relaxed();
+                r->d.count  = count_;
+                std::copy(nodes_, nodes_ + count_, result->inner());
+                std::copy(sizes_, sizes_ + count_, r->d.sizes);
+                return {result, shift_, r};
+            } catch (...) {
+                each_sub(dec_visitor{});
+                throw;
+            }
+        } else {
+            assert(shift_ >= B + BL);
+            return {nodes_[0], shift_ - B, nodes_[0]->relaxed()};
+        }
+    }
+
+    relaxed_pos<Node> realize_e(edit_t e)
+    {
+        if (count_ > 1) {
+            auto result = node_t::make_inner_r_e(e);
+            auto r      = result->relaxed();
+            r->d.count  = count_;
+            std::copy(nodes_, nodes_ + count_, result->inner());
+            std::copy(sizes_, sizes_ + count_, r->d.sizes);
+            return {result, shift_, r};
+        } else {
+            assert(shift_ >= B + BL);
+            return {nodes_[0], shift_ - B, nodes_[0]->relaxed()};
+        }
+    }
+};
+
+template <typename Node>
+struct concat_merger
+{
+    using node_t             = Node;
+    static constexpr auto B  = Node::bits;
+    static constexpr auto BL = Node::bits_leaf;
+
+    using result_t = concat_center_pos<Node>;
+
+    count_t* curr_;
+    count_t n_;
+    result_t result_;
+
+    concat_merger(shift_t shift, count_t* counts, count_t n)
+        : curr_{counts}
+        , n_{n}
+        , result_{
+              shift + B, node_t::make_inner_r_n(std::min(n_, branches<B>)), 0}
+    {}
+
+    node_t* to_        = {};
+    count_t to_offset_ = {};
+    size_t to_size_    = {};
+
+    void add_child(node_t* p, size_t size)
+    {
+        ++curr_;
+        auto parent  = result_.nodes_[result_.count_ - 1];
+        auto relaxed = parent->relaxed();
+        if (relaxed->d.count == branches<B>) {
+            assert(result_.count_ < result_t::max_children);
+            n_ -= branches<B>;
+            parent  = node_t::make_inner_r_n(std::min(n_, branches<B>));
+            relaxed = parent->relaxed();
+            result_.nodes_[result_.count_] = parent;
+            result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1];
+            ++result_.count_;
+        }
+        auto idx = relaxed->d.count++;
+        result_.sizes_[result_.count_ - 1] += size;
+        relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0);
+        parent->inner()[idx]  = p;
+    };
+
+    template <typename Pos>
+    void merge_leaf(Pos&& p)
+    {
+        auto from       = p.node();
+        auto from_size  = p.size();
+        auto from_count = p.count();
+        assert(from_size);
+        if (!to_ && *curr_ == from_count) {
+            add_child(from, from_size);
+            from->inc();
+        } else {
+            auto from_offset = count_t{};
+            auto from_data   = from->leaf();
+            do {
+                if (!to_) {
+                    to_        = node_t::make_leaf_n(*curr_);
+                    to_offset_ = 0;
+                }
+                auto data = to_->leaf();
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                std::uninitialized_copy(from_data + from_offset,
+                                        from_data + from_offset + to_copy,
+                                        data + to_offset_);
+                to_offset_ += to_copy;
+                from_offset += to_copy;
+                if (*curr_ == to_offset_) {
+                    add_child(to_, to_offset_);
+                    to_ = nullptr;
+                }
+            } while (from_offset != from_count);
+        }
+    }
+
+    template <typename Pos>
+    void merge_inner(Pos&& p)
+    {
+        auto from       = p.node();
+        auto from_size  = p.size();
+        auto from_count = p.count();
+        assert(from_size);
+        if (!to_ && *curr_ == from_count) {
+            add_child(from, from_size);
+            from->inc();
+        } else {
+            auto from_offset = count_t{};
+            auto from_data   = from->inner();
+            do {
+                if (!to_) {
+                    to_        = node_t::make_inner_r_n(*curr_);
+                    to_offset_ = 0;
+                    to_size_   = 0;
+                }
+                auto data = to_->inner();
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                std::uninitialized_copy(from_data + from_offset,
+                                        from_data + from_offset + to_copy,
+                                        data + to_offset_);
+                node_t::inc_nodes(from_data + from_offset, to_copy);
+                auto sizes = to_->relaxed()->d.sizes;
+                p.copy_sizes(
+                    from_offset, to_copy, to_size_, sizes + to_offset_);
+                to_offset_ += to_copy;
+                from_offset += to_copy;
+                to_size_ = sizes[to_offset_ - 1];
+                if (*curr_ == to_offset_) {
+                    to_->relaxed()->d.count = to_offset_;
+                    add_child(to_, to_size_);
+                    to_ = nullptr;
+                }
+            } while (from_offset != from_count);
+        }
+    }
+
+    concat_center_pos<Node> finish() const
+    {
+        assert(!to_);
+        return result_;
+    }
+
+    void abort()
+    {
+        auto shift = result_.shift_ - B;
+        if (to_) {
+            if (shift == BL)
+                node_t::delete_leaf(to_, to_offset_);
+            else {
+                to_->relaxed()->d.count = to_offset_;
+                dec_relaxed(to_, shift - B);
+            }
+        }
+        result_.each_sub(dec_visitor());
+    }
+};
+
+struct concat_merger_visitor : visitor_base<concat_merger_visitor>
+{
+    using this_t = concat_merger_visitor;
+
+    template <typename Pos, typename Merger>
+    static void visit_inner(Pos&& p, Merger& merger)
+    {
+        merger.merge_inner(p);
+    }
+
+    template <typename Pos, typename Merger>
+    static void visit_leaf(Pos&& p, Merger& merger)
+    {
+        merger.merge_leaf(p);
+    }
+};
+
+struct concat_rebalance_plan_fill_visitor
+    : visitor_base<concat_rebalance_plan_fill_visitor>
+{
+    using this_t = concat_rebalance_plan_fill_visitor;
+
+    template <typename Pos, typename Plan>
+    static void visit_node(Pos&& p, Plan& plan)
+    {
+        auto count = p.count();
+        assert(plan.n < Plan::max_children);
+        plan.counts[plan.n++] = count;
+        plan.total += count;
+    }
+};
+
+template <bits_t B, bits_t BL>
+struct concat_rebalance_plan
+{
+    static constexpr auto max_children = 2 * branches<B> + 1;
+
+    count_t counts[max_children];
+    count_t n     = 0u;
+    count_t total = 0u;
+
+    template <typename LPos, typename CPos, typename RPos>
+    void fill(LPos&& lpos, CPos&& cpos, RPos&& rpos)
+    {
+        assert(n == 0u);
+        assert(total == 0u);
+        using visitor_t = concat_rebalance_plan_fill_visitor;
+        lpos.each_left_sub(visitor_t{}, *this);
+        cpos.each_sub(visitor_t{}, *this);
+        rpos.each_right_sub(visitor_t{}, *this);
+    }
+
+    void shuffle(shift_t shift)
+    {
+        // gcc seems to not really understand this code... :(
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+        constexpr count_t rrb_extras    = 2;
+        constexpr count_t rrb_invariant = 1;
+        const auto bits                 = shift == BL ? BL : B;
+        const auto branches             = count_t{1} << bits;
+        const auto optimal              = ((total - 1) >> bits) + 1;
+        count_t i                       = 0;
+        while (n >= optimal + rrb_extras) {
+            // skip ok nodes
+            while (counts[i] > branches - rrb_invariant)
+                i++;
+            // short node, redistribute
+            auto remaining = counts[i];
+            do {
+                auto count = std::min(remaining + counts[i + 1], branches);
+                counts[i]  = count;
+                remaining += counts[i + 1] - count;
+                ++i;
+            } while (remaining > 0);
+            // remove node
+            std::move(counts + i + 1, counts + n, counts + i);
+            --n;
+            --i;
+        }
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    template <typename LPos, typename CPos, typename RPos>
+    concat_center_pos<node_type<CPos>>
+    merge(LPos&& lpos, CPos&& cpos, RPos&& rpos)
+    {
+        using node_t    = node_type<CPos>;
+        using merger_t  = concat_merger<node_t>;
+        using visitor_t = concat_merger_visitor;
+        auto merger     = merger_t{cpos.shift(), counts, n};
+        try {
+            lpos.each_left_sub(visitor_t{}, merger);
+            cpos.each_sub(visitor_t{}, merger);
+            rpos.each_right_sub(visitor_t{}, merger);
+            cpos.each_sub(dec_visitor{});
+            return merger.finish();
+        } catch (...) {
+            merger.abort();
+            throw;
+        }
+    }
+};
+
+template <typename Node, typename LPos, typename CPos, typename RPos>
+concat_center_pos<Node> concat_rebalance(LPos&& lpos, CPos&& cpos, RPos&& rpos)
+{
+    auto plan = concat_rebalance_plan<Node::bits, Node::bits_leaf>{};
+    plan.fill(lpos, cpos, rpos);
+    plan.shuffle(cpos.shift());
+    try {
+        return plan.merge(lpos, cpos, rpos);
+    } catch (...) {
+        cpos.each_sub(dec_visitor{});
+        throw;
+    }
+}
+
+template <typename Node, typename LPos, typename TPos, typename RPos>
+concat_center_pos<Node> concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+{
+    static_assert(Node::bits >= 2, "");
+    assert(lpos.shift() == tpos.shift());
+    assert(lpos.shift() == rpos.shift());
+    assert(lpos.shift() == 0);
+    if (tpos.count() > 0)
+        return {
+            Node::bits_leaf,
+            lpos.node()->inc(),
+            lpos.count(),
+            tpos.node()->inc(),
+            tpos.count(),
+            rpos.node()->inc(),
+            rpos.count(),
+        };
+    else
+        return {
+            Node::bits_leaf,
+            lpos.node()->inc(),
+            lpos.count(),
+            rpos.node()->inc(),
+            rpos.count(),
+        };
+}
+
+template <typename Node>
+struct concat_left_visitor;
+template <typename Node>
+struct concat_right_visitor;
+template <typename Node>
+struct concat_both_visitor;
+
+template <typename Node, typename LPos, typename TPos, typename RPos>
+concat_center_pos<Node> concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+{
+    auto lshift = lpos.shift();
+    auto rshift = rpos.shift();
+    if (lshift > rshift) {
+        auto cpos = lpos.last_sub(concat_left_visitor<Node>{}, tpos, rpos);
+        return concat_rebalance<Node>(lpos, cpos, null_sub_pos{});
+    } else if (lshift < rshift) {
+        auto cpos = rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos);
+        return concat_rebalance<Node>(null_sub_pos{}, cpos, rpos);
+    } else {
+        assert(lshift == rshift);
+        assert(Node::bits_leaf == 0u || lshift > 0);
+        auto cpos = lpos.last_sub(concat_both_visitor<Node>{}, tpos, rpos);
+        return concat_rebalance<Node>(lpos, cpos, rpos);
+    }
+}
+
+template <typename Node>
+struct concat_left_visitor : visitor_base<concat_left_visitor<Node>>
+{
+    using this_t = concat_left_visitor;
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_pos<Node>
+    visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    {
+        return concat_inners<Node>(lpos, tpos, rpos);
+    }
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_pos<Node>
+    visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+template <typename Node>
+struct concat_right_visitor : visitor_base<concat_right_visitor<Node>>
+{
+    using this_t = concat_right_visitor;
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_pos<Node>
+    visit_inner(RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    {
+        return concat_inners<Node>(lpos, tpos, rpos);
+    }
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_pos<Node>
+    visit_leaf(RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    {
+        return concat_leafs<Node>(lpos, tpos, rpos);
+    }
+};
+
+template <typename Node>
+struct concat_both_visitor : visitor_base<concat_both_visitor<Node>>
+{
+    using this_t = concat_both_visitor;
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_pos<Node>
+    visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    {
+        return rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos);
+    }
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_pos<Node>
+    visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    {
+        return rpos.first_sub_leaf(concat_right_visitor<Node>{}, lpos, tpos);
+    }
+};
+
+template <typename Node>
+struct concat_trees_right_visitor
+    : visitor_base<concat_trees_right_visitor<Node>>
+{
+    using this_t = concat_trees_right_visitor;
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_pos<Node>
+    visit_node(RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    {
+        return concat_inners<Node>(lpos, tpos, rpos);
+    }
+};
+
+template <typename Node>
+struct concat_trees_left_visitor : visitor_base<concat_trees_left_visitor<Node>>
+{
+    using this_t = concat_trees_left_visitor;
+
+    template <typename LPos, typename TPos, typename... Args>
+    static concat_center_pos<Node>
+    visit_node(LPos&& lpos, TPos&& tpos, Args&&... args)
+    {
+        return visit_maybe_relaxed_sub(
+            args..., concat_trees_right_visitor<Node>{}, lpos, tpos);
+    }
+};
+
+template <typename Node>
+relaxed_pos<Node> concat_trees(Node* lroot,
+                               shift_t lshift,
+                               size_t lsize,
+                               Node* ltail,
+                               count_t ltcount,
+                               Node* rroot,
+                               shift_t rshift,
+                               size_t rsize)
+{
+    return visit_maybe_relaxed_sub(lroot,
+                                   lshift,
+                                   lsize,
+                                   concat_trees_left_visitor<Node>{},
+                                   make_leaf_pos(ltail, ltcount),
+                                   rroot,
+                                   rshift,
+                                   rsize)
+        .realize();
+}
+
+template <typename Node>
+relaxed_pos<Node> concat_trees(
+    Node* ltail, count_t ltcount, Node* rroot, shift_t rshift, size_t rsize)
+{
+    return make_singleton_regular_sub_pos(ltail, ltcount)
+        .visit(concat_trees_left_visitor<Node>{},
+               empty_leaf_pos<Node>{},
+               rroot,
+               rshift,
+               rsize)
+        .realize();
+}
+
+template <typename Node>
+using concat_center_mut_pos = concat_center_pos<Node>;
+
+template <typename Node>
+struct concat_merger_mut
+{
+    using node_t = Node;
+    using edit_t = typename Node::edit_t;
+
+    static constexpr auto B  = Node::bits;
+    static constexpr auto BL = Node::bits_leaf;
+
+    using result_t = concat_center_pos<Node>;
+
+    edit_t ec_ = {};
+
+    count_t* curr_;
+    count_t n_;
+    result_t result_;
+    count_t count_      = 0;
+    node_t* candidate_  = nullptr;
+    edit_t candidate_e_ = Node::memory::transience_t::noone;
+
+    concat_merger_mut(edit_t ec,
+                      shift_t shift,
+                      count_t* counts,
+                      count_t n,
+                      edit_t candidate_e,
+                      node_t* candidate)
+        : ec_{ec}
+        , curr_{counts}
+        , n_{n}
+        , result_{shift + B, nullptr, 0}
+    {
+        if (candidate) {
+            candidate->ensure_mutable_relaxed_e(candidate_e, ec);
+            result_.nodes_[0] = candidate;
+        } else {
+            result_.nodes_[0] = node_t::make_inner_r_e(ec);
+        }
+    }
+
+    node_t* to_        = {};
+    count_t to_offset_ = {};
+    size_t to_size_    = {};
+
+    void set_candidate(edit_t candidate_e, node_t* candidate)
+    {
+        candidate_   = candidate;
+        candidate_e_ = candidate_e;
+    }
+
+    void add_child(node_t* p, size_t size)
+    {
+        ++curr_;
+        auto parent  = result_.nodes_[result_.count_ - 1];
+        auto relaxed = parent->relaxed();
+        if (count_ == branches<B>) {
+            parent->relaxed()->d.count = count_;
+            assert(result_.count_ < result_t::max_children);
+            n_ -= branches<B>;
+            if (candidate_) {
+                parent = candidate_;
+                parent->ensure_mutable_relaxed_e(candidate_e_, ec_);
+                candidate_ = nullptr;
+            } else
+                parent = node_t::make_inner_r_e(ec_);
+            count_                         = 0;
+            relaxed                        = parent->relaxed();
+            result_.nodes_[result_.count_] = parent;
+            result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1];
+            ++result_.count_;
+        }
+        auto idx = count_++;
+        result_.sizes_[result_.count_ - 1] += size;
+        relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0);
+        parent->inner()[idx]  = p;
+    };
+
+    template <typename Pos>
+    void merge_leaf(Pos&& p, edit_t e)
+    {
+        auto from       = p.node();
+        auto from_size  = p.size();
+        auto from_count = p.count();
+        assert(from_size);
+        if (!to_ && *curr_ == from_count) {
+            add_child(from, from_size);
+        } else {
+            auto from_offset = count_t{};
+            auto from_data   = from->leaf();
+            auto from_mutate = from->can_mutate(e);
+            do {
+                if (!to_) {
+                    if (from_mutate) {
+                        node_t::ownee(from) = ec_;
+                        to_                 = from->inc();
+                        assert(from_count);
+                    } else {
+                        to_ = node_t::make_leaf_e(ec_);
+                    }
+                    to_offset_ = 0;
+                }
+                auto data = to_->leaf();
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                if (from == to_) {
+                    if (from_offset != to_offset_)
+                        std::move(from_data + from_offset,
+                                  from_data + from_offset + to_copy,
+                                  data + to_offset_);
+                } else {
+                    if (!from_mutate)
+                        std::uninitialized_copy(from_data + from_offset,
+                                                from_data + from_offset +
+                                                    to_copy,
+                                                data + to_offset_);
+                    else
+                        detail::uninitialized_move(from_data + from_offset,
+                                                   from_data + from_offset +
+                                                       to_copy,
+                                                   data + to_offset_);
+                }
+                to_offset_ += to_copy;
+                from_offset += to_copy;
+                if (*curr_ == to_offset_) {
+                    add_child(to_, to_offset_);
+                    to_ = nullptr;
+                }
+            } while (from_offset != from_count);
+        }
+    }
+
+    template <typename Pos>
+    void merge_inner(Pos&& p, edit_t e)
+    {
+        auto from       = p.node();
+        auto from_size  = p.size();
+        auto from_count = p.count();
+        assert(from_size);
+        if (!to_ && *curr_ == from_count) {
+            add_child(from, from_size);
+        } else {
+            auto from_offset = count_t{};
+            auto from_data   = from->inner();
+            auto from_mutate = from->can_relax() && from->can_mutate(e);
+            do {
+                if (!to_) {
+                    if (from_mutate) {
+                        node_t::ownee(from) = ec_;
+                        from->ensure_mutable_relaxed_e(e, ec_);
+                        to_ = from;
+                    } else {
+                        to_ = node_t::make_inner_r_e(ec_);
+                    }
+                    to_offset_ = 0;
+                    to_size_   = 0;
+                }
+                auto data = to_->inner();
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                auto sizes = to_->relaxed()->d.sizes;
+                if (from != to_ || from_offset != to_offset_) {
+                    std::copy(from_data + from_offset,
+                              from_data + from_offset + to_copy,
+                              data + to_offset_);
+                    p.copy_sizes(
+                        from_offset, to_copy, to_size_, sizes + to_offset_);
+                }
+                to_offset_ += to_copy;
+                from_offset += to_copy;
+                to_size_ = sizes[to_offset_ - 1];
+                if (*curr_ == to_offset_) {
+                    to_->relaxed()->d.count = to_offset_;
+                    add_child(to_, to_size_);
+                    to_ = nullptr;
+                }
+            } while (from_offset != from_count);
+        }
+    }
+
+    concat_center_pos<Node> finish() const
+    {
+        assert(!to_);
+        result_.nodes_[result_.count_ - 1]->relaxed()->d.count = count_;
+        return result_;
+    }
+
+    void abort()
+    {
+        // We may have mutated stuff the tree in place, leaving
+        // everything in a corrupted state...  It should be possible
+        // to define cleanup properly, but that is a task for some
+        // other day... ;)
+        std::terminate();
+    }
+};
+
+struct concat_merger_mut_visitor : visitor_base<concat_merger_mut_visitor>
+{
+    using this_t = concat_merger_mut_visitor;
+
+    template <typename Pos, typename Merger>
+    static void visit_inner(Pos&& p, Merger& merger, edit_type<Pos> e)
+    {
+        merger.merge_inner(p, e);
+    }
+
+    template <typename Pos, typename Merger>
+    static void visit_leaf(Pos&& p, Merger& merger, edit_type<Pos> e)
+    {
+        merger.merge_leaf(p, e);
+    }
+};
+
+template <bits_t B, bits_t BL>
+struct concat_rebalance_plan_mut : concat_rebalance_plan<B, BL>
+{
+    using this_t = concat_rebalance_plan_mut;
+
+    template <typename LPos, typename CPos, typename RPos>
+    concat_center_mut_pos<node_type<CPos>> merge(edit_type<CPos> ec,
+                                                 edit_type<CPos> el,
+                                                 LPos&& lpos,
+                                                 CPos&& cpos,
+                                                 edit_type<CPos> er,
+                                                 RPos&& rpos)
+    {
+        using node_t    = node_type<CPos>;
+        using merger_t  = concat_merger_mut<node_t>;
+        using visitor_t = concat_merger_mut_visitor;
+        auto lnode      = ((node_t*) lpos.node());
+        auto rnode      = ((node_t*) rpos.node());
+        auto lmut2      = lnode && lnode->can_relax() && lnode->can_mutate(el);
+        auto rmut2      = rnode && rnode->can_relax() && rnode->can_mutate(er);
+        auto merger     = merger_t{ec,
+                               cpos.shift(),
+                               this->counts,
+                               this->n,
+                               el,
+                               lmut2 ? lnode : nullptr};
+        try {
+            lpos.each_left_sub(visitor_t{}, merger, el);
+            cpos.each_sub(visitor_t{}, merger, ec);
+            if (rmut2)
+                merger.set_candidate(er, rnode);
+            rpos.each_right_sub(visitor_t{}, merger, er);
+            return merger.finish();
+        } catch (...) {
+            merger.abort();
+            throw;
+        }
+    }
+};
+
+template <typename Node, typename LPos, typename CPos, typename RPos>
+concat_center_pos<Node> concat_rebalance_mut(edit_type<Node> ec,
+                                             edit_type<Node> el,
+                                             LPos&& lpos,
+                                             CPos&& cpos,
+                                             edit_type<Node> er,
+                                             RPos&& rpos)
+{
+    auto plan = concat_rebalance_plan_mut<Node::bits, Node::bits_leaf>{};
+    plan.fill(lpos, cpos, rpos);
+    plan.shuffle(cpos.shift());
+    return plan.merge(ec, el, lpos, cpos, er, rpos);
+}
+
+template <typename Node, typename LPos, typename TPos, typename RPos>
+concat_center_mut_pos<Node> concat_leafs_mut(edit_type<Node> ec,
+                                             edit_type<Node> el,
+                                             LPos&& lpos,
+                                             TPos&& tpos,
+                                             edit_type<Node> er,
+                                             RPos&& rpos)
+{
+    static_assert(Node::bits >= 2, "");
+    assert(lpos.shift() == tpos.shift());
+    assert(lpos.shift() == rpos.shift());
+    assert(lpos.shift() == 0);
+    if (tpos.count() > 0)
+        return {
+            Node::bits_leaf,
+            lpos.node(),
+            lpos.count(),
+            tpos.node(),
+            tpos.count(),
+            rpos.node(),
+            rpos.count(),
+        };
+    else
+        return {
+            Node::bits_leaf,
+            lpos.node(),
+            lpos.count(),
+            rpos.node(),
+            rpos.count(),
+        };
+}
+
+template <typename Node>
+struct concat_left_mut_visitor;
+template <typename Node>
+struct concat_right_mut_visitor;
+template <typename Node>
+struct concat_both_mut_visitor;
+
+template <typename Node, typename LPos, typename TPos, typename RPos>
+concat_center_mut_pos<Node> concat_inners_mut(edit_type<Node> ec,
+                                              edit_type<Node> el,
+                                              LPos&& lpos,
+                                              TPos&& tpos,
+                                              edit_type<Node> er,
+                                              RPos&& rpos)
+{
+    auto lshift = lpos.shift();
+    auto rshift = rpos.shift();
+    // lpos.node() can be null it is a singleton_regular_sub_pos<...>,
+    // this is, when the tree is just a tail...
+    if (lshift > rshift) {
+        auto cpos = lpos.last_sub(
+            concat_left_mut_visitor<Node>{}, ec, el, tpos, er, rpos);
+        return concat_rebalance_mut<Node>(
+            ec, el, lpos, cpos, er, null_sub_pos{});
+    } else if (lshift < rshift) {
+        auto cpos = rpos.first_sub(
+            concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er);
+        return concat_rebalance_mut<Node>(
+            ec, el, null_sub_pos{}, cpos, er, rpos);
+    } else {
+        assert(lshift == rshift);
+        assert(Node::bits_leaf == 0u || lshift > 0);
+        auto cpos = lpos.last_sub(
+            concat_both_mut_visitor<Node>{}, ec, el, tpos, er, rpos);
+        return concat_rebalance_mut<Node>(ec, el, lpos, cpos, er, rpos);
+    }
+}
+
+template <typename Node>
+struct concat_left_mut_visitor : visitor_base<concat_left_mut_visitor<Node>>
+{
+    using this_t = concat_left_mut_visitor;
+    using edit_t = typename Node::edit_t;
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_mut_pos<Node> visit_inner(
+        LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos)
+    {
+        return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos);
+    }
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_mut_pos<Node> visit_leaf(
+        LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos)
+    {
+        IMMER_UNREACHABLE;
+    }
+};
+
+template <typename Node>
+struct concat_right_mut_visitor : visitor_base<concat_right_mut_visitor<Node>>
+{
+    using this_t = concat_right_mut_visitor;
+    using edit_t = typename Node::edit_t;
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_mut_pos<Node> visit_inner(
+        RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er)
+    {
+        return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos);
+    }
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_mut_pos<Node> visit_leaf(
+        RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er)
+    {
+        return concat_leafs_mut<Node>(ec, el, lpos, tpos, er, rpos);
+    }
+};
+
+template <typename Node>
+struct concat_both_mut_visitor : visitor_base<concat_both_mut_visitor<Node>>
+{
+    using this_t = concat_both_mut_visitor;
+    using edit_t = typename Node::edit_t;
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_mut_pos<Node> visit_inner(
+        LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos)
+    {
+        return rpos.first_sub(
+            concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er);
+    }
+
+    template <typename LPos, typename TPos, typename RPos>
+    static concat_center_mut_pos<Node> visit_leaf(
+        LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos)
+    {
+        return rpos.first_sub_leaf(
+            concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er);
+    }
+};
+
+template <typename Node>
+struct concat_trees_right_mut_visitor
+    : visitor_base<concat_trees_right_mut_visitor<Node>>
+{
+    using this_t = concat_trees_right_mut_visitor;
+    using edit_t = typename Node::edit_t;
+
+    template <typename RPos, typename LPos, typename TPos>
+    static concat_center_mut_pos<Node> visit_node(
+        RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er)
+    {
+        return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos);
+    }
+};
+
+template <typename Node>
+struct concat_trees_left_mut_visitor
+    : visitor_base<concat_trees_left_mut_visitor<Node>>
+{
+    using this_t = concat_trees_left_mut_visitor;
+    using edit_t = typename Node::edit_t;
+
+    template <typename LPos, typename TPos, typename... Args>
+    static concat_center_mut_pos<Node> visit_node(LPos&& lpos,
+                                                  edit_t ec,
+                                                  edit_t el,
+                                                  TPos&& tpos,
+                                                  edit_t er,
+                                                  Args&&... args)
+    {
+        return visit_maybe_relaxed_sub(args...,
+                                       concat_trees_right_mut_visitor<Node>{},
+                                       ec,
+                                       el,
+                                       lpos,
+                                       tpos,
+                                       er);
+    }
+};
+
+template <typename Node>
+relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec,
+                                   edit_type<Node> el,
+                                   Node* lroot,
+                                   shift_t lshift,
+                                   size_t lsize,
+                                   Node* ltail,
+                                   count_t ltcount,
+                                   edit_type<Node> er,
+                                   Node* rroot,
+                                   shift_t rshift,
+                                   size_t rsize)
+{
+    return visit_maybe_relaxed_sub(lroot,
+                                   lshift,
+                                   lsize,
+                                   concat_trees_left_mut_visitor<Node>{},
+                                   ec,
+                                   el,
+                                   make_leaf_pos(ltail, ltcount),
+                                   er,
+                                   rroot,
+                                   rshift,
+                                   rsize)
+        .realize_e(ec);
+}
+
+template <typename Node>
+relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec,
+                                   edit_type<Node> el,
+                                   Node* ltail,
+                                   count_t ltcount,
+                                   edit_type<Node> er,
+                                   Node* rroot,
+                                   shift_t rshift,
+                                   size_t rsize)
+{
+    return make_singleton_regular_sub_pos(ltail, ltcount)
+        .visit(concat_trees_left_mut_visitor<Node>{},
+               ec,
+               el,
+               empty_leaf_pos<Node>{},
+               er,
+               rroot,
+               rshift,
+               rsize)
+        .realize_e(ec);
+}
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/position.hpp b/third_party/immer/immer/detail/rbts/position.hpp
new file mode 100644
index 0000000000..e9472b2940
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/position.hpp
@@ -0,0 +1,1977 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/detail/rbts/bits.hpp>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename Pos>
+constexpr auto bits = std::decay_t<Pos>::node_t::bits;
+
+template <typename Pos>
+constexpr auto bits_leaf = std::decay_t<Pos>::node_t::bits_leaf;
+
+template <typename Pos>
+using node_type = typename std::decay<Pos>::type::node_t;
+
+template <typename Pos>
+using edit_type = typename std::decay<Pos>::type::node_t::edit_t;
+
+template <typename NodeT>
+struct empty_regular_pos
+{
+    using node_t = NodeT;
+    node_t* node_;
+
+    count_t count() const { return 0; }
+    node_t* node() const { return node_; }
+    shift_t shift() const { return 0; }
+    size_t size() const { return 0; }
+
+    template <typename Visitor, typename... Args>
+    void each(Visitor, Args&&...)
+    {}
+    template <typename Visitor, typename... Args>
+    bool each_pred(Visitor, Args&&...)
+    {
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+empty_regular_pos<NodeT> make_empty_regular_pos(NodeT* node)
+{
+    return {node};
+}
+
+template <typename NodeT>
+struct empty_leaf_pos
+{
+    using node_t = NodeT;
+    node_t* node_;
+
+    count_t count() const { return 0; }
+    node_t* node() const { return node_; }
+    shift_t shift() const { return 0; }
+    size_t size() const { return 0; }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+empty_leaf_pos<NodeT> make_empty_leaf_pos(NodeT* node)
+{
+    assert(node);
+    return {node};
+}
+
+template <typename NodeT>
+struct leaf_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+    size_t size_;
+
+    count_t count() const { return index(size_ - 1) + 1; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
+    shift_t shift() const { return 0; }
+    count_t index(size_t idx) const { return idx & mask<BL>; }
+    count_t subindex(size_t idx) const { return idx; }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+leaf_pos<NodeT> make_leaf_pos(NodeT* node, size_t size)
+{
+    assert(node);
+    assert(size > 0);
+    return {node, size};
+}
+
+template <typename NodeT>
+struct leaf_sub_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+    count_t count_;
+
+    count_t count() const { return count_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return count_; }
+    shift_t shift() const { return 0; }
+    count_t index(size_t idx) const { return idx & mask<BL>; }
+    count_t subindex(size_t idx) const { return idx; }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+leaf_sub_pos<NodeT> make_leaf_sub_pos(NodeT* node, count_t count)
+{
+    assert(node);
+    assert(count <= branches<NodeT::bits_leaf>);
+    return {node, count};
+}
+
+template <typename NodeT>
+struct leaf_descent_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+
+    node_t* node() const { return node_; }
+    shift_t shift() const { return 0; }
+    count_t index(size_t idx) const { return idx & mask<BL>; }
+
+    template <typename... Args>
+    decltype(auto) descend(Args&&...)
+    {}
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+leaf_descent_pos<NodeT> make_leaf_descent_pos(NodeT* node)
+{
+    assert(node);
+    return {node};
+}
+
+template <typename NodeT>
+struct full_leaf_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+
+    count_t count() const { return branches<BL>; }
+    node_t* node() const { return node_; }
+    size_t size() const { return branches<BL>; }
+    shift_t shift() const { return 0; }
+    count_t index(size_t idx) const { return idx & mask<BL>; }
+    count_t subindex(size_t idx) const { return idx; }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+full_leaf_pos<NodeT> make_full_leaf_pos(NodeT* node)
+{
+    assert(node);
+    return {node};
+}
+
+template <typename NodeT>
+struct regular_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+    shift_t shift_;
+    size_t size_;
+
+    count_t count() const { return index(size_ - 1) + 1; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
+    shift_t shift() const { return shift_; }
+    count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
+    count_t subindex(size_t idx) const { return idx >> shift_; }
+    size_t this_size() const
+    {
+        return ((size_ - 1) & ~(~size_t{} << (shift_ + B))) + 1;
+    }
+
+    template <typename Visitor, typename... Args>
+    void each(Visitor v, Args&&... args)
+    {
+        return each_regular(*this, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred(Visitor v, Args&&... args)
+    {
+        return each_pred_regular(*this, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
+    {
+        return each_pred_zip_regular(*this, v, other, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_pred_i_regular(*this, v, i, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_pred_right_regular(*this, v, start, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_left(Visitor v, count_t n, Args&&... args)
+    {
+        return each_pred_left_regular(*this, v, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_i_regular(*this, v, i, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_right_regular(*this, v, start, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left(Visitor v, count_t n, Args&&... args)
+    {
+        return each_left_regular(*this, v, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, index(idx), count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards_oh_ch(Visitor v,
+                                 size_t idx,
+                                 count_t offset_hint,
+                                 count_t count_hint,
+                                 Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_sub_oh_regular(*this, v, idx, offset_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args)
+    {
+        return last_oh_regular(*this, v, offset_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename Pos, typename Visitor, typename... Args>
+void each_regular(Pos&& p, Visitor v, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    auto n            = p.node()->inner();
+    auto last         = p.count() - 1;
+    auto e            = n + last;
+    if (p.shift() == BL) {
+        for (; n != e; ++n) {
+            IMMER_PREFETCH(n + 1);
+            make_full_leaf_pos(*n).visit(v, args...);
+        }
+        make_leaf_pos(*n, p.size()).visit(v, args...);
+    } else {
+        auto ss = p.shift() - B;
+        for (; n != e; ++n)
+            make_full_pos(*n, ss).visit(v, args...);
+        make_regular_pos(*n, ss, p.size()).visit(v, args...);
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+bool each_pred_regular(Pos&& p, Visitor v, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    auto n            = p.node()->inner();
+    auto last         = p.count() - 1;
+    auto e            = n + last;
+    if (p.shift() == BL) {
+        for (; n != e; ++n) {
+            IMMER_PREFETCH(n + 1);
+            if (!make_full_leaf_pos(*n).visit(v, args...))
+                return false;
+        }
+        return make_leaf_pos(*n, p.size()).visit(v, args...);
+    } else {
+        auto ss = p.shift() - B;
+        for (; n != e; ++n)
+            if (!make_full_pos(*n, ss).visit(v, args...))
+                return false;
+        return make_regular_pos(*n, ss, p.size()).visit(v, args...);
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+bool each_pred_zip_regular(Pos&& p,
+                           Visitor v,
+                           node_type<Pos>* other,
+                           Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+
+    auto n    = p.node()->inner();
+    auto n2   = other->inner();
+    auto last = p.count() - 1;
+    auto e    = n + last;
+    if (p.shift() == BL) {
+        for (; n != e; ++n, ++n2) {
+            IMMER_PREFETCH(n + 1);
+            IMMER_PREFETCH(n2 + 1);
+            if (!make_full_leaf_pos(*n).visit(v, *n2, args...))
+                return false;
+        }
+        return make_leaf_pos(*n, p.size()).visit(v, *n2, args...);
+    } else {
+        auto ss = p.shift() - B;
+        for (; n != e; ++n, ++n2)
+            if (!make_full_pos(*n, ss).visit(v, *n2, args...))
+                return false;
+        return make_regular_pos(*n, ss, p.size()).visit(v, *n2, args...);
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+bool each_pred_i_regular(
+    Pos&& p, Visitor v, count_t f, count_t l, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+
+    if (p.shift() == BL) {
+        if (l > f) {
+            if (l < p.count()) {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l;
+                for (; n < e; ++n) {
+                    IMMER_PREFETCH(n + 1);
+                    if (!make_full_leaf_pos(*n).visit(v, args...))
+                        return false;
+                }
+            } else {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l - 1;
+                for (; n < e; ++n) {
+                    IMMER_PREFETCH(n + 1);
+                    if (!make_full_leaf_pos(*n).visit(v, args...))
+                        return false;
+                }
+                if (!make_leaf_pos(*n, p.size()).visit(v, args...))
+                    return false;
+            }
+        }
+    } else {
+        if (l > f) {
+            auto ss = p.shift() - B;
+            if (l < p.count()) {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l;
+                for (; n < e; ++n)
+                    if (!make_full_pos(*n, ss).visit(v, args...))
+                        return false;
+            } else {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l - 1;
+                for (; n < e; ++n)
+                    if (!make_full_pos(*n, ss).visit(v, args...))
+                        return false;
+                if (!make_regular_pos(*n, ss, p.size()).visit(v, args...))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+bool each_pred_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    assert(last < p.count());
+    if (p.shift() == BL) {
+        auto n = p.node()->inner();
+        auto e = n + last;
+        for (; n != e; ++n) {
+            IMMER_PREFETCH(n + 1);
+            if (!make_full_leaf_pos(*n).visit(v, args...))
+                return false;
+        }
+    } else {
+        auto n  = p.node()->inner();
+        auto e  = n + last;
+        auto ss = p.shift() - B;
+        for (; n != e; ++n)
+            if (!make_full_pos(*n, ss).visit(v, args...))
+                return false;
+    }
+    return true;
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+bool each_pred_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+
+    if (p.shift() == BL) {
+        auto n    = p.node()->inner() + start;
+        auto last = p.count() - 1;
+        auto e    = p.node()->inner() + last;
+        if (n <= e) {
+            for (; n != e; ++n) {
+                IMMER_PREFETCH(n + 1);
+                if (!make_full_leaf_pos(*n).visit(v, args...))
+                    return false;
+            }
+            if (!make_leaf_pos(*n, p.size()).visit(v, args...))
+                return false;
+        }
+    } else {
+        auto n    = p.node()->inner() + start;
+        auto last = p.count() - 1;
+        auto e    = p.node()->inner() + last;
+        auto ss   = p.shift() - B;
+        if (n <= e) {
+            for (; n != e; ++n)
+                if (!make_full_pos(*n, ss).visit(v, args...))
+                    return false;
+            if (!make_regular_pos(*n, ss, p.size()).visit(v, args...))
+                return false;
+        }
+    }
+    return true;
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+void each_i_regular(Pos&& p, Visitor v, count_t f, count_t l, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+
+    if (p.shift() == BL) {
+        if (l > f) {
+            if (l < p.count()) {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l;
+                for (; n < e; ++n) {
+                    IMMER_PREFETCH(n + 1);
+                    make_full_leaf_pos(*n).visit(v, args...);
+                }
+            } else {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l - 1;
+                for (; n < e; ++n) {
+                    IMMER_PREFETCH(n + 1);
+                    make_full_leaf_pos(*n).visit(v, args...);
+                }
+                make_leaf_pos(*n, p.size()).visit(v, args...);
+            }
+        }
+    } else {
+        if (l > f) {
+            auto ss = p.shift() - B;
+            if (l < p.count()) {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l;
+                for (; n < e; ++n)
+                    make_full_pos(*n, ss).visit(v, args...);
+            } else {
+                auto n = p.node()->inner() + f;
+                auto e = p.node()->inner() + l - 1;
+                for (; n < e; ++n)
+                    make_full_pos(*n, ss).visit(v, args...);
+                make_regular_pos(*n, ss, p.size()).visit(v, args...);
+            }
+        }
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+void each_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    assert(last < p.count());
+    if (p.shift() == BL) {
+        auto n = p.node()->inner();
+        auto e = n + last;
+        for (; n != e; ++n) {
+            IMMER_PREFETCH(n + 1);
+            make_full_leaf_pos(*n).visit(v, args...);
+        }
+    } else {
+        auto n  = p.node()->inner();
+        auto e  = n + last;
+        auto ss = p.shift() - B;
+        for (; n != e; ++n)
+            make_full_pos(*n, ss).visit(v, args...);
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+
+    if (p.shift() == BL) {
+        auto n    = p.node()->inner() + start;
+        auto last = p.count() - 1;
+        auto e    = p.node()->inner() + last;
+        if (n <= e) {
+            for (; n != e; ++n) {
+                IMMER_PREFETCH(n + 1);
+                make_full_leaf_pos(*n).visit(v, args...);
+            }
+            make_leaf_pos(*n, p.size()).visit(v, args...);
+        }
+    } else {
+        auto n    = p.node()->inner() + start;
+        auto last = p.count() - 1;
+        auto e    = p.node()->inner() + last;
+        auto ss   = p.shift() - B;
+        if (n <= e) {
+            for (; n != e; ++n)
+                make_full_pos(*n, ss).visit(v, args...);
+            make_regular_pos(*n, ss, p.size()).visit(v, args...);
+        }
+    }
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+decltype(auto) towards_oh_ch_regular(Pos&& p,
+                                     Visitor v,
+                                     size_t idx,
+                                     count_t offset_hint,
+                                     count_t count_hint,
+                                     Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    assert(offset_hint == p.index(idx));
+    assert(count_hint == p.count());
+    auto is_leaf = p.shift() == BL;
+    auto child   = p.node()->inner()[offset_hint];
+    auto is_full = offset_hint + 1 != count_hint;
+    return is_full
+               ? (is_leaf ? make_full_leaf_pos(child).visit(v, idx, args...)
+                          : make_full_pos(child, p.shift() - B)
+                                .visit(v, idx, args...))
+               : (is_leaf
+                      ? make_leaf_pos(child, p.size()).visit(v, idx, args...)
+                      : make_regular_pos(child, p.shift() - B, p.size())
+                            .visit(v, idx, args...));
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+decltype(auto) towards_sub_oh_regular(
+    Pos&& p, Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+{
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    assert(offset_hint == p.index(idx));
+    auto is_leaf = p.shift() == BL;
+    auto child   = p.node()->inner()[offset_hint];
+    auto lsize   = offset_hint << p.shift();
+    auto size    = p.this_size();
+    auto is_full = (size - lsize) >= (size_t{1} << p.shift());
+    return is_full
+               ? (is_leaf
+                      ? make_full_leaf_pos(child).visit(v, idx - lsize, args...)
+                      : make_full_pos(child, p.shift() - B)
+                            .visit(v, idx - lsize, args...))
+               : (is_leaf
+                      ? make_leaf_sub_pos(child, size - lsize)
+                            .visit(v, idx - lsize, args...)
+                      : make_regular_sub_pos(child, p.shift() - B, size - lsize)
+                            .visit(v, idx - lsize, args...));
+}
+
+template <typename Pos, typename Visitor, typename... Args>
+decltype(auto)
+last_oh_regular(Pos&& p, Visitor v, count_t offset_hint, Args&&... args)
+{
+    assert(offset_hint == p.count() - 1);
+    constexpr auto B  = bits<Pos>;
+    constexpr auto BL = bits_leaf<Pos>;
+    auto child        = p.node()->inner()[offset_hint];
+    auto is_leaf      = p.shift() == BL;
+    return is_leaf ? make_leaf_pos(child, p.size()).visit(v, args...)
+                   : make_regular_pos(child, p.shift() - B, p.size())
+                         .visit(v, args...);
+}
+
+template <typename NodeT>
+regular_pos<NodeT> make_regular_pos(NodeT* node, shift_t shift, size_t size)
+{
+    assert(node);
+    assert(shift >= NodeT::bits_leaf);
+    assert(size > 0);
+    return {node, shift, size};
+}
+
+struct null_sub_pos
+{
+    auto node() const { return nullptr; }
+
+    template <typename Visitor, typename... Args>
+    void each_sub(Visitor, Args&&...)
+    {}
+    template <typename Visitor, typename... Args>
+    void each_right_sub(Visitor, Args&&...)
+    {}
+    template <typename Visitor, typename... Args>
+    void each_left_sub(Visitor, Args&&...)
+    {}
+    template <typename Visitor, typename... Args>
+    void visit(Visitor, Args&&...)
+    {}
+};
+
+template <typename NodeT>
+struct singleton_regular_sub_pos
+{
+    // this is a fake regular pos made out of a single child... useful
+    // to treat a single leaf node as a whole tree
+
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* leaf_;
+    count_t count_;
+
+    count_t count() const { return 1; }
+    node_t* node() const { return nullptr; }
+    size_t size() const { return count_; }
+    shift_t shift() const { return BL; }
+    count_t index(size_t idx) const { return 0; }
+    count_t subindex(size_t idx) const { return 0; }
+    size_t size_before(count_t offset) const { return 0; }
+    size_t this_size() const { return count_; }
+    size_t size(count_t offset) { return count_; }
+
+    template <typename Visitor, typename... Args>
+    void each_left_sub(Visitor v, Args&&... args)
+    {}
+    template <typename Visitor, typename... Args>
+    void each(Visitor v, Args&&... args)
+    {}
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_sub(Visitor v, Args&&... args)
+    {
+        return make_leaf_sub_pos(leaf_, count_).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+auto make_singleton_regular_sub_pos(NodeT* leaf, count_t count)
+{
+    assert(leaf);
+    IMMER_ASSERT_TAGGED(leaf->kind() == NodeT::kind_t::leaf);
+    assert(count > 0);
+    return singleton_regular_sub_pos<NodeT>{leaf, count};
+}
+
+template <typename NodeT>
+struct regular_sub_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+    shift_t shift_;
+    size_t size_;
+
+    count_t count() const { return subindex(size_ - 1) + 1; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
+    shift_t shift() const { return shift_; }
+    count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
+    count_t subindex(size_t idx) const { return idx >> shift_; }
+    size_t size_before(count_t offset) const { return offset << shift_; }
+    size_t this_size() const { return size_; }
+
+    auto size(count_t offset)
+    {
+        return offset == subindex(size_ - 1) ? size_ - size_before(offset)
+                                             : size_t{1} << shift_;
+    }
+
+    auto size_sbh(count_t offset, size_t size_before_hint)
+    {
+        assert(size_before_hint == size_before(offset));
+        return offset == subindex(size_ - 1) ? size_ - size_before_hint
+                                             : size_t{1} << shift_;
+    }
+
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
+    {
+        if (n) {
+            auto last = offset + n - 1;
+            auto e    = sizes + n - 1;
+            for (; sizes != e; ++sizes)
+                init = *sizes = init + (size_t{1} << shift_);
+            *sizes = init + size(last);
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    void each(Visitor v, Args&&... args)
+    {
+        return each_regular(*this, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred(Visitor v, Args&&... args)
+    {
+        return each_pred_regular(*this, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
+    {
+        return each_pred_zip_regular(*this, v, other, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_pred_i_regular(*this, v, i, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_pred_right_regular(*this, v, start, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_left(Visitor v, count_t last, Args&&... args)
+    {
+        return each_pred_left_regular(*this, v, last, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_i_regular(*this, v, i, n, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_right_regular(*this, v, start, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left(Visitor v, count_t last, Args&&... args)
+    {
+        return each_left_regular(*this, v, last, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right_sub_(Visitor v, count_t i, Args&&... args)
+    {
+        auto last  = count() - 1;
+        auto lsize = size_ - (last << shift_);
+        auto n     = node()->inner() + i;
+        auto e     = node()->inner() + last;
+        if (shift() == BL) {
+            for (; n != e; ++n) {
+                IMMER_PREFETCH(n + 1);
+                make_full_leaf_pos(*n).visit(v, args...);
+            }
+            make_leaf_sub_pos(*n, lsize).visit(v, args...);
+        } else {
+            auto ss = shift_ - B;
+            for (; n != e; ++n)
+                make_full_pos(*n, ss).visit(v, args...);
+            make_regular_sub_pos(*n, ss, lsize).visit(v, args...);
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_sub(Visitor v, Args&&... args)
+    {
+        each_right_sub_(v, 0, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right_sub(Visitor v, Args&&... args)
+    {
+        if (count() > 1)
+            each_right_sub_(v, 1, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left_sub(Visitor v, Args&&... args)
+    {
+        each_left(v, count() - 1, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, index(idx), count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards_oh_ch(Visitor v,
+                                 size_t idx,
+                                 count_t offset_hint,
+                                 count_t count_hint,
+                                 Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_sub_oh_regular(*this, v, idx, offset_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args)
+    {
+        return last_oh_regular(*this, v, offset_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_sub(Visitor v, Args&&... args)
+    {
+        auto offset  = count() - 1;
+        auto child   = node_->inner()[offset];
+        auto is_leaf = shift_ == BL;
+        auto lsize   = size_ - (offset << shift_);
+        return is_leaf ? make_leaf_sub_pos(child, lsize).visit(v, args...)
+                       : make_regular_sub_pos(child, shift_ - B, lsize)
+                             .visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub(Visitor v, Args&&... args)
+    {
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[0];
+        auto is_full = size_ >= (size_t{1} << shift_);
+        return is_full
+                   ? (is_leaf
+                          ? make_full_leaf_pos(child).visit(v, args...)
+                          : make_full_pos(child, shift_ - B).visit(v, args...))
+                   : (is_leaf
+                          ? make_leaf_sub_pos(child, size_).visit(v, args...)
+                          : make_regular_sub_pos(child, shift_ - B, size_)
+                                .visit(v, args...));
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        auto child   = node_->inner()[0];
+        auto is_full = size_ >= branches<BL>;
+        return is_full ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_leaf_sub_pos(child, size_).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_inner(Visitor v, Args&&... args)
+    {
+        assert(shift_ >= BL);
+        auto child   = node_->inner()[0];
+        auto is_full = size_ >= branches<BL>;
+        return is_full ? make_full_pos(child, shift_ - B).visit(v, args...)
+                       : make_regular_sub_pos(child, shift_ - B, size_)
+                             .visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args)
+    {
+        assert(idx < count());
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[idx];
+        auto lsize   = size(idx);
+        auto is_full = idx + 1 < count();
+        return is_full
+                   ? (is_leaf
+                          ? make_full_leaf_pos(child).visit(v, args...)
+                          : make_full_pos(child, shift_ - B).visit(v, args...))
+                   : (is_leaf
+                          ? make_leaf_sub_pos(child, lsize).visit(v, args...)
+                          : make_regular_sub_pos(child, shift_ - B, lsize)
+                                .visit(v, args...));
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        auto child   = node_->inner()[idx];
+        auto lsize   = size(idx);
+        auto is_full = idx + 1 < count();
+        return is_full ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_leaf_sub_pos(child, lsize).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+regular_sub_pos<NodeT>
+make_regular_sub_pos(NodeT* node, shift_t shift, size_t size)
+{
+    assert(node);
+    assert(shift >= NodeT::bits_leaf);
+    assert(size > 0);
+    assert(size <= (branches<NodeT::bits, size_t> << shift));
+    return {node, shift, size};
+}
+
+template <typename NodeT,
+          shift_t Shift,
+          bits_t B  = NodeT::bits,
+          bits_t BL = NodeT::bits_leaf>
+struct regular_descent_pos
+{
+    static_assert(Shift > 0, "not leaf...");
+
+    using node_t = NodeT;
+    node_t* node_;
+
+    node_t* node() const { return node_; }
+    shift_t shift() const { return Shift; }
+    count_t index(size_t idx) const
+    {
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshift-count-overflow"
+#endif
+        return (idx >> Shift) & mask<B>;
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx)
+    {
+        auto offset = index(idx);
+        auto child  = node_->inner()[offset];
+        return regular_descent_pos<NodeT, Shift - B>{child}.visit(v, idx);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT, bits_t B, bits_t BL>
+struct regular_descent_pos<NodeT, BL, B, BL>
+{
+    using node_t = NodeT;
+    node_t* node_;
+
+    node_t* node() const { return node_; }
+    shift_t shift() const { return BL; }
+    count_t index(size_t idx) const { return (idx >> BL) & mask<B>; }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx)
+    {
+        auto offset = index(idx);
+        auto child  = node_->inner()[offset];
+        return make_leaf_descent_pos(child).visit(v, idx);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT, typename Visitor>
+decltype(auto)
+visit_regular_descent(NodeT* node, shift_t shift, Visitor v, size_t idx)
+{
+    constexpr auto B  = NodeT::bits;
+    constexpr auto BL = NodeT::bits_leaf;
+    assert(node);
+    assert(shift >= BL);
+    switch (shift) {
+    case BL + B * 0:
+        return regular_descent_pos<NodeT, BL + B * 0>{node}.visit(v, idx);
+    case BL + B * 1:
+        return regular_descent_pos<NodeT, BL + B * 1>{node}.visit(v, idx);
+    case BL + B * 2:
+        return regular_descent_pos<NodeT, BL + B * 2>{node}.visit(v, idx);
+    case BL + B * 3:
+        return regular_descent_pos<NodeT, BL + B * 3>{node}.visit(v, idx);
+    case BL + B * 4:
+        return regular_descent_pos<NodeT, BL + B * 4>{node}.visit(v, idx);
+    case BL + B * 5:
+        return regular_descent_pos<NodeT, BL + B * 5>{node}.visit(v, idx);
+#if IMMER_DESCENT_DEEP
+    default:
+        for (auto level = shift; level != endshift<B, BL>; level -= B)
+            node = node->inner()[(idx >> level) & mask<B>];
+        return make_leaf_descent_pos(node).visit(v, idx);
+#endif // IMMER_DEEP_DESCENT
+    }
+    IMMER_UNREACHABLE;
+}
+
+template <typename NodeT>
+struct full_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t = NodeT;
+    node_t* node_;
+    shift_t shift_;
+
+    count_t count() const { return branches<B>; }
+    node_t* node() const { return node_; }
+    size_t size() const { return branches<B> << shift_; }
+    shift_t shift() const { return shift_; }
+    count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
+    count_t subindex(size_t idx) const { return idx >> shift_; }
+    size_t size(count_t offset) const { return size_t{1} << shift_; }
+    size_t size_sbh(count_t offset, size_t) const
+    {
+        return size_t{1} << shift_;
+    }
+    size_t size_before(count_t offset) const { return offset << shift_; }
+
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
+    {
+        auto e = sizes + n;
+        for (; sizes != e; ++sizes)
+            init = *sizes = init + (size_t{1} << shift_);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each(Visitor v, Args&&... args)
+    {
+        auto p = node_->inner();
+        auto e = p + branches<B>;
+        if (shift_ == BL) {
+            for (; p != e; ++p) {
+                IMMER_PREFETCH(p + 1);
+                make_full_leaf_pos(*p).visit(v, args...);
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (; p != e; ++p)
+                make_full_pos(*p, ss).visit(v, args...);
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred(Visitor v, Args&&... args)
+    {
+        auto p = node_->inner();
+        auto e = p + branches<B>;
+        if (shift_ == BL) {
+            for (; p != e; ++p) {
+                IMMER_PREFETCH(p + 1);
+                if (!make_full_leaf_pos(*p).visit(v, args...))
+                    return false;
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (; p != e; ++p)
+                if (!make_full_pos(*p, ss).visit(v, args...))
+                    return false;
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
+    {
+        auto p  = node_->inner();
+        auto p2 = other->inner();
+        auto e  = p + branches<B>;
+        if (shift_ == BL) {
+            for (; p != e; ++p, ++p2) {
+                IMMER_PREFETCH(p + 1);
+                if (!make_full_leaf_pos(*p).visit(v, *p2, args...))
+                    return false;
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (; p != e; ++p, ++p2)
+                if (!make_full_pos(*p, ss).visit(v, *p2, args...))
+                    return false;
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        auto p = node_->inner() + i;
+        auto e = node_->inner() + n;
+        if (shift_ == BL) {
+            for (; p != e; ++p) {
+                IMMER_PREFETCH(p + 1);
+                if (!make_full_leaf_pos(*p).visit(v, args...))
+                    return false;
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (; p != e; ++p)
+                if (!make_full_pos(*p, ss).visit(v, args...))
+                    return false;
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        auto p = node_->inner() + i;
+        auto e = node_->inner() + n;
+        if (shift_ == BL) {
+            for (; p != e; ++p) {
+                IMMER_PREFETCH(p + 1);
+                make_full_leaf_pos(*p).visit(v, args...);
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (; p != e; ++p)
+                make_full_pos(*p, ss).visit(v, args...);
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_pred_i(v, start, branches<B>, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_left(Visitor v, count_t last, Args&&... args)
+    {
+        return each_pred_i(v, 0, last, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_sub(Visitor v, Args&&... args)
+    {
+        each(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left_sub(Visitor v, Args&&... args)
+    {
+        each_i(v, 0, branches<B> - 1, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right_sub(Visitor v, Args&&... args)
+    {
+        each_i(v, 1, branches<B>, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right(Visitor v, count_t start, Args&&... args)
+    {
+        each_i(v, start, branches<B>, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left(Visitor v, count_t last, Args&&... args)
+    {
+        each_i(v, 0, last, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
+    {
+        return towards_oh(v, idx, index(idx), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards_oh_ch(
+        Visitor v, size_t idx, count_t offset_hint, count_t, Args&&... args)
+    {
+        return towards_oh(v, idx, offset_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        assert(offset_hint == index(idx));
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[offset_hint];
+        return is_leaf
+                   ? make_full_leaf_pos(child).visit(v, idx, args...)
+                   : make_full_pos(child, shift_ - B).visit(v, idx, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        assert(offset_hint == index(idx));
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[offset_hint];
+        auto lsize   = offset_hint << shift_;
+        return is_leaf
+                   ? make_full_leaf_pos(child).visit(v, idx - lsize, args...)
+                   : make_full_pos(child, shift_ - B)
+                         .visit(v, idx - lsize, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub(Visitor v, Args&&... args)
+    {
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[0];
+        return is_leaf ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_full_pos(child, shift_ - B).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        auto child = node_->inner()[0];
+        return make_full_leaf_pos(child).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_inner(Visitor v, Args&&... args)
+    {
+        assert(shift_ >= BL);
+        auto child = node_->inner()[0];
+        return make_full_pos(child, shift_ - B).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args)
+    {
+        assert(idx < count());
+        auto is_leaf = shift_ == BL;
+        auto child   = node_->inner()[idx];
+        return is_leaf ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_full_pos(child, shift_ - B).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        assert(idx < count());
+        auto child = node_->inner()[idx];
+        return make_full_leaf_pos(child).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT>
+full_pos<NodeT> make_full_pos(NodeT* node, shift_t shift)
+{
+    assert(node);
+    assert(shift >= NodeT::bits_leaf);
+    return {node, shift};
+}
+
+template <typename NodeT>
+struct relaxed_pos
+{
+    static constexpr auto B  = NodeT::bits;
+    static constexpr auto BL = NodeT::bits_leaf;
+
+    using node_t    = NodeT;
+    using relaxed_t = typename NodeT::relaxed_t;
+    node_t* node_;
+    shift_t shift_;
+    relaxed_t* relaxed_;
+
+    count_t count() const { return relaxed_->d.count; }
+    node_t* node() const { return node_; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+    shift_t shift() const { return shift_; }
+    count_t subindex(size_t idx) const { return index(idx); }
+    relaxed_t* relaxed() const { return relaxed_; }
+
+    size_t size_before(count_t offset) const
+    {
+        return offset ? relaxed_->d.sizes[offset - 1] : 0;
+    }
+
+    size_t size(count_t offset) const
+    {
+        return size_sbh(offset, size_before(offset));
+    }
+
+    size_t size_sbh(count_t offset, size_t size_before_hint) const
+    {
+        assert(size_before_hint == size_before(offset));
+        return relaxed_->d.sizes[offset] - size_before_hint;
+    }
+
+    count_t index(size_t idx) const
+    {
+        auto offset = idx >> shift_;
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
+        return offset;
+    }
+
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
+    {
+        auto e     = sizes + n;
+        auto prev  = size_before(offset);
+        auto these = relaxed_->d.sizes + offset;
+        for (; sizes != e; ++sizes, ++these) {
+            auto this_size = *these;
+            init = *sizes = init + (this_size - prev);
+            prev          = this_size;
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    void each(Visitor v, Args&&... args)
+    {
+        each_left(v, relaxed_->d.count, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred(Visitor v, Args&&... args)
+    {
+        auto p = node_->inner();
+        auto s = size_t{};
+        auto n = count();
+        if (shift_ == BL) {
+            for (auto i = count_t{0}; i < n; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                         .visit(v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (auto i = count_t{0}; i < n; ++i) {
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        if (shift_ == BL) {
+            auto p = node_->inner();
+            auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            for (; i < n; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                         .visit(v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto p  = node_->inner();
+            auto s  = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            auto ss = shift_ - B;
+            for (; i < n; ++i) {
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_left(Visitor v, count_t n, Args&&... args)
+    {
+        auto p = node_->inner();
+        auto s = size_t{};
+        if (shift_ == BL) {
+            for (auto i = count_t{0}; i < n; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                         .visit(v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (auto i = count_t{0}; i < n; ++i) {
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    bool each_pred_right(Visitor v, count_t start, Args&&... args)
+    {
+        assert(start > 0);
+        assert(start <= relaxed_->d.count);
+        auto s = relaxed_->d.sizes[start - 1];
+        auto p = node_->inner();
+        if (shift_ == BL) {
+            for (auto i = start; i < relaxed_->d.count; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                         .visit(v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (auto i = start; i < relaxed_->d.count; ++i) {
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
+                    return false;
+                s = relaxed_->d.sizes[i];
+            }
+        }
+        return true;
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        if (shift_ == BL) {
+            auto p = node_->inner();
+            auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            for (; i < n; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                    .visit(v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto p  = node_->inner();
+            auto s  = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            auto ss = shift_ - B;
+            for (; i < n; ++i) {
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_sub(Visitor v, Args&&... args)
+    {
+        each_left(v, relaxed_->d.count, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left_sub(Visitor v, Args&&... args)
+    {
+        each_left(v, relaxed_->d.count - 1, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_left(Visitor v, count_t n, Args&&... args)
+    {
+        auto p = node_->inner();
+        auto s = size_t{};
+        if (shift_ == BL) {
+            for (auto i = count_t{0}; i < n; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                    .visit(v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (auto i = count_t{0}; i < n; ++i) {
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right_sub(Visitor v, Args&&... args)
+    {
+        each_right(v, 1, std::forward<Args>(args)...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void each_right(Visitor v, count_t start, Args&&... args)
+    {
+        assert(start > 0);
+        assert(start <= relaxed_->d.count);
+        auto s = relaxed_->d.sizes[start - 1];
+        auto p = node_->inner();
+        if (shift_ == BL) {
+            for (auto i = start; i < relaxed_->d.count; ++i) {
+                IMMER_PREFETCH(p + i + 1);
+                make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
+                    .visit(v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        } else {
+            auto ss = shift_ - B;
+            for (auto i = start; i < relaxed_->d.count; ++i) {
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
+                s = relaxed_->d.sizes[i];
+            }
+        }
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
+    {
+        return towards_oh(v, idx, subindex(idx), args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        assert(offset_hint == index(idx));
+        auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0;
+        return towards_oh_sbh(v, idx, offset_hint, left_size, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards_oh_sbh(Visitor v,
+                                  size_t idx,
+                                  count_t offset_hint,
+                                  size_t left_size_hint,
+                                  Args&&... args)
+    {
+        return towards_sub_oh_sbh(v, idx, offset_hint, left_size_hint, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        assert(offset_hint == index(idx));
+        auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0;
+        return towards_sub_oh_sbh(v, idx, offset_hint, left_size, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) towards_sub_oh_sbh(Visitor v,
+                                      size_t idx,
+                                      count_t offset_hint,
+                                      size_t left_size_hint,
+                                      Args&&... args)
+    {
+        assert(offset_hint == index(idx));
+        assert(left_size_hint ==
+               (offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0));
+        auto child     = node_->inner()[offset_hint];
+        auto is_leaf   = shift_ == BL;
+        auto next_size = relaxed_->d.sizes[offset_hint] - left_size_hint;
+        auto next_idx  = idx - left_size_hint;
+        return is_leaf
+                   ? make_leaf_sub_pos(child, next_size)
+                         .visit(v, next_idx, args...)
+                   : visit_maybe_relaxed_sub(
+                         child, shift_ - B, next_size, v, next_idx, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_oh_csh(Visitor v,
+                               count_t offset_hint,
+                               size_t child_size_hint,
+                               Args&&... args)
+    {
+        assert(offset_hint == count() - 1);
+        assert(child_size_hint == size(offset_hint));
+        auto child   = node_->inner()[offset_hint];
+        auto is_leaf = shift_ == BL;
+        return is_leaf
+                   ? make_leaf_sub_pos(child, child_size_hint).visit(v, args...)
+                   : visit_maybe_relaxed_sub(
+                         child, shift_ - B, child_size_hint, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) last_sub(Visitor v, Args&&... args)
+    {
+        auto offset     = relaxed_->d.count - 1;
+        auto child      = node_->inner()[offset];
+        auto child_size = size(offset);
+        auto is_leaf    = shift_ == BL;
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub(Visitor v, Args&&... args)
+    {
+        auto child      = node_->inner()[0];
+        auto child_size = relaxed_->d.sizes[0];
+        auto is_leaf    = shift_ == BL;
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        auto child      = node_->inner()[0];
+        auto child_size = relaxed_->d.sizes[0];
+        return make_leaf_sub_pos(child, child_size).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) first_sub_inner(Visitor v, Args&&... args)
+    {
+        assert(shift_ > BL);
+        auto child      = node_->inner()[0];
+        auto child_size = relaxed_->d.sizes[0];
+        return visit_maybe_relaxed_sub(
+            child, shift_ - B, child_size, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub(count_t offset, Visitor v, Args&&... args)
+    {
+        auto child      = node_->inner()[offset];
+        auto child_size = size(offset);
+        auto is_leaf    = shift_ == BL;
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) nth_sub_leaf(count_t offset, Visitor v, Args&&... args)
+    {
+        assert(shift_ == BL);
+        auto child      = node_->inner()[offset];
+        auto child_size = size(offset);
+        return make_leaf_sub_pos(child, child_size).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename Pos>
+using is_relaxed = std::is_same<relaxed_pos<typename std::decay_t<Pos>::node_t>,
+                                std::decay_t<Pos>>;
+
+template <typename Pos>
+constexpr auto is_relaxed_v = is_relaxed<Pos>::value;
+
+template <typename NodeT>
+relaxed_pos<NodeT>
+make_relaxed_pos(NodeT* node, shift_t shift, typename NodeT::relaxed_t* relaxed)
+{
+    assert(node);
+    assert(relaxed);
+    assert(shift >= NodeT::bits_leaf);
+    return {node, shift, relaxed};
+}
+
+template <typename NodeT, typename Visitor, typename... Args>
+decltype(auto) visit_maybe_relaxed_sub(
+    NodeT* node, shift_t shift, size_t size, Visitor v, Args&&... args)
+{
+    assert(node);
+    auto relaxed = node->relaxed();
+    if (relaxed) {
+        assert(size == relaxed->d.sizes[relaxed->d.count - 1]);
+        return make_relaxed_pos(node, shift, relaxed)
+            .visit(v, std::forward<Args>(args)...);
+    } else {
+        return make_regular_sub_pos(node, shift, size)
+            .visit(v, std::forward<Args>(args)...);
+    }
+}
+
+template <typename NodeT,
+          shift_t Shift,
+          bits_t B  = NodeT::bits,
+          bits_t BL = NodeT::bits_leaf>
+struct relaxed_descent_pos
+{
+    static_assert(Shift > 0, "not leaf...");
+
+    using node_t    = NodeT;
+    using relaxed_t = typename NodeT::relaxed_t;
+    node_t* node_;
+    relaxed_t* relaxed_;
+
+    count_t count() const { return relaxed_->d.count; }
+    node_t* node() const { return node_; }
+    shift_t shift() const { return Shift; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+
+    count_t index(size_t idx) const
+    {
+        // make gcc happy
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshift-count-overflow"
+#endif
+        auto offset = idx >> Shift;
+#if !defined(_MSC_VER)
+#pragma GCC diagnostic pop
+#endif
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
+        return offset;
+    }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx)
+    {
+        auto offset    = index(idx);
+        auto child     = node_->inner()[offset];
+        auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0;
+        auto next_idx  = idx - left_size;
+        auto r         = child->relaxed();
+        return r ? relaxed_descent_pos<NodeT, Shift - B>{child, r}.visit(
+                       v, next_idx)
+                 : regular_descent_pos<NodeT, Shift - B>{child}.visit(v,
+                                                                      next_idx);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT, bits_t B, bits_t BL>
+struct relaxed_descent_pos<NodeT, BL, B, BL>
+{
+    using node_t    = NodeT;
+    using relaxed_t = typename NodeT::relaxed_t;
+    node_t* node_;
+    relaxed_t* relaxed_;
+
+    count_t count() const { return relaxed_->d.count; }
+    node_t* node() const { return node_; }
+    shift_t shift() const { return BL; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+
+    count_t index(size_t idx) const
+    {
+        auto offset = (idx >> BL) & mask<B>;
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
+        return offset;
+    }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx)
+    {
+        auto offset    = index(idx);
+        auto child     = node_->inner()[offset];
+        auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0;
+        auto next_idx  = idx - left_size;
+        return leaf_descent_pos<NodeT>{child}.visit(v, next_idx);
+    }
+
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
+    {
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
+    }
+};
+
+template <typename NodeT, typename Visitor, typename... Args>
+decltype(auto)
+visit_maybe_relaxed_descent(NodeT* node, shift_t shift, Visitor v, size_t idx)
+{
+    constexpr auto B  = NodeT::bits;
+    constexpr auto BL = NodeT::bits_leaf;
+    assert(node);
+    assert(shift >= BL);
+    auto r = node->relaxed();
+    if (r) {
+        switch (shift) {
+        case BL + B * 0:
+            return relaxed_descent_pos<NodeT, BL + B * 0>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 1:
+            return relaxed_descent_pos<NodeT, BL + B * 1>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 2:
+            return relaxed_descent_pos<NodeT, BL + B * 2>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 3:
+            return relaxed_descent_pos<NodeT, BL + B * 3>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 4:
+            return relaxed_descent_pos<NodeT, BL + B * 4>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 5:
+            return relaxed_descent_pos<NodeT, BL + B * 5>{node, r}.visit(v,
+                                                                         idx);
+#if IMMER_DESCENT_DEEP
+        default:
+            for (auto level = shift; level != endshift<B, BL>; level -= B) {
+                auto r = node->relaxed();
+                if (r) {
+                    auto node_idx = (idx >> level) & mask<B>;
+                    while (r->d.sizes[node_idx] <= idx)
+                        ++node_idx;
+                    if (node_idx)
+                        idx -= r->d.sizes[node_idx - 1];
+                    node = node->inner()[node_idx];
+                } else {
+                    do {
+                        node = node->inner()[(idx >> level) & mask<B>];
+                    } while ((level -= B) != endshift<B, BL>);
+                    return make_leaf_descent_pos(node).visit(v, idx);
+                }
+            }
+            return make_leaf_descent_pos(node).visit(v, idx);
+#endif // IMMER_DESCENT_DEEP
+        }
+        IMMER_UNREACHABLE;
+    } else {
+        return visit_regular_descent(node, shift, v, idx);
+    }
+}
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/rbtree.hpp b/third_party/immer/immer/detail/rbts/rbtree.hpp
new file mode 100644
index 0000000000..b31f5e9b07
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/rbtree.hpp
@@ -0,0 +1,509 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/rbts/node.hpp>
+#include <immer/detail/rbts/operations.hpp>
+#include <immer/detail/rbts/position.hpp>
+
+#include <immer/detail/type_traits.hpp>
+
+#include <cassert>
+#include <memory>
+#include <numeric>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
+struct rbtree
+{
+    using node_t  = node<T, MemoryPolicy, B, BL>;
+    using edit_t  = typename node_t::edit_t;
+    using owner_t = typename MemoryPolicy::transience_t::owner;
+
+    size_t size;
+    shift_t shift;
+    node_t* root;
+    node_t* tail;
+
+    static const rbtree& empty()
+    {
+        static const rbtree empty_{
+            0, BL, node_t::make_inner_n(0u), node_t::make_leaf_n(0u)};
+        return empty_;
+    }
+
+    template <typename U>
+    static auto from_initializer_list(std::initializer_list<U> values)
+    {
+        auto e      = owner_t{};
+        auto result = rbtree{empty()};
+        for (auto&& v : values)
+            result.push_back_mut(e, v);
+        return result;
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static auto from_range(Iter first, Sent last)
+    {
+        auto e      = owner_t{};
+        auto result = rbtree{empty()};
+        for (; first != last; ++first)
+            result.push_back_mut(e, *first);
+        return result;
+    }
+
+    static auto from_fill(size_t n, T v)
+    {
+        auto e      = owner_t{};
+        auto result = rbtree{empty()};
+        while (n-- > 0)
+            result.push_back_mut(e, v);
+        return result;
+    }
+
+    rbtree(size_t sz, shift_t sh, node_t* r, node_t* t)
+        : size{sz}
+        , shift{sh}
+        , root{r}
+        , tail{t}
+    {
+        assert(check_tree());
+    }
+
+    rbtree(const rbtree& other)
+        : rbtree{other.size, other.shift, other.root, other.tail}
+    {
+        inc();
+    }
+
+    rbtree(rbtree&& other)
+        : rbtree{empty()}
+    {
+        swap(*this, other);
+    }
+
+    rbtree& operator=(const rbtree& other)
+    {
+        auto next = other;
+        swap(*this, next);
+        return *this;
+    }
+
+    rbtree& operator=(rbtree&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+
+    friend void swap(rbtree& x, rbtree& y)
+    {
+        using std::swap;
+        swap(x.size, y.size);
+        swap(x.shift, y.shift);
+        swap(x.root, y.root);
+        swap(x.tail, y.tail);
+    }
+
+    ~rbtree() { dec(); }
+
+    void inc() const
+    {
+        root->inc();
+        tail->inc();
+    }
+
+    void dec() const { traverse(dec_visitor()); }
+
+    auto tail_size() const { return size ? ((size - 1) & mask<BL>) +1 : 0; }
+
+    auto tail_offset() const { return size ? (size - 1) & ~mask<BL> : 0; }
+
+    template <typename Visitor, typename... Args>
+    void traverse(Visitor v, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+
+        if (tail_off)
+            make_regular_sub_pos(root, shift, tail_off).visit(v, args...);
+        else
+            make_empty_regular_pos(root).visit(v, args...);
+
+        make_leaf_sub_pos(tail, tail_size).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void traverse(Visitor v, size_t first, size_t last, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+
+        if (first < tail_off)
+            make_regular_sub_pos(root, shift, tail_off)
+                .visit(v, first, last < tail_off ? last : tail_off, args...);
+        if (last > tail_off)
+            make_leaf_sub_pos(tail, tail_size)
+                .visit(v,
+                       first > tail_off ? first - tail_off : 0,
+                       last - tail_off,
+                       args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool traverse_p(Visitor v, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+        return (tail_off ? make_regular_sub_pos(root, shift, tail_off)
+                               .visit(v, args...)
+                         : make_empty_regular_pos(root).visit(v, args...)) &&
+               make_leaf_sub_pos(tail, tail_size).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+
+        return (first < tail_off ? make_regular_sub_pos(root, shift, tail_off)
+                                       .visit(v,
+                                              first,
+                                              last < tail_off ? last : tail_off,
+                                              args...)
+                                 : true) &&
+               (last > tail_off
+                    ? make_leaf_sub_pos(tail, tail_size)
+                          .visit(v,
+                                 first > tail_off ? first - tail_off : 0,
+                                 last - tail_off,
+                                 args...)
+                    : true);
+    }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx) const
+    {
+        auto tail_off = tail_offset();
+        return idx >= tail_off ? make_leaf_descent_pos(tail).visit(v, idx)
+                               : visit_regular_descent(root, shift, v, idx);
+    }
+
+    template <typename Fn>
+    void for_each_chunk(Fn&& fn) const
+    {
+        traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    void for_each_chunk(size_t first, size_t last, Fn&& fn) const
+    {
+        traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(Fn&& fn) const
+    {
+        return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const
+    {
+        return traverse_p(
+            for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn));
+    }
+
+    bool equals(const rbtree& other) const
+    {
+        if (size != other.size)
+            return false;
+        if (size == 0)
+            return true;
+        return (size <= branches<BL> ||
+                make_regular_sub_pos(root, shift, tail_offset())
+                    .visit(equals_visitor{}, other.root)) &&
+               make_leaf_sub_pos(tail, tail_size())
+                   .visit(equals_visitor{}, other.tail);
+    }
+
+    void ensure_mutable_tail(edit_t e, count_t n)
+    {
+        if (!tail->can_mutate(e)) {
+            auto new_tail = node_t::copy_leaf_e(e, tail, n);
+            dec_leaf(tail, n);
+            tail = new_tail;
+        }
+    }
+
+    void push_back_mut(edit_t e, T value)
+    {
+        auto tail_off = tail_offset();
+        auto ts       = size - tail_off;
+        if (ts < branches<BL>) {
+            ensure_mutable_tail(e, ts);
+            new (&tail->leaf()[ts]) T{std::move(value)};
+        } else {
+            auto new_tail = node_t::make_leaf_e(e, std::move(value));
+            try {
+                if (tail_off == size_t{branches<B>} << shift) {
+                    auto new_root = node_t::make_inner_e(e);
+                    try {
+                        auto path = node_t::make_path_e(e, shift, tail);
+                        new_root->inner()[0] = root;
+                        new_root->inner()[1] = path;
+                        root                 = new_root;
+                        tail                 = new_tail;
+                        shift += B;
+                    } catch (...) {
+                        node_t::delete_inner_e(new_root);
+                        throw;
+                    }
+                } else if (tail_off) {
+                    auto new_root =
+                        make_regular_sub_pos(root, shift, tail_off)
+                            .visit(push_tail_mut_visitor<node_t>{}, e, tail);
+                    root = new_root;
+                    tail = new_tail;
+                } else {
+                    auto new_root = node_t::make_path_e(e, shift, tail);
+                    assert(tail_off == 0);
+                    dec_empty_regular(root);
+                    root = new_root;
+                    tail = new_tail;
+                }
+            } catch (...) {
+                node_t::delete_leaf(new_tail, 1);
+                throw;
+            }
+        }
+        ++size;
+    }
+
+    rbtree push_back(T value) const
+    {
+        auto tail_off = tail_offset();
+        auto ts       = size - tail_off;
+        if (ts < branches<BL>) {
+            auto new_tail =
+                node_t::copy_leaf_emplace(tail, ts, std::move(value));
+            return {size + 1, shift, root->inc(), new_tail};
+        } else {
+            auto new_tail = node_t::make_leaf_n(1, std::move(value));
+            try {
+                if (tail_off == size_t{branches<B>} << shift) {
+                    auto new_root = node_t::make_inner_n(2);
+                    try {
+                        auto path            = node_t::make_path(shift, tail);
+                        new_root->inner()[0] = root;
+                        new_root->inner()[1] = path;
+                        root->inc();
+                        tail->inc();
+                        return {size + 1, shift + B, new_root, new_tail};
+                    } catch (...) {
+                        node_t::delete_inner(new_root, 2);
+                        throw;
+                    }
+                } else if (tail_off) {
+                    auto new_root =
+                        make_regular_sub_pos(root, shift, tail_off)
+                            .visit(push_tail_visitor<node_t>{}, tail);
+                    tail->inc();
+                    return {size + 1, shift, new_root, new_tail};
+                } else {
+                    auto new_root = node_t::make_path(shift, tail);
+                    tail->inc();
+                    return {size + 1, shift, new_root, new_tail};
+                }
+            } catch (...) {
+                node_t::delete_leaf(new_tail, 1);
+                throw;
+            }
+        }
+    }
+
+    const T* array_for(size_t index) const
+    {
+        return descend(array_for_visitor<T>(), index);
+    }
+
+    T& get_mut(edit_t e, size_t idx)
+    {
+        auto tail_off = tail_offset();
+        if (idx >= tail_off) {
+            ensure_mutable_tail(e, size - tail_off);
+            return tail->leaf()[idx & mask<BL>];
+        } else {
+            return make_regular_sub_pos(root, shift, tail_off)
+                .visit(get_mut_visitor<node_t>{}, idx, e, &root);
+        }
+    }
+
+    const T& get(size_t index) const
+    {
+        return descend(get_visitor<T>(), index);
+    }
+
+    const T& get_check(size_t index) const
+    {
+        if (index >= size)
+            throw std::out_of_range{"index out of range"};
+        return descend(get_visitor<T>(), index);
+    }
+
+    const T& front() const { return get(0); }
+
+    const T& back() const { return tail->leaf()[(size - 1) & mask<BL>]; }
+
+    template <typename FnT>
+    void update_mut(edit_t e, size_t idx, FnT&& fn)
+    {
+        auto& elem = get_mut(e, idx);
+        elem       = std::forward<FnT>(fn)(std::move(elem));
+    }
+
+    template <typename FnT>
+    rbtree update(size_t idx, FnT&& fn) const
+    {
+        auto tail_off = tail_offset();
+        if (idx >= tail_off) {
+            auto tail_size = size - tail_off;
+            auto new_tail =
+                make_leaf_sub_pos(tail, tail_size)
+                    .visit(update_visitor<node_t>{}, idx - tail_off, fn);
+            return {size, shift, root->inc(), new_tail};
+        } else {
+            auto new_root = make_regular_sub_pos(root, shift, tail_off)
+                                .visit(update_visitor<node_t>{}, idx, fn);
+            return {size, shift, new_root, tail->inc()};
+        }
+    }
+
+    void assoc_mut(edit_t e, size_t idx, T value)
+    {
+        update_mut(e, idx, [&](auto&&) { return std::move(value); });
+    }
+
+    rbtree assoc(size_t idx, T value) const
+    {
+        return update(idx, [&](auto&&) { return std::move(value); });
+    }
+
+    rbtree take(size_t new_size) const
+    {
+        auto tail_off = tail_offset();
+        if (new_size == 0) {
+            return empty();
+        } else if (new_size >= size) {
+            return *this;
+        } else if (new_size > tail_off) {
+            auto new_tail = node_t::copy_leaf(tail, new_size - tail_off);
+            return {new_size, shift, root->inc(), new_tail};
+        } else {
+            using std::get;
+            auto l = new_size - 1;
+            auto v = slice_right_visitor<node_t>();
+            auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l);
+            auto new_shift = get<0>(r);
+            auto new_root  = get<1>(r);
+            auto new_tail  = get<3>(r);
+            if (new_root) {
+                IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r));
+                assert(new_root->check(new_shift, new_size - get<2>(r)));
+                return {new_size, new_shift, new_root, new_tail};
+            } else {
+                return {new_size, BL, empty().root->inc(), new_tail};
+            }
+        }
+    }
+
+    void take_mut(edit_t e, size_t new_size)
+    {
+        auto tail_off = tail_offset();
+        if (new_size == 0) {
+            // todo: more efficient?
+            *this = empty();
+        } else if (new_size >= size) {
+            return;
+        } else if (new_size > tail_off) {
+            auto ts    = size - tail_off;
+            auto newts = new_size - tail_off;
+            if (tail->can_mutate(e)) {
+                destroy_n(tail->leaf() + newts, ts - newts);
+            } else {
+                auto new_tail = node_t::copy_leaf_e(e, tail, newts);
+                dec_leaf(tail, ts);
+                tail = new_tail;
+            }
+            size = new_size;
+            return;
+        } else {
+            using std::get;
+            auto l = new_size - 1;
+            auto v = slice_right_mut_visitor<node_t>();
+            auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l, e);
+            auto new_shift = get<0>(r);
+            auto new_root  = get<1>(r);
+            auto new_tail  = get<3>(r);
+            if (new_root) {
+                root  = new_root;
+                shift = new_shift;
+            } else {
+                root  = empty().root->inc();
+                shift = BL;
+            }
+            dec_leaf(tail, size - tail_off);
+            size = new_size;
+            tail = new_tail;
+            return;
+        }
+    }
+
+    bool check_tree() const
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        assert(shift >= BL);
+        assert(tail_offset() <= size);
+        assert(check_root());
+        assert(check_tail());
+#endif
+        return true;
+    }
+
+    bool check_tail() const
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        if (tail_size() > 0)
+            assert(tail->check(0, tail_size()));
+#endif
+        return true;
+    }
+
+    bool check_root() const
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        if (tail_offset() > 0)
+            assert(root->check(shift, tail_offset()));
+        else {
+            IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner);
+            assert(shift == BL);
+        }
+#endif
+        return true;
+    }
+};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp b/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp
new file mode 100644
index 0000000000..90613b10b9
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/rbtree_iterator.hpp
@@ -0,0 +1,99 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/iterator_facade.hpp>
+#include <immer/detail/rbts/rbtree.hpp>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T, typename MP, bits_t B, bits_t BL>
+struct rbtree_iterator
+    : iterator_facade<rbtree_iterator<T, MP, B, BL>,
+                      std::random_access_iterator_tag,
+                      T,
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
+{
+    using tree_t = rbtree<T, MP, B, BL>;
+
+    struct end_t
+    {};
+
+    rbtree_iterator() = default;
+
+    rbtree_iterator(const tree_t& v)
+        : v_{&v}
+        , i_{0}
+        , base_{~size_t{}}
+        , curr_{nullptr}
+    {}
+
+    rbtree_iterator(const tree_t& v, end_t)
+        : v_{&v}
+        , i_{v.size}
+        , base_{~size_t{}}
+        , curr_{nullptr}
+    {}
+
+    const tree_t& impl() const { return *v_; }
+    size_t index() const { return i_; }
+
+private:
+    friend iterator_core_access;
+
+    const tree_t* v_;
+    size_t i_;
+    mutable size_t base_;
+    mutable const T* curr_ = nullptr;
+
+    void increment()
+    {
+        assert(i_ < v_->size);
+        ++i_;
+    }
+
+    void decrement()
+    {
+        assert(i_ > 0);
+        --i_;
+    }
+
+    void advance(std::ptrdiff_t n)
+    {
+        assert(n <= 0 || i_ + static_cast<size_t>(n) <= v_->size);
+        assert(n >= 0 || static_cast<size_t>(-n) <= i_);
+        i_ += n;
+    }
+
+    bool equal(const rbtree_iterator& other) const { return i_ == other.i_; }
+
+    std::ptrdiff_t distance_to(const rbtree_iterator& other) const
+    {
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
+    }
+
+    const T& dereference() const
+    {
+        auto base = i_ & ~mask<BL>;
+        if (base_ != base) {
+            base_ = base;
+            curr_ = v_->array_for(i_);
+        }
+        return curr_[i_ & mask<BL>];
+    }
+};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/rrbtree.hpp b/third_party/immer/immer/detail/rbts/rrbtree.hpp
new file mode 100644
index 0000000000..7bf59e6e92
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/rrbtree.hpp
@@ -0,0 +1,1396 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/detail/rbts/node.hpp>
+#include <immer/detail/rbts/operations.hpp>
+#include <immer/detail/rbts/position.hpp>
+
+#include <immer/detail/type_traits.hpp>
+
+#include <cassert>
+#include <memory>
+#include <numeric>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
+struct rrbtree_iterator;
+
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
+struct rrbtree
+{
+    using node_t  = node<T, MemoryPolicy, B, BL>;
+    using edit_t  = typename node_t::edit_t;
+    using owner_t = typename MemoryPolicy::transience_t::owner;
+
+    size_t size;
+    shift_t shift;
+    node_t* root;
+    node_t* tail;
+
+    static const rrbtree& empty()
+    {
+        static const rrbtree empty_{
+            0, BL, node_t::make_inner_n(0u), node_t::make_leaf_n(0u)};
+        return empty_;
+    }
+
+    template <typename U>
+    static auto from_initializer_list(std::initializer_list<U> values)
+    {
+        auto e      = owner_t{};
+        auto result = rrbtree{empty()};
+        for (auto&& v : values)
+            result.push_back_mut(e, v);
+        return result;
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static auto from_range(Iter first, Sent last)
+    {
+        auto e      = owner_t{};
+        auto result = rrbtree{empty()};
+        for (; first != last; ++first)
+            result.push_back_mut(e, *first);
+        return result;
+    }
+
+    static auto from_fill(size_t n, T v)
+    {
+        auto e      = owner_t{};
+        auto result = rrbtree{empty()};
+        while (n-- > 0)
+            result.push_back_mut(e, v);
+        return result;
+    }
+
+    rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t)
+        : size{sz}
+        , shift{sh}
+        , root{r}
+        , tail{t}
+    {
+        assert(check_tree());
+    }
+
+    rrbtree(const rrbtree& other)
+        : rrbtree{other.size, other.shift, other.root, other.tail}
+    {
+        inc();
+    }
+
+    rrbtree(rrbtree&& other)
+        : rrbtree{empty()}
+    {
+        swap(*this, other);
+    }
+
+    rrbtree& operator=(const rrbtree& other)
+    {
+        auto next{other};
+        swap(*this, next);
+        return *this;
+    }
+
+    rrbtree& operator=(rrbtree&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
+
+    friend void swap(rrbtree& x, rrbtree& y)
+    {
+        using std::swap;
+        swap(x.size, y.size);
+        swap(x.shift, y.shift);
+        swap(x.root, y.root);
+        swap(x.tail, y.tail);
+    }
+
+    ~rrbtree() { dec(); }
+
+    void inc() const
+    {
+        root->inc();
+        tail->inc();
+    }
+
+    void dec() const { traverse(dec_visitor()); }
+
+    auto tail_size() const { return size - tail_offset(); }
+
+    auto tail_offset() const
+    {
+        auto r = root->relaxed();
+        assert(r == nullptr || r->d.count);
+        return r ? r->d.sizes[r->d.count - 1]
+                 : size ? (size - 1) & ~mask<BL>
+                        /* otherwise */ : 0;
+    }
+
+    template <typename Visitor, typename... Args>
+    void traverse(Visitor v, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+
+        if (tail_off)
+            visit_maybe_relaxed_sub(root, shift, tail_off, v, args...);
+        else
+            make_empty_regular_pos(root).visit(v, args...);
+
+        if (tail_size)
+            make_leaf_sub_pos(tail, tail_size).visit(v, args...);
+        else
+            make_empty_leaf_pos(tail).visit(v, args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    void traverse(Visitor v, size_t first, size_t last, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+
+        if (first < tail_off)
+            visit_maybe_relaxed_sub(root,
+                                    shift,
+                                    tail_off,
+                                    v,
+                                    first,
+                                    last < tail_off ? last : tail_off,
+                                    args...);
+        if (last > tail_off)
+            make_leaf_sub_pos(tail, tail_size)
+                .visit(v,
+                       first > tail_off ? first - tail_off : 0,
+                       last - tail_off,
+                       args...);
+    }
+
+    template <typename Visitor, typename... Args>
+    bool traverse_p(Visitor v, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+        return (tail_off
+                    ? visit_maybe_relaxed_sub(root, shift, tail_off, v, args...)
+                    : make_empty_regular_pos(root).visit(v, args...)) &&
+               (tail_size ? make_leaf_sub_pos(tail, tail_size).visit(v, args...)
+                          : make_empty_leaf_pos(tail).visit(v, args...));
+    }
+
+    template <typename Visitor, typename... Args>
+    bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const
+    {
+        auto tail_off  = tail_offset();
+        auto tail_size = size - tail_off;
+        return (first < tail_off
+                    ? visit_maybe_relaxed_sub(root,
+                                              shift,
+                                              tail_off,
+                                              v,
+                                              first,
+                                              last < tail_off ? last : tail_off,
+                                              args...)
+                    : true) &&
+               (last > tail_off
+                    ? make_leaf_sub_pos(tail, tail_size)
+                          .visit(v,
+                                 first > tail_off ? first - tail_off : 0,
+                                 last - tail_off,
+                                 args...)
+                    : true);
+    }
+
+    template <typename Visitor>
+    decltype(auto) descend(Visitor v, size_t idx) const
+    {
+        auto tail_off = tail_offset();
+        return idx >= tail_off
+                   ? make_leaf_descent_pos(tail).visit(v, idx - tail_off)
+                   : visit_maybe_relaxed_descent(root, shift, v, idx);
+    }
+
+    template <typename Fn>
+    void for_each_chunk(Fn&& fn) const
+    {
+        traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    void for_each_chunk(size_t first, size_t last, Fn&& fn) const
+    {
+        traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(Fn&& fn) const
+    {
+        return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const
+    {
+        return traverse_p(
+            for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn));
+    }
+
+    bool equals(const rrbtree& other) const
+    {
+        using iter_t = rrbtree_iterator<T, MemoryPolicy, B, BL>;
+        if (size != other.size)
+            return false;
+        if (size == 0)
+            return true;
+        auto tail_off       = tail_offset();
+        auto tail_off_other = other.tail_offset();
+        // compare trees
+        if (tail_off > 0 && tail_off_other > 0) {
+            // other.shift != shift is a theoretical possibility for
+            // relaxed trees that sadly we haven't managed to exercise
+            // in tests yet...
+            if (other.shift >= shift) {
+                if (!visit_maybe_relaxed_sub(other.root,
+                                             other.shift,
+                                             tail_off_other,
+                                             equals_visitor::rrb{},
+                                             iter_t{other},
+                                             root,
+                                             shift,
+                                             tail_off))
+                    return false;
+            } else {
+                if (!visit_maybe_relaxed_sub(root,
+                                             shift,
+                                             tail_off,
+                                             equals_visitor::rrb{},
+                                             iter_t{*this},
+                                             other.root,
+                                             other.shift,
+                                             tail_off_other))
+                    return false;
+            }
+        }
+        return tail_off == tail_off_other
+                   ? make_leaf_sub_pos(tail, tail_size())
+                         .visit(equals_visitor{}, other.tail)
+                   : tail_off > tail_off_other
+                         ? std::equal(tail->leaf(),
+                                      tail->leaf() + (size - tail_off),
+                                      other.tail->leaf() +
+                                          (tail_off - tail_off_other))
+                         /* otherwise */
+                         : std::equal(tail->leaf(),
+                                      tail->leaf() + (size - tail_off),
+                                      iter_t{other} + tail_off);
+    }
+
+    std::tuple<shift_t, node_t*> push_tail(node_t* root,
+                                           shift_t shift,
+                                           size_t size,
+                                           node_t* tail,
+                                           count_t tail_size) const
+    {
+        if (auto r = root->relaxed()) {
+            auto new_root =
+                make_relaxed_pos(root, shift, r)
+                    .visit(push_tail_visitor<node_t>{}, tail, tail_size);
+            if (new_root)
+                return std::make_tuple(shift, new_root);
+            else {
+                auto new_root = node_t::make_inner_r_n(2);
+                try {
+                    auto new_path        = node_t::make_path(shift, tail);
+                    new_root->inner()[0] = root->inc();
+                    new_root->inner()[1] = new_path;
+                    new_root->relaxed()->d.sizes[0] = size;
+                    new_root->relaxed()->d.sizes[1] = size + tail_size;
+                    new_root->relaxed()->d.count    = 2u;
+                } catch (...) {
+                    node_t::delete_inner_r(new_root, 2);
+                    throw;
+                }
+                return std::make_tuple(shift + B, new_root);
+            }
+        } else if (size == size_t{branches<B>} << shift) {
+            auto new_root = node_t::make_inner_n(2);
+            try {
+                auto new_path        = node_t::make_path(shift, tail);
+                new_root->inner()[0] = root->inc();
+                new_root->inner()[1] = new_path;
+            } catch (...) {
+                node_t::delete_inner(new_root, 2);
+                throw;
+            }
+            return std::make_tuple(shift + B, new_root);
+        } else if (size) {
+            auto new_root = make_regular_sub_pos(root, shift, size)
+                                .visit(push_tail_visitor<node_t>{}, tail);
+            return std::make_tuple(shift, new_root);
+        } else {
+            return std::make_tuple(shift, node_t::make_path(shift, tail));
+        }
+    }
+
+    void
+    push_tail_mut(edit_t e, size_t tail_off, node_t* tail, count_t tail_size)
+    {
+        if (auto r = root->relaxed()) {
+            auto new_root =
+                make_relaxed_pos(root, shift, r)
+                    .visit(push_tail_mut_visitor<node_t>{}, e, tail, tail_size);
+            if (new_root) {
+                root = new_root;
+            } else {
+                auto new_root = node_t::make_inner_r_e(e);
+                try {
+                    auto new_path        = node_t::make_path_e(e, shift, tail);
+                    new_root->inner()[0] = root;
+                    new_root->inner()[1] = new_path;
+                    new_root->relaxed()->d.sizes[0] = tail_off;
+                    new_root->relaxed()->d.sizes[1] = tail_off + tail_size;
+                    new_root->relaxed()->d.count    = 2u;
+                    root                            = new_root;
+                    shift += B;
+                } catch (...) {
+                    node_t::delete_inner_r_e(new_root);
+                    throw;
+                }
+            }
+        } else if (tail_off == size_t{branches<B>} << shift) {
+            auto new_root = node_t::make_inner_e(e);
+            try {
+                auto new_path        = node_t::make_path_e(e, shift, tail);
+                new_root->inner()[0] = root;
+                new_root->inner()[1] = new_path;
+                root                 = new_root;
+                shift += B;
+            } catch (...) {
+                node_t::delete_inner_e(new_root);
+                throw;
+            }
+        } else if (tail_off) {
+            auto new_root =
+                make_regular_sub_pos(root, shift, tail_off)
+                    .visit(push_tail_mut_visitor<node_t>{}, e, tail);
+            root = new_root;
+        } else {
+            auto new_root = node_t::make_path_e(e, shift, tail);
+            dec_empty_regular(root);
+            root = new_root;
+        }
+    }
+
+    void ensure_mutable_tail(edit_t e, count_t n)
+    {
+        if (!tail->can_mutate(e)) {
+            auto new_tail = node_t::copy_leaf_e(e, tail, n);
+            dec_leaf(tail, n);
+            tail = new_tail;
+        }
+    }
+
+    void push_back_mut(edit_t e, T value)
+    {
+        auto ts = tail_size();
+        if (ts < branches<BL>) {
+            ensure_mutable_tail(e, ts);
+            new (&tail->leaf()[ts]) T{std::move(value)};
+        } else {
+            using std::get;
+            auto new_tail = node_t::make_leaf_e(e, std::move(value));
+            auto tail_off = tail_offset();
+            try {
+                push_tail_mut(e, tail_off, tail, ts);
+                tail = new_tail;
+            } catch (...) {
+                node_t::delete_leaf(new_tail, 1u);
+                throw;
+            }
+        }
+        ++size;
+    }
+
+    rrbtree push_back(T value) const
+    {
+        auto ts = tail_size();
+        if (ts < branches<BL>) {
+            auto new_tail =
+                node_t::copy_leaf_emplace(tail, ts, std::move(value));
+            return {size + 1, shift, root->inc(), new_tail};
+        } else {
+            using std::get;
+            auto new_tail = node_t::make_leaf_n(1u, std::move(value));
+            auto tail_off = tail_offset();
+            try {
+                auto new_root =
+                    push_tail(root, shift, tail_off, tail, size - tail_off);
+                tail->inc();
+                return {size + 1, get<0>(new_root), get<1>(new_root), new_tail};
+            } catch (...) {
+                node_t::delete_leaf(new_tail, 1u);
+                throw;
+            }
+        }
+    }
+
+    std::tuple<const T*, size_t, size_t> region_for(size_t idx) const
+    {
+        using std::get;
+        auto tail_off = tail_offset();
+        if (idx >= tail_off) {
+            return std::make_tuple(tail->leaf(), tail_off, size);
+        } else {
+            auto subs = visit_maybe_relaxed_sub(
+                root, shift, tail_off, region_for_visitor<T>(), idx);
+            auto first = idx - get<1>(subs);
+            auto end   = first + get<2>(subs);
+            return std::make_tuple(get<0>(subs), first, end);
+        }
+    }
+
+    T& get_mut(edit_t e, size_t idx)
+    {
+        auto tail_off = tail_offset();
+        if (idx >= tail_off) {
+            ensure_mutable_tail(e, size - tail_off);
+            return tail->leaf()[(idx - tail_off) & mask<BL>];
+        } else {
+            return visit_maybe_relaxed_sub(root,
+                                           shift,
+                                           tail_off,
+                                           get_mut_visitor<node_t>{},
+                                           idx,
+                                           e,
+                                           &root);
+        }
+    }
+
+    const T& get(size_t index) const
+    {
+        return descend(get_visitor<T>(), index);
+    }
+
+    const T& get_check(size_t index) const
+    {
+        if (index >= size)
+            throw std::out_of_range{"out of range"};
+        return descend(get_visitor<T>(), index);
+    }
+
+    const T& front() const { return get(0); }
+
+    const T& back() const { return get(size - 1); }
+
+    template <typename FnT>
+    void update_mut(edit_t e, size_t idx, FnT&& fn)
+    {
+        auto& elem = get_mut(e, idx);
+        elem       = std::forward<FnT>(fn)(std::move(elem));
+    }
+
+    template <typename FnT>
+    rrbtree update(size_t idx, FnT&& fn) const
+    {
+        auto tail_off = tail_offset();
+        if (idx >= tail_off) {
+            auto tail_size = size - tail_off;
+            auto new_tail =
+                make_leaf_sub_pos(tail, tail_size)
+                    .visit(update_visitor<node_t>{}, idx - tail_off, fn);
+            return {size, shift, root->inc(), new_tail};
+        } else {
+            auto new_root = visit_maybe_relaxed_sub(
+                root, shift, tail_off, update_visitor<node_t>{}, idx, fn);
+            return {size, shift, new_root, tail->inc()};
+        }
+    }
+
+    void assoc_mut(edit_t e, size_t idx, T value)
+    {
+        update_mut(e, idx, [&](auto&&) { return std::move(value); });
+    }
+
+    rrbtree assoc(size_t idx, T value) const
+    {
+        return update(idx, [&](auto&&) { return std::move(value); });
+    }
+
+    void take_mut(edit_t e, size_t new_size)
+    {
+        auto tail_off = tail_offset();
+        if (new_size == 0) {
+            *this = empty();
+        } else if (new_size >= size) {
+            return;
+        } else if (new_size > tail_off) {
+            auto ts    = size - tail_off;
+            auto newts = new_size - tail_off;
+            if (tail->can_mutate(e)) {
+                destroy_n(tail->leaf() + newts, ts - newts);
+            } else {
+                auto new_tail = node_t::copy_leaf_e(e, tail, newts);
+                dec_leaf(tail, ts);
+                tail = new_tail;
+            }
+            size = new_size;
+            return;
+        } else {
+            using std::get;
+            auto l = new_size - 1;
+            auto v = slice_right_mut_visitor<node_t>();
+            auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l, e);
+            auto new_shift = get<0>(r);
+            auto new_root  = get<1>(r);
+            auto new_tail  = get<3>(r);
+            if (new_root) {
+                root  = new_root;
+                shift = new_shift;
+            } else {
+                root  = empty().root->inc();
+                shift = BL;
+            }
+            dec_leaf(tail, size - tail_off);
+            size = new_size;
+            tail = new_tail;
+            return;
+        }
+    }
+
+    rrbtree take(size_t new_size) const
+    {
+        auto tail_off = tail_offset();
+        if (new_size == 0) {
+            return empty();
+        } else if (new_size >= size) {
+            return *this;
+        } else if (new_size > tail_off) {
+            auto new_tail = node_t::copy_leaf(tail, new_size - tail_off);
+            return {new_size, shift, root->inc(), new_tail};
+        } else {
+            using std::get;
+            auto l = new_size - 1;
+            auto v = slice_right_visitor<node_t>();
+            auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l);
+            auto new_shift = get<0>(r);
+            auto new_root  = get<1>(r);
+            auto new_tail  = get<3>(r);
+            if (new_root) {
+                IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r));
+                assert(new_root->check(new_shift, new_size - get<2>(r)));
+                return {new_size, new_shift, new_root, new_tail};
+            } else {
+                return {new_size, BL, empty().root->inc(), new_tail};
+            }
+        }
+    }
+
+    void drop_mut(edit_t e, size_t elems)
+    {
+        using std::get;
+        auto tail_off = tail_offset();
+        if (elems == 0) {
+            return;
+        } else if (elems >= size) {
+            *this = empty();
+        } else if (elems == tail_off) {
+            dec_inner(root, shift, tail_off);
+            shift = BL;
+            root  = empty().root->inc();
+            size -= elems;
+            return;
+        } else if (elems > tail_off) {
+            auto v = slice_left_mut_visitor<node_t>();
+            tail   = get<1>(make_leaf_sub_pos(tail, size - tail_off)
+                              .visit(v, elems - tail_off, e));
+            if (root != empty().root) {
+                dec_inner(root, shift, tail_off);
+                shift = BL;
+                root  = empty().root->inc();
+            }
+            size -= elems;
+            return;
+        } else {
+            auto v = slice_left_mut_visitor<node_t>();
+            auto r =
+                visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e);
+            shift = get<0>(r);
+            root  = get<1>(r);
+            size -= elems;
+            return;
+        }
+    }
+
+    rrbtree drop(size_t elems) const
+    {
+        if (elems == 0) {
+            return *this;
+        } else if (elems >= size) {
+            return empty();
+        } else if (elems == tail_offset()) {
+            return {size - elems, BL, empty().root->inc(), tail->inc()};
+        } else if (elems > tail_offset()) {
+            auto tail_off = tail_offset();
+            auto new_tail =
+                node_t::copy_leaf(tail, elems - tail_off, size - tail_off);
+            return {size - elems, BL, empty().root->inc(), new_tail};
+        } else {
+            using std::get;
+            auto v = slice_left_visitor<node_t>();
+            auto r =
+                visit_maybe_relaxed_sub(root, shift, tail_offset(), v, elems);
+            auto new_root  = get<1>(r);
+            auto new_shift = get<0>(r);
+            return {size - elems, new_shift, new_root, tail->inc()};
+        }
+        return *this;
+    }
+
+    rrbtree concat(const rrbtree& r) const
+    {
+        assert(r.size < (std::numeric_limits<size_t>::max() - size));
+        using std::get;
+        if (size == 0)
+            return r;
+        else if (r.size == 0)
+            return *this;
+        else if (r.tail_offset() == 0) {
+            // just concat the tail, similar to push_back
+            auto tail_offst = tail_offset();
+            auto tail_size  = size - tail_offst;
+            if (tail_size == branches<BL>) {
+                auto new_root =
+                    push_tail(root, shift, tail_offst, tail, tail_size);
+                tail->inc();
+                return {size + r.size,
+                        get<0>(new_root),
+                        get<1>(new_root),
+                        r.tail->inc()};
+            } else if (tail_size + r.size <= branches<BL>) {
+                auto new_tail =
+                    node_t::copy_leaf(tail, tail_size, r.tail, r.size);
+                return {size + r.size, shift, root->inc(), new_tail};
+            } else {
+                auto remaining = branches<BL> - tail_size;
+                auto add_tail =
+                    node_t::copy_leaf(tail, tail_size, r.tail, remaining);
+                try {
+                    auto new_tail =
+                        node_t::copy_leaf(r.tail, remaining, r.size);
+                    try {
+                        auto new_root = push_tail(
+                            root, shift, tail_offst, add_tail, branches<BL>);
+                        return {size + r.size,
+                                get<0>(new_root),
+                                get<1>(new_root),
+                                new_tail};
+                    } catch (...) {
+                        node_t::delete_leaf(new_tail, r.size - remaining);
+                        throw;
+                    }
+                } catch (...) {
+                    node_t::delete_leaf(add_tail, branches<BL>);
+                    throw;
+                }
+            }
+        } else if (tail_offset() == 0) {
+            auto tail_offst = tail_offset();
+            auto tail_size  = size - tail_offst;
+            auto concated =
+                concat_trees(tail, tail_size, r.root, r.shift, r.tail_offset());
+            auto new_shift = concated.shift();
+            auto new_root  = concated.node();
+            IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift());
+            assert(new_root->check(new_shift, size + r.tail_offset()));
+            return {size + r.size, new_shift, new_root, r.tail->inc()};
+        } else {
+            auto tail_offst = tail_offset();
+            auto tail_size  = size - tail_offst;
+            auto concated   = concat_trees(root,
+                                         shift,
+                                         tail_offst,
+                                         tail,
+                                         tail_size,
+                                         r.root,
+                                         r.shift,
+                                         r.tail_offset());
+            auto new_shift  = concated.shift();
+            auto new_root   = concated.node();
+            IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift());
+            assert(new_root->check(new_shift, size + r.tail_offset()));
+            return {size + r.size, new_shift, new_root, r.tail->inc()};
+        }
+    }
+
+    constexpr static bool supports_transient_concat =
+        !std::is_empty<edit_t>::value;
+
+    friend void concat_mut_l(rrbtree& l, edit_t el, const rrbtree& r)
+    {
+        assert(&l != &r);
+        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
+        using std::get;
+        if (l.size == 0)
+            l = r;
+        else if (r.size == 0)
+            return;
+        else if (r.tail_offset() == 0) {
+            // just concat the tail, similar to push_back
+            auto tail_offst = l.tail_offset();
+            auto tail_size  = l.size - tail_offst;
+            if (tail_size == branches<BL>) {
+                l.push_tail_mut(el, tail_offst, l.tail, tail_size);
+                l.tail = r.tail->inc();
+                l.size += r.size;
+                return;
+            } else if (tail_size + r.size <= branches<BL>) {
+                l.ensure_mutable_tail(el, tail_size);
+                std::uninitialized_copy(r.tail->leaf(),
+                                        r.tail->leaf() + r.size,
+                                        l.tail->leaf() + tail_size);
+                l.size += r.size;
+                return;
+            } else {
+                auto remaining = branches<BL> - tail_size;
+                l.ensure_mutable_tail(el, tail_size);
+                std::uninitialized_copy(r.tail->leaf(),
+                                        r.tail->leaf() + remaining,
+                                        l.tail->leaf() + tail_size);
+                try {
+                    auto new_tail =
+                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
+                    try {
+                        l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
+                        l.tail = new_tail;
+                        l.size += r.size;
+                        return;
+                    } catch (...) {
+                        node_t::delete_leaf(new_tail, r.size - remaining);
+                        throw;
+                    }
+                } catch (...) {
+                    destroy_n(r.tail->leaf() + tail_size, remaining);
+                    throw;
+                }
+            }
+        } else if (l.tail_offset() == 0) {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated =
+                    concat_trees_mut(el,
+                                     el,
+                                     l.tail,
+                                     tail_size,
+                                     MemoryPolicy::transience_t::noone,
+                                     r.root,
+                                     r.shift,
+                                     r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                l.size += r.size;
+                l.shift = concated.shift();
+                l.root  = concated.node();
+                l.tail  = r.tail;
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(
+                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
+                l = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+                return;
+            }
+        } else {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated =
+                    concat_trees_mut(el,
+                                     el,
+                                     l.root,
+                                     l.shift,
+                                     tail_offst,
+                                     l.tail,
+                                     tail_size,
+                                     MemoryPolicy::transience_t::noone,
+                                     r.root,
+                                     r.shift,
+                                     r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                l.size += r.size;
+                l.shift = concated.shift();
+                l.root  = concated.node();
+                l.tail  = r.tail;
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(l.root,
+                                             l.shift,
+                                             tail_offst,
+                                             l.tail,
+                                             tail_size,
+                                             r.root,
+                                             r.shift,
+                                             r.tail_offset());
+                l               = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+            }
+        }
+    }
+
+    friend void concat_mut_r(const rrbtree& l, rrbtree& r, edit_t er)
+    {
+        assert(&l != &r);
+        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
+        using std::get;
+        if (r.size == 0)
+            r = std::move(l);
+        else if (l.size == 0)
+            return;
+        else if (r.tail_offset() == 0) {
+            // just concat the tail, similar to push_back
+            auto tail_offst = l.tail_offset();
+            auto tail_size  = l.size - tail_offst;
+            if (tail_size == branches<BL>) {
+                // this could be improved by making sure that the
+                // newly created nodes as part of the `push_tail()`
+                // are tagged with `er`
+                auto res =
+                    l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size);
+                l.tail->inc(); // note: leak if mutably concatenated
+                               // with itself, but this is forbidden
+                               // by the interface
+                r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()};
+                return;
+            } else if (tail_size + r.size <= branches<BL>) {
+                // doing this in a exception way mutating way is very
+                // tricky while potential performance gains are
+                // minimal (we need to move every element of the right
+                // tail anyways to make space for the left tail)
+                //
+                // we could however improve this by at least moving the
+                // elements of the right tail...
+                auto new_tail =
+                    node_t::copy_leaf(l.tail, tail_size, r.tail, r.size);
+                r = {l.size + r.size, l.shift, l.root->inc(), new_tail};
+                return;
+            } else {
+                // like the immutable version
+                auto remaining = branches<BL> - tail_size;
+                auto add_tail  = node_t::copy_leaf_e(
+                    er, l.tail, tail_size, r.tail, remaining);
+                try {
+                    auto new_tail =
+                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
+                    try {
+                        // this could be improved by making sure that the
+                        // newly created nodes as part of the `push_tail()`
+                        // are tagged with `er`
+                        auto new_root = l.push_tail(l.root,
+                                                    l.shift,
+                                                    tail_offst,
+                                                    add_tail,
+                                                    branches<BL>);
+                        r             = {l.size + r.size,
+                             get<0>(new_root),
+                             get<1>(new_root),
+                             new_tail};
+                        return;
+                    } catch (...) {
+                        node_t::delete_leaf(new_tail, r.size - remaining);
+                        throw;
+                    }
+                } catch (...) {
+                    node_t::delete_leaf(add_tail, branches<BL>);
+                    throw;
+                }
+                return;
+            }
+        } else if (l.tail_offset() == 0) {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated =
+                    concat_trees_mut(er,
+                                     MemoryPolicy::transience_t::noone,
+                                     l.tail,
+                                     tail_size,
+                                     er,
+                                     r.root,
+                                     r.shift,
+                                     r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                r.size += l.size;
+                r.shift = concated.shift();
+                r.root  = concated.node();
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(
+                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
+                r = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+                return;
+            }
+        } else {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated =
+                    concat_trees_mut(er,
+                                     MemoryPolicy::transience_t::noone,
+                                     l.root,
+                                     l.shift,
+                                     tail_offst,
+                                     l.tail,
+                                     tail_size,
+                                     er,
+                                     r.root,
+                                     r.shift,
+                                     r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                r.size += l.size;
+                r.shift = concated.shift();
+                r.root  = concated.node();
+                return;
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(l.root,
+                                             l.shift,
+                                             tail_offst,
+                                             l.tail,
+                                             tail_size,
+                                             r.root,
+                                             r.shift,
+                                             r.tail_offset());
+                r               = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+                return;
+            }
+        }
+    }
+
+    friend void concat_mut_lr_l(rrbtree& l, edit_t el, rrbtree& r, edit_t er)
+    {
+        assert(&l != &r);
+        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
+        using std::get;
+        if (l.size == 0)
+            l = r;
+        else if (r.size == 0)
+            return;
+        else if (r.tail_offset() == 0) {
+            // just concat the tail, similar to push_back
+            auto tail_offst = l.tail_offset();
+            auto tail_size  = l.size - tail_offst;
+            if (tail_size == branches<BL>) {
+                l.push_tail_mut(el, tail_offst, l.tail, tail_size);
+                l.tail = r.tail->inc();
+                l.size += r.size;
+                return;
+            } else if (tail_size + r.size <= branches<BL>) {
+                l.ensure_mutable_tail(el, tail_size);
+                if (r.tail->can_mutate(er))
+                    detail::uninitialized_move(r.tail->leaf(),
+                                               r.tail->leaf() + r.size,
+                                               l.tail->leaf() + tail_size);
+                else
+                    std::uninitialized_copy(r.tail->leaf(),
+                                            r.tail->leaf() + r.size,
+                                            l.tail->leaf() + tail_size);
+                l.size += r.size;
+                return;
+            } else {
+                auto remaining = branches<BL> - tail_size;
+                l.ensure_mutable_tail(el, tail_size);
+                if (r.tail->can_mutate(er))
+                    detail::uninitialized_move(r.tail->leaf(),
+                                               r.tail->leaf() + remaining,
+                                               l.tail->leaf() + tail_size);
+                else
+                    std::uninitialized_copy(r.tail->leaf(),
+                                            r.tail->leaf() + remaining,
+                                            l.tail->leaf() + tail_size);
+                try {
+                    auto new_tail =
+                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
+                    try {
+                        l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
+                        l.tail = new_tail;
+                        l.size += r.size;
+                        return;
+                    } catch (...) {
+                        node_t::delete_leaf(new_tail, r.size - remaining);
+                        throw;
+                    }
+                } catch (...) {
+                    destroy_n(r.tail->leaf() + tail_size, remaining);
+                    throw;
+                }
+            }
+        } else if (l.tail_offset() == 0) {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees_mut(el,
+                                                 el,
+                                                 l.tail,
+                                                 tail_size,
+                                                 er,
+                                                 r.root,
+                                                 r.shift,
+                                                 r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                l.size += r.size;
+                l.shift = concated.shift();
+                l.root  = concated.node();
+                l.tail  = r.tail;
+                r.hard_reset();
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(
+                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
+                l = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+                return;
+            }
+        } else {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees_mut(el,
+                                                 el,
+                                                 l.root,
+                                                 l.shift,
+                                                 tail_offst,
+                                                 l.tail,
+                                                 tail_size,
+                                                 er,
+                                                 r.root,
+                                                 r.shift,
+                                                 r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                l.size += r.size;
+                l.shift = concated.shift();
+                l.root  = concated.node();
+                l.tail  = r.tail;
+                r.hard_reset();
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(l.root,
+                                             l.shift,
+                                             tail_offst,
+                                             l.tail,
+                                             tail_size,
+                                             r.root,
+                                             r.shift,
+                                             r.tail_offset());
+                l               = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+            }
+        }
+    }
+
+    friend void concat_mut_lr_r(rrbtree& l, edit_t el, rrbtree& r, edit_t er)
+    {
+        assert(&l != &r);
+        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
+        using std::get;
+        if (r.size == 0)
+            r = l;
+        else if (l.size == 0)
+            return;
+        else if (r.tail_offset() == 0) {
+            // just concat the tail, similar to push_back
+            auto tail_offst = l.tail_offset();
+            auto tail_size  = l.size - tail_offst;
+            if (tail_size == branches<BL>) {
+                // this could be improved by making sure that the
+                // newly created nodes as part of the `push_tail()`
+                // are tagged with `er`
+                auto res =
+                    l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size);
+                r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()};
+                return;
+            } else if (tail_size + r.size <= branches<BL>) {
+                // doing this in a exception way mutating way is very
+                // tricky while potential performance gains are
+                // minimal (we need to move every element of the right
+                // tail anyways to make space for the left tail)
+                //
+                // we could however improve this by at least moving the
+                // elements of the mutable tails...
+                auto new_tail =
+                    node_t::copy_leaf(l.tail, tail_size, r.tail, r.size);
+                r = {l.size + r.size, l.shift, l.root->inc(), new_tail};
+                return;
+            } else {
+                // like the immutable version.
+                // we could improve this also by moving elements
+                // instead of just copying them
+                auto remaining = branches<BL> - tail_size;
+                auto add_tail  = node_t::copy_leaf_e(
+                    er, l.tail, tail_size, r.tail, remaining);
+                try {
+                    auto new_tail =
+                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
+                    try {
+                        // this could be improved by making sure that the
+                        // newly created nodes as part of the `push_tail()`
+                        // are tagged with `er`
+                        auto new_root = l.push_tail(l.root,
+                                                    l.shift,
+                                                    tail_offst,
+                                                    add_tail,
+                                                    branches<BL>);
+                        r             = {l.size + r.size,
+                             get<0>(new_root),
+                             get<1>(new_root),
+                             new_tail};
+                        return;
+                    } catch (...) {
+                        node_t::delete_leaf(new_tail, r.size - remaining);
+                        throw;
+                    }
+                } catch (...) {
+                    node_t::delete_leaf(add_tail, branches<BL>);
+                    throw;
+                }
+                return;
+            }
+        } else if (l.tail_offset() == 0) {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees_mut(er,
+                                                 el,
+                                                 l.tail,
+                                                 tail_size,
+                                                 er,
+                                                 r.root,
+                                                 r.shift,
+                                                 r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                r.size += l.size;
+                r.shift = concated.shift();
+                r.root  = concated.node();
+                l.hard_reset();
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(
+                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
+                r = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+                return;
+            }
+        } else {
+            if (supports_transient_concat) {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees_mut(er,
+                                                 el,
+                                                 l.root,
+                                                 l.shift,
+                                                 tail_offst,
+                                                 l.tail,
+                                                 tail_size,
+                                                 er,
+                                                 r.root,
+                                                 r.shift,
+                                                 r.tail_offset());
+                IMMER_ASSERT_TAGGED(concated.shift() ==
+                                    concated.node()->compute_shift());
+                assert(concated.node()->check(concated.shift(),
+                                              l.size + r.tail_offset()));
+                r.size += l.size;
+                r.shift = concated.shift();
+                r.root  = concated.node();
+                l.hard_reset();
+            } else {
+                auto tail_offst = l.tail_offset();
+                auto tail_size  = l.size - tail_offst;
+                auto concated   = concat_trees(l.root,
+                                             l.shift,
+                                             tail_offst,
+                                             l.tail,
+                                             tail_size,
+                                             r.root,
+                                             r.shift,
+                                             r.tail_offset());
+                r               = {l.size + r.size,
+                     concated.shift(),
+                     concated.node(),
+                     r.tail->inc()};
+            }
+        }
+    }
+
+    void hard_reset()
+    {
+        assert(supports_transient_concat);
+        auto&& empty_ = empty();
+        size          = empty_.size;
+        shift         = empty_.shift;
+        root          = empty_.root;
+        tail          = empty_.tail;
+    }
+
+    bool check_tree() const
+    {
+        assert(shift <= sizeof(size_t) * 8 - BL);
+        assert(shift >= BL);
+        assert(tail_offset() <= size);
+        assert(tail_size() <= branches<BL>);
+#if IMMER_DEBUG_DEEP_CHECK
+        assert(check_root());
+        assert(check_tail());
+#endif
+        return true;
+    }
+
+    bool check_tail() const
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        if (tail_size() > 0)
+            assert(tail->check(endshift<B, BL>, tail_size()));
+#endif
+        return true;
+    }
+
+    bool check_root() const
+    {
+#if IMMER_DEBUG_DEEP_CHECK
+        if (tail_offset() > 0)
+            assert(root->check(shift, tail_offset()));
+        else {
+            IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner);
+            assert(shift == BL);
+        }
+#endif
+        return true;
+    }
+
+#if IMMER_DEBUG_PRINT
+    void debug_print(std::ostream& out) const
+    {
+        out << "--" << std::endl
+            << "{" << std::endl
+            << "  size  = " << size << std::endl
+            << "  shift = " << shift << std::endl
+            << "  root  = " << std::endl;
+        debug_print_node(out, root, shift, tail_offset());
+        out << "  tail  = " << std::endl;
+        debug_print_node(out, tail, endshift<B, BL>, tail_size());
+        out << "}" << std::endl;
+    }
+
+    void debug_print_indent(std::ostream& out, unsigned indent) const
+    {
+        while (indent-- > 0)
+            out << ' ';
+    }
+
+    void debug_print_node(std::ostream& out,
+                          node_t* node,
+                          shift_t shift,
+                          size_t size,
+                          unsigned indent = 8) const
+    {
+        const auto indent_step = 4;
+
+        if (shift == endshift<B, BL>) {
+            debug_print_indent(out, indent);
+            out << "- {" << size << "} "
+                << pretty_print_array(node->leaf(), size) << std::endl;
+        } else if (auto r = node->relaxed()) {
+            auto count = r->d.count;
+            debug_print_indent(out, indent);
+            out << "# {" << size << "} "
+                << pretty_print_array(r->d.sizes, r->d.count) << std::endl;
+            auto last_size = size_t{};
+            for (auto i = count_t{}; i < count; ++i) {
+                debug_print_node(out,
+                                 node->inner()[i],
+                                 shift - B,
+                                 r->d.sizes[i] - last_size,
+                                 indent + indent_step);
+                last_size = r->d.sizes[i];
+            }
+        } else {
+            debug_print_indent(out, indent);
+            out << "+ {" << size << "}" << std::endl;
+            auto count =
+                (size >> shift) + (size - ((size >> shift) << shift) > 0);
+            if (count) {
+                for (auto i = count_t{}; i < count - 1; ++i)
+                    debug_print_node(out,
+                                     node->inner()[i],
+                                     shift - B,
+                                     1 << shift,
+                                     indent + indent_step);
+                debug_print_node(out,
+                                 node->inner()[count - 1],
+                                 shift - B,
+                                 size - ((count - 1) << shift),
+                                 indent + indent_step);
+            }
+        }
+    }
+#endif // IMMER_DEBUG_PRINT
+};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp b/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp
new file mode 100644
index 0000000000..af967774e7
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/rrbtree_iterator.hpp
@@ -0,0 +1,98 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/iterator_facade.hpp>
+#include <immer/detail/rbts/rrbtree.hpp>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename T, typename MP, bits_t B, bits_t BL>
+struct rrbtree_iterator
+    : iterator_facade<rrbtree_iterator<T, MP, B, BL>,
+                      std::random_access_iterator_tag,
+                      T,
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
+{
+    using tree_t   = rrbtree<T, MP, B, BL>;
+    using region_t = std::tuple<const T*, size_t, size_t>;
+
+    struct end_t
+    {};
+
+    const tree_t& impl() const { return *v_; }
+    size_t index() const { return i_; }
+
+    rrbtree_iterator() = default;
+
+    rrbtree_iterator(const tree_t& v)
+        : v_{&v}
+        , i_{0}
+        , curr_{nullptr, ~size_t{}, ~size_t{}}
+    {}
+
+    rrbtree_iterator(const tree_t& v, end_t)
+        : v_{&v}
+        , i_{v.size}
+        , curr_{nullptr, ~size_t{}, ~size_t{}}
+    {}
+
+private:
+    friend iterator_core_access;
+
+    const tree_t* v_;
+    size_t i_;
+    mutable region_t curr_;
+
+    void increment()
+    {
+        using std::get;
+        assert(i_ < v_->size);
+        ++i_;
+    }
+
+    void decrement()
+    {
+        using std::get;
+        assert(i_ > 0);
+        --i_;
+    }
+
+    void advance(std::ptrdiff_t n)
+    {
+        using std::get;
+        assert(n <= 0 || i_ + static_cast<size_t>(n) <= v_->size);
+        assert(n >= 0 || static_cast<size_t>(-n) <= i_);
+        i_ += n;
+    }
+
+    bool equal(const rrbtree_iterator& other) const { return i_ == other.i_; }
+
+    std::ptrdiff_t distance_to(const rrbtree_iterator& other) const
+    {
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
+    }
+
+    const T& dereference() const
+    {
+        using std::get;
+        if (i_ < get<1>(curr_) || i_ >= get<2>(curr_))
+            curr_ = v_->region_for(i_);
+        return get<0>(curr_)[i_ - get<1>(curr_)];
+    }
+};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/rbts/visitor.hpp b/third_party/immer/immer/detail/rbts/visitor.hpp
new file mode 100644
index 0000000000..38cd030c4a
--- /dev/null
+++ b/third_party/immer/immer/detail/rbts/visitor.hpp
@@ -0,0 +1,56 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+
+#include <tuple>
+#include <utility>
+
+namespace immer {
+namespace detail {
+namespace rbts {
+
+template <typename Deriv>
+struct visitor_base
+{
+    template <typename... Args>
+    static decltype(auto) visit_node(Args&&... args)
+    {
+        IMMER_UNREACHABLE;
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_relaxed(Args&&... args)
+    {
+        return Deriv::visit_inner(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_regular(Args&&... args)
+    {
+        return Deriv::visit_inner(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_inner(Args&&... args)
+    {
+        return Deriv::visit_node(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_leaf(Args&&... args)
+    {
+        return Deriv::visit_node(std::forward<Args>(args)...);
+    }
+};
+
+} // namespace rbts
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/ref_count_base.hpp b/third_party/immer/immer/detail/ref_count_base.hpp
new file mode 100644
index 0000000000..28f2e46a99
--- /dev/null
+++ b/third_party/immer/immer/detail/ref_count_base.hpp
@@ -0,0 +1,36 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <atomic>
+
+namespace immer {
+namespace detail {
+
+template <typename Deriv>
+struct ref_count_base
+{
+    mutable std::atomic<int> ref_count{0};
+
+    friend void intrusive_ptr_add_ref(const Deriv* x)
+    {
+        x->ref_count.fetch_add(1, std::memory_order_relaxed);
+    }
+
+    friend void intrusive_ptr_release(const Deriv* x)
+    {
+        if (x->ref_count.fetch_sub(1, std::memory_order_release) == 1) {
+            std::atomic_thread_fence(std::memory_order_acquire);
+            delete x;
+        }
+    }
+};
+
+} /* namespace detail */
+} /* namespace immer */
diff --git a/third_party/immer/immer/detail/type_traits.hpp b/third_party/immer/immer/detail/type_traits.hpp
new file mode 100644
index 0000000000..afb2652c55
--- /dev/null
+++ b/third_party/immer/immer/detail/type_traits.hpp
@@ -0,0 +1,223 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace immer {
+namespace detail {
+
+template <typename... Ts>
+struct make_void
+{
+    using type = void;
+};
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename T, typename = void>
+struct is_dereferenceable : std::false_type
+{};
+
+template <typename T>
+struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>>
+    : std::true_type
+{};
+
+template <typename T>
+constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
+
+template <typename T, typename U = T, typename = void>
+struct is_equality_comparable : std::false_type
+{};
+
+template <typename T, typename U>
+struct is_equality_comparable<
+    T,
+    U,
+    std::enable_if_t<std::is_same<bool,
+                                  decltype(std::declval<T&>() ==
+                                           std::declval<U&>())>::value>>
+    : std::true_type
+{};
+
+template <typename T, typename U = T>
+constexpr bool is_equality_comparable_v = is_equality_comparable<T, U>::value;
+
+template <typename T, typename U = T, typename = void>
+struct is_inequality_comparable : std::false_type
+{};
+
+template <typename T, typename U>
+struct is_inequality_comparable<
+    T,
+    U,
+    std::enable_if_t<std::is_same<bool,
+                                  decltype(std::declval<T&>() !=
+                                           std::declval<U&>())>::value>>
+    : std::true_type
+{};
+
+template <typename T, typename U = T>
+constexpr bool is_inequality_comparable_v =
+    is_inequality_comparable<T, U>::value;
+
+template <typename T, typename = void>
+struct is_preincrementable : std::false_type
+{};
+
+template <typename T>
+struct is_preincrementable<
+    T,
+    std::enable_if_t<std::is_same<T&, decltype(++(std::declval<T&>()))>::value>>
+    : std::true_type
+{};
+
+template <typename T>
+constexpr bool is_preincrementable_v = is_preincrementable<T>::value;
+
+template <typename T, typename U = T, typename = void>
+struct is_subtractable : std::false_type
+{};
+
+template <typename T, typename U>
+struct is_subtractable<
+    T,
+    U,
+    void_t<decltype(std::declval<T&>() - std::declval<U&>())>> : std::true_type
+{};
+
+template <typename T, typename U = T>
+constexpr bool is_subtractable_v = is_subtractable<T, U>::value;
+
+namespace swappable {
+
+using std::swap;
+
+template <typename T, typename U, typename = void>
+struct with : std::false_type
+{};
+
+// Does not account for non-referenceable types
+template <typename T, typename U>
+struct with<T,
+            U,
+            void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())),
+                   decltype(swap(std::declval<U&>(), std::declval<T&>()))>>
+    : std::true_type
+{};
+
+} // namespace swappable
+
+template <typename T, typename U>
+using is_swappable_with = swappable::with<T, U>;
+
+template <typename T>
+using is_swappable = is_swappable_with<T, T>;
+
+template <typename T>
+constexpr bool is_swappable_v = is_swappable_with<T&, T&>::value;
+
+template <typename T, typename = void>
+struct is_iterator : std::false_type
+{};
+
+// See http://en.cppreference.com/w/cpp/concept/Iterator
+template <typename T>
+struct is_iterator<
+    T,
+    void_t<
+        std::enable_if_t<is_preincrementable_v<T> &&
+                         is_dereferenceable_v<T>
+                         // accounts for non-referenceable types
+                         && std::is_copy_constructible<T>::value &&
+                         std::is_copy_assignable<T>::value &&
+                         std::is_destructible<T>::value && is_swappable_v<T>>,
+        typename std::iterator_traits<T>::value_type,
+        typename std::iterator_traits<T>::difference_type,
+        typename std::iterator_traits<T>::reference,
+        typename std::iterator_traits<T>::pointer,
+        typename std::iterator_traits<T>::iterator_category>> : std::true_type
+{};
+
+template <typename T>
+constexpr bool is_iterator_v = is_iterator<T>::value;
+
+template <typename T, typename U, typename = void>
+struct compatible_sentinel : std::false_type
+{};
+
+template <typename T, typename U>
+struct compatible_sentinel<
+    T,
+    U,
+    std::enable_if_t<is_iterator_v<T> && is_equality_comparable_v<T, U> &&
+                     is_inequality_comparable_v<T, U>>> : std::true_type
+{};
+
+template <typename T, typename U>
+constexpr bool compatible_sentinel_v = compatible_sentinel<T, U>::value;
+
+template <typename T, typename = void>
+struct is_forward_iterator : std::false_type
+{};
+
+template <typename T>
+struct is_forward_iterator<
+    T,
+    std::enable_if_t<is_iterator_v<T> &&
+                     std::is_base_of<std::forward_iterator_tag,
+                                     typename std::iterator_traits<
+                                         T>::iterator_category>::value>>
+    : std::true_type
+{};
+
+template <typename T>
+constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value;
+
+template <typename T, typename U, typename = void>
+struct std_distance_supports : std::false_type
+{};
+
+template <typename T, typename U>
+struct std_distance_supports<
+    T,
+    U,
+    void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>>
+    : std::true_type
+{};
+
+template <typename T, typename U>
+constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value;
+
+template <typename T, typename U, typename V, typename = void>
+struct std_uninitialized_copy_supports : std::false_type
+{};
+
+template <typename T, typename U, typename V>
+struct std_uninitialized_copy_supports<
+    T,
+    U,
+    V,
+    void_t<decltype(std::uninitialized_copy(
+        std::declval<T>(), std::declval<U>(), std::declval<V>()))>>
+    : std::true_type
+{};
+
+template <typename T, typename U, typename V>
+constexpr bool std_uninitialized_copy_supports_v =
+    std_uninitialized_copy_supports<T, U, V>::value;
+
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/detail/util.hpp b/third_party/immer/immer/detail/util.hpp
new file mode 100644
index 0000000000..fb2a520fc7
--- /dev/null
+++ b/third_party/immer/immer/detail/util.hpp
@@ -0,0 +1,258 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+
+#include <cstddef>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+#include <immer/detail/type_traits.hpp>
+
+#if defined(_MSC_VER)
+#include <intrin.h> // for __lzcnt*
+#endif
+
+namespace immer {
+namespace detail {
+
+template <typename T>
+using aligned_storage_for =
+    typename std::aligned_storage<sizeof(T), alignof(T)>::type;
+
+template <typename T>
+T& auto_const_cast(const T& x)
+{
+    return const_cast<T&>(x);
+}
+template <typename T>
+T&& auto_const_cast(const T&& x)
+{
+    return const_cast<T&&>(std::move(x));
+}
+
+template <typename Iter1, typename Iter2>
+auto uninitialized_move(Iter1 in1, Iter1 in2, Iter2 out)
+{
+    return std::uninitialized_copy(
+        std::make_move_iterator(in1), std::make_move_iterator(in2), out);
+}
+
+template <class T>
+void destroy(T* first, T* last)
+{
+    for (; first != last; ++first)
+        first->~T();
+}
+
+template <class T, class Size>
+void destroy_n(T* p, Size n)
+{
+    auto e = p + n;
+    for (; p != e; ++p)
+        p->~T();
+}
+
+template <typename Heap, typename T, typename... Args>
+T* make(Args&&... args)
+{
+    auto ptr = Heap::allocate(sizeof(T));
+    try {
+        return new (ptr) T{std::forward<Args>(args)...};
+    } catch (...) {
+        Heap::deallocate(sizeof(T), ptr);
+        throw;
+    }
+}
+
+struct not_supported_t
+{};
+struct empty_t
+{};
+
+template <typename T>
+struct exact_t
+{
+    T value;
+    exact_t(T v)
+        : value{v} {};
+};
+
+template <typename T>
+inline constexpr auto clz_(T) -> not_supported_t
+{
+    IMMER_UNREACHABLE;
+    return {};
+}
+#if defined(_MSC_VER)
+// inline auto clz_(unsigned short x) { return __lzcnt16(x); }
+// inline auto clz_(unsigned int x) { return __lzcnt(x); }
+// inline auto clz_(unsigned __int64 x) { return __lzcnt64(x); }
+#else
+inline constexpr auto clz_(unsigned int x) { return __builtin_clz(x); }
+inline constexpr auto clz_(unsigned long x) { return __builtin_clzl(x); }
+inline constexpr auto clz_(unsigned long long x) { return __builtin_clzll(x); }
+#endif
+
+template <typename T>
+inline constexpr T log2_aux(T x, T r = 0)
+{
+    return x <= 1 ? r : log2_aux(x >> 1, r + 1);
+}
+
+template <typename T>
+inline constexpr auto log2(T x) -> std::
+    enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
+{
+    return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x);
+}
+
+template <typename T>
+inline constexpr auto log2(T x)
+    -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value,
+                        T>
+{
+    return log2_aux(x);
+}
+
+template <bool b, typename F>
+auto static_if(F&& f) -> std::enable_if_t<b>
+{
+    std::forward<F>(f)(empty_t{});
+}
+template <bool b, typename F>
+auto static_if(F&& f) -> std::enable_if_t<!b>
+{}
+
+template <bool b, typename R = void, typename F1, typename F2>
+auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<b, R>
+{
+    return std::forward<F1>(f1)(empty_t{});
+}
+template <bool b, typename R = void, typename F1, typename F2>
+auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<!b, R>
+{
+    return std::forward<F2>(f2)(empty_t{});
+}
+
+template <typename T, T value>
+struct constantly
+{
+    template <typename... Args>
+    T operator()(Args&&...) const
+    {
+        return value;
+    }
+};
+
+/*!
+ * An alias to `std::distance`
+ */
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<detail::std_distance_supports_v<Iterator, Sentinel>,
+                           bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    return std::distance(first, last);
+}
+
+/*!
+ * Equivalent of the `std::distance` applied to the sentinel-delimited
+ * forward range @f$ [first, last) @f$
+ */
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<
+              (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail::
+                      is_forward_iterator_v<Iterator> &&
+                  detail::compatible_sentinel_v<Iterator, Sentinel> &&
+                  (!detail::is_subtractable_v<Sentinel, Iterator>),
+              bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    std::size_t result = 0;
+    while (first != last) {
+        ++first;
+        ++result;
+    }
+    return result;
+}
+
+/*!
+ * Equivalent of the `std::distance` applied to the sentinel-delimited
+ * random access range @f$ [first, last) @f$
+ */
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<
+              (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail::
+                      is_forward_iterator_v<Iterator> &&
+                  detail::compatible_sentinel_v<Iterator, Sentinel> &&
+                  detail::is_subtractable_v<Sentinel, Iterator>,
+              bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    return last - first;
+}
+
+/*!
+ * An alias to `std::uninitialized_copy`
+ */
+template <
+    typename Iterator,
+    typename Sentinel,
+    typename SinkIter,
+    std::enable_if_t<
+        detail::std_uninitialized_copy_supports_v<Iterator, Sentinel, SinkIter>,
+        bool> = true>
+SinkIter uninitialized_copy(Iterator first, Sentinel last, SinkIter d_first)
+{
+    return std::uninitialized_copy(first, last, d_first);
+}
+
+/*!
+ * Equivalent of the `std::uninitialized_copy` applied to the
+ * sentinel-delimited forward range @f$ [first, last) @f$
+ */
+template <typename SourceIter,
+          typename Sent,
+          typename SinkIter,
+          std::enable_if_t<
+              (!detail::std_uninitialized_copy_supports_v<SourceIter,
+                                                          Sent,
+                                                          SinkIter>) &&detail::
+                      compatible_sentinel_v<SourceIter, Sent> &&
+                  detail::is_forward_iterator_v<SinkIter>,
+              bool> = true>
+SinkIter uninitialized_copy(SourceIter first, Sent last, SinkIter d_first)
+{
+    auto current = d_first;
+    try {
+        while (first != last) {
+            *current++ = *first;
+            ++first;
+        }
+    } catch (...) {
+        using Value = typename std::iterator_traits<SinkIter>::value_type;
+        for (; d_first != current; ++d_first) {
+            d_first->~Value();
+        }
+        throw;
+    }
+    return current;
+}
+
+} // namespace detail
+} // namespace immer
diff --git a/third_party/immer/immer/experimental/detail/dvektor_impl.hpp b/third_party/immer/immer/experimental/detail/dvektor_impl.hpp
new file mode 100644
index 0000000000..81dbbc59f5
--- /dev/null
+++ b/third_party/immer/immer/experimental/detail/dvektor_impl.hpp
@@ -0,0 +1,498 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/heap_policy.hpp>
+#include <immer/refcount/enable_intrusive_ptr.hpp>
+#include <immer/refcount/refcount_policy.hpp>
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/smart_ptr/intrusive_ref_counter.hpp>
+
+#include <cassert>
+#include <limits>
+
+namespace immer {
+namespace detail {
+namespace dvektor {
+
+constexpr auto fast_log2(std::size_t x)
+{
+    return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - __builtin_clzl(x);
+}
+
+template <int B, typename T = std::size_t>
+constexpr T branches = T{1} << B;
+
+template <int B, typename T = std::size_t>
+constexpr T mask = branches<B, T> - 1;
+
+template <int B, typename T = std::size_t>
+constexpr auto
+    max_depth = fast_log2(std::numeric_limits<std::size_t>::max()) / B;
+
+template <typename T, int B, typename MP>
+struct node;
+
+template <typename T, int B, typename MP>
+using node_ptr = boost::intrusive_ptr<node<T, B, MP>>;
+
+template <typename T, int B>
+using leaf_node = std::array<T, 1 << B>;
+
+template <typename T, int B, typename MP>
+using inner_node = std::array<node_ptr<T, B, MP>, 1 << B>;
+
+template <typename T, int B, typename MP>
+struct node
+    : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount>
+    , enable_optimized_heap_policy<node<T, B, MP>, typename MP::heap>
+{
+    using leaf_node_t  = leaf_node<T, B>;
+    using inner_node_t = inner_node<T, B, MP>;
+
+    enum
+    {
+        leaf_kind,
+        inner_kind
+    } kind;
+
+    union data_t
+    {
+        leaf_node_t leaf;
+        inner_node_t inner;
+        data_t(leaf_node_t n)
+            : leaf(std::move(n))
+        {}
+        data_t(inner_node_t n)
+            : inner(std::move(n))
+        {}
+        ~data_t() {}
+    } data;
+
+    ~node()
+    {
+        switch (kind) {
+        case leaf_kind:
+            data.leaf.~leaf_node_t();
+            break;
+        case inner_kind:
+            data.inner.~inner_node_t();
+            break;
+        }
+    }
+
+    node(leaf_node<T, B> n)
+        : kind{leaf_kind}
+        , data{std::move(n)}
+    {}
+
+    node(inner_node<T, B, MP> n)
+        : kind{inner_kind}
+        , data{std::move(n)}
+    {}
+
+    inner_node_t& inner() &
+    {
+        assert(kind == inner_kind);
+        return data.inner;
+    }
+    const inner_node_t& inner() const&
+    {
+        assert(kind == inner_kind);
+        return data.inner;
+    }
+    inner_node_t&& inner() &&
+    {
+        assert(kind == inner_kind);
+        return std::move(data.inner);
+    }
+
+    leaf_node_t& leaf() &
+    {
+        assert(kind == leaf_kind);
+        return data.leaf;
+    }
+    const leaf_node_t& leaf() const&
+    {
+        assert(kind == leaf_kind);
+        return data.leaf;
+    }
+    leaf_node_t&& leaf() &&
+    {
+        assert(kind == leaf_kind);
+        return std::move(data.leaf);
+    }
+};
+
+template <typename T, int B, typename MP, typename... Ts>
+auto make_node(Ts&&... xs) -> boost::intrusive_ptr<node<T, B, MP>>
+{
+    return new node<T, B, MP>(std::forward<Ts>(xs)...);
+}
+
+template <typename T, int B, typename MP>
+struct ref
+{
+    using inner_t    = inner_node<T, B, MP>;
+    using leaf_t     = leaf_node<T, B>;
+    using node_t     = node<T, B, MP>;
+    using node_ptr_t = node_ptr<T, B, MP>;
+
+    unsigned depth;
+    std::array<node_ptr_t, max_depth<B>> display;
+
+    template <typename... Ts>
+    static auto make_node(Ts&&... xs)
+    {
+        return dvektor::make_node<T, B, MP>(std::forward<Ts>(xs)...);
+    }
+
+    const T& get_elem(std::size_t index, std::size_t xr) const
+    {
+        auto display_idx = fast_log2(xr) / B;
+        auto node        = display[display_idx].get();
+        auto shift       = display_idx * B;
+        while (display_idx--) {
+            node = node->inner()[(index >> shift) & mask<B>].get();
+            shift -= B;
+        }
+        return node->leaf()[index & mask<B>];
+    }
+
+    node_ptr_t null_slot_and_copy_inner(node_ptr_t& node, std::size_t idx)
+    {
+        auto& n = node->inner();
+        auto x  = node_ptr_t{};
+        x.swap(n[idx]);
+        return copy_of_inner(x);
+    }
+
+    node_ptr_t null_slot_and_copy_leaf(node_ptr_t& node, std::size_t idx)
+    {
+        auto& n = node->inner();
+        auto x  = node_ptr_t{};
+        x.swap(n[idx]);
+        return copy_of_leaf(x);
+    }
+
+    node_ptr_t copy_of_inner(const node_ptr_t& n)
+    {
+        return make_node(n->inner());
+    }
+
+    node_ptr_t copy_of_leaf(const node_ptr_t& n)
+    {
+        return make_node(n->leaf());
+    }
+
+    void stabilize(std::size_t index)
+    {
+        auto shift = B;
+        for (auto i = 1u; i < depth; ++i) {
+            display[i] = copy_of_inner(display[i]);
+            display[i]->inner()[(index >> shift) & mask<B>] = display[i - 1];
+            shift += B;
+        }
+    }
+
+    void goto_pos_writable_from_clean(std::size_t old_index,
+                                      std::size_t index,
+                                      std::size_t xr)
+    {
+        assert(depth);
+        auto d = depth - 1;
+        if (d == 0) {
+            display[0] = copy_of_leaf(display[0]);
+        } else {
+            IMMER_UNREACHABLE;
+            display[d] = copy_of_inner(display[d]);
+            auto shift = B * d;
+            while (--d) {
+                display[d] = null_slot_and_copy_inner(
+                    display[d + 1], (index >> shift) & mask<B>);
+                shift -= B;
+            }
+            display[0] =
+                null_slot_and_copy_leaf(display[1], (index >> B) & mask<B>);
+        }
+    }
+
+    void goto_pos_writable_from_dirty(std::size_t old_index,
+                                      std::size_t new_index,
+                                      std::size_t xr)
+    {
+        assert(depth);
+        if (xr < (1 << B)) {
+            display[0] = copy_of_leaf(display[0]);
+        } else {
+            auto display_idx = fast_log2(xr) / B;
+            auto shift       = B;
+            for (auto i = 1u; i <= display_idx; ++i) {
+                display[i] = copy_of_inner(display[i]);
+                display[i]->inner()[(old_index >> shift) & mask<B>] =
+                    display[i - 1];
+                shift += B;
+            }
+            for (auto i = display_idx - 1; i > 0; --i) {
+                shift -= B;
+                display[i] = null_slot_and_copy_inner(
+                    display[i + 1], (new_index >> shift) & mask<B>);
+            }
+            display[0] =
+                null_slot_and_copy_leaf(display[1], (new_index >> B) & mask<B>);
+        }
+    }
+
+    void goto_fresh_pos_writable_from_clean(std::size_t old_index,
+                                            std::size_t new_index,
+                                            std::size_t xr)
+    {
+        auto display_idx = fast_log2(xr) / B;
+        if (display_idx > 0) {
+            auto shift = display_idx * B;
+            if (display_idx == depth) {
+                display[display_idx] = make_node(inner_t{});
+                display[display_idx]->inner()[(old_index >> shift) & mask<B>] =
+                    display[display_idx - 1];
+                ++depth;
+            }
+            while (--display_idx) {
+                auto node = display[display_idx + 1]
+                                ->inner()[(new_index >> shift) & mask<B>];
+                display[display_idx] =
+                    node ? std::move(node) : make_node(inner_t{});
+            }
+            display[0] = make_node(leaf_t{});
+        }
+    }
+
+    void goto_fresh_pos_writable_from_dirty(std::size_t old_index,
+                                            std::size_t new_index,
+                                            std::size_t xr)
+    {
+        stabilize(old_index);
+        goto_fresh_pos_writable_from_clean(old_index, new_index, xr);
+    }
+
+    void goto_next_block_start(std::size_t index, std::size_t xr)
+    {
+        auto display_idx = fast_log2(xr) / B;
+        auto shift       = display_idx * B;
+        if (display_idx > 0) {
+            display[display_idx - 1] =
+                display[display_idx]->inner()[(index >> shift) & mask<B>];
+            while (--display_idx)
+                display[display_idx - 1] = display[display_idx]->inner()[0];
+        }
+    }
+
+    void goto_pos(std::size_t index, std::size_t xr)
+    {
+        auto display_idx = fast_log2(xr) / B;
+        auto shift       = display_idx * B;
+        if (display_idx) {
+            do {
+                display[display_idx - 1] =
+                    display[display_idx]->inner()[(index >> shift) & mask<B>];
+                shift -= B;
+            } while (--display_idx);
+        }
+    }
+};
+
+template <typename T, int B, typename MP>
+struct impl
+{
+    using inner_t    = inner_node<T, B, MP>;
+    using leaf_t     = leaf_node<T, B>;
+    using node_t     = node<T, B, MP>;
+    using node_ptr_t = node_ptr<T, B, MP>;
+    using ref_t      = ref<T, B, MP>;
+
+    std::size_t size;
+    std::size_t focus;
+    bool dirty;
+    ref_t p;
+
+    template <typename... Ts>
+    static auto make_node(Ts&&... xs)
+    {
+        return dvektor::make_node<T, B, MP>(std::forward<Ts>(xs)...);
+    }
+
+    void goto_pos_writable(std::size_t old_index,
+                           std::size_t new_index,
+                           std::size_t xr)
+    {
+        if (dirty) {
+            p.goto_pos_writable_from_dirty(old_index, new_index, xr);
+        } else {
+            p.goto_pos_writable_from_clean(old_index, new_index, xr);
+            dirty = true;
+        }
+    }
+
+    void goto_fresh_pos_writable(std::size_t old_index,
+                                 std::size_t new_index,
+                                 std::size_t xr)
+    {
+        if (dirty) {
+            p.goto_fresh_pos_writable_from_dirty(old_index, new_index, xr);
+        } else {
+            p.goto_fresh_pos_writable_from_clean(old_index, new_index, xr);
+            dirty = true;
+        }
+    }
+
+    impl push_back(T value) const
+    {
+        if (size) {
+            auto block_index = size & ~mask<B>;
+            auto lo          = size & mask<B>;
+            if (size != block_index) {
+                auto s = impl{size + 1, block_index, dirty, p};
+                s.goto_pos_writable(focus, block_index, focus ^ block_index);
+                s.p.display[0]->leaf()[lo] = std::move(value);
+                return s;
+            } else {
+                auto s = impl{size + 1, block_index, dirty, p};
+                s.goto_fresh_pos_writable(
+                    focus, block_index, focus ^ block_index);
+                s.p.display[0]->leaf()[lo] = std::move(value);
+                return s;
+            }
+        } else {
+            return impl{
+                1, 0, false, {1, {{make_node(leaf_t{{std::move(value)}})}}}};
+        }
+    }
+
+    const T& get(std::size_t index) const
+    {
+        return p.get_elem(index, index ^ focus);
+    }
+
+    template <typename FnT>
+    impl update(std::size_t idx, FnT&& fn) const
+    {
+        auto s = impl{size, idx, dirty, p};
+        s.goto_pos_writable(focus, idx, focus ^ idx);
+        auto& v = s.p.display[0]->leaf()[idx & mask<B>];
+        v       = fn(std::move(v));
+        return s;
+    }
+
+    impl assoc(std::size_t idx, T value) const
+    {
+        return update(idx, [&](auto&&) { return std::move(value); });
+    }
+};
+
+template <typename T, int B, typename MP>
+const impl<T, B, MP> empty = {0, 0, false, ref<T, B, MP>{1, {}}};
+
+template <typename T, int B, typename MP>
+struct iterator
+    : boost::iterator_facade<iterator<T, B, MP>,
+                             T,
+                             boost::random_access_traversal_tag,
+                             const T&>
+{
+    struct end_t
+    {};
+
+    iterator() = default;
+
+    iterator(const impl<T, B, MP>& v)
+        : p_{v.p}
+        , i_{0}
+        , base_{0}
+    {
+        if (v.dirty)
+            p_.stabilize(v.focus);
+        p_.goto_pos(0, 0 ^ v.focus);
+        curr_ = p_.display[0]->leaf().begin();
+    }
+
+    iterator(const impl<T, B, MP>& v, end_t)
+        : p_{v.p}
+        , i_{v.size}
+        , base_{(v.size - 1) & ~mask<B>}
+    {
+        if (v.dirty)
+            p_.stabilize(v.focus);
+        p_.goto_pos(base_, base_ ^ v.focus);
+        curr_ = p_.display[0]->leaf().begin() + (i_ - base_);
+    }
+
+private:
+    friend class boost::iterator_core_access;
+    using leaf_iterator = typename leaf_node<T, B>::const_iterator;
+
+    ref<T, B, MP> p_;
+    std::size_t i_;
+    std::size_t base_;
+    leaf_iterator curr_;
+
+    void increment()
+    {
+        ++i_;
+        if (i_ - base_ < branches<B>) {
+            ++curr_;
+        } else {
+            auto new_base = base_ + branches<B>;
+            p_.goto_next_block_start(new_base, base_ ^ new_base);
+            base_ = new_base;
+            curr_ = p_.display[0]->leaf().begin();
+        }
+    }
+
+    void decrement()
+    {
+        assert(i_ > 0);
+        --i_;
+        if (i_ >= base_) {
+            --curr_;
+        } else {
+            auto new_base = base_ - branches<B>;
+            p_.goto_pos(new_base, base_ ^ new_base);
+            base_ = new_base;
+            curr_ = std::prev(p_.display[0]->leaf().end());
+        }
+    }
+
+    void advance(std::ptrdiff_t n)
+    {
+        i_ += n;
+        if (i_ <= base_ && i_ - base_ < branches<B>) {
+            curr_ += n;
+        } else {
+            auto new_base = i_ & ~mask<B>;
+            p_.goto_pos(new_base, base_ ^ new_base);
+            base_ = new_base;
+            curr_ = p_.display[0]->leaf().begin() + (i_ - base_);
+        }
+    }
+
+    bool equal(const iterator& other) const { return i_ == other.i_; }
+
+    std::ptrdiff_t distance_to(const iterator& other) const
+    {
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
+    }
+
+    const T& dereference() const { return *curr_; }
+};
+
+} /* namespace dvektor */
+} /* namespace detail */
+} /* namespace immer */
diff --git a/third_party/immer/immer/experimental/dvektor.hpp b/third_party/immer/immer/experimental/dvektor.hpp
new file mode 100644
index 0000000000..e809e749d5
--- /dev/null
+++ b/third_party/immer/immer/experimental/dvektor.hpp
@@ -0,0 +1,69 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/experimental/detail/dvektor_impl.hpp>
+
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T, int B = 5, typename MemoryPolicy = default_memory_policy>
+class dvektor
+{
+    using impl_t = detail::dvektor::impl<T, B, MemoryPolicy>;
+
+public:
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator         = detail::dvektor::iterator<T, B, MemoryPolicy>;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    dvektor() = default;
+
+    iterator begin() const { return {impl_}; }
+    iterator end() const { return {impl_, typename iterator::end_t{}}; }
+
+    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    reverse_iterator rend() const { return reverse_iterator{begin()}; }
+
+    std::size_t size() const { return impl_.size; }
+    bool empty() const { return impl_.size == 0; }
+
+    reference operator[](size_type index) const { return impl_.get(index); }
+
+    dvektor push_back(value_type value) const
+    {
+        return {impl_.push_back(std::move(value))};
+    }
+
+    dvektor assoc(std::size_t idx, value_type value) const
+    {
+        return {impl_.assoc(idx, std::move(value))};
+    }
+
+    template <typename FnT>
+    dvektor update(std::size_t idx, FnT&& fn) const
+    {
+        return {impl_.update(idx, std::forward<FnT>(fn))};
+    }
+
+private:
+    dvektor(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+    impl_t impl_ = detail::dvektor::empty<T, B, MemoryPolicy>;
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/flex_vector.hpp b/third_party/immer/immer/flex_vector.hpp
new file mode 100644
index 0000000000..d03c3f7e45
--- /dev/null
+++ b/third_party/immer/immer/flex_vector.hpp
@@ -0,0 +1,608 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/rbts/rrbtree.hpp>
+#include <immer/detail/rbts/rrbtree_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T,
+          typename MP,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class vector;
+
+template <typename T,
+          typename MP,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class flex_vector_transient;
+
+/*!
+ * Immutable sequential container supporting both random access,
+ * structural sharing and efficient concatenation and slicing.
+ *
+ * @tparam T The type of the values to be stored in the container.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *         memory_policy.
+ *
+ * @rst
+ *
+ * This container is very similar to `vector`_ but also supports
+ * :math:`O(log(size))` *concatenation*, *slicing* and *insertion* at
+ * any point. Its performance characteristics are almost identical
+ * until one of these operations is performed.  After that,
+ * performance is degraded by a constant factor that usually oscilates
+ * in the range :math:`[1, 2)` depending on the operation and the
+ * amount of flexible operations that have been performed.
+ *
+ * .. tip:: A `vector`_ can be converted to a `flex_vector`_ in
+ *    constant time without any allocation.  This is so because the
+ *    internal structure of a *vector* is a strict subset of the
+ *    internal structure of a *flexible vector*.  You can take
+ *    advantage of this property by creating normal vectors as long as
+ *    the flexible operations are not needed, and convert later in
+ *    your processing pipeline once and if these are needed.
+ *
+ * @endrst
+ */
+template <typename T,
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class flex_vector
+{
+    using impl_t = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>;
+
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
+public:
+    static constexpr auto bits      = B;
+    static constexpr auto bits_leaf = BL;
+    using memory_policy             = MemoryPolicy;
+
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using transient_type = flex_vector_transient<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Default constructor.  It creates a flex_vector of `size() == 0`.
+     * It does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    flex_vector() = default;
+
+    /*!
+     * Constructs a flex_vector containing the elements in `values`.
+     */
+    flex_vector(std::initializer_list<T> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
+
+    /*!
+     * Constructs a flex_vector containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    flex_vector(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
+
+    /*!
+     * Constructs a vector containing the element `val` repeated `n`
+     * times.
+     */
+    flex_vector(size_type n, T v = {})
+        : impl_{impl_t::from_fill(n, v)}
+    {}
+
+    /*!
+     * Default constructor.  It creates a flex_vector with the same
+     * contents as `v`.  It does not allocate memory and is
+     * @f$ O(1) @f$.
+     */
+    flex_vector(vector<T, MemoryPolicy, B, BL> v)
+        : impl_{v.impl_.size,
+                v.impl_.shift,
+                v.impl_.root->inc(),
+                v.impl_.tail->inc()}
+    {}
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Access the last element.
+     */
+    IMMER_NODISCARD const T& back() const { return impl_.back(); }
+
+    /*!
+     * Access the first element.
+     */
+    IMMER_NODISCARD const T& front() const { return impl_.front(); }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Returns whether the vectors are equal.
+     */
+    IMMER_NODISCARD bool operator==(const flex_vector& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const flex_vector& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a flex_vector with `value` inserted at the end.  It may
+     * allocate memory and its complexity is *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: push-back/start
+     *      :end-before:  push-back/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
+
+    /*!
+     * Returns a flex_vector with `value` inserted at the frony.  It may
+     * allocate memory and its complexity is @f$ O(log(size)) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: push-front/start
+     *      :end-before:  push-front/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector push_front(value_type value) const
+    {
+        return flex_vector{}.push_back(value) + *this;
+    }
+
+    /*!
+     * Returns a flex_vector containing value `value` at position `index`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: set/start
+     *      :end-before:  set/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector set(size_type index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
+
+    /*!
+     * Returns a vector containing the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: update/start
+     *      :end-before:  update/end
+     *
+     * @endrst
+
+     */
+    template <typename FnT>
+    IMMER_NODISCARD flex_vector update(size_type index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
+
+    template <typename FnT>
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Returns a vector containing only the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: take/start
+     *      :end-before:  take/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
+
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
+
+    /*!
+     * Returns a vector without the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: drop/start
+     *      :end-before:  drop/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector drop(size_type elems) const&
+    {
+        return impl_.drop(elems);
+    }
+
+    IMMER_NODISCARD decltype(auto) drop(size_type elems) &&
+    {
+        return drop_move(move_t{}, elems);
+    }
+
+    /*!
+     * Concatenation operator. Returns a flex_vector with the contents
+     * of `l` followed by those of `r`.  It may allocate memory
+     * and its complexity is @f$ O(log(max(size_r, size_l))) @f$
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: concat/start
+     *      :end-before:  concat/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD friend flex_vector operator+(const flex_vector& l,
+                                                 const flex_vector& r)
+    {
+        return l.impl_.concat(r.impl_);
+    }
+
+    IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l,
+                                                    const flex_vector& r)
+    {
+        return concat_move(move_t{}, std::move(l), r);
+    }
+
+    IMMER_NODISCARD friend decltype(auto) operator+(const flex_vector& l,
+                                                    flex_vector&& r)
+    {
+        return concat_move(move_t{}, l, std::move(r));
+    }
+
+    IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l,
+                                                    flex_vector&& r)
+    {
+        return concat_move(move_t{}, std::move(l), std::move(r));
+    }
+
+    /*!
+     * Returns a flex_vector with the `value` inserted at index
+     * `pos`. It may allocate memory and its complexity is @f$
+     * O(log(size)) @f$
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: insert/start
+     *      :end-before:  insert/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector insert(size_type pos, T value) const&
+    {
+        return take(pos).push_back(std::move(value)) + drop(pos);
+    }
+    IMMER_NODISCARD decltype(auto) insert(size_type pos, T value) &&
+    {
+        using std::move;
+        auto rs = drop(pos);
+        return std::move(*this).take(pos).push_back(std::move(value)) +
+               std::move(rs);
+    }
+
+    IMMER_NODISCARD flex_vector insert(size_type pos, flex_vector value) const&
+    {
+        return take(pos) + std::move(value) + drop(pos);
+    }
+    IMMER_NODISCARD decltype(auto) insert(size_type pos, flex_vector value) &&
+    {
+        using std::move;
+        auto rs = drop(pos);
+        return std::move(*this).take(pos) + std::move(value) + std::move(rs);
+    }
+
+    /*!
+     * Returns a flex_vector without the element at index `pos`. It
+     * may allocate memory and its complexity is @f$ O(log(size)) @f$
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/flex-vector/flex-vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: erase/start
+     *      :end-before:  erase/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD flex_vector erase(size_type pos) const&
+    {
+        return take(pos) + drop(pos + 1);
+    }
+    IMMER_NODISCARD decltype(auto) erase(size_type pos) &&
+    {
+        auto rs = drop(pos + 1);
+        return std::move(*this).take(pos) + std::move(rs);
+    }
+
+    IMMER_NODISCARD flex_vector erase(size_type pos, size_type lpos) const&
+    {
+        return lpos > pos ? take(pos) + drop(lpos) : *this;
+    }
+    IMMER_NODISCARD decltype(auto) erase(size_type pos, size_type lpos) &&
+    {
+        if (lpos > pos) {
+            auto rs = drop(lpos);
+            return std::move(*this).take(pos) + std::move(rs);
+        } else {
+            return std::move(*this);
+        }
+    }
+
+    /*!
+     * Returns an @a transient form of this container, an
+     * `immer::flex_vector_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+#if IMMER_DEBUG_PRINT
+    void debug_print(std::ostream& out = std::cerr) const
+    {
+        impl_.debug_print(out);
+    }
+#endif
+
+private:
+    friend transient_type;
+
+    flex_vector(impl_t impl)
+        : impl_(std::move(impl))
+    {
+#if IMMER_DEBUG_PRINT
+        // force the compiler to generate debug_print, so we can call
+        // it from a debugger
+        [](volatile auto) {}(&flex_vector::debug_print);
+#endif
+    }
+
+    flex_vector&& push_back_move(std::true_type, value_type value)
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    flex_vector push_back_move(std::false_type, value_type value)
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    flex_vector&& set_move(std::true_type, size_type index, value_type value)
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
+    flex_vector set_move(std::false_type, size_type index, value_type value)
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    template <typename Fn>
+    flex_vector&& update_move(std::true_type, size_type index, Fn&& fn)
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    flex_vector update_move(std::false_type, size_type index, Fn&& fn)
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
+
+    flex_vector&& take_move(std::true_type, size_type elems)
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
+    flex_vector take_move(std::false_type, size_type elems)
+    {
+        return impl_.take(elems);
+    }
+
+    flex_vector&& drop_move(std::true_type, size_type elems)
+    {
+        impl_.drop_mut({}, elems);
+        return std::move(*this);
+    }
+    flex_vector drop_move(std::false_type, size_type elems)
+    {
+        return impl_.drop(elems);
+    }
+
+    static flex_vector&&
+    concat_move(std::true_type, flex_vector&& l, const flex_vector& r)
+    {
+        concat_mut_l(l.impl_, {}, r.impl_);
+        return std::move(l);
+    }
+    static flex_vector&&
+    concat_move(std::true_type, const flex_vector& l, flex_vector&& r)
+    {
+        concat_mut_r(l.impl_, r.impl_, {});
+        return std::move(r);
+    }
+    static flex_vector&&
+    concat_move(std::true_type, flex_vector&& l, flex_vector&& r)
+    {
+        concat_mut_lr_l(l.impl_, {}, r.impl_, {});
+        return std::move(l);
+    }
+    static flex_vector
+    concat_move(std::false_type, const flex_vector& l, const flex_vector& r)
+    {
+        return l.impl_.concat(r.impl_);
+    }
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/flex_vector_transient.hpp b/third_party/immer/immer/flex_vector_transient.hpp
new file mode 100644
index 0000000000..fe0a6a534e
--- /dev/null
+++ b/third_party/immer/immer/flex_vector_transient.hpp
@@ -0,0 +1,251 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/rbts/rrbtree.hpp>
+#include <immer/detail/rbts/rrbtree_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class flex_vector;
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class vector_transient;
+
+/*!
+ * Mutable version of `immer::flex_vector`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
+ */
+template <typename T,
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class flex_vector_transient : MemoryPolicy::transience_t::owner
+{
+    using impl_t  = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>;
+    using base_t  = typename MemoryPolicy::transience_t::owner;
+    using owner_t = typename MemoryPolicy::transience_t::owner;
+
+public:
+    static constexpr auto bits      = B;
+    static constexpr auto bits_leaf = BL;
+    using memory_policy             = MemoryPolicy;
+
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using persistent_type = flex_vector<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Default constructor.  It creates a flex_vector of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    flex_vector_transient() = default;
+
+    /*!
+     * Default constructor.  It creates a flex_vector with the same
+     * contents as `v`.  It does not allocate memory and is
+     * @f$ O(1) @f$.
+     */
+    flex_vector_transient(vector_transient<T, MemoryPolicy, B, BL> v)
+        : base_t{std::move(static_cast<base_t&>(v))}
+        , impl_{v.impl_.size,
+                v.impl_.shift,
+                v.impl_.root->inc(),
+                v.impl_.tail->inc()}
+    {}
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    reference operator[](size_type index) const { return impl_.get(index); }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Inserts `value` at the end.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    void push_back(value_type value)
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
+
+    /*!
+     * Sets to the value `value` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void set(size_type index, value_type value)
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
+
+    /*!
+     * Updates the vector to contain the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `0 >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    template <typename FnT>
+    void update(size_type index, FnT&& fn)
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Resizes the vector to only contain the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
+
+    /*!
+     * Removes the first the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void drop(size_type elems) { impl_.drop_mut(*this, elems); }
+
+    /*!
+     * Appends the contents of the `r` at the end.  It may allocate
+     * memory and its complexity is:
+     * @f$ O(log(max(size_r, size_l))) @f$
+     */
+    void append(flex_vector_transient& r)
+    {
+        r.owner_t::operator=(owner_t{});
+        concat_mut_l(impl_, *this, r.impl_);
+    }
+    void append(flex_vector_transient&& r)
+    {
+        concat_mut_lr_l(impl_, *this, r.impl_, r);
+    }
+
+    /*!
+     * Prepends the contents of the `l` at the beginning.  It may
+     * allocate memory and its complexity is:
+     * @f$ O(log(max(size_r, size_l))) @f$
+     */
+    void prepend(flex_vector_transient& l)
+    {
+        l.owner_t::operator=(owner_t{});
+        concat_mut_r(l.impl_, impl_, *this);
+    }
+    void prepend(flex_vector_transient&& l)
+    {
+        concat_mut_lr_r(l.impl_, l, impl_, *this);
+    }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::flex_vector`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return persistent_type{impl_};
+    }
+    IMMER_NODISCARD persistent_type persistent() &&
+    {
+        return persistent_type{std::move(impl_)};
+    }
+
+private:
+    friend persistent_type;
+
+    flex_vector_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/cpp_heap.hpp b/third_party/immer/immer/heap/cpp_heap.hpp
new file mode 100644
index 0000000000..cd129b406b
--- /dev/null
+++ b/third_party/immer/immer/heap/cpp_heap.hpp
@@ -0,0 +1,41 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <memory>
+
+namespace immer {
+
+/*!
+ * A heap that uses `operator new` and `operator delete`.
+ */
+struct cpp_heap
+{
+    /*!
+     * Returns a pointer to a memory region of size `size`, if the
+     * allocation was successful, and throws otherwise.
+     */
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return ::operator new(size);
+    }
+
+    /*!
+     * Releases a memory region `data` that was previously returned by
+     * `allocate`.  One must not use nor deallocate again a memory
+     * region that once it has been deallocated.
+     */
+    static void deallocate(std::size_t size, void* data)
+    {
+        ::operator delete(data);
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/debug_size_heap.hpp b/third_party/immer/immer/heap/debug_size_heap.hpp
new file mode 100644
index 0000000000..d5288c646f
--- /dev/null
+++ b/third_party/immer/immer/heap/debug_size_heap.hpp
@@ -0,0 +1,69 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/heap/identity_heap.hpp>
+
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <memory>
+
+namespace immer {
+
+#if IMMER_ENABLE_DEBUG_SIZE_HEAP
+
+/*!
+ * A heap that in debug mode ensures that the sizes for allocation and
+ * deallocation do match.
+ */
+template <typename Base>
+struct debug_size_heap
+{
+#if defined(__MINGW32__) && !defined(__MINGW64__)
+    // There is a bug in MinGW 32bit:
+    // https://sourceforge.net/p/mingw-w64/bugs/778/ It causes different
+    // versions of std::max_align_t to be defined, depending on inclusion order
+    // of stddef.h and stdint.h. As we have no control over the inclusion order
+    // here (as it might be set in stone by the outside world), we can't easily
+    // pin it to one of both versions of std::max_align_t. This means, we have
+    // to hardcode extra_size for MinGW 32bit builds until the mentioned bug is
+    // fixed.
+    constexpr static auto extra_size = 8;
+#else
+    constexpr static auto extra_size = sizeof(
+        std::aligned_storage_t<sizeof(std::size_t), alignof(std::max_align_t)>);
+#endif
+
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags... tags)
+    {
+        auto p = (std::size_t*) Base::allocate(size + extra_size, tags...);
+        new (p) std::size_t{size};
+        return ((char*) p) + extra_size;
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* data, Tags... tags)
+    {
+        auto p = (std::size_t*) (((char*) data) - extra_size);
+        assert(*p == size);
+        Base::deallocate(size + extra_size, p, tags...);
+    }
+};
+
+#else // IMMER_ENABLE_DEBUG_SIZE_HEAP
+
+template <typename Base>
+using debug_size_heap = identity_heap<Base>;
+
+#endif // !IMMER_ENABLE_DEBUG_SIZE_HEAP
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/free_list_heap.hpp b/third_party/immer/immer/heap/free_list_heap.hpp
new file mode 100644
index 0000000000..dc25b10184
--- /dev/null
+++ b/third_party/immer/immer/heap/free_list_heap.hpp
@@ -0,0 +1,83 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/free_list_node.hpp>
+#include <immer/heap/with_data.hpp>
+
+#include <atomic>
+#include <cassert>
+
+namespace immer {
+
+/*!
+ * Adaptor that does not release the memory to the parent heap but
+ * instead it keeps the memory in a thread-safe global free list. Must
+ * be preceded by a `with_data<free_list_node, ...>` heap adaptor.
+ *
+ * @tparam Size Maximum size of the objects to be allocated.
+ * @tparam Base Type of the parent heap.
+ */
+template <std::size_t Size, std::size_t Limit, typename Base>
+struct free_list_heap : Base
+{
+    using base_t = Base;
+
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        assert(size <= sizeof(free_list_node) + Size);
+        assert(size >= sizeof(free_list_node));
+
+        free_list_node* n;
+        do {
+            n = head().data;
+            if (!n) {
+                auto p = base_t::allocate(Size + sizeof(free_list_node));
+                return static_cast<free_list_node*>(p);
+            }
+        } while (!head().data.compare_exchange_weak(n, n->next));
+        head().count.fetch_sub(1u, std::memory_order_relaxed);
+        return n;
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* data, Tags...)
+    {
+        assert(size <= sizeof(free_list_node) + Size);
+        assert(size >= sizeof(free_list_node));
+
+        // we use relaxed, because we are fine with temporarily having
+        // a few more/less buffers in free list
+        if (head().count.load(std::memory_order_relaxed) >= Limit) {
+            base_t::deallocate(Size + sizeof(free_list_node), data);
+        } else {
+            auto n = static_cast<free_list_node*>(data);
+            do {
+                n->next = head().data;
+            } while (!head().data.compare_exchange_weak(n->next, n));
+            head().count.fetch_add(1u, std::memory_order_relaxed);
+        }
+    }
+
+private:
+    struct head_t
+    {
+        std::atomic<free_list_node*> data;
+        std::atomic<std::size_t> count;
+    };
+
+    static head_t& head()
+    {
+        static head_t head_{{nullptr}, {0}};
+        return head_;
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/free_list_node.hpp b/third_party/immer/immer/heap/free_list_node.hpp
new file mode 100644
index 0000000000..acab4779aa
--- /dev/null
+++ b/third_party/immer/immer/heap/free_list_node.hpp
@@ -0,0 +1,24 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/with_data.hpp>
+
+namespace immer {
+
+struct free_list_node
+{
+    free_list_node* next;
+};
+
+template <typename Base>
+struct with_free_list_node : with_data<free_list_node, Base>
+{};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/gc_heap.hpp b/third_party/immer/immer/heap/gc_heap.hpp
new file mode 100644
index 0000000000..8494bd2669
--- /dev/null
+++ b/third_party/immer/immer/heap/gc_heap.hpp
@@ -0,0 +1,127 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/heap/tags.hpp>
+
+#if IMMER_HAS_LIBGC
+#include <gc/gc.h>
+#else
+#error "Using garbage collection requires libgc"
+#endif
+
+#include <cstdlib>
+#include <memory>
+
+namespace immer {
+
+#ifdef __APPLE__
+#define IMMER_GC_REQUIRE_INIT 1
+#else
+#define IMMER_GC_REQUIRE_INIT 0
+#endif
+
+#if IMMER_GC_REQUIRE_INIT
+
+namespace detail {
+
+template <int Dummy = 0>
+struct gc_initializer
+{
+    gc_initializer() { GC_init(); }
+    static gc_initializer init;
+};
+
+template <int D>
+gc_initializer<D> gc_initializer<D>::init{};
+
+inline void gc_initializer_guard()
+{
+    static gc_initializer<> init_ = gc_initializer<>::init;
+    (void) init_;
+}
+
+} // namespace detail
+
+#define IMMER_GC_INIT_GUARD_ ::immer::detail::gc_initializer_guard()
+
+#else
+
+#define IMMER_GC_INIT_GUARD_
+
+#endif // IMMER_GC_REQUIRE_INIT
+
+/*!
+ * Heap that uses a tracing garbage collector.
+ *
+ * @rst
+ *
+ * This heap uses the `Boehm's conservative garbage collector`_ under
+ * the hood.  This is a tracing garbage collector that automatically
+ * reclaims unused memory.  Thus, it is not needed to call
+ * ``deallocate()`` in order to release memory.
+ *
+ * .. admonition:: Dependencies
+ *    :class: tip
+ *
+ *    In order to use this header file, you need to make sure that
+ *    Boehm's ``libgc`` is your include path and link to its binary
+ *    library.
+ *
+ * .. caution:: Memory that is allocated with the standard ``malloc``
+ *    and ``free`` is not visible to ``libgc`` when it is looking for
+ *    references.  This means that if, let's say, you store a
+ *    :cpp:class:`immer::vector` using a ``gc_heap`` inside a
+ *    ``std::vector`` that uses a standard allocator, the memory of
+ *    the former might be released automatically at unexpected times
+ *    causing crashes.
+ *
+ * .. caution:: When using a ``gc_heap`` in combination with immutable
+ *    containers, the destructors of the contained objects will never
+ *    be called.  It is ok to store containers inside containers as
+ *    long as all of them use a ``gc_heap`` consistently, but storing
+ *    other kinds of objects with relevant destructors
+ *    (e.g. containers with reference counting or other kinds of
+ *    *resource handles*) might cause memory leaks and other problems.
+ *
+ * .. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc
+ *
+ * @endrst
+ */
+class gc_heap
+{
+public:
+    static void* allocate(std::size_t n)
+    {
+        IMMER_GC_INIT_GUARD_;
+        auto p = GC_malloc(n);
+        if (IMMER_UNLIKELY(!p))
+            throw std::bad_alloc{};
+        return p;
+    }
+
+    static void* allocate(std::size_t n, norefs_tag)
+    {
+        IMMER_GC_INIT_GUARD_;
+        auto p = GC_malloc_atomic(n);
+        if (IMMER_UNLIKELY(!p))
+            throw std::bad_alloc{};
+        return p;
+    }
+
+    static void deallocate(std::size_t, void* data) { GC_free(data); }
+
+    static void deallocate(std::size_t, void* data, norefs_tag)
+    {
+        GC_free(data);
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/heap_policy.hpp b/third_party/immer/immer/heap/heap_policy.hpp
new file mode 100644
index 0000000000..582c113f33
--- /dev/null
+++ b/third_party/immer/immer/heap/heap_policy.hpp
@@ -0,0 +1,141 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/heap/debug_size_heap.hpp>
+#include <immer/heap/free_list_heap.hpp>
+#include <immer/heap/split_heap.hpp>
+#include <immer/heap/thread_local_free_list_heap.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+
+namespace immer {
+
+/*!
+ * Heap policy that unconditionally uses its `Heap` argument.
+ */
+template <typename Heap>
+struct heap_policy
+{
+    using type = Heap;
+
+    template <std::size_t>
+    struct optimized
+    {
+        using type = Heap;
+    };
+};
+
+template <typename Deriv, typename HeapPolicy>
+struct enable_optimized_heap_policy
+{
+    static void* operator new(std::size_t size)
+    {
+        using heap_type =
+            typename HeapPolicy ::template optimized<sizeof(Deriv)>::type;
+
+        return heap_type::allocate(size);
+    }
+
+    static void operator delete(void* data, std::size_t size)
+    {
+        using heap_type =
+            typename HeapPolicy ::template optimized<sizeof(Deriv)>::type;
+
+        heap_type::deallocate(size, data);
+    }
+};
+
+/*!
+ * Heap policy that returns a heap with a free list of objects
+ * of `max_size = max(Sizes...)` on top an underlying `Heap`.  Note
+ * these two properties of the resulting heap:
+ *
+ * - Allocating an object that is bigger than `max_size` may trigger
+ *   *undefined behavior*.
+ *
+ * - Allocating an object of size less than `max_size` still
+ *   returns an object of `max_size`.
+ *
+ * Basically, this heap will always return objects of `max_size`.
+ * When an object is freed, it does not directly invoke `std::free`,
+ * but it keeps the object in a global linked list instead.  When a
+ * new object is requested, it does not need to call `std::malloc` but
+ * it can directly pop and return the other object from the global
+ * list, a much faster operation.
+ *
+ * This actually creates a hierarchy with two free lists:
+ *
+ * - A `thread_local` free list is used first.  It does not need any
+ *   kind of synchronization and is very fast.  When the thread
+ *   finishes, its contents are returned to the next free list.
+ *
+ * - A global free list using lock-free access via atomics.
+ *
+ * @tparam Heap Heap to be used when the free list is empty.
+ *
+ * @rst
+ *
+ * .. tip:: For many applications that use immutable data structures
+ *    significantly, this is actually the best heap policy, and it
+ *    might become the default in the future.
+ *
+ *    Note that most our data structures internally use trees with the
+ *    same big branching factors.  This means that all *vectors*,
+ *    *maps*, etc. can just allocate elements from the same free-list
+ *    optimized heap.  Not only does this lowers the allocation time,
+ *    but also makes up for more efficient *cache utilization*.  When
+ *    a new node is needed, there are high chances the allocator will
+ *    return a node that was just accessed.  When batches of immutable
+ *    updates are made, this can make a significant difference.
+ *
+ * @endrst
+ */
+template <typename Heap, std::size_t Limit = default_free_list_size>
+struct free_list_heap_policy
+{
+    using type = debug_size_heap<Heap>;
+
+    template <std::size_t Size>
+    struct optimized
+    {
+        using type =
+            split_heap<Size,
+                       with_free_list_node<thread_local_free_list_heap<
+                           Size,
+                           Limit,
+                           free_list_heap<Size, Limit, debug_size_heap<Heap>>>>,
+                       debug_size_heap<Heap>>;
+    };
+};
+
+/*!
+ * Similar to @ref free_list_heap_policy, but it assumes no
+ * multi-threading, so a single global free list with no concurrency
+ * checks is used.
+ */
+template <typename Heap, std::size_t Limit = default_free_list_size>
+struct unsafe_free_list_heap_policy
+{
+    using type = Heap;
+
+    template <std::size_t Size>
+    struct optimized
+    {
+        using type = split_heap<
+            Size,
+            with_free_list_node<
+                unsafe_free_list_heap<Size, Limit, debug_size_heap<Heap>>>,
+            debug_size_heap<Heap>>;
+    };
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/identity_heap.hpp b/third_party/immer/immer/heap/identity_heap.hpp
new file mode 100644
index 0000000000..032cb3f221
--- /dev/null
+++ b/third_party/immer/immer/heap/identity_heap.hpp
@@ -0,0 +1,34 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstdlib>
+
+namespace immer {
+
+/*!
+ * A heap that simply passes on to the parent heap.
+ */
+template <typename Base>
+struct identity_heap : Base
+{
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags... tags)
+    {
+        return Base::allocate(size, tags...);
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* data, Tags... tags)
+    {
+        Base::deallocate(size, data, tags...);
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/malloc_heap.hpp b/third_party/immer/immer/heap/malloc_heap.hpp
new file mode 100644
index 0000000000..a0074d17c0
--- /dev/null
+++ b/third_party/immer/immer/heap/malloc_heap.hpp
@@ -0,0 +1,44 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/config.hpp>
+
+#include <cstdlib>
+#include <memory>
+
+namespace immer {
+
+/*!
+ * A heap that uses `std::malloc` and `std::free` to manage memory.
+ */
+struct malloc_heap
+{
+    /*!
+     * Returns a pointer to a memory region of size `size`, if the
+     * allocation was successful and throws `std::bad_alloc` otherwise.
+     */
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        auto p = std::malloc(size);
+        if (IMMER_UNLIKELY(!p))
+            throw std::bad_alloc{};
+        return p;
+    }
+
+    /*!
+     * Releases a memory region `data` that was previously returned by
+     * `allocate`.  One must not use nor deallocate again a memory
+     * region that once it has been deallocated.
+     */
+    static void deallocate(std::size_t, void* data) { std::free(data); }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/split_heap.hpp b/third_party/immer/immer/heap/split_heap.hpp
new file mode 100644
index 0000000000..37272d30ec
--- /dev/null
+++ b/third_party/immer/immer/heap/split_heap.hpp
@@ -0,0 +1,40 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <atomic>
+#include <cassert>
+
+namespace immer {
+
+/*!
+ * Adaptor that uses `SmallHeap` for allocations that are smaller or
+ * equal to `Size` and `BigHeap` otherwise.
+ */
+template <std::size_t Size, typename SmallHeap, typename BigHeap>
+struct split_heap
+{
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags... tags)
+    {
+        return size <= Size ? SmallHeap::allocate(size, tags...)
+                            : BigHeap::allocate(size, tags...);
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* data, Tags... tags)
+    {
+        if (size <= Size)
+            SmallHeap::deallocate(size, data, tags...);
+        else
+            BigHeap::deallocate(size, data, tags...);
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/tags.hpp b/third_party/immer/immer/heap/tags.hpp
new file mode 100644
index 0000000000..d1ce48d05c
--- /dev/null
+++ b/third_party/immer/immer/heap/tags.hpp
@@ -0,0 +1,16 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+namespace immer {
+
+struct norefs_tag
+{};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/thread_local_free_list_heap.hpp b/third_party/immer/immer/heap/thread_local_free_list_heap.hpp
new file mode 100644
index 0000000000..9f7f48f43f
--- /dev/null
+++ b/third_party/immer/immer/heap/thread_local_free_list_heap.hpp
@@ -0,0 +1,55 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/unsafe_free_list_heap.hpp>
+
+namespace immer {
+namespace detail {
+
+template <typename Heap>
+struct thread_local_free_list_storage
+{
+    struct head_t
+    {
+        free_list_node* data;
+        std::size_t count;
+
+        ~head_t() { Heap::clear(); }
+    };
+
+    static head_t& head()
+    {
+        thread_local static head_t head_{nullptr, 0};
+        return head_;
+    }
+};
+
+} // namespace detail
+
+/*!
+ * Adaptor that does not release the memory to the parent heap but
+ * instead it keeps the memory in a `thread_local` global free
+ * list. Must be preceded by a `with_data<free_list_node, ...>` heap
+ * adaptor.  When the current thread finishes, the memory is returned
+ * to the parent heap.
+ *
+ * @tparam Size  Maximum size of the objects to be allocated.
+ * @tparam Limit Maximum number of elements to keep in the free list.
+ * @tparam Base  Type of the parent heap.
+ */
+template <std::size_t Size, std::size_t Limit, typename Base>
+struct thread_local_free_list_heap
+    : detail::unsafe_free_list_heap_impl<detail::thread_local_free_list_storage,
+                                         Size,
+                                         Limit,
+                                         Base>
+{};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/unsafe_free_list_heap.hpp b/third_party/immer/immer/heap/unsafe_free_list_heap.hpp
new file mode 100644
index 0000000000..942f078024
--- /dev/null
+++ b/third_party/immer/immer/heap/unsafe_free_list_heap.hpp
@@ -0,0 +1,109 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cassert>
+#include <immer/config.hpp>
+#include <immer/heap/free_list_node.hpp>
+
+namespace immer {
+namespace detail {
+
+template <typename Heap>
+struct unsafe_free_list_storage
+{
+    struct head_t
+    {
+        free_list_node* data;
+        std::size_t count;
+    };
+
+    static head_t& head()
+    {
+        static head_t head_{nullptr, 0};
+        return head_;
+    }
+};
+
+template <template <class> class Storage,
+          std::size_t Size,
+          std::size_t Limit,
+          typename Base>
+class unsafe_free_list_heap_impl : Base
+{
+    using storage = Storage<unsafe_free_list_heap_impl>;
+
+public:
+    using base_t = Base;
+
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        assert(size <= sizeof(free_list_node) + Size);
+        assert(size >= sizeof(free_list_node));
+
+        auto n = storage::head().data;
+        if (!n) {
+            auto p = base_t::allocate(Size + sizeof(free_list_node));
+            return static_cast<free_list_node*>(p);
+        }
+        --storage::head().count;
+        storage::head().data = n->next;
+        return n;
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* data, Tags...)
+    {
+        assert(size <= sizeof(free_list_node) + Size);
+        assert(size >= sizeof(free_list_node));
+
+        if (storage::head().count >= Limit)
+            base_t::deallocate(Size + sizeof(free_list_node), data);
+        else {
+            auto n               = static_cast<free_list_node*>(data);
+            n->next              = storage::head().data;
+            storage::head().data = n;
+            ++storage::head().count;
+        }
+    }
+
+    static void clear()
+    {
+        while (storage::head().data) {
+            auto n = storage::head().data->next;
+            base_t::deallocate(Size + sizeof(free_list_node),
+                               storage::head().data);
+            storage::head().data = n;
+            --storage::head().count;
+        }
+    }
+};
+
+} // namespace detail
+
+/*!
+ * Adaptor that does not release the memory to the parent heap but
+ * instead it keeps the memory in a global free list that **is not
+ * thread-safe**. Must be preceded by a `with_data<free_list_node,
+ * ...>` heap adaptor.
+ *
+ * @tparam Size  Maximum size of the objects to be allocated.
+ * @tparam Limit Maximum number of elements to keep in the free list.
+ * @tparam Base  Type of the parent heap.
+ */
+template <std::size_t Size, std::size_t Limit, typename Base>
+struct unsafe_free_list_heap
+    : detail::unsafe_free_list_heap_impl<detail::unsafe_free_list_storage,
+                                         Size,
+                                         Limit,
+                                         Base>
+{};
+
+} // namespace immer
diff --git a/third_party/immer/immer/heap/with_data.hpp b/third_party/immer/immer/heap/with_data.hpp
new file mode 100644
index 0000000000..1e8c2abb08
--- /dev/null
+++ b/third_party/immer/immer/heap/with_data.hpp
@@ -0,0 +1,43 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <cstdio>
+
+namespace immer {
+
+/*!
+ * Appends a default constructed extra object of type `T` at the
+ * *before* the requested region.
+ *
+ * @tparam T Type of the appended data.
+ * @tparam Base Type of the parent heap.
+ */
+template <typename T, typename Base>
+struct with_data : Base
+{
+    using base_t = Base;
+
+    template <typename... Tags>
+    static void* allocate(std::size_t size, Tags... tags)
+    {
+        auto p = base_t::allocate(size + sizeof(T), tags...);
+        return new (p) T{} + 1;
+    }
+
+    template <typename... Tags>
+    static void deallocate(std::size_t size, void* p, Tags... tags)
+    {
+        auto dp = static_cast<T*>(p) - 1;
+        dp->~T();
+        base_t::deallocate(size + sizeof(T), dp, tags...);
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/map.hpp b/third_party/immer/immer/map.hpp
new file mode 100644
index 0000000000..58a84d2de9
--- /dev/null
+++ b/third_party/immer/immer/map.hpp
@@ -0,0 +1,342 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <functional>
+
+namespace immer {
+
+template <typename K,
+          typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class map_transient;
+
+/*!
+ * Immutable unordered mapping of values from type `K` to type `T`.
+ *
+ * @tparam K    The type of the keys.
+ * @tparam T    The type of the values to be stored in the container.
+ * @tparam Hash The type of a function object capable of hashing
+ *              values of type `T`.
+ * @tparam Equal The type of a function object capable of comparing
+ *              values of type `T`.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *              memory_policy.
+ *
+ * @rst
+ *
+ * This cotainer provides a good trade-off between cache locality,
+ * search, update performance and structural sharing.  It does so by
+ * storing the data in contiguous chunks of :math:`2^{B}` elements.
+ * When storing big objects, the size of these contiguous chunks can
+ * become too big, damaging performance.  If this is measured to be
+ * problematic for a specific use-case, it can be solved by using a
+ * `immer::box` to wrap the type `T`.
+ *
+ * **Example**
+ *   .. literalinclude:: ../example/map/intro.cpp
+ *      :language: c++
+ *      :start-after: intro/start
+ *      :end-before:  intro/end
+ *
+ * @endrst
+ *
+ */
+template <typename K,
+          typename T,
+          typename Hash           = std::hash<K>,
+          typename Equal          = std::equal_to<K>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class map
+{
+    using value_t = std::pair<K, T>;
+
+    struct project_value
+    {
+        const T& operator()(const value_t& v) const noexcept
+        {
+            return v.second;
+        }
+    };
+
+    struct project_value_ptr
+    {
+        const T* operator()(const value_t& v) const noexcept
+        {
+            return &v.second;
+        }
+    };
+
+    struct combine_value
+    {
+        template <typename Kf, typename Tf>
+        value_t operator()(Kf&& k, Tf&& v) const
+        {
+            return {std::forward<Kf>(k), std::forward<Tf>(v)};
+        }
+    };
+
+    struct default_value
+    {
+        const T& operator()() const
+        {
+            static T v{};
+            return v;
+        }
+    };
+
+    struct error_value
+    {
+        const T& operator()() const
+        {
+            throw std::out_of_range{"key not found"};
+        }
+    };
+
+    struct hash_key
+    {
+        auto operator()(const value_t& v) { return Hash{}(v.first); }
+
+        auto operator()(const K& v) { return Hash{}(v); }
+    };
+
+    struct equal_key
+    {
+        auto operator()(const value_t& a, const value_t& b)
+        {
+            return Equal{}(a.first, b.first);
+        }
+
+        auto operator()(const value_t& a, const K& b)
+        {
+            return Equal{}(a.first, b);
+        }
+    };
+
+    struct equal_value
+    {
+        auto operator()(const value_t& a, const value_t& b)
+        {
+            return Equal{}(a.first, b.first) && a.second == b.second;
+        }
+    };
+
+    using impl_t =
+        detail::hamts::champ<value_t, hash_key, equal_key, MemoryPolicy, B>;
+
+public:
+    using key_type        = K;
+    using mapped_type     = T;
+    using value_type      = std::pair<K, T>;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const value_type&;
+    using const_reference = const value_type&;
+
+    using iterator = detail::hamts::
+        champ_iterator<value_t, hash_key, equal_key, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type = map_transient<K, T, Hash, Equal, MemoryPolicy, B>;
+
+    /*!
+     * Default constructor.  It creates a set of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    map() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the map or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const K& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T& operator[](const K& k) const
+    {
+        return impl_.template get<project_value, default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    const T& at(const K& k) const
+    {
+        return impl_.template get<project_value, error_value>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.  If
+     * the key is not contained in the map, a `nullptr` is returned.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * .. admonition:: Why doesn't this function return an iterator?
+     *
+     *   Associative containers from the C++ standard library provide a
+     *   ``find`` method that returns an iterator pointing to the
+     *   element in the container or ``end()`` when the key is missing.
+     *   In the case of an unordered container, the only meaningful
+     *   thing one may do with it is to compare it with the end, to
+     *   test if the find was succesfull, and dereference it.  This
+     *   comparison is cumbersome compared to testing for a non-empty
+     *   optional value.  Furthermore, for an immutable container,
+     *   returning an iterator would have some additional performance
+     *   cost, with no benefits otherwise.
+     *
+     *   In our opinion, this function should return a
+     *   ``std::optional<const T&>`` but this construction is not valid
+     *   in any current standard.  As a compromise we return a
+     *   pointer, which has similar syntactic properties yet it is
+     *   unfortunatelly unnecessarily unrestricted.
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD const T* find(const K& k) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Returns whether the sets are equal.
+     */
+    IMMER_NODISCARD bool operator==(const map& other) const
+    {
+        return impl_.template equals<equal_value>(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const map& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a map containing the association `value`.  If the key is
+     * already in the map, it replaces its association in the map.
+     * It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    IMMER_NODISCARD map insert(value_type value) const
+    {
+        return impl_.add(std::move(value));
+    }
+
+    /*!
+     * Returns a map containing the association `(k, v)`.  If the key
+     * is already in the map, it replaces its association in the map.
+     * It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    IMMER_NODISCARD map set(key_type k, mapped_type v) const
+    {
+        return impl_.add({std::move(k), std::move(v)});
+    }
+
+    /*!
+     * Returns a map replacing the association `(k, v)` by the
+     * association new association `(k, fn(v))`, where `v` is the
+     * currently associated value for `k` in the map or a default
+     * constructed value otherwise. It may allocate memory
+     * and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    IMMER_NODISCARD map update(key_type k, Fn&& fn) const
+    {
+        return impl_
+            .template update<project_value, default_value, combine_value>(
+                std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Returns a map without the key `k`.  If the key is not
+     * associated in the map it returns the same map.  It may allocate
+     * memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD map erase(const K& k) const { return impl_.sub(k); }
+
+    /*!
+     * Returns an @a transient form of this container, a
+     * `immer::map_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+private:
+    friend transient_type;
+
+    map(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/map_transient.hpp b/third_party/immer/immer/map_transient.hpp
new file mode 100644
index 0000000000..8821c5d12b
--- /dev/null
+++ b/third_party/immer/immer/map_transient.hpp
@@ -0,0 +1,41 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <functional>
+
+namespace immer {
+
+/*!
+ * @rst
+ *
+ * .. admonition:: Become a sponsor!
+ *    :class: danger
+ *
+ *    This component is planned but it has **not been implemented yet**.
+ *
+ *    Transiens can critically improve the performance of applications
+ *    intensively using ``set`` and ``map``. If you are working for an
+ *    organization using the library in a commercial project, please consider
+ *    **sponsoring this work**: juanpe@sinusoid.al
+ *
+ * @endrst
+ */
+template <typename K,
+          typename T,
+          typename Hash           = std::hash<K>,
+          typename Equal          = std::equal_to<K>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class map_transient;
+
+} // namespace immer
diff --git a/third_party/immer/immer/memory_policy.hpp b/third_party/immer/immer/memory_policy.hpp
new file mode 100644
index 0000000000..b4f665bf62
--- /dev/null
+++ b/third_party/immer/immer/memory_policy.hpp
@@ -0,0 +1,135 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/cpp_heap.hpp>
+#include <immer/heap/heap_policy.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+#include <immer/transience/gc_transience_policy.hpp>
+#include <immer/transience/no_transience_policy.hpp>
+#include <type_traits>
+
+namespace immer {
+
+/*!
+ * Metafunction that returns the best *transience policy* to use for a
+ * given *refcount policy*.
+ */
+template <typename RefcountPolicy>
+struct get_transience_policy
+    : std::conditional<std::is_same<RefcountPolicy, no_refcount_policy>::value,
+                       gc_transience_policy,
+                       no_transience_policy>
+{};
+
+template <typename T>
+using get_transience_policy_t = typename get_transience_policy<T>::type;
+
+/*!
+ * Metafunction that returns wether to *prefer fewer bigger objects*
+ * to use for a given *heap policy*.
+ */
+template <typename HeapPolicy>
+struct get_prefer_fewer_bigger_objects
+    : std::integral_constant<
+          bool,
+          std::is_same<HeapPolicy, heap_policy<cpp_heap>>::value>
+{};
+
+template <typename T>
+constexpr auto get_prefer_fewer_bigger_objects_v =
+    get_prefer_fewer_bigger_objects<T>::value;
+
+/*!
+ * Metafunction that returns wether to use *transient R-Values*
+ * for a given *refcount policy*.
+ */
+template <typename RefcountPolicy>
+struct get_use_transient_rvalues
+    : std::integral_constant<
+          bool,
+          !std::is_same<RefcountPolicy, no_refcount_policy>::value>
+{};
+
+template <typename T>
+constexpr auto get_use_transient_rvalues_v =
+    get_use_transient_rvalues<T>::value;
+
+/*!
+ * This is a default implementation of a *memory policy*.  A memory
+ * policy is just a bag of other policies plus some flags with hints
+ * to the user about the best way to use these strategies.
+ *
+ * @tparam HeapPolicy A *heap policy*, for example, @ref heap_policy.
+ * @tparam RefcountPolicy A *reference counting policy*, for example,
+ *         @ref refcount_policy.
+ * @tparam TransiencePolicy A *transience policy*, for example,
+ *         @ref no_transience_policy.
+ * @tparam PreferFewerBiggerObjects Boolean flag indicating whether
+ *         the user should prefer to allocate memory in bigger chungs
+ *         --e.g. by putting various objects in the same memory
+ *         region-- or not.
+ * @tparam UseTransientRValues Boolean flag indicating whether
+ *         immutable containers should try to modify contents in-place
+ *         when manipulating an r-value reference.
+ */
+template <typename HeapPolicy,
+          typename RefcountPolicy,
+          typename TransiencePolicy = get_transience_policy_t<RefcountPolicy>,
+          bool PreferFewerBiggerObjects =
+              get_prefer_fewer_bigger_objects_v<HeapPolicy>,
+          bool UseTransientRValues =
+              get_use_transient_rvalues_v<RefcountPolicy>>
+struct memory_policy
+{
+    using heap       = HeapPolicy;
+    using refcount   = RefcountPolicy;
+    using transience = TransiencePolicy;
+
+    static constexpr bool prefer_fewer_bigger_objects =
+        PreferFewerBiggerObjects;
+
+    static constexpr bool use_transient_rvalues = UseTransientRValues;
+
+    using transience_t = typename transience::template apply<heap>::type;
+};
+
+/*!
+ * The default *heap policy* just uses the standard heap with a
+ * @ref free_list_heap_policy.  If `IMMER_NO_FREE_LIST` is defined to `1`
+ * then it just uses the standard heap.
+ */
+#if IMMER_NO_FREE_LIST
+using default_heap_policy = heap_policy<debug_size_heap<cpp_heap>>;
+#else
+#if IMMER_NO_THREAD_SAFETY
+using default_heap_policy     = unsafe_free_list_heap_policy<cpp_heap>;
+#else
+using default_heap_policy = free_list_heap_policy<cpp_heap>;
+#endif
+#endif
+
+/*!
+ * By default we use thread safe reference counting.
+ */
+#if IMMER_NO_THREAD_SAFETY
+using default_refcount_policy = unsafe_refcount_policy;
+#else
+using default_refcount_policy = refcount_policy;
+#endif
+
+/*!
+ * The default memory policy.
+ */
+using default_memory_policy =
+    memory_policy<default_heap_policy, default_refcount_policy>;
+
+} // namespace immer
diff --git a/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp b/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp
new file mode 100644
index 0000000000..1185a219fd
--- /dev/null
+++ b/third_party/immer/immer/refcount/enable_intrusive_ptr.hpp
@@ -0,0 +1,37 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/refcount/no_refcount_policy.hpp>
+
+namespace immer {
+
+template <typename Deriv, typename RefcountPolicy>
+class enable_intrusive_ptr
+{
+    mutable RefcountPolicy refcount_data_;
+
+public:
+    enable_intrusive_ptr()
+        : refcount_data_{disowned{}}
+    {}
+
+    friend void intrusive_ptr_add_ref(const Deriv* x)
+    {
+        x->refcount_data_.inc();
+    }
+
+    friend void intrusive_ptr_release(const Deriv* x)
+    {
+        if (x->refcount_data_.dec())
+            delete x;
+    }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/refcount/no_refcount_policy.hpp b/third_party/immer/immer/refcount/no_refcount_policy.hpp
new file mode 100644
index 0000000000..24f9d489f5
--- /dev/null
+++ b/third_party/immer/immer/refcount/no_refcount_policy.hpp
@@ -0,0 +1,45 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+namespace immer {
+
+struct disowned
+{};
+
+struct no_spinlock
+{
+    bool try_lock() { return true; }
+    void lock() {}
+    void unlock() {}
+
+    struct scoped_lock
+    {
+        scoped_lock(no_spinlock&) {}
+    };
+};
+
+/*!
+ * Disables reference counting, to be used with an alternative garbage
+ * collection strategy like a `gc_heap`.
+ */
+struct no_refcount_policy
+{
+    using spinlock_type = no_spinlock;
+
+    no_refcount_policy(){};
+    no_refcount_policy(disowned) {}
+
+    void inc() {}
+    bool dec() { return false; }
+    void dec_unsafe() {}
+    bool unique() { return false; }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/refcount/refcount_policy.hpp b/third_party/immer/immer/refcount/refcount_policy.hpp
new file mode 100644
index 0000000000..a7a282cd13
--- /dev/null
+++ b/third_party/immer/immer/refcount/refcount_policy.hpp
@@ -0,0 +1,101 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <atomic>
+#include <cassert>
+#include <thread>
+#include <utility>
+
+// This has been shamelessly copied from boost...
+#if defined(_MSC_VER) && _MSC_VER >= 1310 &&                                   \
+    (defined(_M_IX86) || defined(_M_X64)) && !defined(__c2__)
+extern "C" void _mm_pause();
+#define IMMER_SMT_PAUSE _mm_pause()
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define IMMER_SMT_PAUSE __asm__ __volatile__("rep; nop" : : : "memory")
+#endif
+
+namespace immer {
+
+// This is an atomic spinlock similar to the one used by boost to provide
+// "atomic" shared_ptr operations.  It also does not differ much from the one
+// from libc++ or libstdc++...
+struct spinlock
+{
+    std::atomic_flag v_{};
+
+    bool try_lock() { return !v_.test_and_set(std::memory_order_acquire); }
+
+    void lock()
+    {
+        for (auto k = 0u; !try_lock(); ++k) {
+            if (k < 4)
+                continue;
+#ifdef IMMER_SMT_PAUSE
+            else if (k < 16)
+                IMMER_SMT_PAUSE;
+#endif
+            else
+                std::this_thread::yield();
+        }
+    }
+
+    void unlock() { v_.clear(std::memory_order_release); }
+
+    struct scoped_lock
+    {
+        scoped_lock(const scoped_lock&) = delete;
+        scoped_lock& operator=(const scoped_lock&) = delete;
+
+        explicit scoped_lock(spinlock& sp)
+            : sp_{sp}
+        {
+            sp.lock();
+        }
+
+        ~scoped_lock() { sp_.unlock(); }
+
+    private:
+        spinlock& sp_;
+    };
+};
+
+/*!
+ * A reference counting policy implemented using an *atomic* `int`
+ * count.  It is **thread-safe**.
+ */
+struct refcount_policy
+{
+    using spinlock_type = spinlock;
+
+    mutable std::atomic<int> refcount;
+
+    refcount_policy()
+        : refcount{1} {};
+    refcount_policy(disowned)
+        : refcount{0}
+    {}
+
+    void inc() { refcount.fetch_add(1, std::memory_order_relaxed); }
+
+    bool dec() { return 1 == refcount.fetch_sub(1, std::memory_order_acq_rel); }
+
+    void dec_unsafe()
+    {
+        assert(refcount.load() > 1);
+        refcount.fetch_sub(1, std::memory_order_relaxed);
+    }
+
+    bool unique() { return refcount == 1; }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp b/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp
new file mode 100644
index 0000000000..bcf24578de
--- /dev/null
+++ b/third_party/immer/immer/refcount/unsafe_refcount_policy.hpp
@@ -0,0 +1,40 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <atomic>
+#include <utility>
+
+namespace immer {
+
+/*!
+ * A reference counting policy implemented using a raw `int` count.
+ * It is **not thread-safe**.
+ */
+struct unsafe_refcount_policy
+{
+    using spinlock_type = no_spinlock;
+
+    mutable int refcount;
+
+    unsafe_refcount_policy()
+        : refcount{1} {};
+    unsafe_refcount_policy(disowned)
+        : refcount{0}
+    {}
+
+    void inc() { ++refcount; }
+    bool dec() { return --refcount == 0; }
+    void dec_unsafe() { --refcount; }
+    bool unique() { return refcount == 1; }
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/set.hpp b/third_party/immer/immer/set.hpp
new file mode 100644
index 0000000000..a152ac9553
--- /dev/null
+++ b/third_party/immer/immer/set.hpp
@@ -0,0 +1,198 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <functional>
+
+namespace immer {
+
+template <typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class set_transient;
+
+/*!
+ * Immutable set representing an unordered bag of values.
+ *
+ * @tparam T    The type of the values to be stored in the container.
+ * @tparam Hash The type of a function object capable of hashing
+ *              values of type `T`.
+ * @tparam Equal The type of a function object capable of comparing
+ *              values of type `T`.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *              memory_policy.
+ *
+ * @rst
+ *
+ * This container provides a good trade-off between cache locality,
+ * membership checks, update performance and structural sharing.  It
+ * does so by storing the data in contiguous chunks of :math:`2^{B}`
+ * elements.  When storing big objects, the size of these contiguous
+ * chunks can become too big, damaging performance.  If this is
+ * measured to be problematic for a specific use-case, it can be
+ * solved by using a `immer::box` to wrap the type `T`.
+ *
+ * **Example**
+ *   .. literalinclude:: ../example/set/intro.cpp
+ *      :language: c++
+ *      :start-after: intro/start
+ *      :end-before:  intro/end
+ *
+ * @endrst
+ *
+ */
+template <typename T,
+          typename Hash           = std::hash<T>,
+          typename Equal          = std::equal_to<T>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class set
+{
+    using impl_t = detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>;
+
+    struct project_value_ptr
+    {
+        const T* operator()(const T& v) const noexcept { return &v; }
+    };
+
+public:
+    using key_type        = T;
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const T&;
+    using const_reference = const T&;
+
+    using iterator =
+        detail::hamts::champ_iterator<T, Hash, Equal, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type = set_transient<T, Hash, Equal, MemoryPolicy, B>;
+
+    /*!
+     * Default constructor.  It creates a set of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    set() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when `value` is contained in the set or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const T& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const T& value) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
+
+    /*!
+     * Returns whether the sets are equal.
+     */
+    IMMER_NODISCARD bool operator==(const set& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const set& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a set containing `value`.  If the `value` is already in
+     * the set, it returns the same set.  It may allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD set insert(T value) const
+    {
+        return impl_.add(std::move(value));
+    }
+
+    /*!
+     * Returns a set without `value`.  If the `value` is not in the
+     * set it returns the same set.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD set erase(const T& value) const { return impl_.sub(value); }
+
+    /*!
+     * Returns an @a transient form of this container, a
+     * `immer::set_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+private:
+    friend transient_type;
+
+    set(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/set_transient.hpp b/third_party/immer/immer/set_transient.hpp
new file mode 100644
index 0000000000..cd0f652b95
--- /dev/null
+++ b/third_party/immer/immer/set_transient.hpp
@@ -0,0 +1,40 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <functional>
+
+namespace immer {
+
+/*!
+ * @rst
+ *
+ * .. admonition:: Become a sponsor!
+ *    :class: danger
+ *
+ *    This component is planned but it has **not been implemented yet**.
+ *
+ *    Transiens can critically improve the performance of applications
+ *    intensively using ``set`` and ``map``. If you are working for an
+ *    organization using the library in a commercial project, please consider
+ *    **sponsoring this work**: juanpe@sinusoid.al
+ *
+ * @endrst
+ */
+template <typename T,
+          typename Hash           = std::hash<T>,
+          typename Equal          = std::equal_to<T>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class set_transient;
+
+} // namespace immer
diff --git a/third_party/immer/immer/transience/gc_transience_policy.hpp b/third_party/immer/immer/transience/gc_transience_policy.hpp
new file mode 100644
index 0000000000..d3c30efa1c
--- /dev/null
+++ b/third_party/immer/immer/transience/gc_transience_policy.hpp
@@ -0,0 +1,110 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/tags.hpp>
+
+#include <atomic>
+#include <memory>
+#include <utility>
+
+namespace immer {
+
+/*!
+ * Provides transience ownership tracking when a *tracing garbage
+ * collector* is used instead of reference counting.
+ *
+ * @rst
+ *
+ * .. warning:: Using this policy without an allocation scheme that
+ *    includes automatic tracing garbage collection may cause memory
+ *    leaks.
+ *
+ * @endrst
+ */
+struct gc_transience_policy
+{
+    template <typename HeapPolicy>
+    struct apply
+    {
+        struct type
+        {
+            using heap_ = typename HeapPolicy::type;
+
+            struct edit
+            {
+                void* v;
+                edit() = delete;
+                bool operator==(edit x) const { return v == x.v; }
+                bool operator!=(edit x) const { return v != x.v; }
+            };
+
+            struct owner
+            {
+                void* make_token_()
+                {
+                    return heap_::allocate(1, norefs_tag{});
+                };
+
+                mutable std::atomic<void*> token_;
+
+                operator edit() { return {token_}; }
+
+                owner()
+                    : token_{make_token_()}
+                {}
+                owner(const owner& o)
+                    : token_{make_token_()}
+                {
+                    o.token_ = make_token_();
+                }
+                owner(owner&& o) noexcept
+                    : token_{o.token_.load()}
+                {}
+                owner& operator=(const owner& o)
+                {
+                    o.token_ = make_token_();
+                    token_   = make_token_();
+                    return *this;
+                }
+                owner& operator=(owner&& o) noexcept
+                {
+                    token_ = o.token_.load();
+                    return *this;
+                }
+            };
+
+            struct ownee
+            {
+                edit token_{nullptr};
+
+                ownee& operator=(edit e)
+                {
+                    assert(e != noone);
+                    // This would be a nice safety plug but it sadly
+                    // does not hold during transient concatenation.
+                    // assert(token_ == e || token_ == edit{nullptr});
+                    token_ = e;
+                    return *this;
+                }
+
+                bool can_mutate(edit t) const { return token_ == t; }
+                bool owned() const { return token_ != edit{nullptr}; }
+            };
+
+            static owner noone;
+        };
+    };
+};
+
+template <typename HP>
+typename gc_transience_policy::apply<HP>::type::owner
+    gc_transience_policy::apply<HP>::type::noone = {};
+
+} // namespace immer
diff --git a/third_party/immer/immer/transience/no_transience_policy.hpp b/third_party/immer/immer/transience/no_transience_policy.hpp
new file mode 100644
index 0000000000..2f87df7a39
--- /dev/null
+++ b/third_party/immer/immer/transience/no_transience_policy.hpp
@@ -0,0 +1,48 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+namespace immer {
+
+/*!
+ * Disables any special *transience* tracking.  To be used when
+ * *reference counting* is available instead.
+ */
+struct no_transience_policy
+{
+    template <typename>
+    struct apply
+    {
+        struct type
+        {
+            struct edit
+            {};
+
+            struct owner
+            {
+                operator edit() const { return {}; }
+            };
+
+            struct ownee
+            {
+                ownee& operator=(edit) { return *this; };
+                bool can_mutate(edit) const { return false; }
+                bool owned() const { return false; }
+            };
+
+            static owner noone;
+        };
+    };
+};
+
+template <typename HP>
+typename no_transience_policy::apply<HP>::type::owner
+    no_transience_policy::apply<HP>::type::noone = {};
+
+} // namespace immer
diff --git a/third_party/immer/immer/vector.hpp b/third_party/immer/immer/vector.hpp
new file mode 100644
index 0000000000..4f1a148ccd
--- /dev/null
+++ b/third_party/immer/immer/vector.hpp
@@ -0,0 +1,412 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/rbts/rbtree.hpp>
+#include <immer/detail/rbts/rbtree_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+#if IMMER_DEBUG_PRINT
+#include <immer/flex_vector.hpp>
+#endif
+
+namespace immer {
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class flex_vector;
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class vector_transient;
+
+/*!
+ * Immutable sequential container supporting both random access and
+ * structural sharing.
+ *
+ * @tparam T The type of the values to be stored in the container.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *         memory_policy.
+ *
+ * @rst
+ *
+ * This cotainer provides a good trade-off between cache locality,
+ * random access, update performance and structural sharing.  It does
+ * so by storing the data in contiguous chunks of :math:`2^{BL}`
+ * elements.  By default, when ``sizeof(T) == sizeof(void*)`` then
+ * :math:`B=BL=5`, such that data would be stored in contiguous
+ * chunks of :math:`32` elements.
+ *
+ * You may learn more about the meaning and implications of ``B`` and
+ * ``BL`` parameters in the :doc:`implementation` section.
+ *
+ * .. note:: In several methods we say that their complexity is
+ *    *effectively* :math:`O(...)`. Do not confuse this with the word
+ *    *amortized*, which has a very different meaning.  In this
+ *    context, *effective* means that while the
+ *    mathematically rigurous
+ *    complexity might be higher, for all practical matters the
+ *    provided complexity is more useful to think about the actual
+ *    cost of the operation.
+ *
+ * **Example**
+ *   .. literalinclude:: ../example/vector/intro.cpp
+ *      :language: c++
+ *      :start-after: intro/start
+ *      :end-before:  intro/end
+ *
+ * @endrst
+ */
+template <typename T,
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class vector
+{
+    using impl_t = detail::rbts::rbtree<T, MemoryPolicy, B, BL>;
+    using flex_t = flex_vector<T, MemoryPolicy, B, BL>;
+
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
+public:
+    static constexpr auto bits      = B;
+    static constexpr auto bits_leaf = BL;
+    using memory_policy             = MemoryPolicy;
+
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using transient_type = vector_transient<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Default constructor.  It creates a vector of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    vector() = default;
+
+    /*!
+     * Constructs a vector containing the elements in `values`.
+     */
+    vector(std::initializer_list<T> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
+
+    /*!
+     * Constructs a vector containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    vector(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
+
+    /*!
+     * Constructs a vector containing the element `val` repeated `n`
+     * times.
+     */
+    vector(size_type n, T v = {})
+        : impl_{impl_t::from_fill(n, v)}
+    {}
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Access the last element.
+     */
+    IMMER_NODISCARD const T& back() const { return impl_.back(); }
+
+    /*!
+     * Access the first element.
+     */
+    IMMER_NODISCARD const T& front() const { return impl_.front(); }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Returns whether the vectors are equal.
+     */
+    IMMER_NODISCARD bool operator==(const vector& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const vector& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a vector with `value` inserted at the end.  It may
+     * allocate memory and its complexity is *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/vector/vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: push-back/start
+     *      :end-before:  push-back/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD vector push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
+
+    /*!
+     * Returns a vector containing value `value` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/vector/vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: set/start
+     *      :end-before:  set/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD vector set(size_type index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
+
+    /*!
+     * Returns a vector containing the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `0 >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/vector/vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: update/start
+     *      :end-before:  update/end
+     *
+     * @endrst
+     */
+    template <typename FnT>
+    IMMER_NODISCARD vector update(size_type index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
+
+    template <typename FnT>
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Returns a vector containing only the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * **Example**
+     *   .. literalinclude:: ../example/vector/vector.cpp
+     *      :language: c++
+     *      :dedent: 8
+     *      :start-after: take/start
+     *      :end-before:  take/end
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD vector take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
+
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
+
+    /*!
+     * Returns an @a transient form of this container, an
+     * `immer::vector_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+#if IMMER_DEBUG_PRINT
+    void debug_print(std::ostream& out = std::cerr) const
+    {
+        flex_t{*this}.debug_print(out);
+    }
+#endif
+
+private:
+    friend flex_t;
+    friend transient_type;
+
+    vector(impl_t impl)
+        : impl_(std::move(impl))
+    {
+#if IMMER_DEBUG_PRINT
+        // force the compiler to generate debug_print, so we can call
+        // it from a debugger
+        [](volatile auto) {}(&vector::debug_print);
+#endif
+    }
+
+    vector&& push_back_move(std::true_type, value_type value)
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    vector push_back_move(std::false_type, value_type value)
+    {
+        return impl_.push_back(std::move(value));
+    }
+
+    vector&& set_move(std::true_type, size_type index, value_type value)
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
+    vector set_move(std::false_type, size_type index, value_type value)
+    {
+        return impl_.assoc(index, std::move(value));
+    }
+
+    template <typename Fn>
+    vector&& update_move(std::true_type, size_type index, Fn&& fn)
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    vector update_move(std::false_type, size_type index, Fn&& fn)
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
+
+    vector&& take_move(std::true_type, size_type elems)
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
+    vector take_move(std::false_type, size_type elems)
+    {
+        return impl_.take(elems);
+    }
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/immer/vector_transient.hpp b/third_party/immer/immer/vector_transient.hpp
new file mode 100644
index 0000000000..4d648cab07
--- /dev/null
+++ b/third_party/immer/immer/vector_transient.hpp
@@ -0,0 +1,203 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/rbts/rbtree.hpp>
+#include <immer/detail/rbts/rbtree_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+namespace immer {
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class vector;
+
+template <typename T,
+          typename MemoryPolicy,
+          detail::rbts::bits_t B,
+          detail::rbts::bits_t BL>
+class flex_vector_transient;
+
+/*!
+ * Mutable version of `immer::vector`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
+ */
+template <typename T,
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class vector_transient : MemoryPolicy::transience_t::owner
+{
+    using impl_t  = detail::rbts::rbtree<T, MemoryPolicy, B, BL>;
+    using flex_t  = flex_vector_transient<T, MemoryPolicy, B, BL>;
+    using owner_t = typename MemoryPolicy::transience_t::owner;
+
+public:
+    static constexpr auto bits      = B;
+    static constexpr auto bits_leaf = BL;
+    using memory_policy             = MemoryPolicy;
+
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
+    using difference_type = std::ptrdiff_t;
+    using const_reference = const T&;
+
+    using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
+    using const_iterator   = iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    using persistent_type = vector<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Default constructor.  It creates a mutable vector of `size() ==
+     * 0`.  It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    vector_transient() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing at the first element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
+
+    /*!
+     * Returns an iterator that traverses the collection backwards,
+     * pointing after the last element of the reversed collection. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns a `const` reference to the element at position `index`.
+     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    reference operator[](size_type index) const { return impl_.get(index); }
+
+    /*!
+     * Returns a `const` reference to the element at position
+     * `index`. It throws an `std::out_of_range` exception when @f$
+     * index \geq size() @f$.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    reference at(size_type index) const { return impl_.get_check(index); }
+
+    /*!
+     * Inserts `value` at the end.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    void push_back(value_type value)
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
+
+    /*!
+     * Sets to the value `value` at position `idx`.
+     * Undefined for `index >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void set(size_type index, value_type value)
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
+
+    /*!
+     * Updates the vector to contain the result of the expression
+     * `fn((*this)[idx])` at position `idx`.
+     * Undefined for `0 >= size()`.
+     * It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    template <typename FnT>
+    void update(size_type index, FnT&& fn)
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
+
+    /*!
+     * Resizes the vector to only contain the first `min(elems, size())`
+     * elements. It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::vector`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return persistent_type{impl_};
+    }
+    IMMER_NODISCARD persistent_type persistent() &&
+    {
+        return persistent_type{std::move(impl_)};
+    }
+
+private:
+    friend flex_t;
+    friend persistent_type;
+
+    vector_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/third_party/immer/nix/benchmarks.nix b/third_party/immer/nix/benchmarks.nix
new file mode 100644
index 0000000000..9eaa87e291
--- /dev/null
+++ b/third_party/immer/nix/benchmarks.nix
@@ -0,0 +1,100 @@
+{ nixpkgs ? <nixpkgs>}:
+
+with import nixpkgs {};
+
+rec {
+  c_rrb = stdenv.mkDerivation rec {
+    name = "c-rrb-${version}";
+    version = "git-${commit}";
+    commit = "d908617ff84515af90c454ff4d0f98675ae6b456";
+    src = fetchFromGitHub {
+      owner = "hyPiRion";
+      repo = "c-rrb";
+      rev = commit;
+      sha256 = "0zmha3xi80vgdcwzb4vwdllf97dvggjpjfgahrpsb5f5qi3yshxa";
+    };
+    nativeBuildInputs = [ autoreconfHook ];
+    propagatedBuildInputs = [ boehmgc ];
+    meta = with stdenv.lib; {
+      homepage = "http://hypirion.com/thesis";
+      description = "RRB-tree implemented as a library in C. ";
+      license = licenses.mit;
+    };
+  };
+
+  steady = stdenv.mkDerivation rec {
+    name = "steady-${version}";
+    version = "git-${commit}";
+    commit = "aac65955f18d0de856e3421d8593378d5e1832c4";
+    src = fetchFromGitHub {
+      owner = "marcusz";
+      repo = "steady";
+      rev = commit;
+      sha256 = "0zaxr9aj1q5sr99h133fzxch8lw9daw1pxs2g972s02gdq8r8fzh";
+    };
+    dontBuild = true;
+    installPhase = "mkdir -vp $out/include; cp -vr $src/steady $out/include/";
+    meta = with stdenv.lib; {
+      homepage = "https://github.com/marcusz/steady";
+      description = "This is a fast and reliable persistent (immutable) vector class for C++";
+      license = licenses.asl20;
+    };
+  };
+
+  chunkedseq = stdenv.mkDerivation rec {
+    name = "chunkedseq-${version}";
+    version = "git-${commit}";
+    commit = "f1c4f9470edcb59996fdc4aad34311767c46b25e";
+    src = fetchFromGitHub {
+      owner = "deepsea-inria";
+      repo = "chunkedseq";
+      rev = commit;
+      sha256 = "19bps5r3dy55jlvpwq8x2s4d0mwwx0djb6g2r2vvbf628bg6cx8p";
+    };
+    dontBuild = true;
+    installPhase = "mkdir -vp $out/include/chunkedseq; cp -vr $src/include/* $out/include/chunkedseq/";
+    meta = with stdenv.lib; {
+      homepage = "http://deepsea.inria.fr/chunkedseq";
+      description = "Container data structure for representing sequences by many fixed-capacity heap-allocated buffers--chunks";
+      license = licenses.mit;
+    };
+  };
+
+  immutable_cpp = stdenv.mkDerivation rec {
+    name = "immutable-cpp-${version}";
+    version = "git-${commit}";
+    commit = "a4a32022d895dd0d3c03547a2b2a2b03face01eb";
+    src = fetchFromGitHub {
+      owner = "rsms";
+      repo = "immutable-cpp";
+      rev = commit;
+      sha256 = "11zn67avqcc8baddf43fsd29bpv08p0ri6zqng12105k725fq2p7";
+    };
+    dontBuild = true;
+    installPhase = "mkdir -vp $out/include; cp -vr $src/immutable $out/include/";
+    meta = with stdenv.lib; {
+      homepage = "https://github.com/rsms/immutable-cpp";
+      description = "Persistent immutable data structures for C++";
+      license = licenses.mit;
+    };
+  };
+
+  hash_trie = stdenv.mkDerivation rec {
+    name = "hash_trie-${version}";
+    version = "git-${commit}";
+    commit = "1ce346c74923ba16329332103dc0f425b658c8be";
+    src = fetchFromGitHub {
+      owner = "philsquared";
+      repo = "hash_trie";
+      rev = commit;
+      sha256 = "0rpa1682vm0fvvrs1jr7jdkskcrgkm3qrawrcr7xrmknz5jf0v4l";
+    };
+    dontBuild = true;
+    installPhase = "mkdir -vp $out/include; cp -vr $src/hash_trie.hpp $out/include/";
+    meta = with stdenv.lib; {
+      homepage = "https://github.com/rsms/immutable-cpp";
+      description = "Persistent immutable data structures for C++";
+      license = licenses.mit;
+    };
+  };
+}
diff --git a/third_party/immer/nix/docs.nix b/third_party/immer/nix/docs.nix
new file mode 100644
index 0000000000..f885e6d9e1
--- /dev/null
+++ b/third_party/immer/nix/docs.nix
@@ -0,0 +1,27 @@
+{ nixpkgs ? <nixpkgs>}:
+
+with import nixpkgs {};
+
+rec {
+  breathe = with python27Packages; buildPythonPackage rec {
+    version = "git-arximboldi-${commit}";
+    pname = "breathe";
+    name = "${pname}-${version}";
+    commit = "5074aecb5ad37bb70f50216eaa01d03a375801ec";
+    src = fetchFromGitHub {
+      owner = "arximboldi";
+      repo = "breathe";
+      rev = commit;
+      sha256 = "10kkh3wb0ggyxx1a7x50aklhhw0cq269g3jddf2gb3pv9gpbj7sa";
+    };
+    propagatedBuildInputs = [ docutils sphinx ];
+    meta = with stdenv.lib; {
+      homepage = https://github.com/michaeljones/breathe;
+      license = licenses.bsd3;
+      description = "Sphinx Doxygen renderer";
+      inherit (sphinx.meta) platforms;
+    };
+  };
+
+  recommonmark = python27Packages.recommonmark;
+}
diff --git a/third_party/immer/setup.py b/third_party/immer/setup.py
new file mode 100644
index 0000000000..98c08449bc
--- /dev/null
+++ b/third_party/immer/setup.py
@@ -0,0 +1,68 @@
+
+# immer: immutable data structures for C++
+# Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+#
+# This software is distributed under the Boost Software License, Version 1.0.
+# See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+
+##
+
+import os
+import re
+import sys
+import platform
+import subprocess
+
+from distutils.version import LooseVersion
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext
+
+class CMakeExtension(Extension):
+    def __init__(self, name, sourcedir=''):
+        Extension.__init__(self, name, sources=[])
+        self.sourcedir = os.path.abspath(sourcedir)
+
+class CMakeBuild(build_ext):
+
+    def run(self):
+        try:
+            subprocess.check_output(['cmake', '--version'])
+        except OSError:
+            raise RuntimeError(
+                "CMake must be installed to build the following extensions: " +
+                ", ".join(e.name for e in self.extensions))
+        for ext in self.extensions:
+            self.build_extension(ext)
+
+    def build_extension(self, ext):
+        extdir = os.path.abspath(os.path.dirname(
+            self.get_ext_fullpath(ext.name)))
+        cfg = 'Debug' if self.debug else 'Release'
+        if not os.path.exists(self.build_temp):
+            os.makedirs(self.build_temp)
+        subprocess.check_call([
+            'cmake', ext.sourcedir,
+            '-DCMAKE_BUILD_TYPE=' + cfg,
+            '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
+            '-DPYTHON_EXECUTABLE=' + sys.executable,
+            '-DDISABLE_WERROR=1',
+        ], cwd=self.build_temp)
+        subprocess.check_call([
+            'cmake', '--build', '.',
+            '--config', cfg,
+            '--target', 'python',
+        ], cwd=self.build_temp)
+
+setup(
+    name='immer',
+    version='0.0.0',
+    author='Juan Pedro Bolivar Puente',
+    author_email='raskolnikov@gnu.org',
+    description='Efficient immutable data structures',
+    long_description='',
+    packages=['immer'],
+    package_dir={'': 'extra/python'},
+    ext_modules=[CMakeExtension('immer_python_module', sourcedir='.')],
+    cmdclass=dict(build_ext=CMakeBuild),
+    zip_safe=False,
+)
diff --git a/third_party/immer/shell.nix b/third_party/immer/shell.nix
new file mode 100644
index 0000000000..07629a1971
--- /dev/null
+++ b/third_party/immer/shell.nix
@@ -0,0 +1,65 @@
+{ toolchain ? "",
+  nixpkgs ? (import <nixpkgs> {}).fetchFromGitHub {
+    owner  = "NixOS";
+    repo   = "nixpkgs";
+    rev    = "053ad4e0db7241ae6a02394d62750fdc5d64aa9f";
+    sha256 = "11l9sr8zg8j1n5p43zjkqwpj59gn8c84z1kf16icnsbnv2smzqdc";
+  }}:
+
+with import nixpkgs {};
+
+let
+  # For the documentation tools we use an older Nixpkgs since the
+  # newer versions seem to be not working great...
+  oldNixpkgsSrc = fetchFromGitHub {
+                    owner  = "NixOS";
+                    repo   = "nixpkgs";
+                    rev    = "d0d905668c010b65795b57afdf7f0360aac6245b";
+                    sha256 = "1kqxfmsik1s1jsmim20n5l4kq6wq8743h5h17igfxxbbwwqry88l";
+                  };
+  oldNixpkgs    = import oldNixpkgsSrc {};
+  docs          = import ./nix/docs.nix { nixpkgs = oldNixpkgsSrc; };
+  benchmarks    = import ./nix/benchmarks.nix { inherit nixpkgs; };
+  tc            =
+    if toolchain == ""        then { stdenv = stdenv; cc = stdenv.cc; } else
+    if toolchain == "gnu-6"   then { stdenv = gcc6Stdenv; cc = gcc6; } else
+    if toolchain == "gnu-7"   then { stdenv = gcc7Stdenv; cc = gcc7; } else
+    if toolchain == "gnu-8"   then { stdenv = gcc8Stdenv; cc = gcc8; } else
+    if toolchain == "gnu-9"   then { stdenv = gcc9Stdenv; cc = gcc9; } else
+    if toolchain == "llvm-39" then { stdenv = llvmPackages_39.libcxxStdenv; cc = llvmPackages_39.libcxxClang; } else
+    if toolchain == "llvm-4"  then { stdenv = llvmPackages_4.libcxxStdenv; cc = llvmPackages_4.libcxxClang; } else
+    if toolchain == "llvm-5"  then { stdenv = llvmPackages_5.libcxxStdenv; cc = llvmPackages_5.libcxxClang; } else
+    if toolchain == "llvm-6"  then { stdenv = llvmPackages_6.libcxxStdenv; cc = llvmPackages_6.libcxxClang; } else
+    if toolchain == "llvm-7"  then { stdenv = llvmPackages_7.libcxxStdenv; cc = llvmPackages_7.libcxxClang; } else
+    if toolchain == "llvm-8"  then { stdenv = llvmPackages_8.libcxxStdenv; cc = llvmPackages_8.libcxxClang; } else
+    if toolchain == "llvm-9"  then { stdenv = llvmPackages_9.stdenv; cc = llvmPackages_9.clang; } else
+    abort "unknown toolchain";
+
+in
+tc.stdenv.mkDerivation rec {
+  name = "immer-env";
+  buildInputs = [
+    tc.cc
+    git
+    cmake
+    pkgconfig
+    ninja
+    gdb
+    lldb
+    ccache
+    boost
+    boehmgc
+    benchmarks.c_rrb
+    benchmarks.steady
+    benchmarks.chunkedseq
+    benchmarks.immutable_cpp
+    benchmarks.hash_trie
+    oldNixpkgs.doxygen
+    (oldNixpkgs.python.withPackages (ps: [
+      ps.sphinx
+      docs.breathe
+      docs.recommonmark
+    ]))
+  ];
+  hardeningDisable = [ "fortify" ];
+}
diff --git a/third_party/immer/test/CMakeLists.txt b/third_party/immer/test/CMakeLists.txt
new file mode 100644
index 0000000000..faa61bbe6d
--- /dev/null
+++ b/third_party/immer/test/CMakeLists.txt
@@ -0,0 +1,22 @@
+
+#  Targets
+#  =======
+
+add_custom_target(tests
+  COMMENT "Build all the unit tests.")
+add_dependencies(check tests)
+
+include(CTest)
+
+file(GLOB_RECURSE immer_unit_tests "*.cpp")
+foreach(_file IN LISTS immer_unit_tests)
+  immer_target_name_for(_target _output "${_file}")
+  add_executable(${_target} EXCLUDE_FROM_ALL "${_file}")
+  set_target_properties(${_target} PROPERTIES OUTPUT_NAME ${_output})
+  add_dependencies(tests ${_target})
+  target_compile_definitions(${_target} PUBLIC
+    DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+    CATCH_CONFIG_MAIN)
+  target_link_libraries(${_target} PUBLIC immer-dev)
+  add_test("test/${_output}" ${_output})
+endforeach()
diff --git a/third_party/immer/test/array/default.cpp b/third_party/immer/test/array/default.cpp
new file mode 100644
index 0000000000..928fffaaac
--- /dev/null
+++ b/third_party/immer/test/array/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/array.hpp>
+
+#define VECTOR_T ::immer::array
+#include "../vector/generic.ipp"
diff --git a/third_party/immer/test/array/gc.cpp b/third_party/immer/test/array/gc.cpp
new file mode 100644
index 0000000000..a994208bc7
--- /dev/null
+++ b/third_party/immer/test/array/gc.cpp
@@ -0,0 +1,22 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/array.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_array_t = immer::array<T, gc_memory>;
+
+#define VECTOR_T test_array_t
+#include "../vector/generic.ipp"
diff --git a/third_party/immer/test/array_transient/default.cpp b/third_party/immer/test/array_transient/default.cpp
new file mode 100644
index 0000000000..aa603c7b0a
--- /dev/null
+++ b/third_party/immer/test/array_transient/default.cpp
@@ -0,0 +1,41 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/array.hpp>
+#include <immer/array_transient.hpp>
+
+#define VECTOR_T ::immer::array
+#define VECTOR_TRANSIENT_T ::immer::array_transient
+
+#include "../vector_transient/generic.ipp"
+
+TEST_CASE("array_transient default constructor compiles")
+{
+    immer::array_transient<int> transient;
+}
+
+TEST_CASE("array provides mutable data")
+{
+    auto arr = immer::array<int>(10, 0);
+    CHECK(arr.size() == 10);
+    auto tr = arr.transient();
+    CHECK(tr.data() == arr.data());
+
+    auto d = tr.data_mut();
+    CHECK(tr.data_mut() != arr.data());
+    CHECK(tr.data() == tr.data_mut());
+    CHECK(arr.data() != tr.data_mut());
+
+    arr = tr.persistent();
+    CHECK(arr.data() == d);
+    CHECK(arr.data() == tr.data());
+
+    CHECK(tr.data_mut() != arr.data());
+    CHECK(tr.data() == tr.data_mut());
+    CHECK(arr.data() != tr.data_mut());
+}
diff --git a/third_party/immer/test/array_transient/gc.cpp b/third_party/immer/test/array_transient/gc.cpp
new file mode 100644
index 0000000000..4b40185e94
--- /dev/null
+++ b/third_party/immer/test/array_transient/gc.cpp
@@ -0,0 +1,50 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/array.hpp>
+#include <immer/array_transient.hpp>
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_array_t = immer::array<T, gc_memory>;
+
+template <typename T>
+using test_array_transient_t = immer::array_transient<T, gc_memory>;
+
+#define VECTOR_T test_array_t
+#define VECTOR_TRANSIENT_T test_array_transient_t
+
+#include "../vector_transient/generic.ipp"
+
+TEST_CASE("array provides mutable data")
+{
+    auto arr = immer::array<int, gc_memory>(10, 0);
+    CHECK(arr.size() == 10);
+    auto tr = arr.transient();
+    CHECK(tr.data() == arr.data());
+
+    auto d = tr.data_mut();
+    CHECK(tr.data_mut() != arr.data());
+    CHECK(tr.data() == tr.data_mut());
+    CHECK(arr.data() != tr.data_mut());
+
+    arr = tr.persistent();
+    CHECK(arr.data() == d);
+    CHECK(arr.data() == tr.data());
+
+    CHECK(tr.data_mut() != arr.data());
+    CHECK(tr.data() == tr.data_mut());
+    CHECK(arr.data() != tr.data_mut());
+}
diff --git a/third_party/immer/test/atom/default.cpp b/third_party/immer/test/atom/default.cpp
new file mode 100644
index 0000000000..7a382c70a8
--- /dev/null
+++ b/third_party/immer/test/atom/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/atom.hpp>
+
+#define ATOM_T ::immer::atom
+#include "generic.ipp"
diff --git a/third_party/immer/test/atom/gc.cpp b/third_party/immer/test/atom/gc.cpp
new file mode 100644
index 0000000000..f3219e81c6
--- /dev/null
+++ b/third_party/immer/test/atom/gc.cpp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/atom.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy>;
+
+template <typename T>
+using test_atom_t = immer::atom<T, gc_memory>;
+
+#define ATOM_T test_atom_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/atom/generic.ipp b/third_party/immer/test/atom/generic.ipp
new file mode 100644
index 0000000000..21ff74d7ef
--- /dev/null
+++ b/third_party/immer/test/atom/generic.ipp
@@ -0,0 +1,53 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#ifndef ATOM_T
+#error "define the box template to use in ATOM_T"
+#endif
+
+#include <catch.hpp>
+
+template <typename T>
+using BOX_T = typename ATOM_T<T>::box_type;
+
+TEST_CASE("construction") { ATOM_T<int> x; }
+
+TEST_CASE("store, load, exchange")
+{
+    ATOM_T<int> x;
+    CHECK(x.load() == BOX_T<int>{0});
+    x.store(42);
+    CHECK(x.load() == BOX_T<int>{42});
+    auto old = x.exchange(12);
+    CHECK(old == 42);
+    CHECK(x.load() == BOX_T<int>{12});
+}
+
+TEST_CASE("box conversion")
+{
+    ATOM_T<int> x;
+    auto v1 = BOX_T<int>{42};
+    x       = v1;
+    auto v2 = BOX_T<int>{x};
+    CHECK(v1 == v2);
+}
+
+TEST_CASE("value conversion")
+{
+    ATOM_T<int> x;
+    x      = 42;
+    auto v = int{x};
+    CHECK(v == 42);
+}
+
+TEST_CASE("update")
+{
+    ATOM_T<int> x{42};
+    x.update([](auto x) { return x + 2; });
+    CHECK(x.load() == 44);
+}
diff --git a/third_party/immer/test/box/default.cpp b/third_party/immer/test/box/default.cpp
new file mode 100644
index 0000000000..fbd1b178df
--- /dev/null
+++ b/third_party/immer/test/box/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/box.hpp>
+
+#define BOX_T ::immer::box
+#include "generic.ipp"
diff --git a/third_party/immer/test/box/gc.cpp b/third_party/immer/test/box/gc.cpp
new file mode 100644
index 0000000000..04e459559a
--- /dev/null
+++ b/third_party/immer/test/box/gc.cpp
@@ -0,0 +1,20 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/box.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy>;
+
+template <typename T>
+using test_box_t = immer::box<T, gc_memory>;
+
+#define BOX_T test_box_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/box/generic.ipp b/third_party/immer/test/box/generic.ipp
new file mode 100644
index 0000000000..cfb5f04ea1
--- /dev/null
+++ b/third_party/immer/test/box/generic.ipp
@@ -0,0 +1,58 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#ifndef BOX_T
+#error "define the box template to use in BOX_T"
+#endif
+
+#include <catch.hpp>
+
+TEST_CASE("construction and copy")
+{
+    auto x = BOX_T<int>{};
+    CHECK(x == 0);
+
+    auto y = x;
+    CHECK(&x.get() == &y.get());
+
+    auto z = std::move(x);
+    CHECK(&z.get() == &y.get());
+}
+
+TEST_CASE("equality")
+{
+    auto x = BOX_T<int>{};
+    auto y = x;
+    CHECK(x == 0.0f);
+    CHECK(x == y);
+    CHECK(x == BOX_T<int>{});
+    CHECK(x != BOX_T<int>{42});
+}
+
+TEST_CASE("update")
+{
+    auto x = BOX_T<int>{};
+    auto y = x.update([](auto v) { return v + 1; });
+    CHECK(x == 0);
+    CHECK(y == 1);
+}
+
+TEST_CASE("update move")
+{
+    auto x    = BOX_T<int>{};
+    auto addr = &x.get();
+    auto y    = std::move(x).update(
+        [](auto&& v) { return std::forward<decltype(v)>(v) + 1; });
+
+    CHECK(y == 1);
+    if (std::is_empty<typename BOX_T<int>::memory_policy::refcount>::value) {
+        CHECK(&y.get() != addr);
+    } else {
+        CHECK(&y.get() == addr);
+    }
+}
diff --git a/third_party/immer/test/box/recursive.cpp b/third_party/immer/test/box/recursive.cpp
new file mode 100644
index 0000000000..4c8fce07de
--- /dev/null
+++ b/third_party/immer/test/box/recursive.cpp
@@ -0,0 +1,108 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/algorithm.hpp>
+#include <immer/box.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/map.hpp>
+#include <immer/set.hpp>
+#include <immer/vector.hpp>
+
+#include <catch.hpp>
+
+struct rec_vec
+{
+    int data;
+    immer::vector<immer::box<rec_vec>> children;
+};
+
+TEST_CASE("recursive vector")
+{
+    auto v1 = rec_vec{42,
+                      {rec_vec{12, {}},
+                       rec_vec{13, {rec_vec{5, {}}, rec_vec{7, {}}}},
+                       rec_vec{15, {}}}};
+    CHECK(v1.data == 42);
+    CHECK(v1.children[0]->data == 12);
+    CHECK(v1.children[1]->children[0]->data == 5);
+}
+
+struct rec_fvec
+{
+    int data;
+    immer::flex_vector<immer::box<rec_fvec>> children;
+};
+
+TEST_CASE("recursive flex_vector")
+{
+    auto v1 = rec_fvec{42,
+                       {rec_fvec{12, {}},
+                        rec_fvec{13, {rec_fvec{5, {}}, rec_fvec{7, {}}}},
+                        rec_fvec{15, {}}}};
+    CHECK(v1.data == 42);
+    CHECK(v1.children[0]->data == 12);
+    CHECK(v1.children[1]->children[0]->data == 5);
+}
+
+struct rec_map
+{
+    int data;
+    immer::map<std::string, immer::box<rec_map>> children;
+};
+
+TEST_CASE("recursive map")
+{
+    auto v1 = rec_map{42, {}};
+    auto v2 = rec_map{43, v1.children.set("hello", rec_map{12, {}})};
+    auto v3 = rec_map{44, v2.children.set("world", rec_map{13, {}})};
+
+    CHECK(v3.data == 44);
+    CHECK(v3.children["hello"]->data == 12);
+    CHECK(v3.children["world"]->data == 13);
+}
+
+struct rec_set
+{
+    int data;
+    immer::set<immer::box<rec_set>> children;
+
+    bool operator==(const rec_set& other) const
+    {
+        return data == other.data && children == other.children;
+    }
+    bool operator!=(const rec_set& other) const { return !(*this == other); }
+};
+
+namespace std {
+
+template <>
+struct hash<rec_set>
+{
+    auto operator()(const rec_set& s)
+    {
+        return std::hash<decltype(s.data)>{}(s.data) ^
+               immer::accumulate(
+                   s.children, std::size_t{}, [](auto ac, auto v) {
+                       return std::hash<decltype(v)>{}(v);
+                   });
+    }
+};
+
+} // namespace std
+
+TEST_CASE("recursive set")
+{
+    auto v1 = rec_set{42, {}};
+    auto v2 = rec_set{43, v1.children.insert(rec_set{12, {}})};
+    auto v3 = rec_set{44, v2.children.insert(rec_set{13, {}})};
+
+    CHECK(v3.data == 44);
+    CHECK(v3.children.count(rec_set{12, {}}) == 1);
+    CHECK(v3.children.count(rec_set{13, {}}) == 1);
+    CHECK(v3.children.count(rec_set{14, {}}) == 0);
+}
diff --git a/third_party/immer/test/box/vector-of-boxes-transient.cpp b/third_party/immer/test/box/vector-of-boxes-transient.cpp
new file mode 100644
index 0000000000..623c8ba37d
--- /dev/null
+++ b/third_party/immer/test/box/vector-of-boxes-transient.cpp
@@ -0,0 +1,36 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/box.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <catch.hpp>
+
+TEST_CASE("issue-33")
+{
+    using Element = immer::box<std::string>;
+    auto vect     = immer::vector<Element>{};
+
+    // this one works fine
+    for (auto i = 0; i < 100; ++i) {
+        vect = vect.push_back(Element("x"));
+    }
+
+    // this one doesn't compile
+    auto t = vect.transient();
+
+    for (auto i = 0; i < 100; ++i) {
+        t.push_back(Element("x"));
+    }
+
+    vect = t.persistent();
+
+    CHECK(*vect[0] == "x");
+    CHECK(*vect[99] == "x");
+}
diff --git a/third_party/immer/test/dada.hpp b/third_party/immer/test/dada.hpp
new file mode 100644
index 0000000000..1465bf7bf5
--- /dev/null
+++ b/third_party/immer/test/dada.hpp
@@ -0,0 +1,243 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <utility>
+
+#include <immer/detail/hamts/bits.hpp>
+#include <immer/detail/rbts/bits.hpp>
+
+namespace {
+
+template <typename Iterator>
+struct rotator
+{
+    using value_type = typename std::iterator_traits<Iterator>::value_type;
+
+    Iterator first;
+    Iterator last;
+    Iterator curr;
+
+    void init(Iterator f, Iterator l)
+    {
+        first = f;
+        last  = l;
+        curr  = f;
+    }
+
+    value_type next()
+    {
+        if (curr == last)
+            curr = first;
+        return *curr++;
+    }
+};
+
+template <typename Range>
+struct range_rotator : rotator<typename Range::iterator>
+{
+    using base_t = rotator<typename Range::iterator>;
+
+    Range range;
+
+    range_rotator(Range r)
+        : range{std::move(r)}
+    {
+        base_t::init(range.begin(), range.end());
+    }
+};
+
+template <typename Range>
+auto make_rotator(Range r) -> range_rotator<Range>
+{
+    return {r};
+}
+
+inline auto magic_rotator()
+{
+    return make_rotator(std::array<unsigned, 15>{
+        {7, 11, 2, 3, 5, 7, 11, 13, 17, 19, 23, 5, 29, 31, 37}});
+}
+
+struct dada_error
+{};
+
+struct dadaism;
+static dadaism* g_dadaism = nullptr;
+
+struct dadaism
+{
+    using rotator_t = decltype(magic_rotator());
+
+    rotator_t magic = magic_rotator();
+
+    unsigned step       = magic.next();
+    unsigned count      = 0;
+    unsigned happenings = 0;
+    unsigned last       = 0;
+    bool toggle         = false;
+
+    struct scope
+    {
+        bool moved     = false;
+        dadaism* save_ = g_dadaism;
+        scope(scope&& s)
+        {
+            save_   = s.save_;
+            s.moved = true;
+        }
+        scope(dadaism* self) { g_dadaism = self; }
+        ~scope()
+        {
+            if (!moved)
+                g_dadaism = save_;
+        }
+    };
+
+    static scope disable() { return {nullptr}; }
+
+    scope next()
+    {
+        toggle = last == happenings;
+        last   = happenings;
+        step   = toggle ? step : magic.next();
+        return {this};
+    }
+
+    void dada()
+    {
+        if (toggle && ++count % step == 0) {
+            ++happenings;
+            throw dada_error{};
+        }
+    }
+};
+
+inline void dada()
+{
+    if (g_dadaism)
+        g_dadaism->dada();
+}
+
+inline bool soft_dada()
+{
+    try {
+        dada();
+        return false;
+    } catch (dada_error) {
+        return true;
+    }
+}
+
+template <typename Heap>
+struct dadaist_heap : Heap
+{
+    template <typename... Tags>
+    static auto allocate(std::size_t s, Tags... tags)
+    {
+        dada();
+        return Heap::allocate(s, tags...);
+    }
+};
+
+template <typename MP>
+struct dadaist_memory_policy : MP
+{
+    struct heap
+    {
+        using type = dadaist_heap<typename MP::heap::type>;
+
+        template <std::size_t Size>
+        struct optimized
+        {
+            using base = typename MP::heap::template optimized<Size>::type;
+            using type = dadaist_heap<base>;
+        };
+    };
+};
+
+struct tristan_tzara
+{
+    tristan_tzara() { dada(); }
+    tristan_tzara(const tristan_tzara&) { dada(); }
+    tristan_tzara(tristan_tzara&&) { dada(); }
+    tristan_tzara& operator=(const tristan_tzara&)
+    {
+        dada();
+        return *this;
+    }
+    tristan_tzara& operator=(tristan_tzara&&)
+    {
+        dada();
+        return *this;
+    }
+};
+
+template <typename T>
+struct dadaist : tristan_tzara
+{
+    T value;
+
+    dadaist()
+        : value{}
+    {}
+    dadaist(T v)
+        : value{std::move(v)}
+    {}
+    operator T() const { return value; }
+};
+
+template <typename T>
+struct dadaist_wrapper;
+
+using rbits_t = immer::detail::rbts::bits_t;
+using hbits_t = immer::detail::rbts::bits_t;
+
+template <template <class, class, rbits_t, rbits_t> class V,
+          typename T,
+          typename MP,
+          rbits_t B,
+          rbits_t BL>
+struct dadaist_wrapper<V<T, MP, B, BL>>
+{
+    using type = V<dadaist<T>, dadaist_memory_policy<MP>, B, BL>;
+};
+
+template <template <class, class> class V, typename T, typename MP>
+struct dadaist_wrapper<V<T, MP>>
+{
+    using type = V<dadaist<T>, dadaist_memory_policy<MP>>;
+};
+
+template <template <class, class, class, class, hbits_t> class V,
+          typename T,
+          typename E,
+          typename H,
+          typename MP,
+          hbits_t B>
+struct dadaist_wrapper<V<T, H, E, MP, B>>
+{
+    using type = V<dadaist<T>, H, E, dadaist_memory_policy<MP>, B>;
+};
+
+template <template <class, class, class, class, class, hbits_t> class V,
+          typename K,
+          typename T,
+          typename E,
+          typename H,
+          typename MP,
+          hbits_t B>
+struct dadaist_wrapper<V<K, T, H, E, MP, B>>
+{
+    using type = V<K, dadaist<T>, H, E, dadaist_memory_policy<MP>, B>;
+};
+
+} // anonymous namespace
diff --git a/third_party/immer/test/detail/type_traits.cpp b/third_party/immer/test/detail/type_traits.cpp
new file mode 100644
index 0000000000..8809418cce
--- /dev/null
+++ b/third_party/immer/test/detail/type_traits.cpp
@@ -0,0 +1,215 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <catch.hpp>
+#include <forward_list>
+#include <immer/detail/type_traits.hpp>
+#include <list>
+#include <vector>
+
+struct string_sentinel
+{};
+bool operator==(const char* i, string_sentinel);
+bool operator!=(const char* i, string_sentinel);
+
+TEST_CASE("compatible_sentinel_v")
+{
+    SECTION("iterator pairs")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::compatible_sentinel_v<Iter, Iter>, "");
+    }
+
+    SECTION("pointer pairs")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::compatible_sentinel_v<Iter, Iter>, "");
+    }
+
+    SECTION("iterator/sentinel pair")
+    {
+        using Iter = char*;
+        using Sent = string_sentinel;
+        static_assert(immer::detail::compatible_sentinel_v<Iter, Sent>, "");
+    }
+
+    SECTION("incompatible pair")
+    {
+        using Iter1 = std::vector<int>::iterator;
+        using Iter2 = std::list<double>::iterator;
+        static_assert(not immer::detail::compatible_sentinel_v<Iter1, Iter2>,
+                      "");
+    }
+}
+
+TEST_CASE("equality comparable")
+{
+    SECTION("iterator pairs")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_equality_comparable_v<Iter, Iter>, "");
+    }
+
+    SECTION("pointer pairs")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_equality_comparable_v<Iter, Iter>, "");
+    }
+
+    SECTION("iterator/sentinel pair")
+    {
+        using Iter = char*;
+        using Sent = string_sentinel;
+        static_assert(immer::detail::is_equality_comparable_v<Iter, Sent>, "");
+    }
+
+    SECTION("not equality comparable")
+    {
+        static_assert(
+            not immer::detail::is_equality_comparable_v<std::string, double>,
+            "");
+    }
+}
+
+TEST_CASE("inequality comparable")
+{
+    SECTION("iterator pairs")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_inequality_comparable_v<Iter, Iter>,
+                      "");
+    }
+
+    SECTION("pointer pairs")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_inequality_comparable_v<Iter, Iter>,
+                      "");
+    }
+
+    SECTION("iterator/sentinel pair")
+    {
+        using Iter = char*;
+        using Sent = string_sentinel;
+        static_assert(immer::detail::is_inequality_comparable_v<Iter, Sent>,
+                      "");
+    }
+
+    SECTION("not inequality comparable")
+    {
+        static_assert(
+            not immer::detail::is_inequality_comparable_v<std::string, double>,
+            "");
+    }
+}
+
+TEST_CASE("is dereferenceable")
+{
+    SECTION("iterator")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_dereferenceable_v<Iter>, "");
+    }
+
+    SECTION("pointer")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_dereferenceable_v<Iter>, "");
+    }
+
+    SECTION("not dereferenceable")
+    {
+        static_assert(not immer::detail::is_dereferenceable_v<int>, "");
+    }
+}
+
+TEST_CASE("is forward iterator")
+{
+    SECTION("random access iterator")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+
+    SECTION("bidirectional iterator")
+    {
+        using Iter = std::list<int>::iterator;
+        static_assert(immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+
+    SECTION("forward iterator")
+    {
+        using Iter = std::forward_list<int>::iterator;
+        static_assert(immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+
+    SECTION("input iterator")
+    {
+        using Iter = std::istream_iterator<double>;
+        static_assert(not immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+
+    SECTION("output iterator")
+    {
+        using Iter = std::ostream_iterator<double>;
+        static_assert(not immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+
+    SECTION("pointer")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_forward_iterator_v<Iter>, "");
+    }
+}
+
+TEST_CASE("is iterator")
+{
+    SECTION("iterator")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_iterator_v<Iter>, "");
+    }
+
+    SECTION("pointer")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_iterator_v<Iter>, "");
+    }
+
+    SECTION("not iterator")
+    {
+        static_assert(not immer::detail::is_iterator_v<int>, "");
+    }
+}
+
+TEST_CASE("provides preincrement")
+{
+    SECTION("iterator")
+    {
+        using Iter = std::vector<int>::iterator;
+        static_assert(immer::detail::is_preincrementable_v<Iter>, "");
+    }
+
+    SECTION("pointer")
+    {
+        using Iter = char*;
+        static_assert(immer::detail::is_preincrementable_v<Iter>, "");
+    }
+
+    SECTION("does not provide preincrement")
+    {
+        struct type
+        {};
+        static_assert(not immer::detail::is_preincrementable_v<type>, "");
+    }
+}
+
+TEST_CASE("void_t")
+{
+    static_assert(std::is_same<void, immer::detail::void_t<int>>::value, "");
+}
diff --git a/third_party/immer/test/experimental/dvektor.cpp b/third_party/immer/test/experimental/dvektor.cpp
new file mode 100644
index 0000000000..437384a727
--- /dev/null
+++ b/third_party/immer/test/experimental/dvektor.cpp
@@ -0,0 +1,198 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/experimental/dvektor.hpp>
+
+#include <boost/range/adaptors.hpp>
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+#include <iostream>
+
+#include <doctest.h>
+
+using namespace immer;
+
+TEST_CASE("instantiation")
+{
+    auto v = dvektor<int>{};
+    CHECK(v.size() == 0u);
+}
+
+TEST_CASE("push back one element")
+{
+    SUBCASE("one element")
+    {
+        const auto v1 = dvektor<int>{};
+        auto v2       = v1.push_back(42);
+        CHECK(v1.size() == 0u);
+        CHECK(v2.size() == 1u);
+        CHECK(v2[0] == 42);
+    }
+
+    SUBCASE("many elements")
+    {
+        const auto n = 666u;
+        auto v       = dvektor<unsigned>{};
+        for (auto i = 0u; i < n; ++i) {
+            v = v.push_back(i * 10);
+            CHECK(v.size() == i + 1);
+            for (auto j = 0u; j < v.size(); ++j)
+                CHECK(i + v[j] == i + j * 10);
+        }
+    }
+}
+
+TEST_CASE("update")
+{
+    const auto n = 42u;
+    auto v       = dvektor<unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.push_back(i);
+
+    SUBCASE("assoc")
+    {
+        const auto u = v.assoc(3u, 13u);
+        CHECK(u.size() == v.size());
+        CHECK(u[2u] == 2u);
+        CHECK(u[3u] == 13u);
+        CHECK(u[4u] == 4u);
+        CHECK(u[40u] == 40u);
+        CHECK(v[3u] == 3u);
+    }
+
+    SUBCASE("assoc further")
+    {
+        for (auto i = n; i < 666; ++i)
+            v = v.push_back(i);
+
+        auto u = v.assoc(3u, 13u);
+        u      = u.assoc(200u, 7u);
+        CHECK(u.size() == v.size());
+
+        CHECK(u[2u] == 2u);
+        CHECK(u[4u] == 4u);
+        CHECK(u[40u] == 40u);
+        CHECK(u[600u] == 600u);
+
+        CHECK(u[3u] == 13u);
+        CHECK(u[200u] == 7u);
+
+        CHECK(v[3u] == 3u);
+        CHECK(v[200u] == 200u);
+    }
+
+    SUBCASE("assoc further more")
+    {
+        auto v = immer::dvektor<unsigned, 4>{};
+
+        for (auto i = n; i < 1000u; ++i)
+            v = v.push_back(i);
+
+        for (auto i = 0u; i < v.size(); ++i) {
+            v = v.assoc(i, i + 1);
+            CHECK(v[i] == i + 1);
+        }
+    }
+
+    SUBCASE("update")
+    {
+        const auto u = v.update(10u, [](auto x) { return x + 10; });
+        CHECK(u.size() == v.size());
+        CHECK(u[10u] == 20u);
+        CHECK(v[40u] == 40u);
+
+        const auto w = v.update(40u, [](auto x) { return x - 10; });
+        CHECK(w.size() == v.size());
+        CHECK(w[40u] == 30u);
+        CHECK(v[40u] == 40u);
+    }
+}
+
+#if IMMER_SLOW_TESTS
+TEST_CASE("big")
+{
+    const auto n = 1000000;
+    auto v       = dvektor<unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.push_back(i);
+
+    SUBCASE("read")
+    {
+        for (auto i = 0u; i < n; ++i)
+            CHECK(v[i] == i);
+    }
+
+    SUBCASE("assoc")
+    {
+        for (auto i = 0u; i < n; ++i) {
+            v = v.assoc(i, i + 1);
+            CHECK(v[i] == i + 1);
+        }
+    }
+}
+#endif // IMMER_SLOW_TESTS
+
+TEST_CASE("iterator")
+{
+    const auto n = 666u;
+    auto v       = dvektor<unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.push_back(i);
+
+    SUBCASE("works with range loop")
+    {
+        auto i = 0u;
+        for (const auto& x : v)
+            CHECK(x == i++);
+        CHECK(i == v.size());
+    }
+
+    SUBCASE("works with standard algorithms")
+    {
+        auto s = std::vector<unsigned>(n);
+        std::iota(s.begin(), s.end(), 0u);
+        std::equal(v.begin(), v.end(), s.begin(), s.end());
+    }
+
+    SUBCASE("can go back from end") { CHECK(n - 1 == *--v.end()); }
+
+    SUBCASE("works with reversed range adaptor")
+    {
+        auto r = v | boost::adaptors::reversed;
+        auto i = n;
+        for (const auto& x : r)
+            CHECK(x == --i);
+    }
+
+    SUBCASE("works with strided range adaptor")
+    {
+        auto r = v | boost::adaptors::strided(5);
+        auto i = 0u;
+        for (const auto& x : r)
+            CHECK(x == 5 * i++);
+    }
+
+    SUBCASE("works reversed")
+    {
+        auto i = n;
+        for (auto iter = v.rbegin(), last = v.rend(); iter != last; ++iter)
+            CHECK(*iter == --i);
+    }
+
+    SUBCASE("advance and distance")
+    {
+        auto i1 = v.begin();
+        auto i2 = i1 + 100;
+        CHECK(*i2 == 100u);
+        CHECK(i2 - i1 == 100);
+        CHECK(*(i2 - 50) == 50u);
+        CHECK((i2 - 30) - i2 == -30);
+    }
+}
diff --git a/third_party/immer/test/flex_vector/B3-BL0.cpp b/third_party/immer/test/flex_vector/B3-BL0.cpp
new file mode 100644
index 0000000000..83af21d688
--- /dev/null
+++ b/third_party/immer/test/flex_vector/B3-BL0.cpp
@@ -0,0 +1,21 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_flex_vector_t =
+    immer::flex_vector<T, immer::default_memory_policy, 3u, 0u>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 0u>;
+
+#define FLEX_VECTOR_T test_flex_vector_t
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector/B3-BL3.cpp b/third_party/immer/test/flex_vector/B3-BL3.cpp
new file mode 100644
index 0000000000..e44f7e2bfa
--- /dev/null
+++ b/third_party/immer/test/flex_vector/B3-BL3.cpp
@@ -0,0 +1,21 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_flex_vector_t =
+    immer::flex_vector<T, immer::default_memory_policy, 3u, 3u>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 3u>;
+
+#define FLEX_VECTOR_T test_flex_vector_t
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector/default.cpp b/third_party/immer/test/flex_vector/default.cpp
new file mode 100644
index 0000000000..7258cc0772
--- /dev/null
+++ b/third_party/immer/test/flex_vector/default.cpp
@@ -0,0 +1,14 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+
+#define FLEX_VECTOR_T ::immer::flex_vector
+#define VECTOR_T ::immer::vector
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector/fuzzed-0.cpp b/third_party/immer/test/flex_vector/fuzzed-0.cpp
new file mode 100644
index 0000000000..063ad133a0
--- /dev/null
+++ b/third_party/immer/test/flex_vector/fuzzed-0.cpp
@@ -0,0 +1,380 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "extra/fuzzer/fuzzer_input.hpp"
+#include <array>
+#include <catch.hpp>
+#include <immer/flex_vector.hpp>
+#include <iostream>
+
+#define IMMER_FUZZED_TRACE_ENABLE 0
+
+#if IMMER_FUZZED_TRACE_ENABLE
+#define IMMER_FUZZED_TRACE(...) std::cout << __VA_ARGS__ << std::endl;
+#else
+#define IMMER_FUZZED_TRACE(...)
+#endif
+
+namespace {
+
+template <std::size_t VarCount = 8, unsigned Bits = 3>
+int run_input(const std::uint8_t* data, std::size_t size)
+{
+    using vector_t =
+        immer::flex_vector<int, immer::default_memory_policy, Bits, Bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, VarCount>{};
+
+#if IMMER_FUZZED_TRACE_ENABLE
+    for (auto i = 0u; i < VarCount; ++i)
+        std::cout << "auto var" << i << " = vector_t{};" << std::endl;
+#endif
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < VarCount; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+        };
+        auto src = read<std::uint8_t>(in, is_valid_var);
+        auto dst = read<std::uint8_t>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back:
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src
+                                     << ".push_back(42);");
+            vars[dst] = vars[src].push_back(42);
+            break;
+        case op_update: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".update("
+                                     << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var);
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << " + var"
+                                     << +src2 << ";");
+            vars[dst] = vars[src] + vars[src2];
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
+
+} // anonymous namespace
+
+TEST_CASE("bug: concatenate too big vectors")
+{
+    // The problem here was that since we were using 32 bit sizes,
+    // concatenating big flex_vectors can overflow the sizes.  Let's
+    // just use std::size_t like normal people.
+    //
+    // Still, the problem could re-ocurr with longer inputs.  For this
+    // reason later fuzzing efforts do check that concatenation is
+    // valid for the given vector sizes.  Similar assertions are put
+    // in the code too.
+    SECTION("simplified example")
+    {
+        using vector_t =
+            immer::flex_vector<int, immer::default_memory_policy, 3, 3>;
+        auto var0 = vector_t{};
+        auto var1 = vector_t{};
+        auto var2 = vector_t{};
+        auto var4 = vector_t{};
+        var1      = var1.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var2.push_back(42);
+        var0      = var0.push_back(42);
+        var2      = var0;
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var4      = var4.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0 + var0;
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var2      = var0.push_back(42);
+        var0      = var0 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var1      = var2 + var4;
+        var4      = var4 + var4;
+        var0      = var1.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4.push_back(42);
+    }
+
+#if __GNUC__ != 9
+    // Assertion `!p->relaxed()' failed
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x1,  0x1, 0x0, 0x0, 0x0,  0x0, 0x0, 0x0, 0x0, 0x39, 0x6a, 0x76,
+            0xb9, 0x2, 0x0, 0x0, 0x0,  0x0, 0x0, 0x0, 0x2, 0x1,  0x0,  0x0,
+            0x2a, 0x0, 0x0, 0x0, 0x0,  0x0, 0x4, 0x4, 0x0, 0x0,  0x0,  0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0,  0x4, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0,
+            0x0,  0x0, 0x2, 0x0, 0x0,  0x0, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x0, 0x0,  0x0,  0x0,
+            0x0,  0x0, 0x2, 0x1, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x1,  0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0,  0x0, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x0, 0x0, 0x2a, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4,
+            0x4,  0x4, 0x4, 0x4, 0x0,  0x0, 0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x1,  0x1,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x3,  0x0,  0x0,  0x0,  0x0,  0x0,  0x4,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+            0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x4,  0x4,  0x4,
+            0x4,  0x4,  0xf8, 0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x21, 0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0xb,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x17, 0x4,  0xe2, 0xe2, 0xe2, 0x2a,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x21, 0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x17, 0x4,  0xe2, 0xe2, 0xe2, 0x2a,
+            0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x1f, 0xe2, 0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0xff, 0xe2,
+            0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x0,  0x0,  0x0,  0x15, 0x15, 0x15,
+            0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x15, 0x15, 0x15, 0x15,
+            0x0,  0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x1,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x21,
+            0x0,  0x0,  0x0,  0x0,  0xff, 0xff, 0xff, 0xff, 0xff, 0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x8,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x27, 0x0,  0x21, 0x0,  0x0,  0x0,
+            0x0,  0x0,  0x1,  0x0,  0x3a, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x40, 0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x1,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x4,  0x0,  0x0,
+            0x4,  0x0,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0xe4, 0xe4, 0x0,  0x0,
+            0x0,  0x0,  0xe4, 0x0,  0xe4, 0x0,  0x0,  0x0,  0x0,  0x0,  0x8,
+            0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+
+    // buffer overflow when looking inside the sizes array for the
+    // index of a position
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x0,  0x0,  0x0, 0x0,  0x0,  0x0, 0x0, 0x1,  0x0, 0x0, 0xff,
+            0xff, 0xff, 0xff, 0x0, 0x0,  0x0,  0x0, 0x1, 0x0,  0x0, 0x0, 0x0,
+            0x0,  0x0,  0x0,  0x0, 0x0,  0x0,  0x0, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x0, 0x0, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x0,  0x0, 0x0, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x0, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x3,  0xff, 0x0, 0x0, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x0,  0x0,  0x0, 0x0, 0x0,  0x0, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x0,  0x0, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x1e, 0x0, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x3, 0xff, 0x0,  0x0, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4, 0x4, 0x4,
+            0x4,  0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x0, 0x0, 0x0,
+            0x0,  0x0,  0x0,  0x0, 0xdb, 0x0,  0x0, 0x0, 0x0,  0x0, 0x0, 0x0,
+            0x0,  0x0,  0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x9,  0x0,  0x0, 0x0, 0x0, 0x0, 0x1,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x3, 0xfa, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x0, 0x0, 0x0, 0x9,  0x0,  0x0, 0x0, 0x0, 0x0, 0x1,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x3, 0xfa, 0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x0, 0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x4,  0x4,  0x4, 0x4, 0x4, 0x0, 0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,  0x0,  0x0, 0x0, 0x0, 0x0, 0x0,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x4,
+            0x4,  0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  0x4,  0x4, 0x4, 0x4, 0x4, 0x3,
+            0xfa, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0,  0x55, 0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+
+    // fail when deref some null node
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x0,  0x0, 0x0,  0x0,  0x0, 0x0, 0x4,  0x0,  0x0,  0x4, 0x0,
+            0x0,  0x0,  0x0, 0x0,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x0, 0x0,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x0,  0x0,  0x4, 0x0,
+            0x4,  0x4,  0x4, 0xe4, 0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x6,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0xe5, 0x0, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x0,  0x0,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0xff, 0x3, 0x4, 0x4,  0x4,  0x0,  0x0, 0x0,
+            0x0,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x0, 0x0, 0x4,  0x0,  0x4,  0x4, 0x4,
+            0xe4, 0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x6, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0xe5, 0x0, 0x4,  0x4,  0x4, 0x4, 0x4,  0x4,  0x0,  0x0, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x0, 0x0,  0x4,  0x4,  0x4, 0x4,
+            0x4,  0x4,  0x4, 0x4,  0x4,  0x4, 0x4, 0x4,  0x0,  0x0,  0x0, 0x0,
+            0x4,  0x4,  0x4, 0x4,  0xe1, 0x0, 0x0, 0x80, 0x0,  0x0,  0x1, 0x6,
+            0x0,  0x0,  0x0, 0x0,  0x0,  0x4, 0x0, 0x75, 0x75, 0x45, 0x0, 0x0,
+            0x0,  0x0,  0x0, 0x0,  0x0,  0x0, 0x0, 0x0,  0x0,  0x1,  0x0, 0x0,
+            0x0,  0x75, 0x4, 0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x1,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x85, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0xff, 0xff, 0xff, 0xff, 0xff, 0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x5,  0x4,  0x28, 0x4,  0x4,  0x4,  0x0,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x24, 0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x0,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0xf3, 0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0xf3, 0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x4,
+            0x4,  0x4,  0x4,  0x4,  0x4,  0x4,  0x3,  0x4,  0x4,  0x4,  0xff,
+            0xff, 0xff, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0xad, 0x0,
+            0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
diff --git a/third_party/immer/test/flex_vector/fuzzed-1.cpp b/third_party/immer/test/flex_vector/fuzzed-1.cpp
new file mode 100644
index 0000000000..d924dc3a83
--- /dev/null
+++ b/third_party/immer/test/flex_vector/fuzzed-1.cpp
@@ -0,0 +1,368 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "extra/fuzzer/fuzzer_input.hpp"
+#include <array>
+#include <catch.hpp>
+#include <immer/flex_vector.hpp>
+#include <iostream>
+
+#define IMMER_FUZZED_TRACE_ENABLE 0
+
+#if IMMER_FUZZED_TRACE_ENABLE
+#define IMMER_FUZZED_TRACE(...) std::cout << __VA_ARGS__ << std::endl;
+#else
+#define IMMER_FUZZED_TRACE(...)
+#endif
+
+namespace {
+
+template <std::size_t VarCount = 2, unsigned Bits = 2>
+int run_input(const std::uint8_t* data, std::size_t size)
+{
+    using vector_t =
+        immer::flex_vector<int, immer::default_memory_policy, Bits, Bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, VarCount>{};
+
+#if IMMER_FUZZED_TRACE_ENABLE
+    std::cout << "/// new test run" << std::endl;
+    for (auto i = 0u; i < VarCount; ++i)
+        std::cout << "auto var" << i << " = vector_t{};" << std::endl;
+#endif
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < VarCount; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        return v2.size() < (std::numeric_limits<size_type>::max() - v1.size());
+    };
+    auto can_insert = [](auto&& v1) {
+        using size_type = decltype(v1.size());
+        return v1.size() < std::numeric_limits<size_type>::max();
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_move,
+            op_update_move,
+        };
+        auto src = read<std::uint8_t>(in, is_valid_var);
+        auto dst = read<std::uint8_t>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back:
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src
+                                         << ".push_back(42);");
+                vars[dst] = vars[src].push_back(42);
+            }
+            break;
+        case op_update: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".update("
+                                     << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var);
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << " + var"
+                                         << +src2 << ";");
+                vars[dst] = vars[src] + vars[src2];
+            }
+            break;
+        }
+        case op_push_back_move: {
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                         << ").push_back(21);");
+                vars[dst] = std::move(vars[src]).push_back(21);
+            }
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").update(" << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
+
+} // anonymous namespace
+
+TEST_CASE("bug: memory leak because of move update")
+{
+    // There was a problem caused with shared "sizes buffer" in
+    // relaxed nodes.  In particular, the ensure_mutable_relaxed(...)
+    // function was not decremeting the old sizes buffer. That is why
+    // the last transient push_back (which uses mutable operations)
+    // causes some of the relaxed buffers that are created during the
+    // previous concatenations, and that start to be shared from the
+    // update() onwards, to later be leaked.
+    SECTION("simplified")
+    {
+        using vector_t =
+            immer::flex_vector<int, immer::default_memory_policy, 2, 2>;
+        auto var0 = vector_t{};
+        auto var1 = vector_t{};
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0 + var0;
+        var1      = var0.push_back(42);
+        var0      = var0 + var1;
+        var1      = var0.push_back(42);
+        var0      = var0 + var0;
+        var0      = var1 + var0;
+        var0      = var1.update(5, [](auto x) { return x + 1; });
+        var0      = std::move(var0).push_back(21);
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0xff, 0x0,  0xff, 0x0, 0x0,  0x0,  0x0,  0x0,  0x0,  0x0, 0x0,
+            0x40, 0x0,  0x0,  0x4, 0x0,  0x6d, 0x6d, 0x0,  0x1,  0x0, 0x4,
+            0x6d, 0x6d, 0x6d, 0x0, 0x0,  0x4,  0x1,  0x6d, 0x6d, 0x0, 0x1,
+            0x0,  0x0,  0x0,  0x4, 0x28, 0x0,  0xfc, 0x1,  0x0,  0x4, 0x0,
+            0x0,  0x0,  0xfc, 0x1, 0x0,  0x1,  0x5,  0x0,  0x0,  0x1, 0x5,
+            0x0,  0x0,  0x5,  0x0, 0x0,  0xff, 0xff, 0xff, 0x27,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
+
+TEST_CASE("non-bug: crash")
+{
+    // This is an interesting finding that is left here for
+    // documentation.  This test actually should not run... the
+    // problem is that when we build too large vectors via
+    // concatenation, we can sometimes "overflow the shift".  This is
+    // a degenerate case that we won't fix, but this helped adding
+    // appropriate assertions to the code.
+    //
+    // To prevent this in further fuzzing, the can_concat check has
+    // been made stricter.
+    return;
+
+    SECTION("simplified")
+    {
+        using vector_t =
+            immer::flex_vector<int, immer::default_memory_policy, 2, 2>;
+        auto var4 = vector_t{};
+        var4      = var4.push_back(42);
+        var4      = var4.push_back(42);
+        var4      = var4.push_back(42);
+        var4      = var4.push_back(42);
+        var4      = var4.push_back(42);
+        auto var0 = var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var0 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4 + var4;
+        var4      = var4.update(4, [](auto x) { return x + 1; });
+    }
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00,
+            0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x2a, 0x00,
+            0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0xfc, 0xf9, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0xd5, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x00, 0x01, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00,
+            0x01, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0x13, 0x13, 0x13, 0x13, 0x13, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+            0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x04, 0x04,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x02, 0x00, 0x00, 0x00,
+            0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+            0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x01,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x01, 0x04, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x04, 0x04,
+            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+            0x00,
+        };
+        CHECK(run_input<8>(input, sizeof(input)) == 0);
+    }
+#endif
+}
diff --git a/third_party/immer/test/flex_vector/fuzzed-2.cpp b/third_party/immer/test/flex_vector/fuzzed-2.cpp
new file mode 100644
index 0000000000..ba17c1a620
--- /dev/null
+++ b/third_party/immer/test/flex_vector/fuzzed-2.cpp
@@ -0,0 +1,188 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "extra/fuzzer/fuzzer_input.hpp"
+#include <array>
+#include <catch.hpp>
+#include <immer/flex_vector.hpp>
+#include <iostream>
+
+#define IMMER_FUZZED_TRACE_ENABLE 0
+
+#if IMMER_FUZZED_TRACE_ENABLE
+#define IMMER_FUZZED_TRACE(...) std::cout << __VA_ARGS__ << std::endl;
+#else
+#define IMMER_FUZZED_TRACE(...)
+#endif
+
+namespace {
+
+template <std::size_t VarCount = 2, unsigned Bits = 2>
+int run_input(const std::uint8_t* data, std::size_t size)
+{
+    using vector_t =
+        immer::flex_vector<int, immer::default_memory_policy, Bits, Bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, VarCount>{};
+
+#if IMMER_FUZZED_TRACE_ENABLE
+    std::cout << "/// new test run" << std::endl;
+    for (auto i = 0u; i < VarCount; ++i)
+        std::cout << "auto var" << i << " = vector_t{};" << std::endl;
+#endif
+
+    auto is_valid_var   = [&](auto idx) { return idx >= 0 && idx < VarCount; };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        return v2.size() < (std::numeric_limits<size_type>::max() - v1.size());
+    };
+    auto can_insert = [](auto&& v1) {
+        using size_type = decltype(v1.size());
+        return v1.size() < std::numeric_limits<size_type>::max();
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_move,
+            op_update_move,
+            op_take_move,
+            op_drop_move,
+        };
+        auto src = read<std::uint8_t>(in, is_valid_var);
+        auto dst = read<std::uint8_t>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back:
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src
+                                         << ".push_back(42);");
+                vars[dst] = vars[src].push_back(42);
+            }
+            break;
+        case op_update: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".update("
+                                     << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var);
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << " + var"
+                                         << +src2 << ";");
+                vars[dst] = vars[src] + vars[src2];
+            }
+            break;
+        }
+        case op_push_back_move: {
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                         << ").push_back(21);");
+                vars[dst] = std::move(vars[src]).push_back(21);
+            }
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").update(" << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_move: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").take(" << +idx << ");");
+            vars[dst] = std::move(vars[src]).take(idx);
+            break;
+        }
+        case op_drop_move: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").drop(" << +idx << ");");
+            vars[dst] = std::move(vars[src]).drop(idx);
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
+
+} // anonymous namespace
+
+TEST_CASE("bug: use after free on move-take")
+{
+    SECTION("")
+    {
+        using vector_t =
+            immer::flex_vector<int, immer::default_memory_policy, 2, 2>;
+        auto var0 = vector_t{};
+        auto var1 = vector_t{};
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0 + var0;
+        var0      = var0 + var0;
+        var0      = var0 + var0;
+        var0      = var0 + var0;
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var1      = var0.push_back(42);
+        var0      = std::move(var0).take(68);
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0, 0x0,  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,  0x0,  0x0, 0x0, 0x0,
+            0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x0,  0x0,  0x0, 0x4, 0x0,
+            0x0, 0x0,  0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0,  0x4,  0x0, 0x0, 0x0,
+            0x4, 0x0,  0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4,  0x0,  0x0, 0x0, 0x4,
+            0x0, 0x0,  0x0, 0x1, 0x0, 0x0, 0x0, 0x7, 0x41, 0xb5, 0x0, 0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
diff --git a/third_party/immer/test/flex_vector/fuzzed-3.cpp b/third_party/immer/test/flex_vector/fuzzed-3.cpp
new file mode 100644
index 0000000000..5ef1eaffb7
--- /dev/null
+++ b/third_party/immer/test/flex_vector/fuzzed-3.cpp
@@ -0,0 +1,222 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "extra/fuzzer/fuzzer_input.hpp"
+#include <array>
+#include <catch.hpp>
+#include <immer/flex_vector.hpp>
+#include <iostream>
+
+#define IMMER_FUZZED_TRACE_ENABLE 0
+
+#if IMMER_FUZZED_TRACE_ENABLE
+#define IMMER_FUZZED_TRACE(...) std::cout << __VA_ARGS__ << std::endl;
+#else
+#define IMMER_FUZZED_TRACE(...)
+#endif
+
+namespace {
+
+template <std::size_t VarCount = 2, unsigned Bits = 2>
+int run_input(const std::uint8_t* data, std::size_t size)
+{
+    using vector_t =
+        immer::flex_vector<int, immer::default_memory_policy, Bits, Bits>;
+    using size_t = std::uint8_t;
+
+    auto vars = std::array<vector_t, VarCount>{};
+
+#if IMMER_FUZZED_TRACE_ENABLE
+    std::cout << "/// new test run" << std::endl;
+    for (auto i = 0u; i < VarCount; ++i)
+        std::cout << "auto var" << i << " = vector_t{};" << std::endl;
+#endif
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < VarCount; };
+    auto is_valid_var_neq = [](auto other) {
+        return [=](auto idx) {
+            return idx >= 0 && idx < VarCount && idx != other;
+        };
+    };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        return v2.size() < (std::numeric_limits<size_type>::max() - v1.size());
+    };
+    auto can_insert = [](auto&& v1) {
+        using size_type = decltype(v1.size());
+        return v1.size() < std::numeric_limits<size_type>::max();
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_move,
+            op_update_move,
+            op_take_move,
+            op_drop_move,
+            op_concat_move_l,
+            op_concat_move_r,
+            op_concat_move_lr,
+        };
+        auto src = read<std::uint8_t>(in, is_valid_var);
+        auto dst = read<std::uint8_t>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_push_back:
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src
+                                         << ".push_back(42);");
+                vars[dst] = vars[src].push_back(42);
+            }
+            break;
+        case op_update: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".update("
+                                     << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] = vars[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << ".take("
+                                     << +idx << ");");
+            vars[dst] = vars[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var);
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src << " + var"
+                                         << +src2 << ";");
+                vars[dst] = vars[src] + vars[src2];
+            }
+            break;
+        }
+        case op_push_back_move: {
+            if (can_insert(vars[src])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                         << ").push_back(21);");
+                vars[dst] = std::move(vars[src]).push_back(21);
+            }
+            break;
+        }
+        case op_update_move: {
+            auto idx = read<size_t>(in, is_valid_index(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").update(" << +idx
+                                     << ", [] (auto x) { return x + 1; });");
+            vars[dst] =
+                std::move(vars[src]).update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_move: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").take(" << +idx << ");");
+            vars[dst] = std::move(vars[src]).take(idx);
+            break;
+        }
+        case op_drop_move: {
+            auto idx = read<size_t>(in, is_valid_size(vars[src]));
+            IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                     << ").drop(" << +idx << ");");
+            vars[dst] = std::move(vars[src]).drop(idx);
+            break;
+        }
+        case op_concat_move_l: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                         << ") + var" << +src2 << ";");
+                vars[dst] = std::move(vars[src]) + vars[src2];
+            }
+            break;
+        }
+        case op_concat_move_r: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = var" << +src
+                                         << " + std::move(var" << +src2
+                                         << ");");
+                vars[dst] = vars[src] + std::move(vars[src2]);
+            }
+            break;
+        }
+        case op_concat_move_lr: {
+            auto src2 = read<std::uint8_t>(in, is_valid_var_neq(src));
+            if (can_concat(vars[src], vars[src2])) {
+                IMMER_FUZZED_TRACE("var" << +dst << " = std::move(var" << +src
+                                         << ") + std::move(var" << +src2
+                                         << ");");
+                vars[dst] = std::move(vars[src]) + std::move(vars[src2]);
+            }
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
+
+} // namespace
+
+TEST_CASE("bug: concat with moving the right side")
+{
+    // This was a stupid bug on the concatenation of small vectors
+    // when moving one of the sides.
+    SECTION("simplified")
+    {
+        using vector_t =
+            immer::flex_vector<int, immer::default_memory_policy, 2, 2>;
+        auto var0 = vector_t{};
+        auto var1 = vector_t{};
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var0      = var0.push_back(42);
+        var1      = var0.push_back(42);
+        var0      = var0 + var0;
+        var0      = var0.push_back(42);
+        var0      = var0 + var1;
+        var0      = var0 + var0;
+        var0      = var0 + std::move(var1);
+    }
+
+#if __GNUC__ != 9
+    SECTION("vm")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
+            0x0,  0x0,  0x0,  0x1,  0x0,  0x40, 0x28, 0x0,  0x4,  0x3f,
+            0x20, 0x0,  0x0,  0x4,  0x3f, 0x8,  0x0,  0x0,  0x0,  0x0,
+            0x1,  0x0,  0x0,  0x0,  0x4,  0x3f, 0x0,  0x0,  0x40, 0x0,
+            0x0,  0x0,  0x0,  0x4,  0x3f, 0x1,  0x0,  0x0,  0x4,  0x3f,
+            0x0,  0x0,  0xff, 0x0,  0xa,  0x1,  0x0,  0xff, 0x0,  0x0,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
diff --git a/third_party/immer/test/flex_vector/fuzzed-4.cpp b/third_party/immer/test/flex_vector/fuzzed-4.cpp
new file mode 100644
index 0000000000..d9ecdd7fba
--- /dev/null
+++ b/third_party/immer/test/flex_vector/fuzzed-4.cpp
@@ -0,0 +1,364 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "extra/fuzzer/fuzzer_input.hpp"
+#include <array>
+#include <catch.hpp>
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <iostream>
+
+#define IMMER_FUZZED_TRACE_ENABLE 0
+
+#if IMMER_FUZZED_TRACE_ENABLE
+#define IMMER_FUZZED_TRACE(...) std::cout << __VA_ARGS__ << std::endl;
+#else
+#define IMMER_FUZZED_TRACE(...)
+#endif
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+namespace {
+
+template <std::size_t VarCount = 4, unsigned Bits = 2>
+int run_input(const std::uint8_t* data, std::size_t size)
+{
+    using vector_t    = immer::flex_vector<int, gc_memory, Bits, Bits>;
+    using transient_t = typename vector_t::transient_type;
+    using size_t      = std::uint8_t;
+
+    auto vs = std::array<vector_t, VarCount>{};
+    auto ts = std::array<transient_t, VarCount>{};
+
+#if IMMER_FUZZED_TRACE_ENABLE
+    std::cout << "/// new test run" << std::endl;
+    for (auto i = 0; i < VarCount; ++i)
+        std::cout << "auto v" << i << " = vector_t{};" << std::endl;
+    for (auto i = 0; i < VarCount; ++i)
+        std::cout << "auto t" << i << " = transient_t{};" << std::endl;
+#endif
+
+    auto is_valid_var = [&](auto idx) { return idx >= 0 && idx < VarCount; };
+    auto is_valid_var_neq = [](auto other) {
+        return [=](auto idx) {
+            return idx >= 0 && idx < VarCount && idx != other;
+        };
+    };
+    auto is_valid_index = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx < v.size(); };
+    };
+    auto is_valid_size = [](auto& v) {
+        return [&](auto idx) { return idx >= 0 && idx <= v.size(); };
+    };
+    auto can_concat = [](auto&& v1, auto&& v2) {
+        using size_type = decltype(v1.size());
+        auto max        = std::numeric_limits<size_type>::max() >> (Bits * 4);
+        return v1.size() < max && v2.size() < max;
+    };
+
+    return fuzzer_input{data, size}.run([&](auto& in) {
+        enum ops
+        {
+            op_transient,
+            op_persistent,
+            op_push_back,
+            op_update,
+            op_take,
+            op_drop,
+            op_concat,
+            op_push_back_mut,
+            op_update_mut,
+            op_take_mut,
+            op_drop_mut,
+            op_prepend_mut,
+            op_prepend_mut_move,
+            op_append_mut,
+            op_append_mut_move,
+        };
+        auto dst = read<std::uint8_t>(in, is_valid_var);
+        switch (read<char>(in)) {
+        case op_transient: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            IMMER_FUZZED_TRACE("t" << +dst << " = v" << +src
+                                   << ".transient();");
+            ts[dst] = vs[src].transient();
+            break;
+        }
+        case op_persistent: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            IMMER_FUZZED_TRACE("v" << +dst << " = t" << +src
+                                   << ".persistent();");
+            vs[dst] = ts[src].persistent();
+            break;
+        }
+        case op_push_back: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            IMMER_FUZZED_TRACE("v" << +dst << " = v" << +src
+                                   << ".push_back(42);");
+            vs[dst] = vs[src].push_back(42);
+            break;
+        }
+        case op_update: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_index(vs[src]));
+            IMMER_FUZZED_TRACE("v" << +dst << " = v" << +src << ".update("
+                                   << +idx
+                                   << ", [] (auto x) { return x + 1; });");
+            vs[dst] = vs[src].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            IMMER_FUZZED_TRACE("v" << +dst << " = v" << +src << ".take(" << +idx
+                                   << ");");
+            vs[dst] = vs[src].take(idx);
+            break;
+        }
+        case op_drop: {
+            auto src = read<std::uint8_t>(in, is_valid_var);
+            auto idx = read<size_t>(in, is_valid_size(vs[src]));
+            IMMER_FUZZED_TRACE("v" << +dst << " = v" << +src << ".take(" << +idx
+                                   << ");");
+            vs[dst] = vs[src].drop(idx);
+            break;
+        }
+        case op_concat: {
+            auto src  = read<std::uint8_t>(in, is_valid_var);
+            auto src2 = read<std::uint8_t>(in, is_valid_var);
+            if (can_concat(vs[src], vs[src2])) {
+                IMMER_FUZZED_TRACE("v" << +dst << " = v" << +src << " + v"
+                                       << +src2 << ";");
+                vs[dst] = vs[src] + vs[src2];
+            }
+            break;
+        }
+        case op_push_back_mut: {
+            IMMER_FUZZED_TRACE("t" << +dst << ".push_back(13);");
+            ts[dst].push_back(13);
+            break;
+        }
+        case op_update_mut: {
+            auto idx = read<size_t>(in, is_valid_index(ts[dst]));
+            IMMER_FUZZED_TRACE("t" << +dst << ".update(" << +idx
+                                   << ", [] (auto x) { return x + 1; });");
+            ts[dst].update(idx, [](auto x) { return x + 1; });
+            break;
+        }
+        case op_take_mut: {
+            auto idx = read<size_t>(in, is_valid_size(ts[dst]));
+            IMMER_FUZZED_TRACE("t" << +dst << ").take(" << +idx << ");");
+            ts[dst].take(idx);
+            break;
+        }
+        case op_prepend_mut: {
+            auto src = read<std::uint8_t>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                IMMER_FUZZED_TRACE("t" << +dst << ".prepend(t" << +src << ");");
+                ts[dst].prepend(ts[src]);
+            }
+            break;
+        }
+        case op_prepend_mut_move: {
+            auto src = read<std::uint8_t>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                IMMER_FUZZED_TRACE("t" << +dst << ".prepend(std::move(t" << +src
+                                       << "));"
+                                       << " t" << +src << " = {};");
+                ts[dst].prepend(std::move(ts[src]));
+                ts[src] = {};
+            }
+            break;
+        }
+        case op_append_mut: {
+            auto src = read<std::uint8_t>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                IMMER_FUZZED_TRACE("t" << +dst << ".append(t" << +src << ");");
+                ts[dst].append(ts[src]);
+            }
+            break;
+        }
+        case op_append_mut_move: {
+            auto src = read<std::uint8_t>(in, is_valid_var_neq(dst));
+            if (can_concat(ts[dst], ts[src])) {
+                IMMER_FUZZED_TRACE("t" << +dst << ".append(std::move(t" << +src
+                                       << "));"
+                                       << " t" << +src << " = {};");
+                ts[dst].append(std::move(ts[src]));
+                ts[src] = {};
+            }
+            break;
+        }
+        default:
+            break;
+        };
+        return true;
+    });
+}
+
+} // namespace
+
+TEST_CASE("bug: concatenating transients")
+{
+    // When concatenating two transients vectors the nodes from the
+    // argument become aliased in the result.  As such, we need to
+    // reset the identitiy of the argument.
+    SECTION("simplified")
+    {
+        using vector_t = immer::flex_vector<int, gc_memory, 2, 2>;
+        auto t0        = vector_t{}.transient();
+        t0.push_back(42);
+        t0.push_back(42);
+        t0.push_back(42);
+        t0.push_back(42);
+        t0.push_back(42);
+        t0.push_back(42);
+        auto t1 = t0;
+        t1.append(t0);
+        t1.append(t0);
+        t0.append(t1);
+        t1.append(t0);
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x2, 0x2, 0x2, 0x2, 0x29, 0x32, 0x0, 0x0,  0x2,  0x2,  0x2,
+            0x2, 0x2, 0x2, 0x2, 0x2,  0x2,  0x2, 0x2,  0x2,  0x6,  0x2,
+            0x2, 0x2, 0x2, 0x2, 0x6,  0x2,  0x2, 0x2,  0x0,  0x38, 0x2,
+            0x0, 0x0, 0x2, 0x2, 0xd,  0x0,  0x0, 0x3b, 0xff, 0x3a, 0x2,
+            0xd, 0xd, 0x2, 0x0, 0x0,  0x10, 0xe, 0x0,  0xd,  0x0,  0x0,
+            0x2, 0x2, 0xd, 0x0, 0x1,  0x5,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
+
+TEST_CASE("bug: concatenating moved transients")
+{
+    // A moved from concatenated transient is totally smashed, we can
+    // not do anything with it but reasign...
+    SECTION("simplified")
+    {
+        using vector_t    = immer::flex_vector<int, gc_memory, 2, 2>;
+        using transient_t = typename vector_t::transient_type;
+        auto v0           = vector_t{};
+        auto t0           = transient_t{};
+        auto t2           = transient_t{};
+        v0                = v0.push_back(42);
+        t2                = v0.transient();
+        v0                = v0 + v0;
+        v0                = v0 + v0;
+        v0                = v0 + v0;
+        v0                = v0 + v0;
+        t0                = v0.transient();
+        t0                = v0.transient();
+        t0.append(std::move(t2));
+        t2 = {};
+        t2.append(std::move(t0));
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0x0,  0x2, 0x0,  0x2,  0x0,  0x0,  0x0,  0x6,  0x0,  0x0,  0x0,
+            0x6,  0x0, 0x0,  0x0,  0x9d, 0x0,  0x6,  0x0,  0x0,  0x0,  0x6,
+            0x0,  0x0, 0x0,  0x9d, 0x28, 0x0,  0x0,  0x0,  0x0,  0x0,  0xf7,
+            0xc5, 0x0, 0xa,  0xa,  0x0,  0xfa, 0xe7, 0xff, 0xe7, 0xff, 0x0,
+            0xe,  0x2, 0x9,  0x0,  0x28, 0x2,  0xe,  0x0,  0x0,  0x2,  0xd,
+            0x0,  0x0, 0x28, 0x0,  0xd,  0x2,  0x5,  0x0,  0x2,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+
+    SECTION("simplified")
+    {
+        using vector_t    = immer::flex_vector<int, gc_memory, 2, 2>;
+        using transient_t = typename vector_t::transient_type;
+        auto v0           = vector_t{};
+        auto t0           = transient_t{};
+        auto t2           = transient_t{};
+        v0                = v0.push_back(42);
+        v0                = v0.push_back(42);
+        v0                = v0 + v0;
+        t0                = v0.transient();
+        t2.prepend(std::move(t0));
+        t0 = {};
+        t0 = v0.transient();
+        t0.push_back(13);
+        t2.append(std::move(t0));
+        t0 = {};
+        t0 = v0.transient();
+        t0.push_back(13);
+        t2.prepend(std::move(t0));
+        t0 = {};
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        return;
+        constexpr std::uint8_t input[] = {
+            0x0,  0x2, 0x0,  0x0,  0x2, 0xb7, 0x1, 0x36, 0x40, 0x0,  0x0,
+            0x0,  0x0, 0xb6, 0x0,  0x2, 0x0,  0x0, 0x6,  0xe,  0x0,  0x0,
+            0xfe, 0x0, 0x0,  0xff, 0x0, 0x2,  0xc, 0xff, 0xfc, 0x29, 0x0,
+            0x0,  0x0, 0x0,  0x0,  0x7, 0x2,  0xe, 0xff, 0xfc, 0x29, 0x0,
+            0x0,  0x0, 0x0,  0x0,  0x7, 0x3,  0x0, 0x0,  0x2,  0xc,  0x2,
+            0xc,  0x0, 0xd,  0x0,  0x0, 0x0,  0x0, 0x25, 0x6,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
+
+TEST_CASE("bug: aegsdas")
+{
+    SECTION("simplified")
+    {
+        using vector_t    = immer::flex_vector<int, gc_memory, 2, 2>;
+        using transient_t = typename vector_t::transient_type;
+        auto v2           = vector_t{};
+        auto t0           = transient_t{};
+        auto t1           = transient_t{};
+        v2                = v2.push_back(42);
+        v2                = v2.push_back(42);
+        v2                = v2.push_back(42);
+        v2                = v2.push_back(42);
+        v2                = v2.push_back(42);
+        t0                = v2.transient();
+        t1.prepend(t0);
+        t1.prepend(t0);
+        t1.prepend(t0);
+        t0.prepend(std::move(t1));
+        t1 = {};
+    }
+
+#if __GNUC__ != 9
+    SECTION("")
+    {
+        constexpr std::uint8_t input[] = {
+            0xff, 0xff, 0x2, 0x2,  0x2, 0x2,  0x2,  0x2,  0x2,  0x2,  0x2,
+            0x82, 0x2,  0x2, 0x2,  0x2, 0x2,  0x2,  0x0,  0x0,  0x3d, 0x0,
+            0x0,  0x0,  0x2, 0x84, 0x0, 0x3b, 0x1,  0xb,  0x0,  0xa,  0x1,
+            0xb,  0x0,  0x0, 0x3,  0x2, 0x0,  0x3b, 0x1,  0xb,  0x1,  0x0,
+            0x0,  0xc,  0xb, 0x1,  0x8, 0xff, 0xff, 0xfc, 0xfd, 0x0,  0x3b,
+            0x3,  0x2,  0x0, 0x3b, 0x1, 0x9,  0x1,  0x3b,
+        };
+        CHECK(run_input(input, sizeof(input)) == 0);
+    }
+#endif
+}
diff --git a/third_party/immer/test/flex_vector/gc.cpp b/third_party/immer/test/flex_vector/gc.cpp
new file mode 100644
index 0000000000..57b97f5ed7
--- /dev/null
+++ b/third_party/immer/test/flex_vector/gc.cpp
@@ -0,0 +1,27 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_flex_vector_t = immer::flex_vector<T, gc_memory, 3u>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, gc_memory, 3u>;
+
+#define FLEX_VECTOR_T test_flex_vector_t
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector/generic.ipp b/third_party/immer/test/flex_vector/generic.ipp
new file mode 100644
index 0000000000..6dbd73f07c
--- /dev/null
+++ b/third_party/immer/test/flex_vector/generic.ipp
@@ -0,0 +1,599 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "test/dada.hpp"
+#include "test/transient_tester.hpp"
+#include "test/util.hpp"
+
+#include <immer/algorithm.hpp>
+
+#include <boost/range/adaptors.hpp>
+#include <boost/range/irange.hpp>
+#include <catch.hpp>
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+#include <vector>
+
+#ifndef FLEX_VECTOR_T
+#error "define the vector template to use in FLEX_VECTOR_T"
+#endif
+
+#ifndef VECTOR_T
+#error "define the vector template to use in VECTOR_T"
+#endif
+
+template <typename V = FLEX_VECTOR_T<unsigned>>
+auto make_test_flex_vector(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = min; i < max; ++i)
+        v = v.push_back({i});
+    return v;
+}
+
+template <typename V = FLEX_VECTOR_T<unsigned>>
+auto make_test_flex_vector_front(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = max; i > min;)
+        v = v.push_front({--i});
+    return v;
+}
+
+template <std::size_t N>
+auto make_many_test_flex_vector()
+{
+    using vektor_t = FLEX_VECTOR_T<unsigned>;
+    auto many      = std::array<vektor_t, N>{};
+    std::generate_n(many.begin(), N, [v = vektor_t{}, i = 0u]() mutable {
+        auto r = v;
+        v      = v.push_back(i++);
+        return r;
+    });
+    return many;
+}
+
+template <std::size_t N>
+auto make_many_test_flex_vector_front()
+{
+    using vektor_t = FLEX_VECTOR_T<unsigned>;
+    auto many      = std::array<vektor_t, N>{};
+    std::generate_n(many.begin(), N, [i = 0u]() mutable {
+        return make_test_flex_vector_front(0, i++);
+    });
+    return many;
+}
+
+template <std::size_t N>
+auto make_many_test_flex_vector_front_remainder()
+{
+    using vektor_t = FLEX_VECTOR_T<unsigned>;
+    auto many      = std::array<vektor_t, N>{};
+    std::generate_n(many.begin(), N, [v = vektor_t{}, i = N - 1]() mutable {
+        auto r = v;
+        v      = v.push_front(--i);
+        return r;
+    });
+    return many;
+}
+
+TEST_CASE("set relaxed")
+{
+    auto v = make_test_flex_vector_front(0, 666u);
+    for (decltype(v.size()) i = 0; i < v.size(); ++i) {
+        v = v.set(i, i + 1);
+        CHECK(v[i] == i + 1);
+    }
+}
+
+TEST_CASE("push_front")
+{
+    const auto n = 666u;
+    auto v       = FLEX_VECTOR_T<unsigned>{};
+
+    for (auto i = 0u; i < n; ++i) {
+        v = v.push_front(i);
+        CHECK(v.size() == i + 1);
+        for (decltype(v.size()) j = 0; j < v.size(); ++j)
+            CHECK(v[v.size() - j - 1] == j);
+    }
+}
+
+TEST_CASE("concat")
+{
+#if IMMER_SLOW_TESTS
+    constexpr auto n = 666u;
+#else
+    constexpr auto n = 101u;
+#endif
+
+    auto all_lhs = make_many_test_flex_vector<n>();
+    auto all_rhs = make_many_test_flex_vector_front_remainder<n>();
+
+    SECTION("regular plus regular")
+    {
+        for (auto i : test_irange(0u, n)) {
+            auto c = all_lhs[i] + all_lhs[i];
+            CHECK_VECTOR_EQUALS(
+                c, boost::join(boost::irange(0u, i), boost::irange(0u, i)));
+        }
+    }
+
+    SECTION("regular plus relaxed")
+    {
+        for (auto i : test_irange(0u, n)) {
+            auto c = all_lhs[i] + all_rhs[n - i - 1];
+            CHECK_VECTOR_EQUALS(c, boost::irange(0u, n - 1));
+        }
+    }
+}
+
+auto make_flex_vector_concat(std::size_t min, std::size_t max)
+{
+    using vektor_t = FLEX_VECTOR_T<unsigned>;
+
+    if (max == min)
+        return vektor_t{};
+    else if (max == min + 1)
+        return vektor_t{}.push_back(min);
+    else {
+        auto mid = min + (max - min) / 2;
+        return make_flex_vector_concat(min, mid) +
+               make_flex_vector_concat(mid, max);
+    }
+}
+
+TEST_CASE("concat recursive")
+{
+    const auto n = 666u;
+    auto v       = make_flex_vector_concat(0, n);
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
+}
+
+TEST_CASE("insert")
+{
+    SECTION("normal")
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        v            = v.insert(42, 100);
+        CHECK_VECTOR_EQUALS(v,
+                            boost::join(boost::irange(0u, 42u),
+                                        boost::join(boost::irange(100u, 101u),
+                                                    boost::irange(42u, n))));
+    }
+
+    SECTION("move")
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        v            = std::move(v).insert(42, 100);
+        CHECK_VECTOR_EQUALS(v,
+                            boost::join(boost::irange(0u, 42u),
+                                        boost::join(boost::irange(100u, 101u),
+                                                    boost::irange(42u, n))));
+    }
+
+    SECTION("vec")
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        v            = std::move(v).insert(42, {100, 101, 102});
+        CHECK_VECTOR_EQUALS(v,
+                            boost::join(boost::irange(0u, 42u),
+                                        boost::join(boost::irange(100u, 103u),
+                                                    boost::irange(42u, n))));
+    }
+
+    SECTION("vec move")
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        v            = std::move(v).insert(42, {100, 101, 102});
+        CHECK_VECTOR_EQUALS(v,
+                            boost::join(boost::irange(0u, 42u),
+                                        boost::join(boost::irange(100u, 103u),
+                                                    boost::irange(42u, n))));
+    }
+}
+
+TEST_CASE("erase")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector(0, n);
+    auto vv      = v.erase(0);
+    CHECK_VECTOR_EQUALS(vv, boost::irange(1u, n));
+    CHECK_VECTOR_EQUALS(v.erase(v.size() - 1), boost::irange(0u, n - 1));
+    CHECK_VECTOR_EQUALS(v.erase(v.size() - 1), boost::irange(0u, n - 1));
+    CHECK_VECTOR_EQUALS(
+        v.erase(42),
+        boost::join(boost::irange(0u, 42u), boost::irange(43u, n)));
+    CHECK_VECTOR_EQUALS(v.erase(v.size() - 1, v.size()),
+                        boost::irange(0u, n - 1));
+    CHECK_VECTOR_EQUALS(v.erase(0, 0), boost::irange(0u, n));
+    CHECK_VECTOR_EQUALS(
+        v.erase(42, 50),
+        boost::join(boost::irange(0u, 42u), boost::irange(50u, n)));
+}
+
+TEST_CASE("accumulate relaxed")
+{
+    auto expected_n = [](auto n) { return n * (n - 1) / 2; };
+    auto expected_i = [&](auto i, auto n) {
+        return expected_n(n) - expected_n(i);
+    };
+
+    SECTION("sum")
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector_front(0, n);
+
+        auto sum      = immer::accumulate(v, 0u);
+        auto expected = v.size() * (v.size() - 1) / 2;
+        CHECK(sum == expected);
+    }
+
+    SECTION("sum complex")
+    {
+        const auto n = 20u;
+
+        auto v = FLEX_VECTOR_T<unsigned>{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.push_front(i) + v;
+
+        auto sum      = immer::accumulate(v, 0u);
+        auto expected = (1 << n) - n - 1;
+        CHECK(sum == expected);
+    }
+
+    SECTION("sum range")
+    {
+        using namespace std;
+        const auto n = 666u;
+        auto v       = make_test_flex_vector_front(0, n);
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 300, 0u);
+            CHECK(sum == expected_i(100, 300));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 31, begin(v) + 300, 0u);
+            CHECK(sum == expected_i(31, 300));
+        }
+        {
+            auto sum = immer::accumulate(begin(v), begin(v) + 33, 0u);
+            CHECK(sum == expected_i(0, 33));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 660, 0u);
+            CHECK(sum == expected_i(100, 660));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 105, 0u);
+            CHECK(sum == expected_i(100, 105));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 660, begin(v) + 664, 0u);
+            CHECK(sum == expected_i(660, 664));
+        }
+    }
+}
+
+TEST_CASE("equals")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector_front(0, n);
+
+    CHECK(v == v);
+    CHECK(v == v.set(42, 42));
+    CHECK(v != v.set(42, 24));
+    CHECK(v == v.set(42, 24).set(42, 42));
+    CHECK(v.set(42, 24) == v.set(42, 24));
+    CHECK(v != v.push_back(7));
+    CHECK(v.push_back(7) == v.push_back(7));
+    CHECK(v.push_back(5) != v.push_back(7));
+    CHECK(v != v.set(v.size() - 2, 24));
+    CHECK(v == v.set(v.size() - 2, 24).set(v.size() - 2, v[v.size() - 2]));
+    CHECK(v == v.insert(42, 12).erase(42));
+    CHECK(v == v.insert(0, 12).erase(0));
+}
+
+TEST_CASE("equals bugs")
+{
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        CHECK(v == v.insert(42, 12).erase(42));
+        CHECK(v == v.insert(0, 12).erase(0));
+    }
+    {
+        const auto n = 30u;
+        auto v       = make_test_flex_vector(0, n);
+        CHECK(v == v.insert(10, 12).erase(10));
+        CHECK(v == v.insert(0, 12).erase(0));
+    }
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector(0, n);
+        for (auto i : test_irange(0u, n))
+            CHECK(v == v.insert(i, 42).erase(i));
+    }
+    {
+        const auto n = 666u;
+        auto v       = make_test_flex_vector_front(0, n);
+        for (auto i : test_irange(0u, n))
+            CHECK(v == v.insert(i, 42).erase(i));
+    }
+    {
+        const auto n = 666u;
+        auto v       = FLEX_VECTOR_T<unsigned>{};
+        using size_t = decltype(v.size());
+        for (auto i : test_irange(0u, n)) {
+            while (v.size() < i)
+                v = std::move(v).push_back(i);
+            auto vv = v;
+            for (auto j : test_irange(size_t{}, v.size())) {
+                auto vz = vv.insert(j, 42).erase(j);
+                CHECK(v == vz);
+                CHECK(vv == vz);
+                vv = vz;
+            }
+        }
+    }
+}
+
+TEST_CASE("take relaxed")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector_front(0, n);
+
+    for (auto i : test_irange(0u, n)) {
+        auto vv = v.take(i);
+        CHECK_VECTOR_EQUALS_RANGE(vv, v.begin(), v.begin() + i);
+    }
+}
+
+TEST_CASE("drop")
+{
+    const auto n = 666u;
+
+    SECTION("regular")
+    {
+        auto v = make_test_flex_vector(0, n);
+
+        for (auto i : test_irange(0u, n)) {
+            auto vv = v.drop(i);
+            CHECK_VECTOR_EQUALS_RANGE(vv, v.begin() + i, v.end());
+        }
+    }
+
+    SECTION("relaxed")
+    {
+        auto v = make_test_flex_vector_front(0, n);
+
+        for (auto i : test_irange(0u, n)) {
+            auto vv = v.drop(i);
+            CHECK_VECTOR_EQUALS_RANGE(vv, v.begin() + i, v.end());
+        }
+    }
+}
+
+#if IMMER_SLOW_TESTS
+TEST_CASE("reconcat")
+{
+    constexpr auto n = 666u;
+    auto v           = make_test_flex_vector_front(0, n);
+    auto all_lhs     = make_many_test_flex_vector_front<n + 1>();
+    auto all_rhs     = make_many_test_flex_vector_front_remainder<n + 1>();
+
+    for (auto i = 0u; i < n; ++i) {
+        auto vv = all_lhs[i] + all_rhs[n - i];
+        CHECK_VECTOR_EQUALS(vv, v);
+        CHECK_SLOW(vv == v);
+    }
+}
+
+TEST_CASE("reconcat drop")
+{
+    constexpr auto n = 666u;
+    auto v           = make_test_flex_vector_front(0, n);
+    auto all_lhs     = make_many_test_flex_vector_front<n + 1>();
+
+    for (auto i = 0u; i < n; ++i) {
+        auto vv = all_lhs[i] + v.drop(i);
+        CHECK_VECTOR_EQUALS(vv, v);
+        CHECK_SLOW(vv == v);
+    }
+}
+
+TEST_CASE("reconcat take")
+{
+    constexpr auto n = 666u;
+    auto v           = make_test_flex_vector_front(0, n);
+    auto all_rhs     = make_many_test_flex_vector_front_remainder<n + 1>();
+
+    for (auto i = 0u; i < n; ++i) {
+        auto vv = v.take(i) + all_rhs[n - i];
+        CHECK_VECTOR_EQUALS(vv, v);
+        CHECK_SLOW(vv == v);
+    }
+}
+#endif
+
+TEST_CASE("reconcat take drop")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector_front(0, n);
+
+    for (auto i : test_irange(0u, n)) {
+        auto vv = v.take(i) + v.drop(i);
+        CHECK_VECTOR_EQUALS(vv, v);
+        CHECK_SLOW(vv == v);
+    }
+}
+
+TEST_CASE("reconcat take drop feedback")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector_front(0, n);
+    auto vv      = v;
+    for (auto i : test_irange(0u, n)) {
+        vv = vv.take(i) + vv.drop(i);
+        CHECK_VECTOR_EQUALS(vv, v);
+        CHECK_SLOW(vv == v);
+    }
+}
+
+TEST_CASE("iterator relaxed")
+{
+    const auto n = 666u;
+    auto v       = make_test_flex_vector_front(0, n);
+
+    SECTION("works with range loop")
+    {
+        auto i = 0u;
+        for (const auto& x : v)
+            CHECK(x == i++);
+        CHECK(i == v.size());
+    }
+
+    SECTION("works with standard algorithms")
+    {
+        auto s = std::vector<unsigned>(n);
+        std::iota(s.begin(), s.end(), 0u);
+        std::equal(v.begin(), v.end(), s.begin(), s.end());
+    }
+
+    SECTION("can go back from end")
+    {
+        auto expected = n - 1;
+        CHECK(expected == *--v.end());
+    }
+
+    SECTION("works with reversed range adaptor")
+    {
+        auto r = v | boost::adaptors::reversed;
+        auto i = n;
+        for (const auto& x : r)
+            CHECK(x == --i);
+    }
+
+    SECTION("works with strided range adaptor")
+    {
+        auto r = v | boost::adaptors::strided(5);
+        auto i = 0u;
+        for (const auto& x : r)
+            CHECK(x == 5 * i++);
+    }
+
+    SECTION("works reversed")
+    {
+        auto i = n;
+        for (auto iter = v.rbegin(), last = v.rend(); iter != last; ++iter)
+            CHECK(*iter == --i);
+    }
+
+    SECTION("advance and distance")
+    {
+        auto i1 = v.begin();
+        auto i2 = i1 + 100;
+        CHECK(100u == *i2);
+        CHECK(100 == i2 - i1);
+        CHECK(50u == *(i2 - 50));
+        CHECK(-30 == (i2 - 30) - i2);
+    }
+}
+
+TEST_CASE("adopt regular vector contents")
+{
+    const auto n = 666u;
+    auto v       = VECTOR_T<unsigned>{};
+    for (auto i = 0u; i < n; ++i) {
+        v       = v.push_back(i);
+        auto fv = FLEX_VECTOR_T<unsigned>{v};
+        CHECK_VECTOR_EQUALS_AUX(v, fv, [](auto&& v) { return &v; });
+    }
+}
+
+TEST_CASE("exception safety relaxed")
+{
+    using dadaist_vector_t =
+        typename dadaist_wrapper<FLEX_VECTOR_T<unsigned>>::type;
+    constexpr auto n = 666u;
+
+    SECTION("push back")
+    {
+        auto half = n / 2;
+        auto v    = make_test_flex_vector_front<dadaist_vector_t>(0, half);
+        auto d    = dadaism{};
+        for (auto i = half; v.size() < static_cast<decltype(v.size())>(n);) {
+            auto s = d.next();
+            try {
+                v = v.push_back({i});
+                ++i;
+            } catch (dada_error) {}
+            CHECK_VECTOR_EQUALS(v, boost::irange(0u, i));
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("update")
+    {
+        auto v = make_test_flex_vector_front<dadaist_vector_t>(0, n);
+        auto d = dadaism{};
+        for (auto i = 0u; i < n;) {
+            auto s = d.next();
+            try {
+                v = v.update(i, [](auto x) { return dada(), x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            CHECK_VECTOR_EQUALS(
+                v, boost::join(boost::irange(1u, 1u + i), boost::irange(i, n)));
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("take")
+    {
+        auto v = make_test_flex_vector_front<dadaist_vector_t>(0, n);
+        auto d = dadaism{};
+        for (auto i = 0u; i < n;) {
+            auto s = d.next();
+            auto r = dadaist_vector_t{};
+            try {
+                r = v.take(i);
+                CHECK_VECTOR_EQUALS(r, boost::irange(0u, i++));
+            } catch (dada_error) {
+                CHECK_VECTOR_EQUALS(r, boost::irange(0u, 0u));
+            }
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("concat")
+    {
+        auto v = make_test_flex_vector<dadaist_vector_t>(0, n);
+        auto d = dadaism{};
+        for (auto i = 0u; i < n;) {
+            auto lhs = v.take(i);
+            auto rhs = v.drop(i);
+            auto s   = d.next();
+            try {
+                v = lhs + rhs;
+                ++i;
+            } catch (dada_error) {}
+            CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+}
diff --git a/third_party/immer/test/flex_vector/issue-45.cpp b/third_party/immer/test/flex_vector/issue-45.cpp
new file mode 100644
index 0000000000..76bd77b2e2
--- /dev/null
+++ b/third_party/immer/test/flex_vector/issue-45.cpp
@@ -0,0 +1,35 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// Thanks Guiguiprim for reporting this issue
+// https://github.com/arximboldi/immer/issues/46
+
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <catch.hpp>
+
+#if IMMER_CXX_STANDARD >= 17
+
+#include <variant>
+
+TEST_CASE("error when erasing an element from a "
+          "immer::flex_vector<std::variant/optional/any>")
+{
+    using Vector = immer::flex_vector<std::variant<int, double>>;
+    // using Vector = immer::flex_vector<std::optional<int>>;
+    // using Vector = immer::flex_vector<std::any>;
+
+    Vector v{1, 2, 3, 4};
+    Vector v2 = v.erase(2);
+
+    CHECK(v2.size() == 3);
+}
+
+#endif
diff --git a/third_party/immer/test/flex_vector/issue-47.cpp b/third_party/immer/test/flex_vector/issue-47.cpp
new file mode 100644
index 0000000000..7757fe4ee4
--- /dev/null
+++ b/third_party/immer/test/flex_vector/issue-47.cpp
@@ -0,0 +1,5 @@
+#include <immer/flex_vector.hpp>
+
+immer::flex_vector<int> v{1};
+
+int main() { return 0; }
diff --git a/third_party/immer/test/flex_vector/regular-B3-BL3.cpp b/third_party/immer/test/flex_vector/regular-B3-BL3.cpp
new file mode 100644
index 0000000000..fb5d13d1aa
--- /dev/null
+++ b/third_party/immer/test/flex_vector/regular-B3-BL3.cpp
@@ -0,0 +1,16 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+
+template <typename T>
+using test_vector_t =
+    immer::flex_vector<T, immer::default_memory_policy, 3u, 3u>;
+
+#define VECTOR_T test_vector_t
+#include "../vector/generic.ipp"
diff --git a/third_party/immer/test/flex_vector/regular-default.cpp b/third_party/immer/test/flex_vector/regular-default.cpp
new file mode 100644
index 0000000000..8f28c3d809
--- /dev/null
+++ b/third_party/immer/test/flex_vector/regular-default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+
+#define VECTOR_T ::immer::flex_vector
+#include "../vector/generic.ipp"
diff --git a/third_party/immer/test/flex_vector_transient/B3-BL0.cpp b/third_party/immer/test/flex_vector_transient/B3-BL0.cpp
new file mode 100644
index 0000000000..da28028c06
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/B3-BL0.cpp
@@ -0,0 +1,29 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+template <typename T>
+using test_flex_vector_t =
+    immer::flex_vector<T, immer::default_memory_policy, 3u, 0u>;
+
+template <typename T>
+using test_flex_vector_transient_t =
+    typename test_flex_vector_t<T>::transient_type;
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 0u>;
+
+#define FLEX_VECTOR_T test_flex_vector_t
+#define FLEX_VECTOR_TRANSIENT_T test_flex_vector_transient_t
+#define VECTOR_T test_vector_t
+
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector_transient/default.cpp b/third_party/immer/test/flex_vector_transient/default.cpp
new file mode 100644
index 0000000000..e9d843ccd2
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/default.cpp
@@ -0,0 +1,17 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#define FLEX_VECTOR_T ::immer::flex_vector
+#define FLEX_VECTOR_TRANSIENT_T ::immer::flex_vector_transient
+#define VECTOR_T ::immer::vector
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector_transient/gc.cpp b/third_party/immer/test/flex_vector_transient/gc.cpp
new file mode 100644
index 0000000000..5577ee43df
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/gc.cpp
@@ -0,0 +1,34 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_flex_vector_t = immer::flex_vector<T, gc_memory, 3u>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, gc_memory, 3u>;
+
+template <typename T>
+using test_flex_vector_transient_t =
+    immer::flex_vector_transient<T, gc_memory, 3u>;
+
+#define FLEX_VECTOR_T test_flex_vector_t
+#define FLEX_VECTOR_TRANSIENT_T test_flex_vector_transient_t
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/flex_vector_transient/generic.ipp b/third_party/immer/test/flex_vector_transient/generic.ipp
new file mode 100644
index 0000000000..8c93cd163e
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/generic.ipp
@@ -0,0 +1,398 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "test/dada.hpp"
+#include "test/transient_tester.hpp"
+#include "test/util.hpp"
+
+#include <immer/algorithm.hpp>
+
+#include <boost/range/adaptors.hpp>
+#include <boost/range/irange.hpp>
+#include <catch.hpp>
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+#include <vector>
+
+#ifndef FLEX_VECTOR_T
+#error "define the vector template to use in FLEX_VECTOR_T"
+#endif
+
+#ifndef FLEX_VECTOR_TRANSIENT_T
+#error "define the vector template to use in FLEX_VECTOR_TRANSIENT_T"
+#endif
+
+#ifndef VECTOR_T
+#error "define the vector template to use in VECTOR_T"
+#endif
+
+template <typename V = VECTOR_T<unsigned>>
+auto make_test_flex_vector(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = min; i < max; ++i)
+        v = v.push_back({i});
+    return v;
+}
+
+template <typename V = FLEX_VECTOR_T<unsigned>>
+auto make_test_flex_vector_front(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = max; i > min;)
+        v = v.push_front({--i});
+    return v;
+}
+
+TEST_CASE("from flex_vector and to flex_vector")
+{
+    constexpr auto n = 100u;
+
+    auto v = make_test_flex_vector(0, n).transient();
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
+
+    auto p = v.persistent();
+    CHECK_VECTOR_EQUALS(p, boost::irange(0u, n));
+}
+
+TEST_CASE("adopt regular vector contents")
+{
+    const auto n = 666u;
+    auto v       = VECTOR_T<unsigned>{};
+    for (auto i = 0u; i < n; ++i) {
+        v       = v.push_back(i);
+        auto fv = FLEX_VECTOR_TRANSIENT_T<unsigned>{v.transient()};
+        CHECK_VECTOR_EQUALS_AUX(v, fv, [](auto&& v) { return &v; });
+    }
+}
+
+TEST_CASE("drop move")
+{
+    using vector_t = FLEX_VECTOR_T<unsigned>;
+
+    auto v = vector_t{};
+
+    auto check_move = [&](vector_t&& x) -> vector_t&& {
+        if (vector_t::memory_policy::use_transient_rvalues)
+            CHECK(&x == &v);
+        else
+            CHECK(&x != &v);
+        return std::move(x);
+    };
+
+    v = v.push_back(0).push_back(1);
+
+    auto addr_before = &v[0];
+    v                = check_move(std::move(v).drop(1));
+    auto addr_after  = &v[0];
+
+    if (vector_t::bits_leaf > 0 &&
+        vector_t::memory_policy::use_transient_rvalues)
+        CHECK(addr_before == addr_after);
+    else
+        CHECK(addr_before != addr_after);
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u));
+}
+
+TEST_CASE("exception safety relaxed")
+{
+    using dadaist_vector_t =
+        typename dadaist_wrapper<FLEX_VECTOR_T<unsigned>>::type;
+    constexpr auto n = 667u;
+
+    SECTION("push back")
+    {
+        auto half = n / 2;
+        auto t    = as_transient_tester(
+            make_test_flex_vector_front<dadaist_vector_t>(0, half));
+        auto d = dadaism{};
+        for (auto li = half, i = half; i < n;) {
+            auto s = d.next();
+            try {
+                if (t.transient)
+                    t.vt.push_back({i});
+                else
+                    t.vp = t.vp.push_back({i});
+                ++i;
+            } catch (dada_error) {}
+            if (t.step())
+                li = i;
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+        IMMER_TRACE_E(t.d.happenings);
+    }
+
+    SECTION("update")
+    {
+        using boost::irange;
+        using boost::join;
+
+        auto t = as_transient_tester(
+            make_test_flex_vector_front<dadaist_vector_t>(0, n));
+        auto d = dadaism{};
+        for (auto li = 0u, i = 0u; i < n;) {
+            auto s = d.next();
+            try {
+                if (t.transient) {
+                    t.vt.update(i, [](auto x) { return dada(), x + 1; });
+                } else {
+                    t.vp = t.vp.update(i, [](auto x) { return dada(), x + 1; });
+                }
+                ++i;
+            } catch (dada_error) {}
+            if (t.step())
+                li = i;
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt,
+                                    join(irange(1u, 1u + i), irange(i, n)));
+                CHECK_VECTOR_EQUALS(t.vp,
+                                    join(irange(1u, 1u + li), irange(li, n)));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp,
+                                    join(irange(1u, 1u + i), irange(i, n)));
+                CHECK_VECTOR_EQUALS(t.vt,
+                                    join(irange(1u, 1u + li), irange(li, n)));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("take")
+    {
+        auto t = as_transient_tester(
+            make_test_flex_vector_front<dadaist_vector_t>(0, n));
+        auto d      = dadaism{};
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = n, li = i;;) {
+            auto s = d.next();
+            auto r = dadaist_vector_t{};
+            try {
+                if (t.transient)
+                    t.vt.take(i);
+                else
+                    t.vp = t.vp.take(i);
+                if (t.step())
+                    li = i;
+                delta = deltas.next();
+                if (i < delta)
+                    break;
+                i -= delta;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i + delta));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i + delta));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("drop")
+    {
+        auto t =
+            as_transient_tester(make_test_flex_vector<dadaist_vector_t>(0, n));
+        auto d      = dadaism{};
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = delta, li = 0u; i < n;) {
+            auto s = d.next();
+            auto r = dadaist_vector_t{};
+            try {
+                if (t.transient)
+                    t.vt.drop(delta);
+                else
+                    t.vp = t.vp.drop(delta);
+                if (t.step()) {
+                    li = i;
+                }
+                delta = deltas.next();
+                i += delta;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(i - delta, n));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(li, n));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(i - delta, n));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(li, n));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("append")
+    {
+        auto make_ = [](auto i, auto delta) {
+            auto d = dadaism::disable();
+            return make_test_flex_vector<dadaist_vector_t>(i, i + delta);
+        };
+        auto t      = as_transient_tester(dadaist_vector_t{});
+        auto d      = dadaism();
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = 0u, li = 0u; i < n;) {
+            try {
+                if (t.transient) {
+                    auto data  = make_(i, delta);
+                    auto datat = data.transient();
+                    t.vt.append(datat);
+                } else {
+                    auto data = make_(i, delta);
+                    t.vp      = t.vp + data;
+                }
+                i += delta;
+                if (t.step()) {
+                    li = i;
+                }
+                delta = deltas.next() * 3;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings == 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("append mut")
+    {
+        auto make_ = [](auto i, auto delta) {
+            auto d = dadaism::disable();
+            return make_test_flex_vector<dadaist_vector_t>(i, i + delta);
+        };
+        auto t      = as_transient_tester(dadaist_vector_t{});
+        auto d      = dadaism();
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = 0u, li = 0u; i < n;) {
+            try {
+                if (t.transient) {
+                    auto data  = make_(i, delta);
+                    auto datat = data.transient();
+                    t.vt.append(std::move(datat));
+                } else {
+                    auto data = make_(i, delta);
+                    t.vp      = t.vp + data;
+                }
+                i += delta;
+                if (t.step()) {
+                    li = i;
+                }
+                delta = deltas.next() * 3;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings == 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("prepend")
+    {
+        auto make_ = [](auto i, auto delta) {
+            auto d = dadaism::disable();
+            return make_test_flex_vector<dadaist_vector_t>(i, i + delta);
+        };
+        auto t      = as_transient_tester(dadaist_vector_t{});
+        auto d      = dadaism();
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = n, li = n; i > 0;) {
+            delta = std::min(i, delta);
+            try {
+                if (t.transient) {
+                    auto data  = make_(i - delta, delta);
+                    auto datat = data.transient();
+                    t.vt.prepend(datat);
+                } else {
+                    auto data = make_(i - delta, delta);
+                    t.vp      = data + t.vp;
+                }
+                i -= delta;
+                if (t.step()) {
+                    li = i;
+                }
+                delta = deltas.next() * 3;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(i, n));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(li, n));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(i, n));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(li, n));
+            }
+        }
+        CHECK(d.happenings == 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("prepend mut")
+    {
+        auto make_ = [](auto i, auto delta) {
+            auto d = dadaism::disable();
+            return make_test_flex_vector<dadaist_vector_t>(i, i + delta);
+        };
+        auto t      = as_transient_tester(dadaist_vector_t{});
+        auto d      = dadaism();
+        auto deltas = magic_rotator();
+        auto delta  = deltas.next();
+        for (auto i = n, li = n; i > 0;) {
+            delta = std::min(i, delta);
+            try {
+                if (t.transient) {
+                    auto data  = make_(i - delta, delta);
+                    auto datat = data.transient();
+                    t.vt.prepend(std::move(datat));
+                } else {
+                    auto data = make_(i - delta, delta);
+                    t.vp      = data + t.vp;
+                }
+                i -= delta;
+                if (t.step()) {
+                    li = i;
+                }
+                delta = deltas.next() * 3;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(i, n));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(li, n));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(i, n));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(li, n));
+            }
+        }
+        CHECK(d.happenings == 0);
+        CHECK(t.d.happenings > 0);
+    }
+}
diff --git a/third_party/immer/test/flex_vector_transient/regular-default.cpp b/third_party/immer/test/flex_vector_transient/regular-default.cpp
new file mode 100644
index 0000000000..d8443afb55
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/regular-default.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+
+#define VECTOR_T ::immer::flex_vector
+#define VECTOR_TRANSIENT_T ::immer::flex_vector_transient
+
+#include "../vector_transient/generic.ipp"
diff --git a/third_party/immer/test/flex_vector_transient/regular-gc.cpp b/third_party/immer/test/flex_vector_transient/regular-gc.cpp
new file mode 100644
index 0000000000..f1e0ec2dd5
--- /dev/null
+++ b/third_party/immer/test/flex_vector_transient/regular-gc.cpp
@@ -0,0 +1,31 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_flex_vector_t = immer::flex_vector<T, gc_memory, 3u>;
+
+template <typename T>
+using test_flex_vector_transient_t =
+    immer::flex_vector_transient<T, gc_memory, 3u>;
+
+#define VECTOR_T test_flex_vector_t
+#define VECTOR_TRANSIENT_T test_flex_vector_transient_t
+
+#include "../vector_transient/generic.ipp"
diff --git a/third_party/immer/test/map/B3.cpp b/third_party/immer/test/map/B3.cpp
new file mode 100644
index 0000000000..61458a0135
--- /dev/null
+++ b/third_party/immer/test/map/B3.cpp
@@ -0,0 +1,18 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/map.hpp>
+
+template <typename K,
+          typename T,
+          typename Hash = std::hash<K>,
+          typename Eq   = std::equal_to<K>>
+using test_map_t = immer::map<K, T, Hash, Eq, immer::default_memory_policy, 3u>;
+
+#define MAP_T test_map_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/map/B6.cpp b/third_party/immer/test/map/B6.cpp
new file mode 100644
index 0000000000..52da689c67
--- /dev/null
+++ b/third_party/immer/test/map/B6.cpp
@@ -0,0 +1,18 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/map.hpp>
+
+template <typename K,
+          typename T,
+          typename Hash = std::hash<K>,
+          typename Eq   = std::equal_to<K>>
+using test_map_t = immer::map<K, T, Hash, Eq, immer::default_memory_policy, 6u>;
+
+#define MAP_T test_map_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/map/default.cpp b/third_party/immer/test/map/default.cpp
new file mode 100644
index 0000000000..cb453c4f81
--- /dev/null
+++ b/third_party/immer/test/map/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/map.hpp>
+
+#define MAP_T ::immer::map
+#include "generic.ipp"
diff --git a/third_party/immer/test/map/gc.cpp b/third_party/immer/test/map/gc.cpp
new file mode 100644
index 0000000000..818fb03aaa
--- /dev/null
+++ b/third_party/immer/test/map/gc.cpp
@@ -0,0 +1,25 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/map.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename K,
+          typename T,
+          typename Hash = std::hash<K>,
+          typename Eq   = std::equal_to<K>>
+using test_map_t = immer::map<K, T, Hash, Eq, gc_memory, 3u>;
+
+#define MAP_T test_map_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/map/generic.ipp b/third_party/immer/test/map/generic.ipp
new file mode 100644
index 0000000000..18fc9d11f5
--- /dev/null
+++ b/third_party/immer/test/map/generic.ipp
@@ -0,0 +1,312 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#ifndef MAP_T
+#error "define the map template to use in MAP_T"
+#include <immer/map.hpp>
+#define MAP_T ::immer::map
+#endif
+
+#include <immer/algorithm.hpp>
+
+#include "test/dada.hpp"
+#include "test/util.hpp"
+
+#include <catch.hpp>
+
+#include <random>
+#include <unordered_set>
+
+template <typename T = unsigned>
+auto make_generator()
+{
+    auto engine = std::default_random_engine{42};
+    auto dist   = std::uniform_int_distribution<T>{};
+    return std::bind(dist, engine);
+}
+
+struct conflictor
+{
+    unsigned v1;
+    unsigned v2;
+
+    bool operator==(const conflictor& x) const
+    {
+        return v1 == x.v1 && v2 == x.v2;
+    }
+};
+
+struct hash_conflictor
+{
+    std::size_t operator()(const conflictor& x) const { return x.v1; }
+};
+
+auto make_values_with_collisions(unsigned n)
+{
+    auto gen   = make_generator();
+    auto vals  = std::vector<std::pair<conflictor, unsigned>>{};
+    auto vals_ = std::unordered_set<conflictor, hash_conflictor>{};
+    auto i     = 0u;
+    generate_n(back_inserter(vals), n, [&] {
+        auto newv = conflictor{};
+        do {
+            newv = {unsigned(gen() % (n / 2)), gen()};
+        } while (!vals_.insert(newv).second);
+        return std::pair<conflictor, unsigned>{newv, i++};
+    });
+    return vals;
+}
+
+auto make_test_map(unsigned n)
+{
+    auto s = MAP_T<unsigned, unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        s = s.insert({i, i});
+    return s;
+}
+
+auto make_test_map(const std::vector<std::pair<conflictor, unsigned>>& vals)
+{
+    auto s = MAP_T<conflictor, unsigned, hash_conflictor>{};
+    for (auto&& v : vals)
+        s = s.insert(v);
+    return s;
+}
+
+TEST_CASE("instantiation")
+{
+    SECTION("default")
+    {
+        auto v = MAP_T<int, int>{};
+        CHECK(v.size() == 0u);
+    }
+}
+
+TEST_CASE("basic insertion")
+{
+    auto v1 = MAP_T<int, int>{};
+    CHECK(v1.count(42) == 0);
+
+    auto v2 = v1.insert({42, {}});
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+
+    auto v3 = v2.insert({42, {}});
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+    CHECK(v3.count(42) == 1);
+}
+
+TEST_CASE("accessor")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(v[0] == 0);
+    CHECK(v[42] == 42);
+    CHECK(v[665] == 665);
+    CHECK(v[666] == 0);
+    CHECK(v[1234] == 0);
+}
+
+TEST_CASE("at")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(v.at(0) == 0);
+    CHECK(v.at(42) == 42);
+    CHECK(v.at(665) == 665);
+    CHECK_THROWS_AS(v.at(666), std::out_of_range&);
+    CHECK_THROWS_AS(v.at(1234), std::out_of_range&);
+}
+
+TEST_CASE("find")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(*v.find(0) == 0);
+    CHECK(*v.find(42) == 42);
+    CHECK(*v.find(665) == 665);
+    CHECK(v.find(666) == nullptr);
+    CHECK(v.find(1234) == nullptr);
+}
+
+TEST_CASE("equals and setting")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+
+    CHECK(v == v);
+    CHECK(v != v.insert({1234, 42}));
+    CHECK(v != v.erase(32));
+    CHECK(v == v.insert({1234, 42}).erase(1234));
+    CHECK(v == v.erase(32).insert({32, 32}));
+
+    CHECK(v.set(1234, 42) == v.insert({1234, 42}));
+    CHECK(v.update(1234, [](auto&& x) { return x + 1; }) == v.set(1234, 1));
+    CHECK(v.update(42, [](auto&& x) { return x + 1; }) == v.set(42, 43));
+}
+
+TEST_CASE("iterator")
+{
+    const auto N = 666u;
+    auto v       = make_test_map(N);
+
+    SECTION("empty set")
+    {
+        auto s = MAP_T<unsigned, unsigned>{};
+        CHECK(s.begin() == s.end());
+    }
+
+    SECTION("works with range loop")
+    {
+        auto seen = std::unordered_set<unsigned>{};
+        for (const auto& x : v)
+            CHECK(seen.insert(x.first).second);
+        CHECK(seen.size() == v.size());
+    }
+
+    SECTION("iterator and collisions")
+    {
+        auto vals = make_values_with_collisions(N);
+        auto s    = make_test_map(vals);
+        auto seen = std::unordered_set<conflictor, hash_conflictor>{};
+        for (const auto& x : s)
+            CHECK(seen.insert(x.first).second);
+        CHECK(seen.size() == s.size());
+    }
+}
+
+TEST_CASE("accumulate")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+
+    auto expected_n = [](auto n) { return n * (n - 1) / 2; };
+
+    SECTION("sum collection")
+    {
+        auto acc = [](unsigned acc, const std::pair<unsigned, unsigned>& x) {
+            return acc + x.first + x.second;
+        };
+        auto sum = immer::accumulate(v, 0u, acc);
+        CHECK(sum == 2 * expected_n(v.size()));
+    }
+
+    SECTION("sum collisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto s    = make_test_map(vals);
+        auto acc  = [](unsigned r, std::pair<conflictor, unsigned> x) {
+            return r + x.first.v1 + x.first.v2 + x.second;
+        };
+        auto sum1 = std::accumulate(vals.begin(), vals.end(), 0u, acc);
+        auto sum2 = immer::accumulate(s, 0u, acc);
+        CHECK(sum1 == sum2);
+    }
+}
+
+TEST_CASE("update a lot")
+{
+    auto v = make_test_map(666u);
+
+    for (decltype(v.size()) i = 0; i < v.size(); ++i) {
+        v = v.update(i, [](auto&& x) { return x + 1; });
+        CHECK(v[i] == i + 1);
+    }
+}
+
+TEST_CASE("exception safety")
+{
+    constexpr auto n = 2666u;
+
+    using dadaist_map_t =
+        typename dadaist_wrapper<MAP_T<unsigned, unsigned>>::type;
+    using dadaist_conflictor_map_t = typename dadaist_wrapper<
+        MAP_T<conflictor, unsigned, hash_conflictor>>::type;
+
+    SECTION("update collisions")
+    {
+        auto v = dadaist_map_t{};
+        auto d = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.set(i, i);
+        for (auto i = 0u; i < v.size();) {
+            try {
+                auto s = d.next();
+                v      = v.update(i, [](auto x) { return x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.at(i) == i + 1);
+            for (auto i : test_irange(i, n))
+                CHECK(v.at(i) == i);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("update collisisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto v    = dadaist_conflictor_map_t{};
+        auto d    = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(vals[i]);
+        for (auto i = 0u; i < v.size();) {
+            try {
+                auto s = d.next();
+                v      = v.update(vals[i].first, [](auto x) { return x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.at(vals[i].first) == vals[i].second + 1);
+            for (auto i : test_irange(i, n))
+                CHECK(v.at(vals[i].first) == vals[i].second);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+}
+
+namespace {
+
+class KElem
+{
+public:
+    KElem(int* elem) { this->elem = elem; }
+
+    bool operator==(const KElem& other) const
+    {
+        return this->elem == other.elem;
+    }
+
+    bool operator!=(const KElem& other) const { return !(*this == other); }
+
+    int* elem;
+};
+
+struct HashBlock
+{
+    size_t operator()(const KElem& block) const noexcept
+    {
+        return (uintptr_t) block.elem & 0xffffffff00000000;
+    }
+};
+
+using map = immer::map<KElem, KElem, HashBlock, std::equal_to<KElem>>;
+
+TEST_CASE("issue 134")
+{
+    int a[100];
+    map m;
+    for (int i = 0; i < 100; i++) {
+        m = m.set(KElem(a + i), KElem(a + i));
+    }
+}
+
+} // namespace
diff --git a/third_party/immer/test/map/issue-56.cpp b/third_party/immer/test/map/issue-56.cpp
new file mode 100644
index 0000000000..447c623dda
--- /dev/null
+++ b/third_party/immer/test/map/issue-56.cpp
@@ -0,0 +1,42 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// Thanks @dgel for reporting this issue
+// https://github.com/arximboldi/immer/issues/56
+
+#include <immer/flex_vector.hpp>
+#include <immer/map.hpp>
+#include <immer/vector.hpp>
+
+#include <catch.hpp>
+
+TEST_CASE("const map")
+{
+    const auto x = immer::map<std::string, int>{}.set("A", 1);
+    auto it      = x.begin();
+    CHECK(it->first == "A");
+    CHECK(it->second == 1);
+}
+
+TEST_CASE("const vector")
+{
+    const auto x =
+        immer::vector<std::pair<std::string, int>>{}.push_back({"A", 1});
+    auto it = x.begin();
+    CHECK(it->first == "A");
+    CHECK(it->second == 1);
+}
+
+TEST_CASE("const flex vector")
+{
+    const auto x =
+        immer::flex_vector<std::pair<std::string, int>>{}.push_back({"A", 1});
+    auto it = x.begin();
+    CHECK(it->first == "A");
+    CHECK(it->second == 1);
+}
diff --git a/third_party/immer/test/memory/heaps.cpp b/third_party/immer/test/memory/heaps.cpp
new file mode 100644
index 0000000000..94af2be208
--- /dev/null
+++ b/third_party/immer/test/memory/heaps.cpp
@@ -0,0 +1,101 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/heap/cpp_heap.hpp>
+#include <immer/heap/free_list_heap.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <immer/heap/malloc_heap.hpp>
+#include <immer/heap/thread_local_free_list_heap.hpp>
+
+#include <catch.hpp>
+#include <numeric>
+
+void do_stuff_to(void* buf, std::size_t size)
+{
+    auto ptr = static_cast<unsigned char*>(buf);
+    CHECK(buf != 0);
+    std::iota(ptr, ptr + size, 42);
+}
+
+TEST_CASE("malloc")
+{
+    using heap = immer::malloc_heap;
+
+    SECTION("basic")
+    {
+        auto p = heap::allocate(42u);
+        do_stuff_to(p, 42u);
+        heap::deallocate(42, p);
+    }
+}
+
+TEST_CASE("gc")
+{
+    using heap = immer::gc_heap;
+
+    SECTION("basic")
+    {
+        auto p = heap::allocate(42u);
+        do_stuff_to(p, 42u);
+        heap::deallocate(42, p);
+    }
+}
+
+TEST_CASE("cpp")
+{
+    using heap = immer::cpp_heap;
+
+    SECTION("basic")
+    {
+        auto p = heap::allocate(42u);
+        do_stuff_to(p, 42u);
+        heap::deallocate(42, p);
+    }
+}
+
+template <typename Heap>
+void test_free_list_heap()
+{
+    using heap = Heap;
+
+    SECTION("basic")
+    {
+        auto p = heap::allocate(42u);
+        do_stuff_to(p, 42u);
+        heap::deallocate(42, p);
+    }
+
+    SECTION("reuse")
+    {
+        auto p = heap::allocate(42u);
+        do_stuff_to(p, 42u);
+        heap::deallocate(42, p);
+
+        auto u = heap::allocate(12u);
+        do_stuff_to(u, 12u);
+        heap::deallocate(12, u);
+        CHECK(u == p);
+    }
+}
+
+TEST_CASE("free list")
+{
+    test_free_list_heap<immer::free_list_heap<42u, 2, immer::malloc_heap>>();
+}
+
+TEST_CASE("thread local free list")
+{
+    test_free_list_heap<
+        immer::thread_local_free_list_heap<42u, 2, immer::malloc_heap>>();
+}
+
+TEST_CASE("unsafe free_list")
+{
+    test_free_list_heap<
+        immer::unsafe_free_list_heap<42u, 2, immer::malloc_heap>>();
+}
diff --git a/third_party/immer/test/memory/refcounts.cpp b/third_party/immer/test/memory/refcounts.cpp
new file mode 100644
index 0000000000..f9278ca779
--- /dev/null
+++ b/third_party/immer/test/memory/refcounts.cpp
@@ -0,0 +1,62 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/refcount/refcount_policy.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#include <catch.hpp>
+
+TEST_CASE("no refcount has no data")
+{
+    static_assert(std::is_empty<immer::no_refcount_policy>{}, "");
+}
+
+template <typename RefcountPolicy>
+void test_refcount()
+{
+    using refcount = RefcountPolicy;
+
+    SECTION("starts at one")
+    {
+        refcount elem{};
+        CHECK(elem.dec());
+    }
+
+    SECTION("disowned starts at zero")
+    {
+        refcount elem{immer::disowned{}};
+        elem.inc();
+        CHECK(elem.dec());
+    }
+
+    SECTION("inc dec")
+    {
+        refcount elem{};
+        elem.inc();
+        CHECK(!elem.dec());
+        CHECK(elem.dec());
+    }
+
+    SECTION("inc dec unsafe")
+    {
+        refcount elem{};
+        elem.inc();
+        CHECK(!elem.dec());
+        elem.inc();
+        elem.dec_unsafe();
+        CHECK(elem.dec());
+    }
+}
+
+TEST_CASE("basic refcount") { test_refcount<immer::refcount_policy>(); }
+
+TEST_CASE("thread unsafe refcount")
+{
+    test_refcount<immer::unsafe_refcount_policy>();
+}
diff --git a/third_party/immer/test/set/B3.cpp b/third_party/immer/test/set/B3.cpp
new file mode 100644
index 0000000000..192b0f8e9a
--- /dev/null
+++ b/third_party/immer/test/set/B3.cpp
@@ -0,0 +1,17 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/set.hpp>
+
+template <typename T,
+          typename Hash = std::hash<T>,
+          typename Eq   = std::equal_to<T>>
+using test_set_t = immer::set<T, Hash, Eq, immer::default_memory_policy, 3u>;
+
+#define SET_T test_set_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/set/B6.cpp b/third_party/immer/test/set/B6.cpp
new file mode 100644
index 0000000000..7fe3e5b772
--- /dev/null
+++ b/third_party/immer/test/set/B6.cpp
@@ -0,0 +1,17 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/set.hpp>
+
+template <typename T,
+          typename Hash = std::hash<T>,
+          typename Eq   = std::equal_to<T>>
+using test_set_t = immer::set<T, Hash, Eq, immer::default_memory_policy, 6u>;
+
+#define SET_T test_set_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/set/default.cpp b/third_party/immer/test/set/default.cpp
new file mode 100644
index 0000000000..8e6611a4ea
--- /dev/null
+++ b/third_party/immer/test/set/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/set.hpp>
+
+#define SET_T ::immer::set
+#include "generic.ipp"
diff --git a/third_party/immer/test/set/gc.cpp b/third_party/immer/test/set/gc.cpp
new file mode 100644
index 0000000000..098c8c97d0
--- /dev/null
+++ b/third_party/immer/test/set/gc.cpp
@@ -0,0 +1,24 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/set.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T,
+          typename Hash = std::hash<T>,
+          typename Eq   = std::equal_to<T>>
+using test_set_t = immer::set<T, Hash, Eq, gc_memory, 3u>;
+
+#define SET_T test_set_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/set/generic.ipp b/third_party/immer/test/set/generic.ipp
new file mode 100644
index 0000000000..8d75373787
--- /dev/null
+++ b/third_party/immer/test/set/generic.ipp
@@ -0,0 +1,459 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#ifndef SET_T
+#error "define the set template to use in SET_T"
+#include <immer/set.hpp>
+#define SET_T ::immer::set
+#endif
+
+#include "test/dada.hpp"
+#include "test/util.hpp"
+
+#include <immer/algorithm.hpp>
+
+#include <catch.hpp>
+
+#include <random>
+#include <unordered_set>
+
+template <typename T = unsigned>
+auto make_generator()
+{
+    auto engine = std::default_random_engine{42};
+    auto dist   = std::uniform_int_distribution<T>{};
+    return std::bind(dist, engine);
+}
+
+struct conflictor
+{
+    unsigned v1;
+    unsigned v2;
+
+    bool operator==(const conflictor& x) const
+    {
+        return v1 == x.v1 && v2 == x.v2;
+    }
+};
+
+struct hash_conflictor
+{
+    std::size_t operator()(const conflictor& x) const { return x.v1; }
+};
+
+auto make_values_with_collisions(unsigned n)
+{
+    auto gen   = make_generator();
+    auto vals  = std::vector<conflictor>{};
+    auto vals_ = std::unordered_set<conflictor, hash_conflictor>{};
+    generate_n(back_inserter(vals), n, [&] {
+        auto newv = conflictor{};
+        do {
+            newv = {unsigned(gen() % (n / 2)), gen()};
+        } while (!vals_.insert(newv).second);
+        return newv;
+    });
+    return vals;
+}
+
+auto make_test_set(unsigned n)
+{
+    auto s = SET_T<unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        s = s.insert(i);
+    return s;
+}
+
+auto make_test_set(const std::vector<conflictor>& vals)
+{
+    auto s = SET_T<conflictor, hash_conflictor>{};
+    for (auto&& v : vals)
+        s = s.insert(v);
+    return s;
+}
+
+template <std::size_t BufLen>
+struct unaligned_str
+{
+    std::array<char, BufLen> m_data{};
+
+    unaligned_str() = default;
+    unaligned_str(const std::string& in)
+    {
+        for (std::size_t i = 0; i < std::min(m_data.size() - 1, in.size()); i++)
+            m_data[i] = in[i];
+    }
+    unaligned_str(const char* in)
+        : unaligned_str{std::string{in}}
+    {}
+
+    std::string str() const { return m_data.data(); }
+
+    bool operator==(unaligned_str other) const
+    {
+        return m_data == other.m_data;
+    }
+
+    bool operator!=(unaligned_str other) const
+    {
+        return m_data != other.m_data;
+    }
+};
+
+namespace std {
+template <size_t BufLen>
+struct hash<unaligned_str<BufLen>>
+{
+    size_t operator()(const unaligned_str<BufLen>& str) const
+    {
+        return std::hash<std::string>{}(str.str());
+    }
+};
+} // namespace std
+
+template <size_t BufLen>
+void check_with_len()
+{
+    auto v = SET_T<unaligned_str<BufLen>>{};
+
+    for (int i = 0; i < 1; i++)
+        v = v.insert(std::to_string(i));
+
+    CHECK(v.count("0") == 1);
+}
+
+TEST_CASE("insert type with no alignment requirement")
+{
+    check_with_len<1>();
+    check_with_len<9>();
+    check_with_len<15>();
+    check_with_len<17>();
+}
+
+TEST_CASE("instantiation")
+{
+    SECTION("default")
+    {
+        auto v = SET_T<unsigned>{};
+        CHECK(v.size() == 0u);
+    }
+}
+
+TEST_CASE("basic insertion")
+{
+    auto v1 = SET_T<unsigned>{};
+    CHECK(v1.count(42) == 0);
+
+    auto v2 = v1.insert(42);
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+
+    auto v3 = v2.insert(42);
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+    CHECK(v3.count(42) == 1);
+}
+
+TEST_CASE("insert a lot")
+{
+    constexpr auto N = 666u;
+
+    auto gen  = make_generator();
+    auto vals = std::vector<unsigned>{};
+    generate_n(back_inserter(vals), N, gen);
+    auto s = SET_T<unsigned>{};
+
+    for (auto i = 0u; i < N; ++i) {
+        s = s.insert(vals[i]);
+        CHECK(s.size() == i + 1);
+        for (auto j : test_irange(0u, i + 1))
+            CHECK(s.count(vals[j]) == 1);
+        for (auto j : test_irange(i + 1u, N))
+            CHECK(s.count(vals[j]) == 0);
+    }
+}
+
+TEST_CASE("insert conflicts")
+{
+    constexpr auto N = 666u;
+    auto vals        = make_values_with_collisions(N);
+    auto s           = SET_T<conflictor, hash_conflictor>{};
+    for (auto i = 0u; i < N; ++i) {
+        s = s.insert(vals[i]);
+        CHECK(s.size() == i + 1);
+        for (auto j : test_irange(0u, i + 1))
+            CHECK(s.count(vals[j]) == 1);
+        for (auto j : test_irange(i + 1u, N))
+            CHECK(s.count(vals[j]) == 0);
+    }
+}
+
+TEST_CASE("erase a lot")
+{
+    constexpr auto N = 666u;
+    auto gen         = make_generator();
+    auto vals        = std::vector<unsigned>{};
+    generate_n(back_inserter(vals), N, gen);
+
+    auto s = SET_T<unsigned>{};
+    for (auto i = 0u; i < N; ++i)
+        s = s.insert(vals[i]);
+
+    for (auto i = 0u; i < N; ++i) {
+        s = s.erase(vals[i]);
+        CHECK(s.size() == N - i - 1);
+        for (auto j : test_irange(0u, i + 1))
+            CHECK(s.count(vals[j]) == 0);
+        for (auto j : test_irange(i + 1u, N))
+            CHECK(s.count(vals[j]) == 1);
+    }
+}
+
+TEST_CASE("erase conflicts")
+{
+    constexpr auto N = 666u;
+    auto vals        = make_values_with_collisions(N);
+    auto s           = SET_T<conflictor, hash_conflictor>{};
+    for (auto i = 0u; i < N; ++i)
+        s = s.insert(vals[i]);
+
+    for (auto i = 0u; i < N; ++i) {
+        s = s.erase(vals[i]);
+        CHECK(s.size() == N - i - 1);
+        for (auto j : test_irange(0u, i + 1))
+            CHECK(s.count(vals[j]) == 0);
+        for (auto j : test_irange(i + 1u, N))
+            CHECK(s.count(vals[j]) == 1);
+    }
+}
+
+TEST_CASE("accumulate")
+{
+    const auto n = 666u;
+    auto v       = make_test_set(n);
+
+    auto expected_n = [](auto n) { return n * (n - 1) / 2; };
+
+    SECTION("sum collection")
+    {
+        auto sum = immer::accumulate(v, 0u);
+        CHECK(sum == expected_n(v.size()));
+    }
+
+    SECTION("sum collisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto s    = make_test_set(vals);
+        auto acc  = [](unsigned r, conflictor x) { return r + x.v1 + x.v2; };
+        auto sum1 = std::accumulate(vals.begin(), vals.end(), 0, acc);
+        auto sum2 = immer::accumulate(s, 0u, acc);
+        CHECK(sum1 == sum2);
+    }
+}
+
+TEST_CASE("find")
+{
+    const auto n = 666u;
+    auto v       = make_test_set(n);
+    CHECK(*v.find(0) == 0);
+    CHECK(*v.find(42) == 42);
+    CHECK(*v.find(665) == 665);
+    CHECK(v.find(666) == nullptr);
+    CHECK(v.find(1234) == nullptr);
+}
+
+TEST_CASE("iterator")
+{
+    const auto N = 666u;
+    auto v       = make_test_set(N);
+
+    SECTION("empty set")
+    {
+        auto s = SET_T<unsigned>{};
+        CHECK(s.begin() == s.end());
+    }
+
+    SECTION("works with range loop")
+    {
+        auto seen = std::unordered_set<unsigned>{};
+        for (const auto& x : v)
+            CHECK(seen.insert(x).second);
+        CHECK(seen.size() == v.size());
+    }
+
+    SECTION("works with standard algorithms")
+    {
+        auto s = std::vector<unsigned>(N);
+        std::iota(s.begin(), s.end(), 0u);
+        std::equal(v.begin(), v.end(), s.begin(), s.end());
+    }
+
+    SECTION("iterator and collisions")
+    {
+        auto vals = make_values_with_collisions(N);
+        auto s    = make_test_set(vals);
+        auto seen = std::unordered_set<conflictor, hash_conflictor>{};
+        for (const auto& x : s)
+            CHECK(seen.insert(x).second);
+        CHECK(seen.size() == s.size());
+    }
+}
+
+struct non_default
+{
+    unsigned value;
+    non_default() = delete;
+    operator unsigned() const { return value; }
+
+#if IMMER_DEBUG_PRINT
+    friend std::ostream& operator<<(std::ostream& os, non_default x)
+    {
+        os << "ND{" << x.value << "}";
+        return os;
+    }
+#endif
+};
+
+namespace std {
+
+template <>
+struct hash<non_default>
+{
+    std::size_t operator()(const non_default& x)
+    {
+        return std::hash<decltype(x.value)>{}(x.value);
+    }
+};
+
+} // namespace std
+
+TEST_CASE("non default")
+{
+    const auto n = 666u;
+
+    auto v = SET_T<non_default>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.insert({i});
+
+    CHECK(v.size() == n);
+}
+
+TEST_CASE("equals")
+{
+    const auto n = 666u;
+    auto v       = make_test_set(n);
+
+    CHECK(v == v);
+    CHECK(v != v.insert(1234));
+    CHECK(v == v.erase(1234));
+    CHECK(v == v.insert(1234).erase(1234));
+    CHECK(v == v.erase(64).insert(64));
+    CHECK(v != v.erase(1234).insert(1234));
+}
+
+TEST_CASE("equals collisions")
+{
+    const auto n = 666u;
+    auto vals    = make_values_with_collisions(n);
+    auto v       = make_test_set(vals);
+
+    CHECK(v == v);
+    CHECK(v != v.erase(vals[42]));
+    CHECK(v == v.erase(vals[42]).insert(vals[42]));
+    CHECK(v ==
+          v.erase(vals[42]).erase(vals[13]).insert(vals[42]).insert(vals[13]));
+    CHECK(v ==
+          v.erase(vals[42]).erase(vals[13]).insert(vals[13]).insert(vals[42]));
+}
+
+TEST_CASE("exception safety")
+{
+    constexpr auto n = 2666u;
+
+    using dadaist_set_t = typename dadaist_wrapper<SET_T<unsigned>>::type;
+    using dadaist_conflictor_set_t =
+        typename dadaist_wrapper<SET_T<conflictor, hash_conflictor>>::type;
+
+    SECTION("insert")
+    {
+        auto v = dadaist_set_t{};
+        auto d = dadaism{};
+        for (auto i = 0u; v.size() < n;) {
+            try {
+                auto s = d.next();
+                v      = v.insert({i});
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.count({i}) == 1);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("insert collisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto v    = dadaist_conflictor_set_t{};
+        auto d    = dadaism{};
+        for (auto i = 0u; v.size() < n;) {
+            try {
+                auto s = d.next();
+                v      = v.insert({vals[i]});
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.count({vals[i]}) == 1);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("erase")
+    {
+        auto v = dadaist_set_t{};
+        auto d = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert({i});
+        for (auto i = 0u; v.size() > 0;) {
+            try {
+                auto s = d.next();
+                v      = v.erase({i});
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.count({i}) == 0);
+            for (auto i : test_irange(i, n))
+                CHECK(v.count({i}) == 1);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("erase collisisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto v    = dadaist_conflictor_set_t{};
+        auto d    = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert({vals[i]});
+        for (auto i = 0u; v.size() > 0;) {
+            try {
+                auto s = d.next();
+                v      = v.erase({vals[i]});
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.count({vals[i]}) == 0);
+            for (auto i : test_irange(i, n))
+                CHECK(v.count({vals[i]}) == 1);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+}
diff --git a/third_party/immer/test/transient_tester.hpp b/third_party/immer/test/transient_tester.hpp
new file mode 100644
index 0000000000..3cf891bef9
--- /dev/null
+++ b/third_party/immer/test/transient_tester.hpp
@@ -0,0 +1,54 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include "dada.hpp"
+
+namespace {
+
+template <typename VP, typename VT>
+struct transient_tester
+{
+    VP vp;
+    VT vt;
+    dadaism d      = {};
+    bool transient = false;
+
+    transient_tester(VP vp)
+        : vp{vp}
+        , vt{vp.transient()}
+    {}
+
+    bool step()
+    {
+        auto s = d.next();
+        if (soft_dada()) {
+            auto new_transient = !transient;
+            try {
+                if (new_transient)
+                    vt = vp.transient();
+                else
+                    vp = vt.persistent();
+            } catch (const dada_error&) {
+                return false;
+            }
+            transient = new_transient;
+            return true;
+        } else
+            return false;
+    }
+};
+
+template <typename VP>
+transient_tester<VP, typename VP::transient_type> as_transient_tester(VP p)
+{
+    return {std::move(p)};
+}
+
+} // anonymous namespace
diff --git a/third_party/immer/test/util.hpp b/third_party/immer/test/util.hpp
new file mode 100644
index 0000000000..41904f5a89
--- /dev/null
+++ b/third_party/immer/test/util.hpp
@@ -0,0 +1,103 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <boost/range/irange.hpp>
+#include <boost/range/join.hpp>
+#include <cstddef>
+
+namespace {
+
+struct identity_t
+{
+    template <typename T>
+    decltype(auto) operator()(T&& x)
+    {
+        return std::forward<decltype(x)>(x);
+    }
+};
+
+template <typename Integer>
+auto test_irange(Integer from, Integer to)
+{
+#if IMMER_SLOW_TESTS
+    return boost::irange(from, to);
+#else
+    if (to - from < Integer{10})
+        return boost::join(boost::irange(Integer{}, Integer{}),
+                           boost::join(boost::irange(from, to, 1),
+                                       boost::irange(Integer{}, Integer{})));
+    else
+        return boost::join(boost::irange(from, from + Integer{2}),
+                           boost::join(boost::irange(from + Integer{2},
+                                                     to - Integer{2},
+                                                     (to - from) / Integer{5}),
+                                       boost::irange(to - Integer{2}, to)));
+#endif
+}
+
+} // anonymous namespace
+
+#if IMMER_SLOW_TESTS
+#define CHECK_SLOW(...) CHECK(__VA_ARGS__)
+#else
+#define CHECK_SLOW(...)
+#endif
+
+#if IMMER_SLOW_TESTS
+#define CHECK_VECTOR_EQUALS_RANGE_AUX(v1_, first_, last_, xf_)                 \
+    [](auto&& v1, auto&& first, auto&& last, auto&& xf) {                      \
+        auto size = std::distance(first, last);                                \
+        CHECK(static_cast<std::ptrdiff_t>(v1.size()) == size);                 \
+        if (static_cast<std::ptrdiff_t>(v1.size()) != size)                    \
+            return;                                                            \
+        for (auto j = 0u; j < size; ++j)                                       \
+            CHECK(xf(v1[j]) == xf(*first++));                                  \
+    }(v1_, first_, last_, xf_) // CHECK_EQUALS
+#else
+#define CHECK_VECTOR_EQUALS_RANGE_AUX(v1_, first_, last_, ...)                 \
+    [](auto&& v1, auto&& first, auto&& last, auto&& xf) {                      \
+        auto size = std::distance(first, last);                                \
+        CHECK(static_cast<std::ptrdiff_t>(v1.size()) == size);                 \
+        if (static_cast<std::ptrdiff_t>(v1.size()) != size)                    \
+            return;                                                            \
+        if (size > 0) {                                                        \
+            CHECK(xf(v1[0]) == xf(*(first + (0))));                            \
+            CHECK(xf(v1[size - 1]) == xf(*(first + (size - 1))));              \
+            CHECK(xf(v1[size / 2]) == xf(*(first + (size / 2))));              \
+            CHECK(xf(v1[size / 3]) == xf(*(first + (size / 3))));              \
+            CHECK(xf(v1[size / 4]) == xf(*(first + (size / 4))));              \
+            CHECK(xf(v1[size - 1 - size / 2]) ==                               \
+                  xf(*(first + (size - 1 - size / 2))));                       \
+            CHECK(xf(v1[size - 1 - size / 3]) ==                               \
+                  xf(*(first + (size - 1 - size / 3))));                       \
+            CHECK(xf(v1[size - 1 - size / 4]) ==                               \
+                  xf(*(first + (size - 1 - size / 4))));                       \
+        }                                                                      \
+        if (size > 1) {                                                        \
+            CHECK(xf(v1[1]) == xf(*(first + (1))));                            \
+            CHECK(xf(v1[size - 2]) == xf(*(first + (size - 2))));              \
+        }                                                                      \
+        if (size > 2) {                                                        \
+            CHECK(xf(v1[2]) == xf(*(first + (2))));                            \
+            CHECK(xf(v1[size - 3]) == xf(*(first + (size - 3))));              \
+        }                                                                      \
+    }(v1_, first_, last_, __VA_ARGS__) // CHECK_EQUALS
+#endif                                 // IMMER_SLOW_TESTS
+
+#define CHECK_VECTOR_EQUALS_AUX(v1_, v2_, ...)                                 \
+    [](auto&& v1, auto&& v2, auto&&... xs) {                                   \
+        CHECK_VECTOR_EQUALS_RANGE_AUX(v1, v2.begin(), v2.end(), xs...);        \
+    }(v1_, v2_, __VA_ARGS__)
+
+#define CHECK_VECTOR_EQUALS_RANGE(v1, b, e)                                    \
+    CHECK_VECTOR_EQUALS_RANGE_AUX((v1), (b), (e), identity_t{})
+
+#define CHECK_VECTOR_EQUALS(v1, v2)                                            \
+    CHECK_VECTOR_EQUALS_AUX((v1), (v2), identity_t{})
diff --git a/third_party/immer/test/vector/B3-BL0.cpp b/third_party/immer/test/vector/B3-BL0.cpp
new file mode 100644
index 0000000000..c0ddd2d665
--- /dev/null
+++ b/third_party/immer/test/vector/B3-BL0.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 0u>;
+
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/B3-BL2.cpp b/third_party/immer/test/vector/B3-BL2.cpp
new file mode 100644
index 0000000000..15cfeb9e2f
--- /dev/null
+++ b/third_party/immer/test/vector/B3-BL2.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 2u>;
+
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/B3-BL3.cpp b/third_party/immer/test/vector/B3-BL3.cpp
new file mode 100644
index 0000000000..5901940eab
--- /dev/null
+++ b/third_party/immer/test/vector/B3-BL3.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 3u>;
+
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/B3-BL4.cpp b/third_party/immer/test/vector/B3-BL4.cpp
new file mode 100644
index 0000000000..5f3b8c17ee
--- /dev/null
+++ b/third_party/immer/test/vector/B3-BL4.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 4u>;
+
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/default.cpp b/third_party/immer/test/vector/default.cpp
new file mode 100644
index 0000000000..1a1459ca12
--- /dev/null
+++ b/third_party/immer/test/vector/default.cpp
@@ -0,0 +1,12 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+
+#define VECTOR_T ::immer::vector
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/gc.cpp b/third_party/immer/test/vector/gc.cpp
new file mode 100644
index 0000000000..ec006adfb7
--- /dev/null
+++ b/third_party/immer/test/vector/gc.cpp
@@ -0,0 +1,22 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+#include <immer/vector.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, gc_memory, 3u>;
+
+#define VECTOR_T test_vector_t
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector/generic.ipp b/third_party/immer/test/vector/generic.ipp
new file mode 100644
index 0000000000..ff277edbdb
--- /dev/null
+++ b/third_party/immer/test/vector/generic.ipp
@@ -0,0 +1,488 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "test/dada.hpp"
+#include "test/util.hpp"
+
+#include <immer/algorithm.hpp>
+
+#include <boost/range/adaptors.hpp>
+#include <catch.hpp>
+
+#include <algorithm>
+#include <numeric>
+#include <string>
+#include <vector>
+
+using namespace std::string_literals;
+
+#ifndef VECTOR_T
+#error "define the vector template to use in VECTOR_T"
+#endif
+
+template <typename V = VECTOR_T<unsigned>>
+auto make_test_vector(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = min; i < max; ++i)
+        v = v.push_back({i});
+    return v;
+}
+
+struct big_object
+{
+    std::array<std::size_t, 42> v;
+};
+
+struct string_sentinel
+{};
+
+bool operator==(const char16_t* i, string_sentinel) { return *i == '\0'; }
+
+bool operator!=(const char16_t* i, string_sentinel) { return *i != '\0'; }
+
+TEST_CASE("instantiation")
+{
+    SECTION("default")
+    {
+        auto v = VECTOR_T<int>{};
+        CHECK(v.size() == 0u);
+        CHECK(v.empty());
+    }
+
+    SECTION("initializer list")
+    {
+        auto v = VECTOR_T<unsigned>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        CHECK_VECTOR_EQUALS(v, boost::irange(0u, 10u));
+        CHECK(!v.empty());
+    }
+
+    SECTION("big object")
+    {
+        auto v = VECTOR_T<big_object>{{}, {}, {}, {}};
+        CHECK(v.size() == 4);
+    }
+
+    SECTION("range")
+    {
+        auto r = std::vector<int>{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
+        auto v = VECTOR_T<unsigned>{r.begin(), r.end()};
+        CHECK_VECTOR_EQUALS(v, boost::irange(0u, 10u));
+    }
+
+    SECTION("empty range")
+    {
+        auto r = std::vector<int>{};
+        auto v = VECTOR_T<unsigned>{r.begin(), r.end()};
+        CHECK(v.size() == 0);
+    }
+
+    SECTION("iterator/sentinel")
+    {
+        auto r = u"012345678";
+        string_sentinel s;
+        auto v = VECTOR_T<unsigned>{r, s};
+        CHECK_VECTOR_EQUALS(v, boost::irange(u'0', u'9'));
+    }
+
+    SECTION("fill")
+    {
+        auto v1 = VECTOR_T<int>(4);
+        CHECK(v1.size() == 4);
+        auto v2 = VECTOR_T<int>(4, 42);
+        CHECK(v2.size() == 4);
+        CHECK(v2[2] == 42);
+    }
+}
+
+TEST_CASE("back and front")
+{
+    auto v = VECTOR_T<unsigned>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    CHECK(v.front() == 0);
+    CHECK(v.back() == 9);
+}
+
+TEST_CASE("at")
+{
+    auto v = VECTOR_T<unsigned>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    CHECK(v.at(0) == 0);
+    CHECK(v.at(5) == 5);
+    CHECK_THROWS_AS(v.at(10), const std::out_of_range&);
+    CHECK_THROWS_AS(v.at(11), const std::out_of_range&);
+}
+
+TEST_CASE("push back one element")
+{
+    SECTION("one element")
+    {
+        const auto v1 = VECTOR_T<int>{};
+        auto v2       = v1.push_back(42);
+        CHECK(v1.size() == 0u);
+        CHECK(v2.size() == 1u);
+        CHECK(v2[0] == 42);
+    }
+
+    SECTION("many elements")
+    {
+        const auto n = 666u;
+        auto v       = VECTOR_T<unsigned>{};
+        for (auto i = 0u; i < n; ++i) {
+            v = v.push_back(i * 42);
+            CHECK(v.size() == i + 1);
+            for (decltype(v.size()) j = 0; j < v.size(); ++j)
+                CHECK(v[j] == j * 42);
+        }
+    }
+}
+
+TEST_CASE("update")
+{
+    const auto n = 42u;
+    auto v       = make_test_vector(0, n);
+
+    SECTION("set")
+    {
+        const auto u = v.set(3u, 13u);
+        CHECK(u.size() == v.size());
+        CHECK(u[2u] == 2u);
+        CHECK(u[3u] == 13u);
+        CHECK(u[4u] == 4u);
+        CHECK(u[40u] == 40u);
+        CHECK(v[3u] == 3u);
+    }
+
+    SECTION("set further")
+    {
+        auto v = make_test_vector(0, 666);
+
+        auto u = v.set(3u, 13u);
+        u      = u.set(200u, 7u);
+        CHECK(u.size() == v.size());
+
+        CHECK(u[2u] == 2u);
+        CHECK(u[4u] == 4u);
+        CHECK(u[40u] == 40u);
+        CHECK(u[600u] == 600u);
+
+        CHECK(u[3u] == 13u);
+        CHECK(u[200u] == 7u);
+
+        CHECK(v[3u] == 3u);
+        CHECK(v[200u] == 200u);
+    }
+
+    SECTION("set further more")
+    {
+        auto v = make_test_vector(0, 666u);
+
+        for (decltype(v.size()) i = 0; i < v.size(); ++i) {
+            v = v.set(i, i + 1);
+            CHECK(v[i] == i + 1);
+        }
+    }
+
+    SECTION("update")
+    {
+        const auto u = v.update(10u, [](auto x) { return x + 10; });
+        CHECK(u.size() == v.size());
+        CHECK(u[10u] == 20u);
+        CHECK(v[40u] == 40u);
+
+        const auto w = v.update(40u, [](auto x) { return x - 10; });
+        CHECK(w.size() == v.size());
+        CHECK(w[40u] == 30u);
+        CHECK(v[40u] == 40u);
+    }
+}
+
+TEST_CASE("iterator")
+{
+    const auto n = 666u;
+    auto v       = make_test_vector(0, n);
+
+    SECTION("empty vector")
+    {
+        auto v = VECTOR_T<unsigned>{};
+        CHECK(v.begin() == v.end());
+    }
+
+    SECTION("works with range loop")
+    {
+        auto i = 0u;
+        for (const auto& x : v)
+            CHECK(x == i++);
+        CHECK(i == v.size());
+    }
+
+    SECTION("works with standard algorithms")
+    {
+        auto s = std::vector<unsigned>(n);
+        std::iota(s.begin(), s.end(), 0u);
+        std::equal(v.begin(), v.end(), s.begin(), s.end());
+    }
+
+    SECTION("can go back from end")
+    {
+        auto expected = n - 1;
+        auto last     = v.end();
+        CHECK(expected == *--last);
+    }
+
+    SECTION("works with reversed range adaptor")
+    {
+        auto r = v | boost::adaptors::reversed;
+        auto i = n;
+        for (const auto& x : r)
+            CHECK(x == --i);
+    }
+
+    SECTION("works with strided range adaptor")
+    {
+        auto r = v | boost::adaptors::strided(5);
+        auto i = 0u;
+        for (const auto& x : r)
+            CHECK(x == 5 * i++);
+    }
+
+    SECTION("works reversed")
+    {
+        auto i = n;
+        for (auto iter = v.rbegin(), last = v.rend(); iter != last; ++iter)
+            CHECK(*iter == --i);
+    }
+
+    SECTION("advance and distance")
+    {
+        auto i1 = v.begin();
+        auto i2 = i1 + 100;
+        CHECK(100u == *i2);
+        CHECK(100 == i2 - i1);
+        CHECK(50u == *(i2 - 50));
+        CHECK(-30 == (i2 - 30) - i2);
+    }
+}
+
+TEST_CASE("equals")
+{
+    const auto n = 666u;
+    auto v       = make_test_vector(0, n);
+
+    CHECK(v == v);
+    CHECK(v == v.set(42, 42));
+    CHECK(v != v.set(42, 24));
+    CHECK(v == v.set(42, 24).set(42, 42));
+    CHECK(v.set(42, 24) == v.set(42, 24));
+    CHECK(v != v.push_back(7));
+    CHECK(v.push_back(7) == v.push_back(7));
+    CHECK(v.push_back(5) != v.push_back(7));
+    CHECK(v != v.set(v.size() - 2, 24));
+    CHECK(v == v.set(v.size() - 2, 24).set(v.size() - 2, v[v.size() - 2]));
+}
+
+TEST_CASE("all of")
+{
+    const auto n = 666u;
+    auto v       = make_test_vector(0, n);
+
+    SECTION("false")
+    {
+        auto res = immer::all_of(v, [](auto x) { return x < 100; });
+        CHECK(res == false);
+    }
+    SECTION("true")
+    {
+        auto res = immer::all_of(v, [](auto x) { return x < 1000; });
+        CHECK(res == true);
+    }
+    SECTION("bounded, true")
+    {
+        auto res = immer::all_of(
+            v.begin() + 101, v.end() - 10, [](auto x) { return x > 100; });
+        CHECK(res == true);
+    }
+    SECTION("bounded, false")
+    {
+        auto res = immer::all_of(
+            v.begin() + 101, v.end() - 10, [](auto x) { return x < 600; });
+        CHECK(res == false);
+    }
+}
+
+TEST_CASE("accumulate")
+{
+    const auto n = 666u;
+    auto v       = make_test_vector(0, n);
+
+    auto expected_n = [](auto n) { return n * (n - 1) / 2; };
+    auto expected_i = [&](auto i, auto n) {
+        return expected_n(n) - expected_n(i);
+    };
+
+    SECTION("sum collection")
+    {
+        auto sum = immer::accumulate(v, 0u);
+        CHECK(sum == expected_n(v.size()));
+    }
+
+    SECTION("sum range")
+    {
+        using namespace std;
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 300, 0u);
+            CHECK(sum == expected_i(100, 300));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 31, begin(v) + 300, 0u);
+            CHECK(sum == expected_i(31, 300));
+        }
+        {
+            auto sum = immer::accumulate(begin(v), begin(v) + 33, 0u);
+            CHECK(sum == expected_i(0, 33));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 660, 0u);
+            CHECK(sum == expected_i(100, 660));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 100, begin(v) + 105, 0u);
+            CHECK(sum == expected_i(100, 105));
+        }
+        {
+            auto sum = immer::accumulate(begin(v) + 660, begin(v) + 664, 0u);
+            CHECK(sum == expected_i(660, 664));
+        }
+    }
+}
+
+TEST_CASE("vector of strings")
+{
+    const auto n = 666u;
+
+    auto v = VECTOR_T<std::string>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.push_back(std::to_string(i));
+
+    for (decltype(v.size()) i = 0; i < v.size(); ++i)
+        CHECK(v[i] == std::to_string(i));
+
+    SECTION("set")
+    {
+        for (auto i = 0u; i < n; ++i)
+            v = v.set(i, "foo " + std::to_string(i));
+        for (auto i = 0u; i < n; ++i)
+            CHECK(v[i] == "foo " + std::to_string(i));
+    }
+}
+
+struct non_default
+{
+    unsigned value;
+    non_default() = delete;
+    operator unsigned() const { return value; }
+
+#if IMMER_DEBUG_PRINT
+    friend std::ostream& operator<<(std::ostream& os, non_default x)
+    {
+        os << "ND{" << x.value << "}";
+        return os;
+    }
+#endif
+};
+
+TEST_CASE("non default")
+{
+    const auto n = 666u;
+
+    auto v = VECTOR_T<non_default>{};
+    for (auto i = 0u; i < n; ++i)
+        v = v.push_back({i});
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
+
+    SECTION("set")
+    {
+        for (auto i = 0u; i < n; ++i)
+            v = v.set(i, {i + 1});
+
+        CHECK_VECTOR_EQUALS(v, boost::irange(1u, n + 1u));
+    }
+}
+
+TEST_CASE("take")
+{
+    const auto n = 666u;
+
+    SECTION("anywhere")
+    {
+        auto v = make_test_vector(0, n);
+
+        for (auto i : test_irange(0u, n)) {
+            auto vv = v.take(i);
+            CHECK(vv.size() == i);
+            CHECK_VECTOR_EQUALS_RANGE(vv, v.begin(), v.begin() + i);
+        }
+    }
+}
+
+TEST_CASE("exception safety")
+{
+    constexpr auto n = 666u;
+
+    using dadaist_vector_t = typename dadaist_wrapper<VECTOR_T<unsigned>>::type;
+
+    SECTION("push back")
+    {
+        auto v = dadaist_vector_t{};
+        auto d = dadaism{};
+        for (auto i = 0u; v.size() < static_cast<decltype(v.size())>(n);) {
+            auto s = d.next();
+            try {
+                v = v.push_back({i});
+                ++i;
+            } catch (dada_error) {}
+            CHECK_VECTOR_EQUALS(v, boost::irange(0u, i));
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("update")
+    {
+        auto v = make_test_vector<dadaist_vector_t>(0, n);
+        auto d = dadaism{};
+        for (auto i = 0u; i < n;) {
+            auto s = d.next();
+            try {
+                v = v.update(i, [](auto x) { return dada(), x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            CHECK_VECTOR_EQUALS(
+                v, boost::join(boost::irange(1u, 1u + i), boost::irange(i, n)));
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("take")
+    {
+        auto v = make_test_vector<dadaist_vector_t>(0, n);
+        auto d = dadaism{};
+        for (auto i = 0u; i < n;) {
+            auto s = d.next();
+            auto r = dadaist_vector_t{};
+            try {
+                r = v.take(i);
+                CHECK_VECTOR_EQUALS(r, boost::irange(0u, i++));
+            } catch (dada_error) {
+                CHECK_VECTOR_EQUALS(r, boost::irange(0u, 0u));
+            }
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+}
diff --git a/third_party/immer/test/vector/issue-16.cpp b/third_party/immer/test/vector/issue-16.cpp
new file mode 100644
index 0000000000..81156b240b
--- /dev/null
+++ b/third_party/immer/test/vector/issue-16.cpp
@@ -0,0 +1,122 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// Thanks Dominic Chen <ddchen@cs.cmu.edu> for reporting this issue
+// https://github.com/arximboldi/immer/issues/16
+
+#include <immer/flex_vector.hpp>
+#include <immer/flex_vector_transient.hpp>
+#include <immer/heap/gc_heap.hpp>
+#include <vector>
+
+std::vector<unsigned> init = {
+    291,  438, 755,  608, 594,  912,  73,   97,   496,  101,  837,  696,  978,
+    948,  126, 131,  624, 26,   590,  201,  219,  339,  193,  218,  866,  407,
+    538,  780, 312,  909, 129,  883,  571,  865,  854,  372,  488,  695,  750,
+    341,  798, 375,  350, 1013, 361,  1012, 743,  425,  68,   280,  245,  149,
+    342,  535, 901,  223, 285,  928,  666,  534,  166,  963,  171,  893,  297,
+    1017, 549, 172,  505, 965,  831,  563,  79,   180,  147,  543,  639,  1022,
+    880,  559, 426,  27,  657,  33,   252,  758,  8,    641,  699,  399,  335,
+    473,  625, 703,  264, 796,  61,   737,  527,  795,  123,  200,  287,  857,
+    208,  897, 354,  885, 581,  57,   752,  855,  56,   1007, 357,  643,  476,
+    113,  955, 349,  299, 76,   393,  557,  967,  995,  233,  990,  469,  742,
+    681,  874, 673,  530, 835,  24,   178,  81,   69,   40,   368,  981,  941,
+    645,  808, 511,  982, 888,  136,  100,  960,  804,  806,  600,  500,  337,
+    580,  551, 593,  612, 318,  660,  232,  17,   879,  86,   424,  202,  112,
+    235,  791, 873,  838, 47,   574,  858,  78,   237,  846,  340,  802,  907,
+    72,   38,  44,   902, 595,  940,  182,  784,  207,  355,  815,  545,  555,
+    258,  144, 564,  463, 198,  1004, 4,    175,  468,  658,  59,   938,  63,
+    20,   179, 714,  721, 723,  381,  3,    745,  415,  119,  413,  923,  157,
+    824,  668, 409,  459, 560,  353,  379,  450,  430,  479,  274,  309,  661,
+    508,  474, 771,  504, 674,  51,   173,  670,  83,   921,  1019, 956,  725,
+    556,  427, 597,  794, 918,  95,   514,  98,   638,  205,  727,  162,  905,
+    728,  431, 203,  828, 971,  648,  871,  215,  646,  1006, 816,  277,  861,
+    576,  99,  637,  67,  28,   161,  137,  14,   506,  717,  93,   298,  251,
+    975,  927, 818,  43,  284,  994,  196,  195,  260,  705,  604,  169,  296,
+    785,  911, 398,  687, 760,  486,  642,  417,  445,  925,  763,  756,  653,
+    662,  359, 84,   151, 980,  914,  440,  546,  329,  665,  435,  732,  922,
+    377,  105, 532,  282, 822,  878,  419,  659,  65,   111,  421,  672,  146,
+    320,  316, 31,   522, 145,  513,  373,  773,  184,  974,  621,  366,  124,
+    623,  953, 267,  712, 121,  512,  214,  1002, 455,  694,  834,  618,  324,
+    656,  257, 206,  554, 334,  606,  305,  1008, 190,  160,  502,  1024, 891,
+    751,  789, 821,  462, 489,  908,  370,  164,  746,  270,  319,  491,  443,
+    531,  775, 977,  244, 485,  840,  892,  850,  347,  729,  317,  74,   731,
+    738,  790, 988,  903, 176,  211,  890,  509,  592,  444,  307,  45,   810,
+    390,  239, 678,  999, 414,  693,  345,  363,  382,  602,  516,  801,  310,
+    754,  774, 364,  480, 290,  322,  887,  168,  37,   140,  937,  578,  1005,
+    614,  734, 449,  230, 541,  761,  931,  882,  920,  210,  807,  115,  964,
+    521,  862, 628,  747, 490,  422,  788,  691,  675,  652,  25,   582,  585,
+    961,  472, 722,  7,   367,  15,   275,  700,  249,  116,  969,  325,  706,
+    493,  524, 537,  875, 22,   688,  843,  575,  288,  141,  830,  698,  562,
+    833,  143, 117,  683, 1000, 238,  397,  827,  619,  686,  1018, 973,  80,
+    689,  165, 702,  88,  332,  192,  730,  847,  408,  58,   634,  471,  958,
+    204,  236, 314,  494, 158,  412,  226,  135,  103,  998,  85,   904,  587,
+    194,  6,   380,  987, 266,  853,  369,  949,  34,   799,  884,  16,   944,
+    701,  313, 300,  611, 148,  457,  216,  900,  10,   715,  586,  561,  917,
+    1020, 800, 640,  104, 864,  185,  566,  492,  709,  1009, 498,  859,  945,
+    710,  433, 819,  411, 418,  649,  876,  733,  286,  326,  599,  986,  465,
+    929,  272, 916,  935, 1,    87,   650,  609,  344,  598,  951,  221,  242,
+    352,  635, 680,  464, 75,   256,  962,  336,  82,   383,  836,  993,  222,
+    446,  110, 970,  591, 439,  677,  308,  186,  664,  188,  253,  770,  5,
+    651,  467, 684,  139, 762,  315,  89,   507,  477,  406,  358,  753,  877,
+    442,  32,  2,    997, 120,  946,  391,  220,  996,  125,  943,  749,  130,
+    553,  385, 692,  814, 615,  396,  388,  610,  630,  957,  371,  741,  268,
+    199,  740, 823,  588, 250,  269,  577,  254,  845,  825,  153,  62,   303,
+    842,  453, 241,  114, 483,  868,  550,  952,  429,  434,  529,  503,  333,
+    616,  913, 23,   872, 679,  497,  9,    841,  772,  150,  776,  52,   947,
+    805,  613, 620,  343, 895,  812,  782,  138,  281,  778,  829,  289,  820,
+    106,  400, 484,  402, 915,  720,  122,  724,  783,  436,  499,  605,  627,
+    191,  607, 536,  91,  461,  108,  976,  851,  46,   676,  765,  109,  96,
+    437,  323, 261,  330, 273,  311,  271,  972,  719,  306,  889,  707,  384,
+    764,  979, 572,  475, 231,  519,  682,  832,  448,  356,  966,  1010, 404,
+    757,  209, 548,  617, 228,  482,  156,  596,  1014, 405,  187,  860,  420,
+    655,  276, 797,  243, 1003, 229,  950,  869,  163,  321,  848,  395,  302,
+    803,  711, 515,  376, 886,  278,  128,  870,  1015, 989,  636,  1023, 573,
+    704,  984, 601,  932, 18,   386,  926,  632,  647,  579,  365,  669,  910,
+    768,  809, 526,  769, 66,   432,  213,  293,  48,   839,  217,  224,  813,
+    899,  328, 177,  654, 401,  544,  154,  118,  736,  766,  495,  936,  767,
+    523,  569, 458,  460, 685,  107,  718,  631,  844,  248,  933,  60,   227,
+    374,  246, 247,  331, 894,  183,  279,  338,  387,  633,  565,  403,  744,
+    849,  41,  942,  517, 389,  518,  133,  622,  35,   779,  362,  726,  1021,
+    189,  787, 71,   539, 262,  423,  346,  856,  212,  748,  64,   930,  667,
+    881,  781, 603,  817, 410,  378,  170,  225,  558,  29,   906,  174,  939,
+    21,   292, 304,  663, 240,  793,  567,  456,  255,  811,  954,  735,  867,
+    428,  991, 152,  528, 70,   263,  392,  697,  671,  155,  49,   301,  792,
+    525,  959, 1001, 90,  924,  896,  826,  452,  968,  11,   542,  53,   327,
+    777,  294, 94,   589, 547,  584,  416,  351,  713,  478,  55,   451,  36,
+    708,  501, 132,  863, 54,   19,   142,  13,   934,  134,  30,   466,  716,
+    295,  92,  447,  102, 181,  520,  644,  481,  470,  540,  583,  552,  786,
+    690,  487, 394,  983, 259,  510,  50,   568,  533,  759,  992,  283,  39,
+    360,  919, 167,  852, 42,   629,  1016, 454,  441,  1011, 0,    77,   197,
+    234,  127, 159,  12,  898,  348,  265,  626,  739,  985};
+
+using gc_policy = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+using immvec = immer::flex_vector_transient<unsigned, gc_policy>;
+
+void erase(immvec& v, unsigned idx)
+{
+    auto v2(v);
+    v.take(idx);
+    v2.drop(idx + 1);
+    v.append(v2);
+}
+
+int main(int argc, char** argv)
+{
+    immvec vector;
+    for (auto i : init)
+        vector.push_back(i);
+
+    erase(vector, 470);
+    vector.push_back(224);
+    erase(vector, 958);
+}
diff --git a/third_party/immer/test/vector/issue-46.cpp b/third_party/immer/test/vector/issue-46.cpp
new file mode 100644
index 0000000000..6475be210d
--- /dev/null
+++ b/third_party/immer/test/vector/issue-46.cpp
@@ -0,0 +1,39 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+// Thanks Guiguiprim for reporting this issue
+// https://github.com/arximboldi/immer/issues/46
+
+#include <immer/flex_vector.hpp>
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <catch.hpp>
+
+TEST_CASE("operator==() may return bad result")
+{
+    using bool_vec = immer::flex_vector<bool>;
+
+    immer::vector<bool_vec> v0;
+    auto tv = v0.transient();
+    tv.push_back(bool_vec(9, false));
+    tv.push_back(bool_vec(10, false));
+    tv.push_back(bool_vec(8, false));
+    tv.push_back(bool_vec(6, false));
+    tv.push_back(bool_vec(9, false));
+    tv.push_back(bool_vec(7, false));
+    tv.push_back(bool_vec(8, false));
+    tv.push_back(bool_vec(9, false));
+    tv.push_back(bool_vec(10, false));
+    v0 = tv.persistent();
+
+    auto v1 = v0.update(1, [](bool_vec vec) { return vec.set(8, true); });
+
+    CHECK(v0[1] != v1[1]);
+    CHECK(v0 != v1);
+}
diff --git a/third_party/immer/test/vector/issue-74.cpp b/third_party/immer/test/vector/issue-74.cpp
new file mode 100644
index 0000000000..8650be014f
--- /dev/null
+++ b/third_party/immer/test/vector/issue-74.cpp
@@ -0,0 +1,24 @@
+#include "immer/box.hpp"
+#include "immer/set.hpp"
+#include "immer/vector.hpp"
+
+#include <functional>
+
+struct my_type
+{
+    using container_t = immer::vector<immer::box<my_type>>;
+    using func_t      = std::function<int(int)>;
+
+    int ival;
+    double dval;
+    func_t func;
+    container_t children;
+};
+
+int main()
+{
+    my_type::container_t items = {my_type()};
+    immer::set<int> items2;
+    auto items3 = items2.insert(10);
+    return 0;
+}
diff --git a/third_party/immer/test/vector_transient/B3-BL0.cpp b/third_party/immer/test/vector_transient/B3-BL0.cpp
new file mode 100644
index 0000000000..a0af0a1712
--- /dev/null
+++ b/third_party/immer/test/vector_transient/B3-BL0.cpp
@@ -0,0 +1,21 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+template <typename T>
+using test_vector_t = immer::vector<T, immer::default_memory_policy, 3u, 0u>;
+
+template <typename T>
+using test_vector_transient_t = typename test_vector_t<T>::transient_type;
+
+#define VECTOR_T test_vector_t
+#define VECTOR_TRANSIENT_T test_vector_transient_t
+
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector_transient/default.cpp b/third_party/immer/test/vector_transient/default.cpp
new file mode 100644
index 0000000000..f3089086a9
--- /dev/null
+++ b/third_party/immer/test/vector_transient/default.cpp
@@ -0,0 +1,15 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#define VECTOR_T ::immer::vector
+#define VECTOR_TRANSIENT_T ::immer::vector_transient
+
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector_transient/gc.cpp b/third_party/immer/test/vector_transient/gc.cpp
new file mode 100644
index 0000000000..393d2d1bbc
--- /dev/null
+++ b/third_party/immer/test/vector_transient/gc.cpp
@@ -0,0 +1,29 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <immer/vector.hpp>
+#include <immer/vector_transient.hpp>
+
+#include <immer/heap/gc_heap.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+using gc_memory = immer::memory_policy<immer::heap_policy<immer::gc_heap>,
+                                       immer::no_refcount_policy,
+                                       immer::gc_transience_policy,
+                                       false>;
+
+template <typename T>
+using test_vector_t = immer::vector<T, gc_memory, 3u>;
+
+template <typename T>
+using test_vector_transient_t = immer::vector_transient<T, gc_memory, 3u>;
+
+#define VECTOR_T test_vector_t
+#define VECTOR_TRANSIENT_T test_vector_transient_t
+
+#include "generic.ipp"
diff --git a/third_party/immer/test/vector_transient/generic.ipp b/third_party/immer/test/vector_transient/generic.ipp
new file mode 100644
index 0000000000..6cd25a567d
--- /dev/null
+++ b/third_party/immer/test/vector_transient/generic.ipp
@@ -0,0 +1,267 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include "test/dada.hpp"
+#include "test/transient_tester.hpp"
+#include "test/util.hpp"
+
+#include <catch.hpp>
+
+#ifndef VECTOR_T
+#error "define the vector template to use in VECTOR_T"
+#endif
+
+#ifndef VECTOR_TRANSIENT_T
+#error "define the vector template to use in VECTOR_TRANSIENT_T"
+#endif
+
+template <typename V = VECTOR_T<unsigned>>
+auto make_test_vector(unsigned min, unsigned max)
+{
+    auto v = V{};
+    for (auto i = min; i < max; ++i)
+        v = v.push_back({i});
+    return v;
+}
+
+TEST_CASE("from vector and to vector")
+{
+    constexpr auto n = 100u;
+
+    auto v = make_test_vector(0, n).transient();
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
+
+    auto p = v.persistent();
+    CHECK_VECTOR_EQUALS(p, boost::irange(0u, n));
+}
+
+TEST_CASE("protect persistence")
+{
+    auto v = VECTOR_T<unsigned>{}.transient();
+    v.push_back(12);
+    auto p = v.persistent();
+    v.set(0, 42);
+    CHECK(p[0] == 12);
+    CHECK(v[0] == 42);
+}
+
+TEST_CASE("push back move")
+{
+    using vector_t = VECTOR_T<unsigned>;
+
+    auto v = vector_t{};
+
+    auto check_move = [&](vector_t&& x) -> vector_t&& {
+        if (vector_t::memory_policy::use_transient_rvalues)
+            CHECK(&x == &v);
+        else
+            CHECK(&x != &v);
+        return std::move(x);
+    };
+
+    v                = check_move(std::move(v).push_back(0));
+    v                = check_move(std::move(v).push_back(1));
+    v                = check_move(std::move(v).push_back(2));
+    auto addr_before = &v[0];
+    v                = check_move(std::move(v).push_back(3));
+    auto addr_after  = &v[0];
+
+    if (vector_t::memory_policy::use_transient_rvalues)
+        CHECK(addr_before == addr_after);
+    else
+        CHECK(addr_before != addr_after);
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, 4u));
+}
+
+TEST_CASE("set move")
+{
+    using vector_t = VECTOR_T<unsigned>;
+
+    auto v = vector_t{};
+
+    auto check_move = [&](vector_t&& x) -> vector_t&& {
+        if (vector_t::memory_policy::use_transient_rvalues)
+            CHECK(&x == &v);
+        else
+            CHECK(&x != &v);
+        return std::move(x);
+    };
+
+    v = v.push_back(0);
+
+    auto addr_before = &v[0];
+    v                = check_move(std::move(v).set(0, 1));
+    auto addr_after  = &v[0];
+
+    if (vector_t::memory_policy::use_transient_rvalues)
+        CHECK(addr_before == addr_after);
+    else
+        CHECK(addr_before != addr_after);
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u));
+}
+
+TEST_CASE("update move")
+{
+    using vector_t = VECTOR_T<unsigned>;
+
+    auto v = vector_t{};
+
+    auto check_move = [&](vector_t&& x) -> vector_t&& {
+        if (vector_t::memory_policy::use_transient_rvalues)
+            CHECK(&x == &v);
+        else
+            CHECK(&x != &v);
+        return std::move(x);
+    };
+
+    v = v.push_back(0);
+
+    auto addr_before = &v[0];
+    v = check_move(std::move(v).update(0, [](auto x) { return x + 1; }));
+    auto addr_after = &v[0];
+
+    if (vector_t::memory_policy::use_transient_rvalues)
+        CHECK(addr_before == addr_after);
+    else
+        CHECK(addr_before != addr_after);
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u));
+}
+
+TEST_CASE("take move")
+{
+    using vector_t = VECTOR_T<unsigned>;
+
+    auto v = vector_t{};
+
+    auto check_move = [&](vector_t&& x) -> vector_t&& {
+        if (vector_t::memory_policy::use_transient_rvalues)
+            CHECK(&x == &v);
+        else
+            CHECK(&x != &v);
+        return std::move(x);
+    };
+
+    v = v.push_back(0).push_back(1);
+
+    auto addr_before = &v[0];
+    v                = check_move(std::move(v).take(1));
+    auto addr_after  = &v[0];
+
+    if (vector_t::memory_policy::use_transient_rvalues)
+        CHECK(addr_before == addr_after);
+    else
+        CHECK(addr_before != addr_after);
+
+    CHECK_VECTOR_EQUALS(v, boost::irange(0u, 1u));
+}
+
+TEST_CASE("exception safety")
+{
+    constexpr auto n = 667u;
+
+    using dadaist_vector_t = typename dadaist_wrapper<VECTOR_T<unsigned>>::type;
+
+    SECTION("push back")
+    {
+        auto t = as_transient_tester(dadaist_vector_t{});
+        auto d = dadaism{};
+        for (auto li = 0u, i = 0u; i < n;) {
+            auto s = d.next();
+            try {
+                if (t.transient)
+                    t.vt.push_back({i});
+                else
+                    t.vp = t.vp.push_back({i});
+                ++i;
+                if (t.step())
+                    li = i;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+        IMMER_TRACE_E(t.d.happenings);
+    }
+
+    SECTION("update")
+    {
+        using boost::irange;
+        using boost::join;
+
+        auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n));
+        auto d = dadaism{};
+        for (auto li = 0u, i = 0u; i < n;) {
+            auto s = d.next();
+            try {
+                if (t.transient)
+                    t.vt.update(i, [](auto x) { return dada(), x + 1; });
+                else
+                    t.vp = t.vp.update(i, [](auto x) { return dada(), x + 1; });
+                ++i;
+                if (t.step())
+                    li = i;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt,
+                                    join(irange(1u, 1u + i), irange(i, n)));
+                CHECK_VECTOR_EQUALS(t.vp,
+                                    join(irange(1u, 1u + li), irange(li, n)));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp,
+                                    join(irange(1u, 1u + i), irange(i, n)));
+                CHECK_VECTOR_EQUALS(t.vt,
+                                    join(irange(1u, 1u + li), irange(li, n)));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+    }
+
+    SECTION("take")
+    {
+        auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n));
+        auto d = dadaism{};
+        auto deltas = magic_rotator();
+        auto delta  = 0u;
+        for (auto i = n, li = i;;) {
+            auto s = d.next();
+            auto r = dadaist_vector_t{};
+            try {
+                if (t.transient)
+                    t.vt.take(i);
+                else
+                    t.vp = t.vp.take(i);
+                if (t.step())
+                    li = i;
+                delta = deltas.next();
+                if (i < delta)
+                    break;
+                i -= delta;
+            } catch (dada_error) {}
+            if (t.transient) {
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i + delta));
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
+            } else {
+                CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i + delta));
+                CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
+            }
+        }
+        CHECK(d.happenings > 0);
+        CHECK(t.d.happenings > 0);
+    }
+}
diff --git a/third_party/immer/tools/clojure/README.md b/third_party/immer/tools/clojure/README.md
new file mode 100644
index 0000000000..2628aa2e5f
--- /dev/null
+++ b/third_party/immer/tools/clojure/README.md
@@ -0,0 +1,4 @@
+
+```
+lein run
+```
diff --git a/third_party/immer/tools/clojure/project.clj b/third_party/immer/tools/clojure/project.clj
new file mode 100644
index 0000000000..735deab2f9
--- /dev/null
+++ b/third_party/immer/tools/clojure/project.clj
@@ -0,0 +1,12 @@
+(defproject immer-benchmarks "0.1.0-SNAPSHOT"
+  :description "Benchmarks for immutable data structures"
+  :url "http://example.com/FIXME"
+  :dependencies [[org.clojure/clojure "1.8.0"]
+                 [org.clojure/core.rrb-vector "0.0.11"]
+                 [criterium "0.4.4"]]
+  :plugins [[lein-git-deps "0.0.1-SNAPSHOT"]]
+  ;;:git-dependencies [["https://github.com/clojure/core.rrb-vector.git"]]
+  :source-paths [;;".lein-git-deps/core.rrb-vector/src/main/clojure"
+                 "src"]
+
+  :main immer-benchmark)
diff --git a/third_party/immer/tools/clojure/src/immer_benchmark.clj b/third_party/immer/tools/clojure/src/immer_benchmark.clj
new file mode 100644
index 0000000000..f3d6f73e6c
--- /dev/null
+++ b/third_party/immer/tools/clojure/src/immer_benchmark.clj
@@ -0,0 +1,131 @@
+(ns immer-benchmark
+  (:require [criterium.core :as c]
+            [clojure.core.rrb-vector :as fv]))
+
+(defn h0 [& args]
+  (apply println "\n####" args))
+
+(defn h1 [& args]
+  (apply println "\n====" args))
+
+(defn h2 [& args]
+  (apply println "\n----" args))
+
+(defn run-benchmarks [N S]
+  (h0 "Running benchmarks:  N =" N " S =" S)
+
+  (def c-steps 10)
+
+  (defn vector-push-f [v]
+    (loop [v v
+           i 0]
+      (if (< i N)
+        (recur (fv/catvec (fv/vector i) v)
+               (inc i))
+        v)))
+
+  (defn vector-push [v]
+    (loop [v v
+           i 0]
+      (if (< i N)
+        (recur (conj v i)
+               (inc i))
+        v)))
+
+  (defn vector-push! [v]
+    (loop [v (transient v)
+           i 0]
+      (if (< i N)
+        (recur (conj! v i)
+               (inc i))
+        (persistent! v))))
+
+  (defn vector-concat [v vc]
+    (loop [v v
+           i 0]
+      (if (< i c-steps)
+        (recur (fv/catvec v vc)
+               (inc i))
+        v)))
+
+  (defn vector-update [v]
+    (loop [v v
+           i 0]
+      (if (< i N)
+        (recur (assoc v i (+ i 1))
+               (inc i))
+        v)))
+
+  (defn vector-update-random [v]
+    (loop [v v
+           i 0]
+      (if (< i N)
+        (recur (assoc v (rand-int N) i)
+               (inc i))
+        v)))
+
+  (defn vector-update! [v]
+    (loop [v (transient v)
+           i 0]
+      (if (< i N)
+        (recur (assoc! v i (+ i 1))
+               (inc i))
+        (persistent! v))))
+
+  (defn vector-update-random! [v]
+    (loop [v (transient v)
+           i 0]
+      (if (< i N)
+        (recur (assoc! v (rand-int N) i)
+               (inc i))
+        (persistent! v))))
+
+  (defn vector-iter [v]
+    (reduce + 0 v))
+
+  (defn the-benchmarks [empty-v]
+    (def full-v (into empty-v (range N)))
+    (h2 "iter")
+    (c/bench (vector-iter full-v) :samples S)
+    (if (> N 100000)
+      (h2 "skipping updates 'cuz N > 100000 (would run out of mem)")
+      (do
+        (h2 "update!")
+        (c/bench (vector-update! full-v) :samples S)
+        (h2 "update-random!")
+        (c/bench (vector-update-random! full-v) :samples S)
+        (h2 "push!")
+        (c/bench (vector-push! empty-v) :samples S)
+        (h2 "update")
+        (c/bench (vector-update full-v) :samples S)
+        (h2 "update-random")
+        (c/bench (vector-update-random full-v) :samples S)
+        (h2 "push")
+        (c/bench (vector-push empty-v) :samples S))))
+
+  (defn the-rrb-benchmarks [empty-v]
+    (if (> N 1000)
+      (h2 "skipping relaxed test 'cuz N > 1000 (rrb-vector bug)")
+      (do
+        (def full-v (vector-push-f empty-v))
+        (h2 "iter/f")
+        (c/bench (vector-iter full-v) :samples S)
+        (h2 "update/f")
+        (c/bench (vector-update full-v) :samples S)
+        (h2 "update-random/f")
+        (c/bench (vector-update-random full-v) :samples S)))
+    (def short-v (into empty-v (range (/ N c-steps))))
+    (h2 "concat")
+    (c/bench (vector-concat empty-v short-v)) :samples S)
+
+  (h1 "vector")
+  (the-benchmarks (vector))
+
+  (h1 "rrb-vector")
+  (the-benchmarks (fv/vector))
+  (the-rrb-benchmarks (fv/vector)))
+
+(defn -main []
+  (run-benchmarks 1000 100)
+  (run-benchmarks 100000 20)
+  (run-benchmarks 10000000 3))
diff --git a/third_party/immer/tools/docker/icfp17/Dockerfile b/third_party/immer/tools/docker/icfp17/Dockerfile
new file mode 100644
index 0000000000..5e6d1292e5
--- /dev/null
+++ b/third_party/immer/tools/docker/icfp17/Dockerfile
@@ -0,0 +1,77 @@
+FROM debian:stretch
+
+MAINTAINER arximboldi
+
+## install immer
+
+RUN apt-get update && \
+    apt-get install -y git
+RUN git clone https://github.com/arximboldi/immer.git
+
+## prepare test dependencies
+
+RUN apt-get update && \
+    apt-get install -y \
+            autoconf \
+            automake \
+            cmake \
+            g++ \
+            libboost-dev \
+            libtool \
+            make \
+            pkg-config \
+            --
+
+RUN mkdir /immer/build
+WORKDIR /immer/build
+RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCHECK_BENCHMARKS=1
+RUN make deps
+RUN make tests examples benchmarks
+
+## prepare clojure dependencies
+
+RUN apt-get update && \
+    apt-get install -y default-jdk curl
+
+RUN curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein \
+         > /usr/local/bin/lein && \
+    chmod +x /usr/local/bin/lein
+
+WORKDIR /immer/tools/clojure
+ENV LEIN_ROOT ok
+RUN lein deps
+RUN lein compile
+
+## prepare scala dependencies
+
+RUN apt-get update && \
+    apt-get install -y gnupg2 apt-transport-https
+RUN echo "deb https://dl.bintray.com/sbt/debian /" \
+         > /etc/apt/sources.list.d/sbt.list && \
+    apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 \
+                --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823 && \
+    apt-get update && \
+    apt-get install -y sbt
+
+WORKDIR /immer/tools/scala
+RUN sbt compile
+
+## prepare python dependencies
+
+RUN apt-get update && \
+    apt-get install -y python-pip
+
+RUN pip install \
+        pytest-benchmark \
+        pyrsistent
+
+RUN pip install /immer
+
+## add some editors
+
+RUN apt-get update && \
+    apt-get install -y emacs vim nano
+
+## go to a useful working dir
+
+WORKDIR /immer
diff --git a/third_party/immer/tools/include/catch.hpp b/third_party/immer/tools/include/catch.hpp
new file mode 100644
index 0000000000..c86bc6adc3
--- /dev/null
+++ b/third_party/immer/tools/include/catch.hpp
@@ -0,0 +1,9458 @@
+/*
+ *  CATCH v1.1 build 3 (master branch)
+ *  Generated: 2015-05-20 13:23:44.423074
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+ *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#   ifdef __ICC // icpc defines the __clang__ macro
+#       pragma warning(push)
+#       pragma warning(disable: 161 1682)
+#   else // __ICC
+#       pragma clang diagnostic ignored "-Wglobal-constructors"
+#       pragma clang diagnostic ignored "-Wvariadic-macros"
+#       pragma clang diagnostic ignored "-Wc99-extensions"
+#       pragma clang diagnostic ignored "-Wunused-variable"
+#       pragma clang diagnostic push
+#       pragma clang diagnostic ignored "-Wpadded"
+#       pragma clang diagnostic ignored "-Wc++98-compat"
+#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic ignored "-Wvariadic-macros"
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_SFINAE : is basic (C++03) SFINAE supported?
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// A lot of this code is based on Boost (1.53)
+
+#ifdef __clang__
+
+#  if __has_feature(cxx_nullptr)
+#    define CATCH_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  if __has_feature(cxx_noexcept)
+#    define CATCH_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#if (__BORLANDC__ > 0x582 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#if (__EDG_VERSION__ > 238 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#if (__DMC__ > 0x840 )
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+
+#if (__GNUC_MINOR__ >= 96 )
+//#define CATCH_CONFIG_SFINAE
+#endif
+
+#elif __GNUC__ >= 3
+
+// #define CATCH_CONFIG_SFINAE // Taking this out completely for now
+
+#endif // __GNUC__ < 3
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1310 ) // (VC++ 7.0+)
+//#define CATCH_CONFIG_SFINAE // Not confirmed
+#endif
+
+#if (_MSC_VER >= 1600)
+#define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_CONFIG_CPP11_NOEXCEPT
+#define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS
+#define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if (__cplusplus >= 201103L)
+
+#  define CATCH_CPP11_OR_GREATER
+
+#  ifndef CATCH_CONFIG_CPP11_NULLPTR
+#    define CATCH_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_CONFIG_CPP11_IS_ENUM
+#    define CATCH_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_CONFIG_CPP11_TUPLE
+#    define CATCH_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_CONFIG_SFINAE
+//#    define CATCH_CONFIG_SFINAE // Don't use, for now
+#  endif
+
+#  ifndef CATCH_CONFIG_VARIADIC_MACROS
+#    define CATCH_CONFIG_VARIADIC_MACROS
+#  endif
+
+#endif // __cplusplus >= 201103L
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+#  define CATCH_NOEXCEPT noexcept
+#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+#  define CATCH_NOEXCEPT throw()
+#  define CATCH_NOEXCEPT_IS(x)
+#endif
+
+namespace Catch {
+
+    class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        NonCopyable( NonCopyable const& )              = delete;
+        NonCopyable( NonCopyable && )                  = delete;
+        NonCopyable& operator = ( NonCopyable const& ) = delete;
+        NonCopyable& operator = ( NonCopyable && )     = delete;
+#else
+        NonCopyable( NonCopyable const& info );
+        NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+    protected:
+        NonCopyable() {}
+        virtual ~NonCopyable();
+    };
+
+    class SafeBool {
+    public:
+        typedef void (SafeBool::*type)() const;
+
+        static type makeSafe( bool value ) {
+            return value ? &SafeBool::trueValue : 0;
+        }
+    private:
+        void trueValue() const {}
+    };
+
+    template<typename ContainerT>
+    inline void deleteAll( ContainerT& container ) {
+        typename ContainerT::const_iterator it = container.begin();
+        typename ContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete *it;
+    }
+    template<typename AssociativeContainerT>
+    inline void deleteAllValues( AssociativeContainerT& container ) {
+        typename AssociativeContainerT::const_iterator it = container.begin();
+        typename AssociativeContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete it->second;
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    std::string trim( std::string const& str );
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo();
+        SourceLineInfo( char const* _file, std::size_t _line );
+        SourceLineInfo( SourceLineInfo const& other );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SourceLineInfo( SourceLineInfo && )                  = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
+#  endif
+        bool empty() const;
+        bool operator == ( SourceLineInfo const& other ) const;
+        bool operator < ( SourceLineInfo const& other ) const;
+
+        std::string file;
+        std::size_t line;
+    };
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+    // This is just here to avoid compiler warnings with macro constants and boolean literals
+    inline bool isTrue( bool value ){ return value; }
+    inline bool alwaysTrue() { return true; }
+    inline bool alwaysFalse() { return false; }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() {
+            return std::string();
+        }
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include <ostream>
+
+namespace Catch {
+
+    class NotImplementedException : public std::exception
+    {
+    public:
+        NotImplementedException( SourceLineInfo const& lineInfo );
+        NotImplementedException( NotImplementedException const& ) {}
+
+        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+        virtual const char* what() const CATCH_NOEXCEPT;
+
+    private:
+        std::string m_what;
+        SourceLineInfo m_lineInfo;
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct IGeneratorInfo {
+        virtual ~IGeneratorInfo();
+        virtual bool moveNext() = 0;
+        virtual std::size_t getCurrentIndex() const = 0;
+    };
+
+    struct IGeneratorsForTest {
+        virtual ~IGeneratorsForTest();
+
+        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+        virtual bool moveNext() = 0;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    // An intrusive reference counting smart pointer.
+    // T must implement addRef() and release() methods
+    // typically implementing the IShared interface
+    template<typename T>
+    class Ptr {
+    public:
+        Ptr() : m_p( NULL ){}
+        Ptr( T* p ) : m_p( p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        Ptr( Ptr const& other ) : m_p( other.m_p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        ~Ptr(){
+            if( m_p )
+                m_p->release();
+        }
+        void reset() {
+            if( m_p )
+                m_p->release();
+            m_p = NULL;
+        }
+        Ptr& operator = ( T* p ){
+            Ptr temp( p );
+            swap( temp );
+            return *this;
+        }
+        Ptr& operator = ( Ptr const& other ){
+            Ptr temp( other );
+            swap( temp );
+            return *this;
+        }
+        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+        T* get() { return m_p; }
+        const T* get() const{ return m_p; }
+        T& operator*() const { return *m_p; }
+        T* operator->() const { return m_p; }
+        bool operator !() const { return m_p == NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+    private:
+        T* m_p;
+    };
+
+    struct IShared : NonCopyable {
+        virtual ~IShared();
+        virtual void addRef() const = 0;
+        virtual void release() const = 0;
+    };
+
+    template<typename T = IShared>
+    struct SharedImpl : T {
+
+        SharedImpl() : m_rc( 0 ){}
+
+        virtual void addRef() const {
+            ++m_rc;
+        }
+        virtual void release() const {
+            if( --m_rc == 0 )
+                delete this;
+        }
+
+        mutable unsigned int m_rc;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <stdlib.h>
+
+namespace Catch {
+
+    class TestCase;
+    class Stream;
+    struct IResultCapture;
+    struct IRunner;
+    struct IGeneratorsForTest;
+    struct IConfig;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+        virtual bool advanceGeneratorsForCurrentTest() = 0;
+        virtual Ptr<IConfig const> getConfig() const = 0;
+    };
+
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+    };
+
+    IContext& getCurrentContext();
+    IMutableContext& getCurrentMutableContext();
+    void cleanUpContext();
+    Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestCase : IShared {
+        virtual void invoke () const = 0;
+    protected:
+        virtual ~ITestCase();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
+
+    };
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+    virtual void invoke() const {
+        C obj;
+        (obj.*m_method)();
+    }
+
+private:
+    virtual ~MethodTestCase() {}
+
+    void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+    NameAndDesc( const char* _name = "", const char* _description= "" )
+    : name( _name ), description( _description )
+    {}
+
+    const char* name;
+    const char* description;
+};
+
+struct AutoReg {
+
+    AutoReg(    TestFunction function,
+                SourceLineInfo const& lineInfo,
+                NameAndDesc const& nameAndDesc );
+
+    template<typename C>
+    AutoReg(    void (C::*method)(),
+                char const* className,
+                NameAndDesc const& nameAndDesc,
+                SourceLineInfo const& lineInfo ) {
+        registerTestCase(   new MethodTestCase<C>( method ),
+                            className,
+                            nameAndDesc,
+                            lineInfo );
+    }
+
+    void registerTestCase(  ITestCase* testCase,
+                            char const* className,
+                            NameAndDesc const& nameAndDesc,
+                            SourceLineInfo const& lineInfo );
+
+    ~AutoReg();
+
+private:
+    AutoReg( AutoReg const& );
+    void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+        namespace{ \
+            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        namespace{ \
+            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    inline bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    inline bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct AssertionInfo
+    {
+        AssertionInfo() {}
+        AssertionInfo(  std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        std::string const& _capturedExpression,
+                        ResultDisposition::Flags _resultDisposition );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        std::string capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+    };
+
+    struct AssertionResultData
+    {
+        AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+        std::string reconstructedExpression;
+        std::string message;
+        ResultWas::OfType resultType;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult();
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+        ~AssertionResult();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+         AssertionResult( AssertionResult const& )              = default;
+         AssertionResult( AssertionResult && )                  = default;
+         AssertionResult& operator = ( AssertionResult const& ) = default;
+         AssertionResult& operator = ( AssertionResult && )     = default;
+#  endif
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        std::string getTestMacroName() const;
+
+    protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str("");
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator->* ( T const& operand );
+        ExpressionLhs<bool> operator->* ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+        ResultBuilder& setLhs( std::string const& lhs );
+        ResultBuilder& setRhs( std::string const& rhs );
+        ResultBuilder& setOp( std::string const& op );
+
+        void endExpression();
+
+        std::string reconstructExpression() const;
+        AssertionResult build() const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        struct ExprComponents {
+            ExprComponents() : testFalse( false ) {}
+            bool testFalse;
+            std::string lhs, rhs, op;
+        } m_exprComponents;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    class Evaluator{};
+
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return opCast( lhs ) ==  opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) != opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) < opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) > opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) >= opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) <= opCast( rhs );
+        }
+    };
+
+    template<Operator Op, typename T1, typename T2>
+    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // This level of indirection allows us to specialise for integer types
+    // to avoid signed/ unsigned warnings
+
+    // "base" overload
+    template<Operator Op, typename T1, typename T2>
+    bool compare( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // unsigned X to int
+    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+
+    // unsigned X to long
+    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+
+    // int to unsigned X
+    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+
+    // long to unsigned X
+    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // pointer to long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+    // pointer to int (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    // pointer to nullptr_t (when comparing against nullptr)
+    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+    }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+// #included from: catch_sfinae.hpp
+#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED
+
+// Try to detect if the current compiler supports SFINAE
+
+namespace Catch {
+
+    struct TrueType {
+        static const bool value = true;
+        typedef void Enable;
+        char sizer[1];
+    };
+    struct FalseType {
+        static const bool value = false;
+        typedef void Disable;
+        char sizer[2];
+    };
+
+#ifdef CATCH_CONFIG_SFINAE
+
+    template<bool> struct NotABooleanExpression;
+
+    template<bool c> struct If : NotABooleanExpression<c> {};
+    template<> struct If<true> : TrueType {};
+    template<> struct If<false> : FalseType {};
+
+    template<int size> struct SizedIf;
+    template<> struct SizedIf<sizeof(TrueType)> : TrueType {};
+    template<> struct SizedIf<sizeof(FalseType)> : FalseType {};
+
+#endif // CATCH_CONFIG_SFINAE
+
+} // end namespace Catch
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring );
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+    std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+    extern std::string unprintableString;
+
+// SFINAE is currently disabled by default for all compilers.
+// If the non SFINAE version of IsStreamInsertable is ambiguous for you
+// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE
+#ifdef CATCH_CONFIG_SFINAE
+
+    template<typename T>
+    class IsStreamInsertableHelper {
+        template<int N> struct TrueIfSizeable : TrueType {};
+
+        template<typename T2>
+        static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*);
+        static FalseType dummy(...);
+
+    public:
+        typedef SizedIf<sizeof(dummy((T*)0))> type;
+    };
+
+    template<typename T>
+    struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {};
+
+#else
+
+    struct BorgType {
+        template<typename T> BorgType( T const& );
+    };
+
+    TrueType& testStreamable( std::ostream& );
+    FalseType testStreamable( FalseType );
+
+    FalseType operator<<( std::ostream const&, BorgType const& );
+
+    template<typename T>
+    struct IsStreamInsertable {
+        static std::ostream &s;
+        static T  const&t;
+        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+    };
+
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+    template<typename T,
+             bool IsEnum = std::is_enum<T>::value
+             >
+    struct EnumStringMaker
+    {
+        static std::string convert( T const& ) { return unprintableString; }
+    };
+
+    template<typename T>
+    struct EnumStringMaker<T,true>
+    {
+        static std::string convert( T const& v )
+        {
+            return ::Catch::toString(
+                static_cast<typename std::underlying_type<T>::type>(v)
+                );
+        }
+    };
+#endif
+    template<bool C>
+    struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+        template<typename T>
+        static std::string convert( T const& v )
+        {
+            return EnumStringMaker<T>::convert( v );
+        }
+#else
+        template<typename T>
+        static std::string convert( T const& ) { return unprintableString; }
+#endif
+    };
+
+    template<>
+    struct StringMakerBase<true> {
+        template<typename T>
+        static std::string convert( T const& _value ) {
+            std::ostringstream oss;
+            oss << _value;
+            return oss.str();
+        }
+    };
+
+    std::string rawMemoryToString( const void *object, std::size_t size );
+
+    template<typename T>
+    inline std::string rawMemoryToString( const T& object ) {
+      return rawMemoryToString( &object, sizeof(object) );
+    }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+    template<typename U>
+    static std::string convert( U* p ) {
+        if( !p )
+            return INTERNAL_CATCH_STRINGIFY( NULL );
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+    static std::string convert( R C::* p ) {
+        if( !p )
+            return INTERNAL_CATCH_STRINGIFY( NULL );
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+//    static std::string convert( std::vector<T,Allocator> const& v ) {
+//        return Detail::rangeToString( v.begin(), v.end() );
+//    }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+    return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+  template<
+      typename Tuple,
+      std::size_t N = 0,
+      bool = (N < std::tuple_size<Tuple>::value)
+      >
+  struct ElementPrinter {
+      static void print( const Tuple& tuple, std::ostream& os )
+      {
+          os << ( N ? ", " : " " )
+             << Catch::toString(std::get<N>(tuple));
+          ElementPrinter<Tuple,N+1>::print(tuple,os);
+      }
+  };
+
+  template<
+      typename Tuple,
+      std::size_t N
+      >
+  struct ElementPrinter<Tuple,N,false> {
+      static void print( const Tuple&, std::ostream& ) {}
+  };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+    static std::string convert( const std::tuple<Types...>& tuple )
+    {
+        std::ostringstream os;
+        os << '{';
+        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+        os << " }";
+        return os.str();
+    }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+    template<typename T>
+    std::string makeString( T const& value ) {
+        return StringMaker<T>::convert( value );
+    }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+    return StringMaker<T>::convert( value );
+}
+
+    namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last ) {
+        std::ostringstream oss;
+        oss << "{ ";
+        if( first != last ) {
+            oss << Catch::toString( *first );
+            for( ++first ; first != last ; ++first )
+                oss << ", " << Catch::toString( *first );
+        }
+        oss << " }";
+        return oss.str();
+    }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template<typename T>
+class ExpressionLhs {
+    ExpressionLhs& operator = ( ExpressionLhs const& );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+    ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+#  endif
+
+public:
+    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+    ExpressionLhs( ExpressionLhs const& ) = default;
+    ExpressionLhs( ExpressionLhs && )     = default;
+#  endif
+
+    template<typename RhsT>
+    ResultBuilder& operator == ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator != ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator < ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThan>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator > ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThan>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator <= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator >= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+    }
+
+    ResultBuilder& operator == ( bool rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    ResultBuilder& operator != ( bool rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    void endExpression() {
+        bool value = m_lhs ? true : false;
+        m_rb
+            .setLhs( Catch::toString( value ) )
+            .setResultType( value )
+            .endExpression();
+    }
+
+    // Only simple binary expressions are allowed on the LHS.
+    // If more complex compositions are required then place the sub expression in parentheses
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+    template<Internal::Operator Op, typename RhsT>
+    ResultBuilder& captureExpression( RhsT const& rhs ) {
+        return m_rb
+            .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
+            .setLhs( Catch::toString( m_lhs ) )
+            .setRhs( Catch::toString( rhs ) )
+            .setOp( Internal::OperatorTraits<Op>::getName() );
+    }
+
+private:
+    ResultBuilder& m_rb;
+    T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+    template<typename T>
+    inline ExpressionLhs<T const&> ResultBuilder::operator->* ( T const& operand ) {
+        return ExpressionLhs<T const&>( *this, operand );
+    }
+
+    inline ExpressionLhs<bool> ResultBuilder::operator->* ( bool value ) {
+        return ExpressionLhs<bool>( *this, value );
+    }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        std::string message;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const {
+            return sequence == other.sequence;
+        }
+        bool operator < ( MessageInfo const& other ) const {
+            return sequence < other.sequence;
+        }
+    private:
+        static unsigned int globalCount;
+    };
+
+    struct MessageBuilder {
+        MessageBuilder( std::string const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type )
+        : m_info( macroName, lineInfo, type )
+        {}
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+        std::ostringstream m_stream;
+    };
+
+    class ScopedMessage {
+    public:
+        ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage const& other );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct MessageInfo;
+    class ScopedMessageBuilder;
+    struct Counts;
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual void assertionEnded( AssertionResult const& result ) = 0;
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+        virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+
+        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_MAC
+#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define CATCH_PLATFORM_IPHONE
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include <csignal>
+#include <string>
+
+namespace Catch{
+
+    bool isDebuggerActive();
+    void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    // The following code snippet based on:
+    // http://cocoawithlove.com/2008/03/break-into-debugger.html
+    #ifdef DEBUG
+        #if defined(__ppc64__) || defined(__ppc__)
+            #define CATCH_BREAK_INTO_DEBUGGER() \
+                if( Catch::isDebuggerActive() ) { \
+                    __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+                    : : : "memory","r0","r3","r4" ); \
+                }
+        #else
+            #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+        #endif
+    #endif
+
+#elif defined(_MSC_VER)
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#else
+    #define CATCH_BREAK_INTO_DEBUGGER() raise(SIGTRAP);
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+    class TestCase;
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+    resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            ( __catchResult->*expr ).endExpression(); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            expr; \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                expr; \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( ... ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                expr; \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( exceptionType ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+            catch( ... ) { \
+                __catchResult.useActiveException( resultDisposition ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#else
+    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << log + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( log, macroName ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+        try { \
+            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+            __catchResult \
+                .setLhs( Catch::toString( arg ) ) \
+                .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
+                .setOp( "matches" ) \
+                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+            __catchResult.captureExpression(); \
+        } catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+    struct Counts {
+        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+        Counts operator - ( Counts const& other ) const {
+            Counts diff;
+            diff.passed = passed - other.passed;
+            diff.failed = failed - other.failed;
+            diff.failedButOk = failedButOk - other.failedButOk;
+            return diff;
+        }
+        Counts& operator += ( Counts const& other ) {
+            passed += other.passed;
+            failed += other.failed;
+            failedButOk += other.failedButOk;
+            return *this;
+        }
+
+        std::size_t total() const {
+            return passed + failed + failedButOk;
+        }
+        bool allPassed() const {
+            return failed == 0 && failedButOk == 0;
+        }
+        bool allOk() const {
+            return failed == 0;
+        }
+
+        std::size_t passed;
+        std::size_t failed;
+        std::size_t failedButOk;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const {
+            Totals diff;
+            diff.assertions = assertions - other.assertions;
+            diff.testCases = testCases - other.testCases;
+            return diff;
+        }
+
+        Totals delta( Totals const& prevTotals ) const {
+            Totals diff = *this - prevTotals;
+            if( diff.assertions.failed > 0 )
+                ++diff.testCases.failed;
+            else if( diff.assertions.failedButOk > 0 )
+                ++diff.testCases.failedButOk;
+            else
+                ++diff.testCases.passed;
+            return diff;
+        }
+
+        Totals& operator += ( Totals const& other ) {
+            assertions += other.assertions;
+            testCases += other.testCases;
+            return *this;
+        }
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+    class Timer {
+    public:
+        Timer() : m_ticks( 0 ) {}
+        void start();
+        unsigned int getElapsedMicroseconds() const;
+        unsigned int getElapsedMilliseconds() const;
+        double getElapsedSeconds() const;
+
+    private:
+        uint64_t m_ticks;
+    };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_SECTION( ... ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+    #define INTERNAL_CATCH_SECTION( name, desc ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+    virtual ~IGenerator() {}
+    virtual T getValue( std::size_t index ) const = 0;
+    virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+    BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+    virtual T getValue( std::size_t index ) const {
+        return m_from+static_cast<int>( index );
+    }
+
+    virtual std::size_t size() const {
+        return static_cast<std::size_t>( 1+m_to-m_from );
+    }
+
+private:
+
+    T m_from;
+    T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+    ValuesGenerator(){}
+
+    void add( T value ) {
+        m_values.push_back( value );
+    }
+
+    virtual T getValue( std::size_t index ) const {
+        return m_values[index];
+    }
+
+    virtual std::size_t size() const {
+        return m_values.size();
+    }
+
+private:
+    std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+    CompositeGenerator() : m_totalSize( 0 ) {}
+
+    // *** Move semantics, similar to auto_ptr ***
+    CompositeGenerator( CompositeGenerator& other )
+    :   m_fileInfo( other.m_fileInfo ),
+        m_totalSize( 0 )
+    {
+        move( other );
+    }
+
+    CompositeGenerator& setFileInfo( const char* fileInfo ) {
+        m_fileInfo = fileInfo;
+        return *this;
+    }
+
+    ~CompositeGenerator() {
+        deleteAll( m_composed );
+    }
+
+    operator T () const {
+        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+        typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+        typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+        for( size_t index = 0; it != itEnd; ++it )
+        {
+            const IGenerator<T>* generator = *it;
+            if( overallIndex >= index && overallIndex < index + generator->size() )
+            {
+                return generator->getValue( overallIndex-index );
+            }
+            index += generator->size();
+        }
+        CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+        return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+    }
+
+    void add( const IGenerator<T>* generator ) {
+        m_totalSize += generator->size();
+        m_composed.push_back( generator );
+    }
+
+    CompositeGenerator& then( CompositeGenerator& other ) {
+        move( other );
+        return *this;
+    }
+
+    CompositeGenerator& then( T value ) {
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( value );
+        add( valuesGen );
+        return *this;
+    }
+
+private:
+
+    void move( CompositeGenerator& other ) {
+        std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+        m_totalSize += other.m_totalSize;
+        other.m_composed.clear();
+    }
+
+    std::vector<const IGenerator<T>*> m_composed;
+    std::string m_fileInfo;
+    size_t m_totalSize;
+};
+
+namespace Generators
+{
+    template<typename T>
+    CompositeGenerator<T> between( T from, T to ) {
+        CompositeGenerator<T> generators;
+        generators.add( new BetweenGenerator<T>( from, to ) );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3 ){
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        valuesGen->add( val4 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    struct ITestCaseRegistry;
+    struct IExceptionTranslatorRegistry;
+    struct IExceptionTranslator;
+    struct IReporterRegistry;
+    struct IReporterFactory;
+
+    struct IRegistryHub {
+        virtual ~IRegistryHub();
+
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+    };
+
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+    };
+
+    IRegistryHub& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+
+namespace Catch {
+
+    typedef std::string(*exceptionTranslateFunction)();
+
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate() const = 0;
+    };
+
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
+
+        virtual std::string translateActiveException() const = 0;
+    };
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            virtual std::string translate() const {
+                try {
+                    throw;
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+namespace Catch {
+namespace Detail {
+
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
+
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
+
+        static Approx custom() {
+            return Approx( 0 );
+        }
+
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.scale( m_scale );
+            return approx;
+        }
+
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+        }
+
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
+
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
+
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
+
+    private:
+        double m_epsilon;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+    template<typename ExpressionT>
+    struct Matcher : SharedImpl<IShared>
+    {
+        typedef ExpressionT ExpressionType;
+
+        virtual ~Matcher() {}
+        virtual Ptr<Matcher> clone() const = 0;
+        virtual bool match( ExpressionT const& expr ) const = 0;
+        virtual std::string toString() const = 0;
+    };
+
+    template<typename DerivedT, typename ExpressionT>
+    struct MatcherImpl : Matcher<ExpressionT> {
+
+        virtual Ptr<Matcher<ExpressionT> > clone() const {
+            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
+        }
+    };
+
+    namespace Generic {
+
+        template<typename ExpressionT>
+        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+        public:
+
+            AllOf() {}
+            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+
+            AllOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( !m_matchers[i]->match( expr ) )
+                        return false;
+                return true;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " and ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
+
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
+
+        template<typename ExpressionT>
+        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+        public:
+
+            AnyOf() {}
+            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
+
+            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( m_matchers[i]->match( expr ) )
+                        return true;
+                return false;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " or ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
+
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
+
+    }
+
+    namespace StdString {
+
+        inline std::string makeString( std::string const& str ) { return str; }
+        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+        struct Equals : MatcherImpl<Equals, std::string> {
+            Equals( std::string const& str ) : m_str( str ){}
+            Equals( Equals const& other ) : m_str( other.m_str ){}
+
+            virtual ~Equals();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_str == expr;
+            }
+            virtual std::string toString() const {
+                return "equals: \"" + m_str + "\"";
+            }
+
+            std::string m_str;
+        };
+
+        struct Contains : MatcherImpl<Contains, std::string> {
+            Contains( std::string const& substr ) : m_substr( substr ){}
+            Contains( Contains const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~Contains();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) != std::string::npos;
+            }
+            virtual std::string toString() const {
+                return "contains: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+
+        struct StartsWith : MatcherImpl<StartsWith, std::string> {
+            StartsWith( std::string const& substr ) : m_substr( substr ){}
+            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~StartsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) == 0;
+            }
+            virtual std::string toString() const {
+                return "starts with: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+
+        struct EndsWith : MatcherImpl<EndsWith, std::string> {
+            EndsWith( std::string const& substr ) : m_substr( substr ){}
+            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~EndsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) == expr.size() - m_substr.size();
+            }
+            virtual std::string toString() const {
+                return "ends with: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+    } // namespace StdString
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+
+    inline Impl::StdString::Equals      Equals( std::string const& str ) {
+        return Impl::StdString::Equals( str );
+    }
+    inline Impl::StdString::Equals      Equals( const char* str ) {
+        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
+    }
+    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
+        return Impl::StdString::Contains( substr );
+    }
+    inline Impl::StdString::Contains    Contains( const char* substr ) {
+        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
+        return Impl::StdString::StartsWith( substr );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
+        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
+        return Impl::StdString::EndsWith( substr );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
+        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Option {
+    public:
+        Option() : nullableValue( NULL ) {}
+        Option( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Option( Option const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+        {}
+
+        ~Option() {
+            reset();
+        }
+
+        Option& operator= ( Option const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
+            }
+            return *this;
+        }
+        Option& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
+        }
+
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = NULL;
+        }
+
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
+
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
+        }
+
+        bool some() const { return nullableValue != NULL; }
+        bool none() const { return nullableValue == NULL; }
+
+        bool operator !() const { return nullableValue == NULL; }
+        operator SafeBool::type() const {
+            return SafeBool::makeSafe( some() );
+        }
+
+    private:
+        T* nullableValue;
+        char storage[sizeof(T)];
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    struct ITestCase;
+
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4
+        };
+
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::set<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
+
+        TestCaseInfo( TestCaseInfo const& other );
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        std::string name;
+        std::string className;
+        std::string description;
+        std::set<std::string> tags;
+        std::set<std::string> lcaseTags;
+        std::string tagsAsString;
+        SourceLineInfo lineInfo;
+        SpecialProperties properties;
+    };
+
+    class TestCase : public TestCaseInfo {
+    public:
+
+        TestCase( ITestCase* testCase, TestCaseInfo const& info );
+        TestCase( TestCase const& other );
+
+        TestCase withName( std::string const& _newName ) const;
+
+        void invoke() const;
+
+        TestCaseInfo const& getTestCaseInfo() const;
+
+        void swap( TestCase& other );
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
+        TestCase& operator = ( TestCase const& other );
+
+    private:
+        Ptr<ITestCase> test;
+    };
+
+    TestCase makeTestCase(  ITestCase* testCase,
+                            std::string const& className,
+                            std::string const& name,
+                            std::string const& description,
+                            SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+    class OcMethod : public SharedImpl<ITestCase> {
+
+    public:
+        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+        virtual void invoke() const {
+            id obj = [[m_cls alloc] init];
+
+            performOptionalSelector( obj, @selector(setUp)  );
+            performOptionalSelector( obj, m_sel );
+            performOptionalSelector( obj, @selector(tearDown)  );
+
+            arcSafeRelease( obj );
+        }
+    private:
+        virtual ~OcMethod() {}
+
+        Class m_cls;
+        SEL m_sel;
+    };
+
+    namespace Detail{
+
+        inline std::string getAnnotation(   Class cls,
+                                            std::string const& annotationName,
+                                            std::string const& testCaseName ) {
+            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+            SEL sel = NSSelectorFromString( selStr );
+            arcSafeRelease( selStr );
+            id value = performOptionalSelector( cls, sel );
+            if( value )
+                return [(NSString*)value UTF8String];
+            return "";
+        }
+    }
+
+    inline size_t registerTestMethods() {
+        size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( NULL, 0 );
+
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
+
+        for( int c = 0; c < noClasses; c++ ) {
+            Class cls = classes[c];
+            {
+                u_int count;
+                Method* methods = class_copyMethodList( cls, &count );
+                for( u_int m = 0; m < count ; m++ ) {
+                    SEL selector = method_getName(methods[m]);
+                    std::string methodName = sel_getName(selector);
+                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
+                        std::string testCaseName = methodName.substr( 15 );
+                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+                        const char* className = class_getName( cls );
+
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
+            }
+        }
+        return noTestMethods;
+    }
+
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
+
+            template<typename MatcherT>
+            struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
+
+                NSString* m_substr;
+            };
+
+            struct Equals : StringHolder<Equals> {
+                Equals( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
+
+                virtual std::string toString() const {
+                    return "equals string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct Contains : StringHolder<Contains> {
+                Contains( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
+
+                virtual std::string toString() const {
+                    return "contains string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct StartsWith : StringHolder<StartsWith> {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
+                }
+
+                virtual std::string toString() const {
+                    return "starts with: " + Catch::toString( m_substr );
+                }
+            };
+            struct EndsWith : StringHolder<EndsWith> {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+                }
+
+                virtual std::string toString() const {
+                    return "ends with: " + Catch::toString( m_substr );
+                }
+            };
+
+        } // namespace NSStringMatchers
+        } // namespace Impl
+
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+    } // namespace Matchers
+
+    using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_runner.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec {
+        struct Pattern : SharedImpl<> {
+            virtual ~Pattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+        };
+        class NamePattern : public Pattern {
+            enum WildcardPosition {
+                NoWildcard = 0,
+                WildcardAtStart = 1,
+                WildcardAtEnd = 2,
+                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+            };
+
+        public:
+            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
+                if( startsWith( m_name, "*" ) ) {
+                    m_name = m_name.substr( 1 );
+                    m_wildcard = WildcardAtStart;
+                }
+                if( endsWith( m_name, "*" ) ) {
+                    m_name = m_name.substr( 0, m_name.size()-1 );
+                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+                }
+            }
+            virtual ~NamePattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                switch( m_wildcard ) {
+                    case NoWildcard:
+                        return m_name == toLower( testCase.name );
+                    case WildcardAtStart:
+                        return endsWith( toLower( testCase.name ), m_name );
+                    case WildcardAtEnd:
+                        return startsWith( toLower( testCase.name ), m_name );
+                    case WildcardAtBothEnds:
+                        return contains( toLower( testCase.name ), m_name );
+                }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+                throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+            }
+        private:
+            std::string m_name;
+            WildcardPosition m_wildcard;
+        };
+        class TagPattern : public Pattern {
+        public:
+            TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+            virtual ~TagPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+            }
+        private:
+            std::string m_tag;
+        };
+        class ExcludedPattern : public Pattern {
+        public:
+            ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+            virtual ~ExcludedPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+        private:
+            Ptr<Pattern> m_underlyingPattern;
+        };
+
+        struct Filter {
+            std::vector<Ptr<Pattern> > m_patterns;
+
+            bool matches( TestCaseInfo const& testCase ) const {
+                // All patterns in a filter must match for the filter to be a match
+                for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
+                    if( !(*it)->matches( testCase ) )
+                        return false;
+                    return true;
+            }
+        };
+
+    public:
+        bool hasFilters() const {
+            return !m_filters.empty();
+        }
+        bool matches( TestCaseInfo const& testCase ) const {
+            // A TestSpec matches if any filter matches
+            for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+                if( it->matches( testCase ) )
+                    return true;
+            return false;
+        }
+
+    private:
+        std::vector<Filter> m_filters;
+
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag };
+        Mode m_mode;
+        bool m_exclusion;
+        std::size_t m_start, m_pos;
+        std::string m_arg;
+        TestSpec::Filter m_currentFilter;
+        TestSpec m_testSpec;
+        ITagAliasRegistry const* m_tagAliases;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+        TestSpecParser& parse( std::string const& arg ) {
+            m_mode = None;
+            m_exclusion = false;
+            m_start = std::string::npos;
+            m_arg = m_tagAliases->expandAliases( arg );
+            for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+                visitChar( m_arg[m_pos] );
+            if( m_mode == Name )
+                addPattern<TestSpec::NamePattern>();
+            return *this;
+        }
+        TestSpec testSpec() {
+            addFilter();
+            return m_testSpec;
+        }
+    private:
+        void visitChar( char c ) {
+            if( m_mode == None ) {
+                switch( c ) {
+                case ' ': return;
+                case '~': m_exclusion = true; return;
+                case '[': return startNewMode( Tag, ++m_pos );
+                case '"': return startNewMode( QuotedName, ++m_pos );
+                default: startNewMode( Name, m_pos ); break;
+                }
+            }
+            if( m_mode == Name ) {
+                if( c == ',' ) {
+                    addPattern<TestSpec::NamePattern>();
+                    addFilter();
+                }
+                else if( c == '[' ) {
+                    if( subString() == "exclude:" )
+                        m_exclusion = true;
+                    else
+                        addPattern<TestSpec::NamePattern>();
+                    startNewMode( Tag, ++m_pos );
+                }
+            }
+            else if( m_mode == QuotedName && c == '"' )
+                addPattern<TestSpec::NamePattern>();
+            else if( m_mode == Tag && c == ']' )
+                addPattern<TestSpec::TagPattern>();
+        }
+        void startNewMode( Mode mode, std::size_t start ) {
+            m_mode = mode;
+            m_start = start;
+        }
+        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+        template<typename T>
+        void addPattern() {
+            std::string token = subString();
+            if( startsWith( token, "exclude:" ) ) {
+                m_exclusion = true;
+                token = token.substr( 8 );
+            }
+            if( !token.empty() ) {
+                Ptr<TestSpec::Pattern> pattern = new T( token );
+                if( m_exclusion )
+                    pattern = new TestSpec::ExcludedPattern( pattern );
+                m_currentFilter.m_patterns.push_back( pattern );
+            }
+            m_exclusion = false;
+            m_mode = None;
+        }
+        void addFilter() {
+            if( !m_currentFilter.m_patterns.empty() ) {
+                m_testSpec.m_filters.push_back( m_currentFilter );
+                m_currentFilter = TestSpec::Filter();
+            }
+        }
+    };
+    inline TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct Verbosity { enum Level {
+        NoOutput = 0,
+        Quiet,
+        Normal
+    }; };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01
+    }; };
+
+    struct ShowDurations { enum OrNot {
+        DefaultForReporter,
+        Always,
+        Never
+    }; };
+    struct RunTests { enum InWhatOrder {
+        InDeclarationOrder,
+        InLexicographicalOrder,
+        InRandomOrder
+    }; };
+
+    class TestSpec;
+
+    struct IConfig : IShared {
+
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual std::ostream& stream() const = 0;
+        virtual std::string name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual int abortAfter() const = 0;
+        virtual bool showInvisibles() const = 0;
+        virtual ShowDurations::OrNot showDurations() const = 0;
+        virtual TestSpec const& testSpec() const = 0;
+        virtual RunTests::InWhatOrder runOrder() const = 0;
+        virtual unsigned int rngSeed() const = 0;
+        virtual bool forceColour() const = 0;
+    };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+#include <streambuf>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    class Stream {
+    public:
+        Stream();
+        Stream( std::streambuf* _streamBuf, bool _isOwned );
+        void release();
+
+        std::streambuf* streamBuf;
+
+    private:
+        bool isOwned;
+    };
+
+    std::ostream& cout();
+    std::ostream& cerr();
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <ctime>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+    struct ConfigData {
+
+        ConfigData()
+        :   listTests( false ),
+            listTags( false ),
+            listReporters( false ),
+            listTestNamesOnly( false ),
+            showSuccessfulTests( false ),
+            shouldDebugBreak( false ),
+            noThrow( false ),
+            showHelp( false ),
+            showInvisibles( false ),
+            forceColour( false ),
+            abortAfter( -1 ),
+            rngSeed( 0 ),
+            verbosity( Verbosity::Normal ),
+            warnings( WarnAbout::Nothing ),
+            showDurations( ShowDurations::DefaultForReporter ),
+            runOrder( RunTests::InDeclarationOrder )
+        {}
+
+        bool listTests;
+        bool listTags;
+        bool listReporters;
+        bool listTestNamesOnly;
+
+        bool showSuccessfulTests;
+        bool shouldDebugBreak;
+        bool noThrow;
+        bool showHelp;
+        bool showInvisibles;
+        bool forceColour;
+
+        int abortAfter;
+        unsigned int rngSeed;
+
+        Verbosity::Level verbosity;
+        WarnAbout::What warnings;
+        ShowDurations::OrNot showDurations;
+        RunTests::InWhatOrder runOrder;
+
+        std::string reporterName;
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+
+        std::vector<std::string> testsOrTags;
+    };
+
+    class Config : public SharedImpl<IConfig> {
+    private:
+        Config( Config const& other );
+        Config& operator = ( Config const& other );
+        virtual void dummy();
+    public:
+
+        Config()
+        :   m_os( Catch::cout().rdbuf() )
+        {}
+
+        Config( ConfigData const& data )
+        :   m_data( data ),
+            m_os( Catch::cout().rdbuf() )
+        {
+            if( !data.testsOrTags.empty() ) {
+                TestSpecParser parser( ITagAliasRegistry::get() );
+                for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+                    parser.parse( data.testsOrTags[i] );
+                m_testSpec = parser.testSpec();
+            }
+        }
+
+        virtual ~Config() {
+            m_os.rdbuf( Catch::cout().rdbuf() );
+            m_stream.release();
+        }
+
+        void setFilename( std::string const& filename ) {
+            m_data.outputFilename = filename;
+        }
+
+        std::string const& getFilename() const {
+            return m_data.outputFilename ;
+        }
+
+        bool listTests() const { return m_data.listTests; }
+        bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+        bool listTags() const { return m_data.listTags; }
+        bool listReporters() const { return m_data.listReporters; }
+
+        std::string getProcessName() const { return m_data.processName; }
+
+        bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+
+        void setStreamBuf( std::streambuf* buf ) {
+            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
+        }
+
+        void useStream( std::string const& streamName ) {
+            Stream stream = createStream( streamName );
+            setStreamBuf( stream.streamBuf );
+            m_stream.release();
+            m_stream = stream;
+        }
+
+        std::string getReporterName() const { return m_data.reporterName; }
+
+        int abortAfter() const { return m_data.abortAfter; }
+
+        TestSpec const& testSpec() const { return m_testSpec; }
+
+        bool showHelp() const { return m_data.showHelp; }
+        bool showInvisibles() const { return m_data.showInvisibles; }
+
+        // IConfig interface
+        virtual bool allowThrows() const        { return !m_data.noThrow; }
+        virtual std::ostream& stream() const    { return m_os; }
+        virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
+        virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
+        virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
+        virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
+        virtual RunTests::InWhatOrder runOrder() const  { return m_data.runOrder; }
+        virtual unsigned int rngSeed() const    { return m_data.rngSeed; }
+        virtual bool forceColour() const { return m_data.forceColour; }
+
+    private:
+        ConfigData m_data;
+
+        Stream m_stream;
+        mutable std::ostream m_os;
+        TestSpec m_testSpec;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+        char tabChar;               // If this char is seen the indent is changed to current pos
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            std::string wrappableChars = " [({.,/|\\-";
+            std::size_t indent = _attr.initialIndent != std::string::npos
+                ? _attr.initialIndent
+                : _attr.indent;
+            std::string remainder = _str;
+
+            while( !remainder.empty() ) {
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+                std::size_t tabPos = std::string::npos;
+                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+                std::size_t pos = remainder.find_first_of( '\n' );
+                if( pos <= width ) {
+                    width = pos;
+                }
+                pos = remainder.find_last_of( _attr.tabChar, width );
+                if( pos != std::string::npos ) {
+                    tabPos = pos;
+                    if( remainder[width] == '\n' )
+                        width--;
+                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+                }
+
+                if( width == remainder.size() ) {
+                    spliceLine( indent, remainder, width );
+                }
+                else if( remainder[width] == '\n' ) {
+                    spliceLine( indent, remainder, width );
+                    if( width <= 1 || remainder.size() != 1 )
+                        remainder = remainder.substr( 1 );
+                    indent = _attr.indent;
+                }
+                else {
+                    pos = remainder.find_last_of( wrappableChars, width );
+                    if( pos != std::string::npos && pos > 0 ) {
+                        spliceLine( indent, remainder, pos );
+                        if( remainder[0] == ' ' )
+                            remainder = remainder.substr( 1 );
+                    }
+                    else {
+                        spliceLine( indent, remainder, width-1 );
+                        lines.back() += "-";
+                    }
+                    if( lines.size() == 1 )
+                        indent = _attr.indent;
+                    if( tabPos != std::string::npos )
+                        indent += tabPos;
+                }
+            }
+        }
+
+        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+            _remainder = _remainder.substr( _pos );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <memory>
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+    struct UnpositionalTag {};
+
+    extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+    UnpositionalTag _;
+#endif
+
+    namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+        using namespace Tbc;
+
+        inline bool startsWith( std::string const& str, std::string const& prefix ) {
+            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+        }
+
+        template<typename T> struct RemoveConstRef{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+        template<typename T>    struct IsBool       { static const bool value = false; };
+        template<>              struct IsBool<bool> { static const bool value = true; };
+
+        template<typename T>
+        void convertInto( std::string const& _source, T& _dest ) {
+            std::stringstream ss;
+            ss << _source;
+            ss >> _dest;
+            if( ss.fail() )
+                throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+        }
+        inline void convertInto( std::string const& _source, std::string& _dest ) {
+            _dest = _source;
+        }
+        inline void convertInto( std::string const& _source, bool& _dest ) {
+            std::string sourceLC = _source;
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+            if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+                _dest = true;
+            else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+                _dest = false;
+            else
+                throw std::runtime_error( "Expected a boolean value but did not recognise:\n  '" + _source + "'" );
+        }
+        inline void convertInto( bool _source, bool& _dest ) {
+            _dest = _source;
+        }
+        template<typename T>
+        inline void convertInto( bool, T& ) {
+            throw std::runtime_error( "Invalid conversion" );
+        }
+
+        template<typename ConfigT>
+        struct IArgFunction {
+            virtual ~IArgFunction() {}
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+            IArgFunction()                      = default;
+            IArgFunction( IArgFunction const& ) = default;
+#  endif
+            virtual void set( ConfigT& config, std::string const& value ) const = 0;
+            virtual void setFlag( ConfigT& config ) const = 0;
+            virtual bool takesArg() const = 0;
+            virtual IArgFunction* clone() const = 0;
+        };
+
+        template<typename ConfigT>
+        class BoundArgFunction {
+        public:
+            BoundArgFunction() : functionObj( NULL ) {}
+            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+                delete functionObj;
+                functionObj = newFunctionObj;
+                return *this;
+            }
+            ~BoundArgFunction() { delete functionObj; }
+
+            void set( ConfigT& config, std::string const& value ) const {
+                functionObj->set( config, value );
+            }
+            void setFlag( ConfigT& config ) const {
+                functionObj->setFlag( config );
+            }
+            bool takesArg() const { return functionObj->takesArg(); }
+
+            bool isSet() const {
+                return functionObj != NULL;
+            }
+        private:
+            IArgFunction<ConfigT>* functionObj;
+        };
+
+        template<typename C>
+        struct NullBinder : IArgFunction<C>{
+            virtual void set( C&, std::string const& ) const {}
+            virtual void setFlag( C& ) const {}
+            virtual bool takesArg() const { return true; }
+            virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+        };
+
+        template<typename C, typename M>
+        struct BoundDataMember : IArgFunction<C>{
+            BoundDataMember( M C::* _member ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                convertInto( stringValue, p.*member );
+            }
+            virtual void setFlag( C& p ) const {
+                convertInto( true, p.*member );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+            M C::* member;
+        };
+        template<typename C, typename M>
+        struct BoundUnaryMethod : IArgFunction<C>{
+            BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                typename RemoveConstRef<M>::type value;
+                convertInto( stringValue, value );
+                (p.*member)( value );
+            }
+            virtual void setFlag( C& p ) const {
+                typename RemoveConstRef<M>::type value;
+                convertInto( true, value );
+                (p.*member)( value );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+            void (C::*member)( M );
+        };
+        template<typename C>
+        struct BoundNullaryMethod : IArgFunction<C>{
+            BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    (p.*member)();
+            }
+            virtual void setFlag( C& p ) const {
+                (p.*member)();
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+            void (C::*member)();
+        };
+
+        template<typename C>
+        struct BoundUnaryFunction : IArgFunction<C>{
+            BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    function( obj );
+            }
+            virtual void setFlag( C& p ) const {
+                function( p );
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+            void (*function)( C& );
+        };
+
+        template<typename C, typename T>
+        struct BoundBinaryFunction : IArgFunction<C>{
+            BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                typename RemoveConstRef<T>::type value;
+                convertInto( stringValue, value );
+                function( obj, value );
+            }
+            virtual void setFlag( C& obj ) const {
+                typename RemoveConstRef<T>::type value;
+                convertInto( true, value );
+                function( obj, value );
+            }
+            virtual bool takesArg() const { return !IsBool<T>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+            void (*function)( C&, T );
+        };
+
+    } // namespace Detail
+
+    struct Parser {
+        Parser() : separators( " \t=:" ) {}
+
+        struct Token {
+            enum Type { Positional, ShortOpt, LongOpt };
+            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+            Type type;
+            std::string data;
+        };
+
+        void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+            const std::string doubleDash = "--";
+            for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
+                parseIntoTokens( argv[i] , tokens);
+        }
+        void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
+            while( !arg.empty() ) {
+                Parser::Token token( Parser::Token::Positional, arg );
+                arg = "";
+                if( token.data[0] == '-' ) {
+                    if( token.data.size() > 1 && token.data[1] == '-' ) {
+                        token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
+                    }
+                    else {
+                        token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
+                        if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
+                            arg = "-" + token.data.substr( 1 );
+                            token.data = token.data.substr( 0, 1 );
+                        }
+                    }
+                }
+                if( token.type != Parser::Token::Positional ) {
+                    std::size_t pos = token.data.find_first_of( separators );
+                    if( pos != std::string::npos ) {
+                        arg = token.data.substr( pos+1 );
+                        token.data = token.data.substr( 0, pos );
+                    }
+                }
+                tokens.push_back( token );
+            }
+        }
+        std::string separators;
+    };
+
+    template<typename ConfigT>
+    struct CommonArgProperties {
+        CommonArgProperties() {}
+        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+        Detail::BoundArgFunction<ConfigT> boundField;
+        std::string description;
+        std::string detail;
+        std::string placeholder; // Only value if boundField takes an arg
+
+        bool takesArg() const {
+            return !placeholder.empty();
+        }
+        void validate() const {
+            if( !boundField.isSet() )
+                throw std::logic_error( "option not bound" );
+        }
+    };
+    struct OptionArgProperties {
+        std::vector<std::string> shortNames;
+        std::string longName;
+
+        bool hasShortName( std::string const& shortName ) const {
+            return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+        }
+        bool hasLongName( std::string const& _longName ) const {
+            return _longName == longName;
+        }
+    };
+    struct PositionalArgProperties {
+        PositionalArgProperties() : position( -1 ) {}
+        int position; // -1 means non-positional (floating)
+
+        bool isFixedPositional() const {
+            return position != -1;
+        }
+    };
+
+    template<typename ConfigT>
+    class CommandLine {
+
+        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+            Arg() {}
+            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+            using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+            std::string dbgName() const {
+                if( !longName.empty() )
+                    return "--" + longName;
+                if( !shortNames.empty() )
+                    return "-" + shortNames[0];
+                return "positional args";
+            }
+            std::string commands() const {
+                std::ostringstream oss;
+                bool first = true;
+                std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+                for(; it != itEnd; ++it ) {
+                    if( first )
+                        first = false;
+                    else
+                        oss << ", ";
+                    oss << "-" << *it;
+                }
+                if( !longName.empty() ) {
+                    if( !first )
+                        oss << ", ";
+                    oss << "--" << longName;
+                }
+                if( !placeholder.empty() )
+                    oss << " <" << placeholder << ">";
+                return oss.str();
+            }
+        };
+
+        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
+#if defined(__cplusplus) && __cplusplus > 199711L
+        typedef std::unique_ptr<Arg> ArgAutoPtr;
+#else
+        typedef std::auto_ptr<Arg> ArgAutoPtr;
+#endif
+
+        friend void addOptName( Arg& arg, std::string const& optName )
+        {
+            if( optName.empty() )
+                return;
+            if( Detail::startsWith( optName, "--" ) ) {
+                if( !arg.longName.empty() )
+                    throw std::logic_error( "Only one long opt may be specified. '"
+                        + arg.longName
+                        + "' already specified, now attempting to add '"
+                        + optName + "'" );
+                arg.longName = optName.substr( 2 );
+            }
+            else if( Detail::startsWith( optName, "-" ) )
+                arg.shortNames.push_back( optName.substr( 1 ) );
+            else
+                throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+        }
+        friend void setPositionalArg( Arg& arg, int position )
+        {
+            arg.position = position;
+        }
+
+        class ArgBuilder {
+        public:
+            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+            // Bind a non-boolean data member (requires placeholder string)
+            template<typename C, typename M>
+            void bind( M C::* field, std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+                m_arg->placeholder = placeholder;
+            }
+            // Bind a boolean data member (no placeholder required)
+            template<typename C>
+            void bind( bool C::* field ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+            }
+
+            // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+            template<typename C, typename M>
+            void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+                m_arg->placeholder = placeholder;
+            }
+
+            // Bind a method taking a single, boolean argument (no placeholder string required)
+            template<typename C>
+            void bind( void (C::* unaryMethod)( bool ) ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+            }
+
+            // Bind a method that takes no arguments (will be called if opt is present)
+            template<typename C>
+            void bind( void (C::* nullaryMethod)() ) {
+                m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+            template<typename C>
+            void bind( void (* unaryFunction)( C& ) ) {
+                m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+            template<typename C, typename T>
+            void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+                m_arg->placeholder = placeholder;
+            }
+
+            ArgBuilder& describe( std::string const& description ) {
+                m_arg->description = description;
+                return *this;
+            }
+            ArgBuilder& detail( std::string const& detail ) {
+                m_arg->detail = detail;
+                return *this;
+            }
+
+        protected:
+            Arg* m_arg;
+        };
+
+        class OptBuilder : public ArgBuilder {
+        public:
+            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+            OptBuilder& operator[]( std::string const& optName ) {
+                addOptName( *ArgBuilder::m_arg, optName );
+                return *this;
+            }
+        };
+
+    public:
+
+        CommandLine()
+        :   m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+            m_highestSpecifiedArgPosition( 0 ),
+            m_throwOnUnrecognisedTokens( false )
+        {}
+        CommandLine( CommandLine const& other )
+        :   m_boundProcessName( other.m_boundProcessName ),
+            m_options ( other.m_options ),
+            m_positionalArgs( other.m_positionalArgs ),
+            m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+            m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+        {
+            if( other.m_floatingArg.get() )
+                m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+        }
+
+        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+            m_throwOnUnrecognisedTokens = shouldThrow;
+            return *this;
+        }
+
+        OptBuilder operator[]( std::string const& optName ) {
+            m_options.push_back( Arg() );
+            addOptName( m_options.back(), optName );
+            OptBuilder builder( &m_options.back() );
+            return builder;
+        }
+
+        ArgBuilder operator[]( int position ) {
+            m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+            if( position > m_highestSpecifiedArgPosition )
+                m_highestSpecifiedArgPosition = position;
+            setPositionalArg( m_positionalArgs[position], position );
+            ArgBuilder builder( &m_positionalArgs[position] );
+            return builder;
+        }
+
+        // Invoke this with the _ instance
+        ArgBuilder operator[]( UnpositionalTag ) {
+            if( m_floatingArg.get() )
+                throw std::logic_error( "Only one unpositional argument can be added" );
+            m_floatingArg.reset( new Arg() );
+            ArgBuilder builder( m_floatingArg.get() );
+            return builder;
+        }
+
+        template<typename C, typename M>
+        void bindProcessName( M C::* field ) {
+            m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+        }
+        template<typename C, typename M>
+        void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+            m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+        }
+
+        void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+            typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+            std::size_t maxWidth = 0;
+            for( it = itBegin; it != itEnd; ++it )
+                maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+            for( it = itBegin; it != itEnd; ++it ) {
+                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                                                        .setWidth( maxWidth+indent )
+                                                        .setIndent( indent ) );
+                Detail::Text desc( it->description, Detail::TextAttributes()
+                                                        .setWidth( width - maxWidth - 3 ) );
+
+                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                    os << usageCol;
+
+                    if( i < desc.size() && !desc[i].empty() )
+                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+                            << desc[i];
+                    os << "\n";
+                }
+            }
+        }
+        std::string optUsage() const {
+            std::ostringstream oss;
+            optUsage( oss );
+            return oss.str();
+        }
+
+        void argSynopsis( std::ostream& os ) const {
+            for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+                if( i > 1 )
+                    os << " ";
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+                if( it != m_positionalArgs.end() )
+                    os << "<" << it->second.placeholder << ">";
+                else if( m_floatingArg.get() )
+                    os << "<" << m_floatingArg->placeholder << ">";
+                else
+                    throw std::logic_error( "non consecutive positional arguments with no floating args" );
+            }
+            // !TBD No indication of mandatory args
+            if( m_floatingArg.get() ) {
+                if( m_highestSpecifiedArgPosition > 1 )
+                    os << " ";
+                os << "[<" << m_floatingArg->placeholder << "> ...]";
+            }
+        }
+        std::string argSynopsis() const {
+            std::ostringstream oss;
+            argSynopsis( oss );
+            return oss.str();
+        }
+
+        void usage( std::ostream& os, std::string const& procName ) const {
+            validate();
+            os << "usage:\n  " << procName << " ";
+            argSynopsis( os );
+            if( !m_options.empty() ) {
+                os << " [options]\n\nwhere options are: \n";
+                optUsage( os, 2 );
+            }
+            os << "\n";
+        }
+        std::string usage( std::string const& procName ) const {
+            std::ostringstream oss;
+            usage( oss, procName );
+            return oss.str();
+        }
+
+        ConfigT parse( int argc, char const * const * argv ) const {
+            ConfigT config;
+            parseInto( argc, argv, config );
+            return config;
+        }
+
+        std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+            std::string processName = argv[0];
+            std::size_t lastSlash = processName.find_last_of( "/\\" );
+            if( lastSlash != std::string::npos )
+                processName = processName.substr( lastSlash+1 );
+            m_boundProcessName.set( config, processName );
+            std::vector<Parser::Token> tokens;
+            Parser parser;
+            parser.parseIntoTokens( argc, argv, tokens );
+            return populate( tokens, config );
+        }
+
+        std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            validate();
+            std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+            unusedTokens = populateFixedArgs( unusedTokens, config );
+            unusedTokens = populateFloatingArgs( unusedTokens, config );
+            return unusedTokens;
+        }
+
+        std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            std::vector<std::string> errors;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+                for(; it != itEnd; ++it ) {
+                    Arg const& arg = *it;
+
+                    try {
+                        if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+                            ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+                            if( arg.takesArg() ) {
+                                if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+                                    errors.push_back( "Expected argument to option: " + token.data );
+                                else
+                                    arg.boundField.set( config, tokens[++i].data );
+                            }
+                            else {
+                                arg.boundField.setFlag( config );
+                            }
+                            break;
+                        }
+                    }
+                    catch( std::exception& ex ) {
+                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+                    }
+                }
+                if( it == itEnd ) {
+                    if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+                        unusedTokens.push_back( token );
+                    else if( errors.empty() && m_throwOnUnrecognisedTokens )
+                        errors.push_back( "unrecognised option: " + token.data );
+                }
+            }
+            if( !errors.empty() ) {
+                std::ostringstream oss;
+                for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+                        it != itEnd;
+                        ++it ) {
+                    if( it != errors.begin() )
+                        oss << "\n";
+                    oss << *it;
+                }
+                throw std::runtime_error( oss.str() );
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            int position = 1;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+                if( it != m_positionalArgs.end() )
+                    it->second.boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+                if( token.type == Parser::Token::Positional )
+                    position++;
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            if( !m_floatingArg.get() )
+                return tokens;
+            std::vector<Parser::Token> unusedTokens;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                if( token.type == Parser::Token::Positional )
+                    m_floatingArg->boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+            }
+            return unusedTokens;
+        }
+
+        void validate() const
+        {
+            if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+                throw std::logic_error( "No options or arguments specified" );
+
+            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
+                                                            itEnd = m_options.end();
+                    it != itEnd; ++it )
+                it->validate();
+        }
+
+    private:
+        Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+        std::vector<Arg> m_options;
+        std::map<int, Arg> m_positionalArgs;
+        ArgAutoPtr m_floatingArg;
+        int m_highestSpecifiedArgPosition;
+        bool m_throwOnUnrecognisedTokens;
+    };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+
+namespace Catch {
+
+    inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+    inline void abortAfterX( ConfigData& config, int x ) {
+        if( x < 1 )
+            throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+        config.abortAfter = x;
+    }
+    inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+
+    inline void addWarning( ConfigData& config, std::string const& _warning ) {
+        if( _warning == "NoAssertions" )
+            config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+        else
+            throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
+    }
+    inline void setOrder( ConfigData& config, std::string const& order ) {
+        if( startsWith( "declared", order ) )
+            config.runOrder = RunTests::InDeclarationOrder;
+        else if( startsWith( "lexical", order ) )
+            config.runOrder = RunTests::InLexicographicalOrder;
+        else if( startsWith( "random", order ) )
+            config.runOrder = RunTests::InRandomOrder;
+        else
+            throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
+    }
+    inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+        if( seed == "time" ) {
+            config.rngSeed = static_cast<unsigned int>( std::time(0) );
+        }
+        else {
+            std::stringstream ss;
+            ss << seed;
+            ss >> config.rngSeed;
+            if( ss.fail() )
+                throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
+        }
+    }
+    inline void setVerbosity( ConfigData& config, int level ) {
+        // !TBD: accept strings?
+        config.verbosity = static_cast<Verbosity::Level>( level );
+    }
+    inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+        config.showDurations = _showDurations
+            ? ShowDurations::Always
+            : ShowDurations::Never;
+    }
+    inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+        std::ifstream f( _filename.c_str() );
+        if( !f.is_open() )
+            throw std::domain_error( "Unable to load input file: " + _filename );
+
+        std::string line;
+        while( std::getline( f, line ) ) {
+            line = trim(line);
+            if( !line.empty() && !startsWith( line, "#" ) )
+                addTestOrTags( config, "\"" + line + "\"," );
+        }
+    }
+
+    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+        using namespace Clara;
+        CommandLine<ConfigData> cli;
+
+        cli.bindProcessName( &ConfigData::processName );
+
+        cli["-?"]["-h"]["--help"]
+            .describe( "display usage information" )
+            .bind( &ConfigData::showHelp );
+
+        cli["-l"]["--list-tests"]
+            .describe( "list all/matching test cases" )
+            .bind( &ConfigData::listTests );
+
+        cli["-t"]["--list-tags"]
+            .describe( "list all/matching tags" )
+            .bind( &ConfigData::listTags );
+
+        cli["-s"]["--success"]
+            .describe( "include successful tests in output" )
+            .bind( &ConfigData::showSuccessfulTests );
+
+        cli["-b"]["--break"]
+            .describe( "break into debugger on failure" )
+            .bind( &ConfigData::shouldDebugBreak );
+
+        cli["-e"]["--nothrow"]
+            .describe( "skip exception tests" )
+            .bind( &ConfigData::noThrow );
+
+        cli["-i"]["--invisibles"]
+            .describe( "show invisibles (tabs, newlines)" )
+            .bind( &ConfigData::showInvisibles );
+
+        cli["-o"]["--out"]
+            .describe( "output filename" )
+            .bind( &ConfigData::outputFilename, "filename" );
+
+        cli["-r"]["--reporter"]
+//            .placeholder( "name[:filename]" )
+            .describe( "reporter to use (defaults to console)" )
+            .bind( &ConfigData::reporterName, "name" );
+
+        cli["-n"]["--name"]
+            .describe( "suite name" )
+            .bind( &ConfigData::name, "name" );
+
+        cli["-a"]["--abort"]
+            .describe( "abort at first failure" )
+            .bind( &abortAfterFirst );
+
+        cli["-x"]["--abortx"]
+            .describe( "abort after x failures" )
+            .bind( &abortAfterX, "no. failures" );
+
+        cli["-w"]["--warn"]
+            .describe( "enable warnings" )
+            .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+//        cli.into( &setVerbosity )
+//            .describe( "level of verbosity (0=no output)" )
+//            .shortOpt( "v")
+//            .longOpt( "verbosity" )
+//            .placeholder( "level" );
+
+        cli[_]
+            .describe( "which test or tests to use" )
+            .bind( &addTestOrTags, "test name, pattern or tags" );
+
+        cli["-d"]["--durations"]
+            .describe( "show test durations" )
+            .bind( &setShowDurations, "yes/no" );
+
+        cli["-f"]["--input-file"]
+            .describe( "load test names to run from a file" )
+            .bind( &loadTestNamesFromFile, "filename" );
+
+        // Less common commands which don't have a short form
+        cli["--list-test-names-only"]
+            .describe( "list all/matching test cases names only" )
+            .bind( &ConfigData::listTestNamesOnly );
+
+        cli["--list-reporters"]
+            .describe( "list all reporters" )
+            .bind( &ConfigData::listReporters );
+
+        cli["--order"]
+            .describe( "test case order (defaults to decl)" )
+            .bind( &setOrder, "decl|lex|rand" );
+
+        cli["--rng-seed"]
+            .describe( "set a specific seed for random numbers" )
+            .bind( &setRngSeed, "'time'|number" );
+
+        cli["--force-colour"]
+            .describe( "force colourised output" )
+            .bind( &ConfigData::forceColour );
+
+        return cli;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+#  ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#   define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#  endif
+# else
+#  define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+        char tabChar;               // If this char is seen the indent is changed to current pos
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            std::string wrappableChars = " [({.,/|\\-";
+            std::size_t indent = _attr.initialIndent != std::string::npos
+                ? _attr.initialIndent
+                : _attr.indent;
+            std::string remainder = _str;
+
+            while( !remainder.empty() ) {
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+                std::size_t tabPos = std::string::npos;
+                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+                std::size_t pos = remainder.find_first_of( '\n' );
+                if( pos <= width ) {
+                    width = pos;
+                }
+                pos = remainder.find_last_of( _attr.tabChar, width );
+                if( pos != std::string::npos ) {
+                    tabPos = pos;
+                    if( remainder[width] == '\n' )
+                        width--;
+                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+                }
+
+                if( width == remainder.size() ) {
+                    spliceLine( indent, remainder, width );
+                }
+                else if( remainder[width] == '\n' ) {
+                    spliceLine( indent, remainder, width );
+                    if( width <= 1 || remainder.size() != 1 )
+                        remainder = remainder.substr( 1 );
+                    indent = _attr.indent;
+                }
+                else {
+                    pos = remainder.find_last_of( wrappableChars, width );
+                    if( pos != std::string::npos && pos > 0 ) {
+                        spliceLine( indent, remainder, pos );
+                        if( remainder[0] == ' ' )
+                            remainder = remainder.substr( 1 );
+                    }
+                    else {
+                        spliceLine( indent, remainder, width-1 );
+                        lines.back() += "-";
+                    }
+                    if( lines.size() == 1 )
+                        indent = _attr.indent;
+                    if( tabPos != std::string::npos )
+                        indent += tabPos;
+                }
+            }
+        }
+
+        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+            _remainder = _remainder.substr( _pos );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+    using Tbc::Text;
+    using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = Yellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = Yellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // Use constructed object for RAII guard
+        Colour( Code _colourCode );
+        Colour( Colour const& other );
+        ~Colour();
+
+        // Use static method for one-shot changes
+        static void use( Code _colourCode );
+
+    private:
+        bool m_moved;
+    };
+
+    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+#include <assert.h>
+
+namespace Catch
+{
+    struct ReporterConfig {
+        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+        std::ostream& stream() const    { return *m_stream; }
+        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+
+    private:
+        std::ostream* m_stream;
+        Ptr<IConfig> m_fullConfig;
+    };
+
+    struct ReporterPreferences {
+        ReporterPreferences()
+        : shouldRedirectStdOut( false )
+        {}
+
+        bool shouldRedirectStdOut;
+    };
+
+    template<typename T>
+    struct LazyStat : Option<T> {
+        LazyStat() : used( false ) {}
+        LazyStat& operator=( T const& _value ) {
+            Option<T>::operator=( _value );
+            used = false;
+            return *this;
+        }
+        void reset() {
+            Option<T>::reset();
+            used = false;
+        }
+        bool used;
+    };
+
+    struct TestRunInfo {
+        TestRunInfo( std::string const& _name ) : name( _name ) {}
+        std::string name;
+    };
+    struct GroupInfo {
+        GroupInfo(  std::string const& _name,
+                    std::size_t _groupIndex,
+                    std::size_t _groupsCount )
+        :   name( _name ),
+            groupIndex( _groupIndex ),
+            groupsCounts( _groupsCount )
+        {}
+
+        std::string name;
+        std::size_t groupIndex;
+        std::size_t groupsCounts;
+    };
+
+    struct AssertionStats {
+        AssertionStats( AssertionResult const& _assertionResult,
+                        std::vector<MessageInfo> const& _infoMessages,
+                        Totals const& _totals )
+        :   assertionResult( _assertionResult ),
+            infoMessages( _infoMessages ),
+            totals( _totals )
+        {
+            if( assertionResult.hasMessage() ) {
+                // Copy message into messages list.
+                // !TBD This should have been done earlier, somewhere
+                MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+                builder << assertionResult.getMessage();
+                builder.m_info.message = builder.m_stream.str();
+
+                infoMessages.push_back( builder.m_info );
+            }
+        }
+        virtual ~AssertionStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = default;
+        AssertionStats& operator = ( AssertionStats && )     = default;
+#  endif
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        SectionStats(   SectionInfo const& _sectionInfo,
+                        Counts const& _assertions,
+                        double _durationInSeconds,
+                        bool _missingAssertions )
+        :   sectionInfo( _sectionInfo ),
+            assertions( _assertions ),
+            durationInSeconds( _durationInSeconds ),
+            missingAssertions( _missingAssertions )
+        {}
+        virtual ~SectionStats();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SectionStats( SectionStats const& )              = default;
+        SectionStats( SectionStats && )                  = default;
+        SectionStats& operator = ( SectionStats const& ) = default;
+        SectionStats& operator = ( SectionStats && )     = default;
+#  endif
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct TestCaseStats {
+        TestCaseStats(  TestCaseInfo const& _testInfo,
+                        Totals const& _totals,
+                        std::string const& _stdOut,
+                        std::string const& _stdErr,
+                        bool _aborting )
+        : testInfo( _testInfo ),
+            totals( _totals ),
+            stdOut( _stdOut ),
+            stdErr( _stdErr ),
+            aborting( _aborting )
+        {}
+        virtual ~TestCaseStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestCaseStats( TestCaseStats const& )              = default;
+        TestCaseStats( TestCaseStats && )                  = default;
+        TestCaseStats& operator = ( TestCaseStats const& ) = default;
+        TestCaseStats& operator = ( TestCaseStats && )     = default;
+#  endif
+
+        TestCaseInfo testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    struct TestGroupStats {
+        TestGroupStats( GroupInfo const& _groupInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   groupInfo( _groupInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        TestGroupStats( GroupInfo const& _groupInfo )
+        :   groupInfo( _groupInfo ),
+            aborting( false )
+        {}
+        virtual ~TestGroupStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestGroupStats( TestGroupStats const& )              = default;
+        TestGroupStats( TestGroupStats && )                  = default;
+        TestGroupStats& operator = ( TestGroupStats const& ) = default;
+        TestGroupStats& operator = ( TestGroupStats && )     = default;
+#  endif
+
+        GroupInfo groupInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   runInfo( _runInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        virtual ~TestRunStats();
+
+#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestRunStats( TestRunStats const& _other )
+        :   runInfo( _other.runInfo ),
+            totals( _other.totals ),
+            aborting( _other.aborting )
+        {}
+#  else
+        TestRunStats( TestRunStats const& )              = default;
+        TestRunStats( TestRunStats && )                  = default;
+        TestRunStats& operator = ( TestRunStats const& ) = default;
+        TestRunStats& operator = ( TestRunStats && )     = default;
+#  endif
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct IStreamingReporter : IShared {
+        virtual ~IStreamingReporter();
+
+        // Implementing class must also provide the following static method:
+        // static std::string getDescription();
+
+        virtual ReporterPreferences getPreferences() const = 0;
+
+        virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        // The return value indicates if the messages buffer should be cleared:
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+    };
+
+    struct IReporterFactory {
+        virtual ~IReporterFactory();
+        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+
+    struct IReporterRegistry {
+        typedef std::map<std::string, IReporterFactory*> FactoryMap;
+
+        virtual ~IReporterRegistry();
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+    };
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+    inline std::size_t listTests( Config const& config ) {
+
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Matching test cases:\n";
+        else {
+            Catch::cout() << "All available test cases:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::size_t matchedTests = 0;
+        TextAttributes nameAttr, tagsAttr;
+        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+        tagsAttr.setIndent( 6 );
+
+        std::vector<TestCase> matchedTestCases;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            Colour colourGuard( colour );
+
+            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+        }
+
+        if( !config.testSpec().hasFilters() )
+            Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
+        else
+            Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
+        return matchedTests;
+    }
+
+    inline std::size_t listTestsNamesOnly( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( !config.testSpec().hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        std::size_t matchedTests = 0;
+        std::vector<TestCase> matchedTestCases;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            Catch::cout() << testCaseInfo.name << std::endl;
+        }
+        return matchedTests;
+    }
+
+    struct TagInfo {
+        TagInfo() : count ( 0 ) {}
+        void add( std::string const& spelling ) {
+            ++count;
+            spellings.insert( spelling );
+        }
+        std::string all() const {
+            std::string out;
+            for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+                        it != itEnd;
+                        ++it )
+                out += "[" + *it + "]";
+            return out;
+        }
+        std::set<std::string> spellings;
+        std::size_t count;
+    };
+
+    inline std::size_t listTags( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Tags for matching test cases:\n";
+        else {
+            Catch::cout() << "All available tags:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            for( std::set<std::string>::const_iterator  tagIt = it->getTestCaseInfo().tags.begin(),
+                                                        tagItEnd = it->getTestCaseInfo().tags.end();
+                    tagIt != tagItEnd;
+                    ++tagIt ) {
+                std::string tagName = *tagIt;
+                std::string lcaseTagName = toLower( tagName );
+                std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+                if( countIt == tagCounts.end() )
+                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+                countIt->second.add( tagName );
+            }
+        }
+
+        for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+                                                            countItEnd = tagCounts.end();
+                countIt != countItEnd;
+                ++countIt ) {
+            std::ostringstream oss;
+            oss << "  " << std::setw(2) << countIt->second.count << "  ";
+            Text wrapper( countIt->second.all(), TextAttributes()
+                                                    .setInitialIndent( 0 )
+                                                    .setIndent( oss.str().size() )
+                                                    .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+            Catch::cout() << oss.str() << wrapper << "\n";
+        }
+        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
+        return tagCounts.size();
+    }
+
+    inline std::size_t listReporters( Config const& /*config*/ ) {
+        Catch::cout() << "Available reporters:\n";
+        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+        IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+        std::size_t maxNameLen = 0;
+        for(it = itBegin; it != itEnd; ++it )
+            maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+        for(it = itBegin; it != itEnd; ++it ) {
+            Text wrapper( it->second->getDescription(), TextAttributes()
+                                                        .setInitialIndent( 0 )
+                                                        .setIndent( 7+maxNameLen )
+                                                        .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+            Catch::cout() << "  "
+                    << it->first
+                    << ":"
+                    << std::string( maxNameLen - it->first.size() + 2, ' ' )
+                    << wrapper << "\n";
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    inline Option<std::size_t> list( Config const& config ) {
+        Option<std::size_t> listedCount;
+        if( config.listTests() )
+            listedCount = listedCount.valueOr(0) + listTests( config );
+        if( config.listTestNamesOnly() )
+            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+        if( config.listTags() )
+            listedCount = listedCount.valueOr(0) + listTags( config );
+        if( config.listReporters() )
+            listedCount = listedCount.valueOr(0) + listReporters( config );
+        return listedCount;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_runner_impl.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+
+namespace Catch {
+namespace SectionTracking {
+
+    class TrackedSection {
+
+        typedef std::map<std::string, TrackedSection> TrackedSections;
+
+    public:
+        enum RunState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            Completed
+        };
+
+        TrackedSection( std::string const& name, TrackedSection* parent )
+        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
+        {}
+
+        RunState runState() const { return m_runState; }
+
+        TrackedSection* findChild( std::string const& childName ) {
+            TrackedSections::iterator it = m_children.find( childName );
+            return it != m_children.end()
+                ? &it->second
+                : NULL;
+        }
+        TrackedSection* acquireChild( std::string const& childName ) {
+            if( TrackedSection* child = findChild( childName ) )
+                return child;
+            m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
+            return findChild( childName );
+        }
+        void enter() {
+            if( m_runState == NotStarted )
+                m_runState = Executing;
+        }
+        void leave() {
+            for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
+                    it != itEnd;
+                    ++it )
+                if( it->second.runState() != Completed ) {
+                    m_runState = ExecutingChildren;
+                    return;
+                }
+            m_runState = Completed;
+        }
+        TrackedSection* getParent() {
+            return m_parent;
+        }
+        bool hasChildren() const {
+            return !m_children.empty();
+        }
+
+    private:
+        std::string m_name;
+        RunState m_runState;
+        TrackedSections m_children;
+        TrackedSection* m_parent;
+
+    };
+
+    class TestCaseTracker {
+    public:
+        TestCaseTracker( std::string const& testCaseName )
+        :   m_testCase( testCaseName, NULL ),
+            m_currentSection( &m_testCase ),
+            m_completedASectionThisRun( false )
+        {}
+
+        bool enterSection( std::string const& name ) {
+            TrackedSection* child = m_currentSection->acquireChild( name );
+            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
+                return false;
+
+            m_currentSection = child;
+            m_currentSection->enter();
+            return true;
+        }
+        void leaveSection() {
+            m_currentSection->leave();
+            m_currentSection = m_currentSection->getParent();
+            assert( m_currentSection != NULL );
+            m_completedASectionThisRun = true;
+        }
+
+        bool currentSectionHasChildren() const {
+            return m_currentSection->hasChildren();
+        }
+        bool isCompleted() const {
+            return m_testCase.runState() == TrackedSection::Completed;
+        }
+
+        class Guard {
+        public:
+            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
+                m_tracker.enterTestCase();
+            }
+            ~Guard() {
+                m_tracker.leaveTestCase();
+            }
+        private:
+            Guard( Guard const& );
+            void operator = ( Guard const& );
+            TestCaseTracker& m_tracker;
+        };
+
+    private:
+        void enterTestCase() {
+            m_currentSection = &m_testCase;
+            m_completedASectionThisRun = false;
+            m_testCase.enter();
+        }
+        void leaveTestCase() {
+            m_testCase.leave();
+        }
+
+        TrackedSection m_testCase;
+        TrackedSection* m_currentSection;
+        bool m_completedASectionThisRun;
+    };
+
+} // namespace SectionTracking
+
+using SectionTracking::TestCaseTracker;
+
+} // namespace Catch
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+    // Report the error condition then exit the process
+    inline void fatal( std::string const& message, int exitCode ) {
+        IContext& context = Catch::getCurrentContext();
+        IResultCapture* resultCapture = context.getResultCapture();
+        resultCapture->handleFatalErrorCondition( message );
+
+		if( Catch::alwaysTrue() ) // avoids "no return" warnings
+            exit( exitCode );
+    }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+
+    struct FatalConditionHandler {
+		void reset() {}
+	};
+
+} // namespace Catch
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs { int id; const char* name; };
+    extern SignalDefs signalDefs[];
+    SignalDefs signalDefs[] = {
+            { SIGINT,  "SIGINT - Terminal interrupt signal" },
+            { SIGILL,  "SIGILL - Illegal instruction signal" },
+            { SIGFPE,  "SIGFPE - Floating point error signal" },
+            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+            { SIGTERM, "SIGTERM - Termination request signal" },
+            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+        };
+
+    struct FatalConditionHandler {
+
+        static void handleSignal( int sig ) {
+            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                if( sig == signalDefs[i].id )
+                    fatal( signalDefs[i].name, -sig );
+            fatal( "<unknown signal>", -sig );
+        }
+
+        FatalConditionHandler() : m_isSet( true ) {
+            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                signal( signalDefs[i].id, handleSignal );
+        }
+        ~FatalConditionHandler() {
+            reset();
+        }
+        void reset() {
+            if( m_isSet ) {
+                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                    signal( signalDefs[i].id, SIG_DFL );
+                m_isSet = false;
+            }
+        }
+
+        bool m_isSet;
+    };
+
+} // namespace Catch
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+    class StreamRedirect {
+
+    public:
+        StreamRedirect( std::ostream& stream, std::string& targetString )
+        :   m_stream( stream ),
+            m_prevBuf( stream.rdbuf() ),
+            m_targetString( targetString )
+        {
+            stream.rdbuf( m_oss.rdbuf() );
+        }
+
+        ~StreamRedirect() {
+            m_targetString += m_oss.str();
+            m_stream.rdbuf( m_prevBuf );
+        }
+
+    private:
+        std::ostream& m_stream;
+        std::streambuf* m_prevBuf;
+        std::ostringstream m_oss;
+        std::string& m_targetString;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+        RunContext( RunContext const& );
+        void operator =( RunContext const& );
+
+    public:
+
+        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( config->name() ),
+            m_context( getCurrentMutableContext() ),
+            m_activeTestCase( NULL ),
+            m_config( config ),
+            m_reporter( reporter ),
+            m_prevRunner( m_context.getRunner() ),
+            m_prevResultCapture( m_context.getResultCapture() ),
+            m_prevConfig( m_context.getConfig() )
+        {
+            m_context.setRunner( this );
+            m_context.setConfig( m_config );
+            m_context.setResultCapture( this );
+            m_reporter->testRunStarting( m_runInfo );
+        }
+
+        virtual ~RunContext() {
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+            m_context.setRunner( m_prevRunner );
+            m_context.setConfig( NULL );
+            m_context.setResultCapture( m_prevResultCapture );
+            m_context.setConfig( m_prevConfig );
+        }
+
+        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+        }
+        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+        }
+
+        Totals runTest( TestCase const& testCase ) {
+            Totals prevTotals = m_totals;
+
+            std::string redirectedCout;
+            std::string redirectedCerr;
+
+            TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+            m_reporter->testCaseStarting( testInfo );
+
+            m_activeTestCase = &testCase;
+            m_testCaseTracker = TestCaseTracker( testInfo.name );
+
+            do {
+                do {
+                    runCurrentTest( redirectedCout, redirectedCerr );
+                }
+                while( !m_testCaseTracker->isCompleted() && !aborting() );
+            }
+            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+            Totals deltaTotals = m_totals.delta( prevTotals );
+            m_totals.testCases += deltaTotals.testCases;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        redirectedCout,
+                                                        redirectedCerr,
+                                                        aborting() ) );
+
+            m_activeTestCase = NULL;
+            m_testCaseTracker.reset();
+
+            return deltaTotals;
+        }
+
+        Ptr<IConfig const> config() const {
+            return m_config;
+        }
+
+    private: // IResultCapture
+
+        virtual void assertionEnded( AssertionResult const& result ) {
+            if( result.getResultType() == ResultWas::Ok ) {
+                m_totals.assertions.passed++;
+            }
+            else if( !result.isOk() ) {
+                m_totals.assertions.failed++;
+            }
+
+            if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+                m_messages.clear();
+
+            // Reset working state
+            m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+            m_lastResult = result;
+        }
+
+        virtual bool sectionStarted (
+            SectionInfo const& sectionInfo,
+            Counts& assertions
+        )
+        {
+            std::ostringstream oss;
+            oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
+
+            if( !m_testCaseTracker->enterSection( oss.str() ) )
+                return false;
+
+            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+            m_reporter->sectionStarting( sectionInfo );
+
+            assertions = m_totals.assertions;
+
+            return true;
+        }
+        bool testForMissingAssertions( Counts& assertions ) {
+            if( assertions.total() != 0 ||
+                    !m_config->warnAboutMissingAssertions() ||
+                    m_testCaseTracker->currentSectionHasChildren() )
+                return false;
+            m_totals.assertions.failed++;
+            assertions.failed++;
+            return true;
+        }
+
+        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
+            if( std::uncaught_exception() ) {
+                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
+                return;
+            }
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            m_testCaseTracker->leaveSection();
+
+            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+            m_messages.clear();
+        }
+
+        virtual void pushScopedMessage( MessageInfo const& message ) {
+            m_messages.push_back( message );
+        }
+
+        virtual void popScopedMessage( MessageInfo const& message ) {
+            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+        }
+
+        virtual std::string getCurrentTestName() const {
+            return m_activeTestCase
+                ? m_activeTestCase->getTestCaseInfo().name
+                : "";
+        }
+
+        virtual const AssertionResult* getLastResult() const {
+            return &m_lastResult;
+        }
+
+        virtual void handleFatalErrorCondition( std::string const& message ) {
+            ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
+            resultBuilder.setResultType( ResultWas::FatalErrorCondition );
+            resultBuilder << message;
+            resultBuilder.captureExpression();
+
+            handleUnfinishedSections();
+
+            // Recreate section for test case (as we will lose the one that was in scope)
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+            Counts assertions;
+            assertions.failed = 1;
+            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+            m_reporter->sectionEnded( testCaseSectionStats );
+
+            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+            Totals deltaTotals;
+            deltaTotals.testCases.failed = 1;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        "",
+                                                        "",
+                                                        false ) );
+            m_totals.testCases.failed++;
+            testGroupEnded( "", m_totals, 1, 1 );
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+        }
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const {
+            return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+        }
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+            m_reporter->sectionStarting( testCaseSection );
+            Counts prevAssertions = m_totals.assertions;
+            double duration = 0;
+            try {
+                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+                TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+                Timer timer;
+                timer.start();
+                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+                    StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+                    invokeActiveTestCase();
+                }
+                else {
+                    invokeActiveTestCase();
+                }
+                duration = timer.getElapsedSeconds();
+            }
+            catch( TestFailureException& ) {
+                // This just means the test was aborted due to failure
+            }
+            catch(...) {
+                makeUnexpectedResultBuilder().useActiveException();
+            }
+            handleUnfinishedSections();
+            m_messages.clear();
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( testCaseInfo.okToFail() ) {
+                std::swap( assertions.failedButOk, assertions.failed );
+                m_totals.assertions.failed -= assertions.failedButOk;
+                m_totals.assertions.failedButOk += assertions.failedButOk;
+            }
+
+            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+            m_reporter->sectionEnded( testCaseSectionStats );
+        }
+
+        void invokeActiveTestCase() {
+            FatalConditionHandler fatalConditionHandler; // Handle signals
+            m_activeTestCase->invoke();
+            fatalConditionHandler.reset();
+        }
+
+    private:
+
+        ResultBuilder makeUnexpectedResultBuilder() const {
+            return ResultBuilder(   m_lastAssertionInfo.macroName.c_str(),
+                                    m_lastAssertionInfo.lineInfo,
+                                    m_lastAssertionInfo.capturedExpression.c_str(),
+                                    m_lastAssertionInfo.resultDisposition );
+        }
+
+        void handleUnfinishedSections() {
+            // If sections ended prematurely due to an exception we stored their
+            // infos here so we can tear them down outside the unwind process.
+            for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+                        itEnd = m_unfinishedSections.rend();
+                    it != itEnd;
+                    ++it )
+                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+            m_unfinishedSections.clear();
+        }
+
+        struct UnfinishedSections {
+            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
+            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+            {}
+
+            SectionInfo info;
+            Counts prevAssertions;
+            double durationInSeconds;
+        };
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase;
+        Option<TestCaseTracker> m_testCaseTracker;
+        AssertionResult m_lastResult;
+
+        Ptr<IConfig const> m_config;
+        Totals m_totals;
+        Ptr<IStreamingReporter> m_reporter;
+        std::vector<MessageInfo> m_messages;
+        IRunner* m_prevRunner;
+        IResultCapture* m_prevResultCapture;
+        Ptr<IConfig const> m_prevConfig;
+        AssertionInfo m_lastAssertionInfo;
+        std::vector<UnfinishedSections> m_unfinishedSections;
+    };
+
+    IResultCapture& getResultCapture() {
+        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+            return *capture;
+        else
+            throw std::logic_error( "No result capture instance" );
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _buildNumber,
+                    char const* const _branchName )
+        :   majorVersion( _majorVersion ),
+            minorVersion( _minorVersion ),
+            buildNumber( _buildNumber ),
+            branchName( _branchName )
+        {}
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const buildNumber;
+        char const* const branchName;
+
+    private:
+        void operator=( Version const& );
+    };
+
+    extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+    class Runner {
+
+    public:
+        Runner( Ptr<Config> const& config )
+        :   m_config( config )
+        {
+            openStream();
+            makeReporter();
+        }
+
+        Totals runTests() {
+
+            RunContext context( m_config.get(), m_reporter );
+
+            Totals totals;
+
+            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+
+            TestSpec testSpec = m_config->testSpec();
+            if( !testSpec.hasFilters() )
+                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+            std::vector<TestCase> testCases;
+            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+
+            int testsRunForGroup = 0;
+            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                    it != itEnd;
+                    ++it ) {
+                testsRunForGroup++;
+                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+
+                    if( context.aborting() )
+                        break;
+
+                    totals += context.runTest( *it );
+                    m_testsAlreadyRun.insert( *it );
+                }
+            }
+            std::vector<TestCase> skippedTestCases;
+            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+
+            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
+                    it != itEnd;
+                    ++it )
+                m_reporter->skipTest( *it );
+
+            context.testGroupEnded( "all tests", totals, 1, 1 );
+            return totals;
+        }
+
+    private:
+        void openStream() {
+            // Open output file, if specified
+            if( !m_config->getFilename().empty() ) {
+                m_ofs.open( m_config->getFilename().c_str() );
+                if( m_ofs.fail() ) {
+                    std::ostringstream oss;
+                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
+                    throw std::domain_error( oss.str() );
+                }
+                m_config->setStreamBuf( m_ofs.rdbuf() );
+            }
+        }
+        void makeReporter() {
+            std::string reporterName = m_config->getReporterName().empty()
+                ? "console"
+                : m_config->getReporterName();
+
+            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
+            if( !m_reporter ) {
+                std::ostringstream oss;
+                oss << "No reporter registered with name: '" << reporterName << "'";
+                throw std::domain_error( oss.str() );
+            }
+        }
+
+    private:
+        Ptr<Config> m_config;
+        std::ofstream m_ofs;
+        Ptr<IStreamingReporter> m_reporter;
+        std::set<TestCase> m_testsAlreadyRun;
+    };
+
+    class Session : NonCopyable {
+        static bool alreadyInstantiated;
+
+    public:
+
+        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+        Session()
+        : m_cli( makeCommandLineParser() ) {
+            if( alreadyInstantiated ) {
+                std::string msg = "Only one instance of Catch::Session can ever be used";
+                Catch::cerr() << msg << std::endl;
+                throw std::logic_error( msg );
+            }
+            alreadyInstantiated = true;
+        }
+        ~Session() {
+            Catch::cleanUp();
+        }
+
+        void showHelp( std::string const& processName ) {
+            Catch::cout() << "\nCatch v"    << libraryVersion.majorVersion << "."
+                                        << libraryVersion.minorVersion << " build "
+                                        << libraryVersion.buildNumber;
+            if( libraryVersion.branchName != std::string( "master" ) )
+                Catch::cout() << " (" << libraryVersion.branchName << " branch)";
+            Catch::cout() << "\n";
+
+            m_cli.usage( Catch::cout(), processName );
+            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+        }
+
+        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+            try {
+                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+                m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+                if( m_configData.showHelp )
+                    showHelp( m_configData.processName );
+                m_config.reset();
+            }
+            catch( std::exception& ex ) {
+                {
+                    Colour colourGuard( Colour::Red );
+                    Catch::cerr()   << "\nError(s) in input:\n"
+                                << Text( ex.what(), TextAttributes().setIndent(2) )
+                                << "\n\n";
+                }
+                m_cli.usage( Catch::cout(), m_configData.processName );
+                return (std::numeric_limits<int>::max)();
+            }
+            return 0;
+        }
+
+        void useConfigData( ConfigData const& _configData ) {
+            m_configData = _configData;
+            m_config.reset();
+        }
+
+        int run( int argc, char* const argv[] ) {
+
+            int returnCode = applyCommandLine( argc, argv );
+            if( returnCode == 0 )
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run() {
+            if( m_configData.showHelp )
+                return 0;
+
+            try
+            {
+                config(); // Force config to be constructed
+
+                std::srand( m_configData.rngSeed );
+
+                Runner runner( m_config );
+
+                // Handle list request
+                if( Option<std::size_t> listed = list( config() ) )
+                    return static_cast<int>( *listed );
+
+                return static_cast<int>( runner.runTests().assertions.failed );
+            }
+            catch( std::exception& ex ) {
+                Catch::cerr() << ex.what() << std::endl;
+                return (std::numeric_limits<int>::max)();
+            }
+        }
+
+        Clara::CommandLine<ConfigData> const& cli() const {
+            return m_cli;
+        }
+        std::vector<Clara::Parser::Token> const& unusedTokens() const {
+            return m_unusedTokens;
+        }
+        ConfigData& configData() {
+            return m_configData;
+        }
+        Config& config() {
+            if( !m_config )
+                m_config = new Config( m_configData );
+            return *m_config;
+        }
+
+    private:
+        Clara::CommandLine<ConfigData> m_cli;
+        std::vector<Clara::Parser::Token> m_unusedTokens;
+        ConfigData m_configData;
+        Ptr<Config> m_config;
+    };
+
+    bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+
+namespace Catch {
+
+    class TestRegistry : public ITestCaseRegistry {
+        struct LexSort {
+            bool operator() (TestCase i,TestCase j) const { return (i<j);}
+        };
+        struct RandomNumberGenerator {
+            int operator()( int n ) const { return std::rand() % n; }
+        };
+
+    public:
+        TestRegistry() : m_unnamedCount( 0 ) {}
+        virtual ~TestRegistry();
+
+        virtual void registerTest( TestCase const& testCase ) {
+            std::string name = testCase.getTestCaseInfo().name;
+            if( name == "" ) {
+                std::ostringstream oss;
+                oss << "Anonymous test case " << ++m_unnamedCount;
+                return registerTest( testCase.withName( oss.str() ) );
+            }
+
+            if( m_functions.find( testCase ) == m_functions.end() ) {
+                m_functions.insert( testCase );
+                m_functionsInOrder.push_back( testCase );
+                if( !testCase.isHidden() )
+                    m_nonHiddenFunctions.push_back( testCase );
+            }
+            else {
+                TestCase const& prev = *m_functions.find( testCase );
+                {
+                    Colour colourGuard( Colour::Red );
+                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
+                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
+                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
+                }
+                exit(1);
+            }
+        }
+
+        virtual std::vector<TestCase> const& getAllTests() const {
+            return m_functionsInOrder;
+        }
+
+        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
+            return m_nonHiddenFunctions;
+        }
+
+        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
+
+            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
+                                                        itEnd = m_functionsInOrder.end();
+                    it != itEnd;
+                    ++it ) {
+                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
+                if( includeTest != negated )
+                    matchingTestCases.push_back( *it );
+            }
+            sortTests( config, matchingTestCases );
+        }
+
+    private:
+
+        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
+
+            switch( config.runOrder() ) {
+                case RunTests::InLexicographicalOrder:
+                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
+                    break;
+                case RunTests::InRandomOrder:
+                {
+                    RandomNumberGenerator rng;
+                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
+                }
+                    break;
+                case RunTests::InDeclarationOrder:
+                    // already in declaration order
+                    break;
+            }
+        }
+        std::set<TestCase> m_functions;
+        std::vector<TestCase> m_functionsInOrder;
+        std::vector<TestCase> m_nonHiddenFunctions;
+        size_t m_unnamedCount;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+    public:
+
+        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+        virtual void invoke() const {
+            m_fun();
+        }
+
+    private:
+        virtual ~FreeFunctionTestCase();
+
+        TestFunction m_fun;
+    };
+
+    inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+        std::string className = classOrQualifiedMethodName;
+        if( startsWith( className, "&" ) )
+        {
+            std::size_t lastColons = className.rfind( "::" );
+            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+            if( penultimateColons == std::string::npos )
+                penultimateColons = 1;
+            className = className.substr( penultimateColons, lastColons-penultimateColons );
+        }
+        return className;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    AutoReg::AutoReg(   TestFunction function,
+                        SourceLineInfo const& lineInfo,
+                        NameAndDesc const& nameAndDesc ) {
+        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+    }
+
+    AutoReg::~AutoReg() {}
+
+    void AutoReg::registerTestCase( ITestCase* testCase,
+                                    char const* classOrQualifiedMethodName,
+                                    NameAndDesc const& nameAndDesc,
+                                    SourceLineInfo const& lineInfo ) {
+
+        getMutableRegistryHub().registerTest
+            ( makeTestCase( testCase,
+                            extractClassName( classOrQualifiedMethodName ),
+                            nameAndDesc.name,
+                            nameAndDesc.description,
+                            lineInfo ) );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        virtual ~ReporterRegistry() {
+            deleteAllValues( m_factories );
+        }
+
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+            FactoryMap::const_iterator it =  m_factories.find( name );
+            if( it == m_factories.end() )
+                return NULL;
+            return it->second->create( ReporterConfig( config ) );
+        }
+
+        void registerReporter( std::string const& name, IReporterFactory* factory ) {
+            m_factories.insert( std::make_pair( name, factory ) );
+        }
+
+        FactoryMap const& getFactories() const {
+            return m_factories;
+        }
+
+    private:
+        FactoryMap m_factories;
+    };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry() {
+            deleteAll( m_translators );
+        }
+
+        virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            m_translators.push_back( translator );
+        }
+
+        virtual std::string translateActiveException() const {
+            try {
+#ifdef __OBJC__
+                // In Objective-C try objective-c exceptions first
+                @try {
+                    throw;
+                }
+                @catch (NSException *exception) {
+                    return Catch::toString( [exception description] );
+                }
+#else
+                throw;
+#endif
+            }
+            catch( TestFailureException& ) {
+                throw;
+            }
+            catch( std::exception& ex ) {
+                return ex.what();
+            }
+            catch( std::string& msg ) {
+                return msg;
+            }
+            catch( const char* msg ) {
+                return msg;
+            }
+            catch(...) {
+                return tryTranslators( m_translators.begin() );
+            }
+        }
+
+        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
+            if( it == m_translators.end() )
+                return "Unknown exception";
+
+            try {
+                return (*it)->translate();
+            }
+            catch(...) {
+                return tryTranslators( it+1 );
+            }
+        }
+
+    private:
+        std::vector<const IExceptionTranslator*> m_translators;
+    };
+}
+
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+            RegistryHub( RegistryHub const& );
+            void operator=( RegistryHub const& );
+
+        public: // IRegistryHub
+            RegistryHub() {
+            }
+            virtual IReporterRegistry const& getReporterRegistry() const {
+                return m_reporterRegistry;
+            }
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+                return m_testCaseRegistry;
+            }
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+                return m_exceptionTranslatorRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            virtual void registerTranslator( const IExceptionTranslator* translator ) {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+
+        private:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+        };
+
+        // Single, global, instance
+        inline RegistryHub*& getTheRegistryHub() {
+            static RegistryHub* theRegistryHub = NULL;
+            if( !theRegistryHub )
+                theRegistryHub = new RegistryHub();
+            return theRegistryHub;
+        }
+    }
+
+    IRegistryHub& getRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    void cleanUp() {
+        delete getTheRegistryHub();
+        getTheRegistryHub() = NULL;
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <ostream>
+
+namespace Catch {
+
+    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+    :   m_lineInfo( lineInfo ) {
+        std::ostringstream oss;
+        oss << lineInfo << ": function ";
+        oss << "not implemented";
+        m_what = oss.str();
+    }
+
+    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+        return m_what.c_str();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+    class StreamBufBase : public std::streambuf {
+    public:
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+    template<typename WriterF, size_t bufferSize=256>
+    class StreamBufImpl : public StreamBufBase {
+        char data[bufferSize];
+        WriterF m_writer;
+
+    public:
+        StreamBufImpl() {
+            setp( data, data + sizeof(data) );
+        }
+
+        ~StreamBufImpl() CATCH_NOEXCEPT {
+            sync();
+        }
+
+    private:
+        int overflow( int c ) {
+            sync();
+
+            if( c != EOF ) {
+                if( pbase() == epptr() )
+                    m_writer( std::string( 1, static_cast<char>( c ) ) );
+                else
+                    sputc( static_cast<char>( c ) );
+            }
+            return 0;
+        }
+
+        int sync() {
+            if( pbase() != pptr() ) {
+                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                setp( pbase(), epptr() );
+            }
+            return 0;
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    struct OutputDebugWriter {
+
+        void operator()( std::string const&str ) {
+            writeToDebugConsole( str );
+        }
+    };
+
+    Stream::Stream()
+    : streamBuf( NULL ), isOwned( false )
+    {}
+
+    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
+    : streamBuf( _streamBuf ), isOwned( _isOwned )
+    {}
+
+    void Stream::release() {
+        if( isOwned ) {
+            delete streamBuf;
+            streamBuf = NULL;
+            isOwned = false;
+        }
+    }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+    std::ostream& cout() {
+        return std::cout;
+    }
+    std::ostream& cerr() {
+        return std::cerr;
+    }
+#endif
+}
+
+namespace Catch {
+
+    class Context : public IMutableContext {
+
+        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+        Context( Context const& );
+        void operator=( Context const& );
+
+    public: // IContext
+        virtual IResultCapture* getResultCapture() {
+            return m_resultCapture;
+        }
+        virtual IRunner* getRunner() {
+            return m_runner;
+        }
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+            return getGeneratorsForCurrentTest()
+            .getGeneratorInfo( fileInfo, totalSize )
+            .getCurrentIndex();
+        }
+        virtual bool advanceGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            return generators && generators->moveNext();
+        }
+
+        virtual Ptr<IConfig const> getConfig() const {
+            return m_config;
+        }
+
+    public: // IMutableContext
+        virtual void setResultCapture( IResultCapture* resultCapture ) {
+            m_resultCapture = resultCapture;
+        }
+        virtual void setRunner( IRunner* runner ) {
+            m_runner = runner;
+        }
+        virtual void setConfig( Ptr<IConfig const> const& config ) {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IGeneratorsForTest* findGeneratorsForCurrentTest() {
+            std::string testName = getResultCapture()->getCurrentTestName();
+
+            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+                m_generatorsByTestName.find( testName );
+            return it != m_generatorsByTestName.end()
+                ? it->second
+                : NULL;
+        }
+
+        IGeneratorsForTest& getGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            if( !generators ) {
+                std::string testName = getResultCapture()->getCurrentTestName();
+                generators = createGeneratorsForTest();
+                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+            }
+            return *generators;
+        }
+
+    private:
+        Ptr<IConfig const> m_config;
+        IRunner* m_runner;
+        IResultCapture* m_resultCapture;
+        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+    };
+
+    namespace {
+        Context* currentContext = NULL;
+    }
+    IMutableContext& getCurrentMutableContext() {
+        if( !currentContext )
+            currentContext = new Context();
+        return *currentContext;
+    }
+    IContext& getCurrentContext() {
+        return getCurrentMutableContext();
+    }
+
+    Stream createStream( std::string const& streamName ) {
+        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
+        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
+        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
+
+        throw std::domain_error( "Unknown stream: " + streamName );
+    }
+
+    void cleanUpContext() {
+        delete currentContext;
+        currentContext = NULL;
+    }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch {
+    namespace {
+
+        struct IColourImpl {
+            virtual ~IColourImpl() {}
+            virtual void use( Colour::Code _colourCode ) = 0;
+        };
+
+        struct NoColourImpl : IColourImpl {
+            void use( Colour::Code ) {}
+
+            static IColourImpl* instance() {
+                static NoColourImpl s_instance;
+                return &s_instance;
+            }
+        };
+
+    } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+#   ifdef CATCH_PLATFORM_WINDOWS
+#       define CATCH_CONFIG_COLOUR_WINDOWS
+#   else
+#       define CATCH_CONFIG_COLOUR_ANSI
+#   endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalAttributes = csbiInfo.wAttributes;
+        }
+
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalAttributes );
+                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+        }
+        HANDLE stdoutHandle;
+        WORD originalAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+        return &s_instance;
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+    // use POSIX/ ANSI console terminal codes
+    // Thanks to Adam Strzelecki for original contribution
+    // (http://github.com/nanoant)
+    // https://github.com/philsquared/Catch/pull/131
+    class PosixColourImpl : public IColourImpl {
+    public:
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0:34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            Catch::cout() << '\033' << _escapeCode;
+        }
+    };
+
+    IColourImpl* platformColourInstance() {
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+            ? PosixColourImpl::instance()
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else  // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+    Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+    Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+    Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = isDebuggerActive()
+            ? NoColourImpl::instance()
+            : platformColourInstance();
+        impl->use( _colourCode );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+    struct GeneratorInfo : IGeneratorInfo {
+
+        GeneratorInfo( std::size_t size )
+        :   m_size( size ),
+            m_currentIndex( 0 )
+        {}
+
+        bool moveNext() {
+            if( ++m_currentIndex == m_size ) {
+                m_currentIndex = 0;
+                return false;
+            }
+            return true;
+        }
+
+        std::size_t getCurrentIndex() const {
+            return m_currentIndex;
+        }
+
+        std::size_t m_size;
+        std::size_t m_currentIndex;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class GeneratorsForTest : public IGeneratorsForTest {
+
+    public:
+        ~GeneratorsForTest() {
+            deleteAll( m_generatorsInOrder );
+        }
+
+        IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+            std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+            if( it == m_generatorsByName.end() ) {
+                IGeneratorInfo* info = new GeneratorInfo( size );
+                m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+                m_generatorsInOrder.push_back( info );
+                return *info;
+            }
+            return *it->second;
+        }
+
+        bool moveNext() {
+            std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+            std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+            for(; it != itEnd; ++it ) {
+                if( (*it)->moveNext() )
+                    return true;
+            }
+            return false;
+        }
+
+    private:
+        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+        std::vector<IGeneratorInfo*> m_generatorsInOrder;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest()
+    {
+        return new GeneratorsForTest();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+    AssertionInfo::AssertionInfo(   std::string const& _macroName,
+                                    SourceLineInfo const& _lineInfo,
+                                    std::string const& _capturedExpression,
+                                    ResultDisposition::Flags _resultDisposition )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        capturedExpression( _capturedExpression ),
+        resultDisposition( _resultDisposition )
+    {}
+
+    AssertionResult::AssertionResult() {}
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    AssertionResult::~AssertionResult() {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return !m_info.capturedExpression.empty();
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string AssertionResult::getExpression() const {
+        if( isFalseTest( m_info.resultDisposition ) )
+            return "!" + m_info.capturedExpression;
+        else
+            return m_info.capturedExpression;
+    }
+    std::string AssertionResult::getExpressionInMacro() const {
+        if( m_info.macroName.empty() )
+            return m_info.capturedExpression;
+        else
+            return m_info.macroName + "( " + m_info.capturedExpression + " )";
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        return m_resultData.reconstructedExpression;
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    std::string AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+namespace Catch {
+
+    inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+        if( startsWith( tag, "." ) ||
+            tag == "hide" ||
+            tag == "!hide" )
+            return TestCaseInfo::IsHidden;
+        else if( tag == "!throws" )
+            return TestCaseInfo::Throws;
+        else if( tag == "!shouldfail" )
+            return TestCaseInfo::ShouldFail;
+        else if( tag == "!mayfail" )
+            return TestCaseInfo::MayFail;
+        else
+            return TestCaseInfo::None;
+    }
+    inline bool isReservedTag( std::string const& tag ) {
+        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+    }
+    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+        if( isReservedTag( tag ) ) {
+            {
+                Colour colourGuard( Colour::Red );
+                Catch::cerr()
+                    << "Tag name [" << tag << "] not allowed.\n"
+                    << "Tag names starting with non alpha-numeric characters are reserved\n";
+            }
+            {
+                Colour colourGuard( Colour::FileName );
+                Catch::cerr() << _lineInfo << std::endl;
+            }
+            exit(1);
+        }
+    }
+
+    TestCase makeTestCase(  ITestCase* _testCase,
+                            std::string const& _className,
+                            std::string const& _name,
+                            std::string const& _descOrTags,
+                            SourceLineInfo const& _lineInfo )
+    {
+        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+        // Parse out tags
+        std::set<std::string> tags;
+        std::string desc, tag;
+        bool inTag = false;
+        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+            char c = _descOrTags[i];
+            if( !inTag ) {
+                if( c == '[' )
+                    inTag = true;
+                else
+                    desc += c;
+            }
+            else {
+                if( c == ']' ) {
+                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+                    if( prop == TestCaseInfo::IsHidden )
+                        isHidden = true;
+                    else if( prop == TestCaseInfo::None )
+                        enforceNotReservedTag( tag, _lineInfo );
+
+                    tags.insert( tag );
+                    tag.clear();
+                    inTag = false;
+                }
+                else
+                    tag += c;
+            }
+        }
+        if( isHidden ) {
+            tags.insert( "hide" );
+            tags.insert( "." );
+        }
+
+        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, info );
+    }
+
+    TestCaseInfo::TestCaseInfo( std::string const& _name,
+                                std::string const& _className,
+                                std::string const& _description,
+                                std::set<std::string> const& _tags,
+                                SourceLineInfo const& _lineInfo )
+    :   name( _name ),
+        className( _className ),
+        description( _description ),
+        tags( _tags ),
+        lineInfo( _lineInfo ),
+        properties( None )
+    {
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
+            oss << "[" << *it << "]";
+            std::string lcaseTag = toLower( *it );
+            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
+            lcaseTags.insert( lcaseTag );
+        }
+        tagsAsString = oss.str();
+    }
+
+    TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+    :   name( other.name ),
+        className( other.className ),
+        description( other.description ),
+        tags( other.tags ),
+        lcaseTags( other.lcaseTags ),
+        tagsAsString( other.tagsAsString ),
+        lineInfo( other.lineInfo ),
+        properties( other.properties )
+    {}
+
+    bool TestCaseInfo::isHidden() const {
+        return ( properties & IsHidden ) != 0;
+    }
+    bool TestCaseInfo::throws() const {
+        return ( properties & Throws ) != 0;
+    }
+    bool TestCaseInfo::okToFail() const {
+        return ( properties & (ShouldFail | MayFail ) ) != 0;
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return ( properties & (ShouldFail ) ) != 0;
+    }
+
+    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+    TestCase::TestCase( TestCase const& other )
+    :   TestCaseInfo( other ),
+        test( other.test )
+    {}
+
+    TestCase TestCase::withName( std::string const& _newName ) const {
+        TestCase other( *this );
+        other.name = _newName;
+        return other;
+    }
+
+    void TestCase::swap( TestCase& other ) {
+        test.swap( other.test );
+        name.swap( other.name );
+        className.swap( other.className );
+        description.swap( other.description );
+        tags.swap( other.tags );
+        lcaseTags.swap( other.lcaseTags );
+        tagsAsString.swap( other.tagsAsString );
+        std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+        std::swap( lineInfo, other.lineInfo );
+    }
+
+    void TestCase::invoke() const {
+        test->invoke();
+    }
+
+    bool TestCase::operator == ( TestCase const& other ) const {
+        return  test.get() == other.test.get() &&
+                name == other.name &&
+                className == other.className;
+    }
+
+    bool TestCase::operator < ( TestCase const& other ) const {
+        return name < other.name;
+    }
+    TestCase& TestCase::operator = ( TestCase const& other ) {
+        TestCase temp( other );
+        swap( temp );
+        return *this;
+    }
+
+    TestCaseInfo const& TestCase::getTestCaseInfo() const
+    {
+        return *this;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+    // These numbers are maintained by a script
+    Version libraryVersion( 1, 1, 3, "master" );
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   std::string const& _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+    : m_info( builder.m_info )
+    {
+        m_info.message = builder.m_stream.str();
+        getResultCapture().pushScopedMessage( m_info );
+    }
+    ScopedMessage::ScopedMessage( ScopedMessage const& other )
+    : m_info( other.m_info )
+    {}
+
+    ScopedMessage::~ScopedMessage() {
+        getResultCapture().popScopedMessage( m_info );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+    // Deprecated
+    struct IReporter : IShared {
+        virtual ~IReporter();
+
+        virtual bool shouldRedirectStdout() const = 0;
+
+        virtual void StartTesting() = 0;
+        virtual void EndTesting( Totals const& totals ) = 0;
+        virtual void StartGroup( std::string const& groupName ) = 0;
+        virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+        virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+        virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+        virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+        virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+        virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+        virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+        virtual void Aborted() = 0;
+        virtual void Result( AssertionResult const& result ) = 0;
+    };
+
+    class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+    {
+    public:
+        LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+        virtual ~LegacyReporterAdapter();
+
+        virtual ReporterPreferences getPreferences() const;
+        virtual void noMatchingTestCases( std::string const& );
+        virtual void testRunStarting( TestRunInfo const& );
+        virtual void testGroupStarting( GroupInfo const& groupInfo );
+        virtual void testCaseStarting( TestCaseInfo const& testInfo );
+        virtual void sectionStarting( SectionInfo const& sectionInfo );
+        virtual void assertionStarting( AssertionInfo const& );
+        virtual bool assertionEnded( AssertionStats const& assertionStats );
+        virtual void sectionEnded( SectionStats const& sectionStats );
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+        virtual void testRunEnded( TestRunStats const& testRunStats );
+        virtual void skipTest( TestCaseInfo const& );
+
+    private:
+        Ptr<IReporter> m_legacyReporter;
+    };
+}
+
+namespace Catch
+{
+    LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+    :   m_legacyReporter( legacyReporter )
+    {}
+    LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+    ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+        ReporterPreferences prefs;
+        prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+        return prefs;
+    }
+
+    void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+    void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+        m_legacyReporter->StartTesting();
+    }
+    void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+        m_legacyReporter->StartGroup( groupInfo.name );
+    }
+    void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        m_legacyReporter->StartTestCase( testInfo );
+    }
+    void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+        m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+    }
+    void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+        // Not on legacy interface
+    }
+
+    bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+            for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                    it != itEnd;
+                    ++it ) {
+                if( it->type == ResultWas::Info ) {
+                    ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+                    rb << it->message;
+                    rb.setResultType( ResultWas::Info );
+                    AssertionResult result = rb.build();
+                    m_legacyReporter->Result( result );
+                }
+            }
+        }
+        m_legacyReporter->Result( assertionStats.assertionResult );
+        return true;
+    }
+    void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+        if( sectionStats.missingAssertions )
+            m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+        m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+    }
+    void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        m_legacyReporter->EndTestCase
+            (   testCaseStats.testInfo,
+                testCaseStats.totals,
+                testCaseStats.stdOut,
+                testCaseStats.stdErr );
+    }
+    void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        if( testGroupStats.aborting )
+            m_legacyReporter->Aborted();
+        m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+    }
+    void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+        m_legacyReporter->EndTesting( testRunStats.totals );
+    }
+    void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+    }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace Catch {
+
+    namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+        uint64_t getCurrentTicks() {
+            static uint64_t hz=0, hzo=0;
+            if (!hz) {
+                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+            }
+            uint64_t t;
+            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+            return ((t-hzo)*1000000)/hz;
+        }
+#else
+        uint64_t getCurrentTicks() {
+            timeval t;
+            gettimeofday(&t,NULL);
+            return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
+        }
+#endif
+    }
+
+    void Timer::start() {
+        m_ticks = getCurrentTicks();
+    }
+    unsigned int Timer::getElapsedMicroseconds() const {
+        return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+    }
+    unsigned int Timer::getElapsedMilliseconds() const {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    double Timer::getElapsedSeconds() const {
+        return getElapsedMicroseconds()/1000000.0;
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), ::tolower );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
+    }
+
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+        bool replaced = false;
+        std::size_t i = str.find( replaceThis );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        return replaced;
+    }
+
+    pluralise::pluralise( std::size_t count, std::string const& label )
+    :   m_count( count ),
+        m_label( label )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << " " << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << "s";
+        return os;
+    }
+
+    SourceLineInfo::SourceLineInfo() : line( 0 ){}
+    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+    :   file( _file ),
+        line( _line )
+    {}
+    SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
+    :   file( other.file ),
+        line( other.line )
+    {}
+    bool SourceLineInfo::empty() const {
+        return file.empty();
+    }
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+        return line == other.line && file == other.file;
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+        return line < other.line || ( line == other.line  && file < other.file );
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << "(" << info.line << ")";
+#else
+        os << info.file << ":" << info.line;
+#endif
+        return os;
+    }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+        std::ostringstream oss;
+        oss << locationInfo << ": Internal Catch error: '" << message << "'";
+        if( alwaysTrue() )
+            throw std::logic_error( oss.str() );
+    }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name,
+            std::string const& _description )
+    :   name( _name ),
+        description( _description ),
+        lineInfo( _lineInfo )
+    {}
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded )
+            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#include <iostream>
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #include <assert.h>
+    #include <stdbool.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <sys/sysctl.h>
+
+    namespace Catch{
+
+        // The following function is taken directly from the following technical note:
+        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+        // Returns true if the current process is being debugged (either
+        // running under the debugger or has a debugger attached post facto).
+        bool isDebuggerActive(){
+
+            int                 mib[4];
+            struct kinfo_proc   info;
+            size_t              size;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+        }
+    } // namespace Catch
+
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       inline bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+    extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+#else
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+    std::string unprintableString = "{?}";
+
+    namespace {
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                union _{
+                    int asInt;
+                    char asChar[sizeof (int)];
+                } u;
+
+                u.asInt = 1;
+                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+            }
+        };
+    }
+
+    std::string rawMemoryToString( const void *object, std::size_t size )
+    {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
+
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        std::ostringstream os;
+        os << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return os.str();
+    }
+}
+
+std::string toString( std::string const& value ) {
+    std::string s = value;
+    if( getCurrentContext().getConfig()->showInvisibles() ) {
+        for(size_t i = 0; i < s.size(); ++i ) {
+            std::string subs;
+            switch( s[i] ) {
+            case '\n': subs = "\\n"; break;
+            case '\t': subs = "\\t"; break;
+            default: break;
+            }
+            if( !subs.empty() ) {
+                s = s.substr( 0, i ) + subs + s.substr( i+1 );
+                ++i;
+            }
+        }
+    }
+    return "\"" + s + "\"";
+}
+std::string toString( std::wstring const& value ) {
+
+    std::string s;
+    s.reserve( value.size() );
+    for(size_t i = 0; i < value.size(); ++i )
+        s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+    return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+    return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+	return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+	return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+    std::ostringstream oss;
+    if( value > 8192 )
+        oss << "0x" << std::hex << value;
+    else
+        oss << value;
+    return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+    std::ostringstream oss;
+    if( value > 8192 )
+        oss << "0x" << std::hex << value;
+    else
+        oss << value;
+    return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+    return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+    std::ostringstream oss;
+    oss << std::setprecision( precision )
+        << std::fixed
+        << value;
+    std::string d = oss.str();
+    std::size_t i = d.find_last_not_of( '0' );
+    if( i != std::string::npos && i != d.size()-1 ) {
+        if( d[i] == '.' )
+            i++;
+        d = d.substr( 0, i+1 );
+    }
+    return d;
+}
+
+std::string toString( const double value ) {
+    return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+    return fpToString( value, 5 ) + "f";
+}
+
+std::string toString( bool value ) {
+    return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+    return value < ' '
+        ? toString( static_cast<unsigned int>( value ) )
+        : Detail::makeString( value );
+}
+
+std::string toString( signed char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+    return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSObject* const& nsObject ) {
+        return toString( [nsObject description] );
+    }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+    ResultBuilder::ResultBuilder(   char const* macroName,
+                                    SourceLineInfo const& lineInfo,
+                                    char const* capturedExpression,
+                                    ResultDisposition::Flags resultDisposition )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+        m_shouldDebugBreak( false ),
+        m_shouldThrow( false )
+    {}
+
+    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+        m_data.resultType = result;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setResultType( bool result ) {
+        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
+        m_exprComponents.lhs = lhs;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
+        m_exprComponents.rhs = rhs;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
+        m_exprComponents.op = op;
+        return *this;
+    }
+
+    void ResultBuilder::endExpression() {
+        m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
+        captureExpression();
+    }
+
+    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+        m_assertionInfo.resultDisposition = resultDisposition;
+        m_stream.oss << Catch::translateActiveException();
+        captureResult( ResultWas::ThrewException );
+    }
+
+    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+        setResultType( resultType );
+        captureExpression();
+    }
+
+    void ResultBuilder::captureExpression() {
+        AssertionResult result = build();
+        getResultCapture().assertionEnded( result );
+
+        if( !result.isOk() ) {
+            if( getCurrentContext().getConfig()->shouldDebugBreak() )
+                m_shouldDebugBreak = true;
+            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+                m_shouldThrow = true;
+        }
+    }
+    void ResultBuilder::react() {
+        if( m_shouldThrow )
+            throw Catch::TestFailureException();
+    }
+
+    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+    AssertionResult ResultBuilder::build() const
+    {
+        assert( m_data.resultType != ResultWas::Unknown );
+
+        AssertionResultData data = m_data;
+
+        // Flip bool results if testFalse is set
+        if( m_exprComponents.testFalse ) {
+            if( data.resultType == ResultWas::Ok )
+                data.resultType = ResultWas::ExpressionFailed;
+            else if( data.resultType == ResultWas::ExpressionFailed )
+                data.resultType = ResultWas::Ok;
+        }
+
+        data.message = m_stream.oss.str();
+        data.reconstructedExpression = reconstructExpression();
+        if( m_exprComponents.testFalse ) {
+            if( m_exprComponents.op == "" )
+                data.reconstructedExpression = "!" + data.reconstructedExpression;
+            else
+                data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
+        }
+        return AssertionResult( m_assertionInfo, data );
+    }
+    std::string ResultBuilder::reconstructExpression() const {
+        if( m_exprComponents.op == "" )
+            return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+        else if( m_exprComponents.op == "matches" )
+            return m_exprComponents.lhs + " " + m_exprComponents.rhs;
+        else if( m_exprComponents.op != "!" ) {
+            if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
+                m_exprComponents.lhs.find("\n") == std::string::npos &&
+                m_exprComponents.rhs.find("\n") == std::string::npos )
+                return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
+            else
+                return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
+        }
+        else
+            return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+    }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        virtual ~TagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+        void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+        static TagAliasRegistry& get();
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+#include <map>
+#include <iostream>
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+        std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return it->second;
+        else
+            return Option<TagAlias>();
+    }
+
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+                it != itEnd;
+                ++it ) {
+            std::size_t pos = expandedTestSpec.find( it->first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    it->second.tag +
+                                    expandedTestSpec.substr( pos + it->first.size() );
+            }
+        }
+        return expandedTestSpec;
+    }
+
+    void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+        if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+                << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+                << "\tRedefined at " << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+    }
+
+    TagAliasRegistry& TagAliasRegistry::get() {
+        static TagAliasRegistry instance;
+        return instance;
+
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() {}
+    ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+        try {
+            TagAliasRegistry::get().add( alias, tag, lineInfo );
+        }
+        catch( std::exception& ex ) {
+            Colour colourGuard( Colour::Red );
+            Catch::cerr() << ex.what() << std::endl;
+            exit(1);
+        }
+    }
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+
+namespace Catch {
+
+    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {}
+
+        virtual ~StreamingReporterBase();
+
+        virtual void noMatchingTestCases( std::string const& ) {}
+
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+            currentTestRunInfo = _testRunInfo;
+        }
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+            currentGroupInfo = _groupInfo;
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+            currentTestCaseInfo = _testInfo;
+        }
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+            m_sectionStack.push_back( _sectionInfo );
+        }
+
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+            currentTestCaseInfo.reset();
+        }
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+            currentGroupInfo.reset();
+        }
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
+
+        virtual void skipTest( TestCaseInfo const& ) {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+        Ptr<IConfig> m_config;
+        std::ostream& stream;
+
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+        std::vector<SectionInfo> m_sectionStack;
+    };
+
+    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+        template<typename T, typename ChildNodeT>
+        struct Node : SharedImpl<> {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
+
+            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode : SharedImpl<> {
+            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+            virtual ~SectionNode();
+
+            bool operator == ( SectionNode const& other ) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == ( Ptr<SectionNode> const& other ) const {
+                return operator==( *other );
+            }
+
+            SectionStats stats;
+            typedef std::vector<Ptr<SectionNode> > ChildSections;
+            typedef std::vector<AssertionStats> Assertions;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+			BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+            bool operator() ( Ptr<SectionNode> const& node ) const {
+                return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+            }
+        private:
+			void operator=( BySectionInfo const& );
+            SectionInfo const& m_other;
+        };
+
+        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {}
+        ~CumulativeReporterBase();
+
+        virtual void testRunStarting( TestRunInfo const& ) {}
+        virtual void testGroupStarting( GroupInfo const& ) {}
+
+        virtual void testCaseStarting( TestCaseInfo const& ) {}
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+            Ptr<SectionNode> node;
+            if( m_sectionStack.empty() ) {
+                if( !m_rootSection )
+                    m_rootSection = new SectionNode( incompleteStats );
+                node = m_rootSection;
+            }
+            else {
+                SectionNode& parentNode = *m_sectionStack.back();
+                SectionNode::ChildSections::const_iterator it =
+                    std::find_if(   parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+                if( it == parentNode.childSections.end() ) {
+                    node = new SectionNode( incompleteStats );
+                    parentNode.childSections.push_back( node );
+                }
+                else
+                    node = *it;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = node;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {}
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            assert( !m_sectionStack.empty() );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back( assertionStats );
+            return true;
+        }
+        virtual void sectionEnded( SectionStats const& sectionStats ) {
+            assert( !m_sectionStack.empty() );
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+            assert( m_sectionStack.size() == 0 );
+            node->children.push_back( m_rootSection );
+            m_testCases.push_back( node );
+            m_rootSection.reset();
+
+            assert( m_deepestSection );
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
+        }
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+            node->children.swap( m_testCases );
+            m_testGroups.push_back( node );
+        }
+        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+            node->children.swap( m_testGroups );
+            m_testRuns.push_back( node );
+            testRunEndedCumulative();
+        }
+        virtual void testRunEndedCumulative() = 0;
+
+        virtual void skipTest( TestCaseInfo const& ) {}
+
+        Ptr<IConfig> m_config;
+        std::ostream& stream;
+        std::vector<AssertionStats> m_assertions;
+        std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+        std::vector<Ptr<TestCaseNode> > m_testCases;
+        std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+        std::vector<Ptr<TestRunNode> > m_testRuns;
+
+        Ptr<SectionNode> m_rootSection;
+        Ptr<SectionNode> m_deepestSection;
+        std::vector<Ptr<SectionNode> > m_sectionStack;
+
+    };
+
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+        }
+        return line;
+    }
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+    template<typename T>
+    class LegacyReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new LegacyReporterAdapter( new T( config ) );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        LegacyReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+
+            // *** Please Note ***:
+            // - If you end up here looking at a compiler error because it's trying to register
+            // your custom reporter class be aware that the native reporter interface has changed
+            // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+            // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+            // However please consider updating to the new interface as the old one is now
+            // deprecated and will probably be removed quite soon!
+            // Please contact me via github if you have any questions at all about this.
+            // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+            // no idea who is actually using custom reporters at all (possibly no-one!).
+            // The new interface is designed to minimise exposure to interface changes in the future.
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer )
+            :   m_writer( writer )
+            {}
+
+            ScopedElement( ScopedElement const& other )
+            :   m_writer( other.m_writer ){
+                other.m_writer = NULL;
+            }
+
+            ~ScopedElement() {
+                if( m_writer )
+                    m_writer->endElement();
+            }
+
+            ScopedElement& writeText( std::string const& text, bool indent = true ) {
+                m_writer->writeText( text, indent );
+                return *this;
+            }
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            mutable XmlWriter* m_writer;
+        };
+
+        XmlWriter()
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( &Catch::cout() )
+        {}
+
+        XmlWriter( std::ostream& os )
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( &os )
+        {}
+
+        ~XmlWriter() {
+            while( !m_tags.empty() )
+                endElement();
+        }
+
+        XmlWriter& startElement( std::string const& name ) {
+            ensureTagClosed();
+            newlineIfNecessary();
+            stream() << m_indent << "<" << name;
+            m_tags.push_back( name );
+            m_indent += "  ";
+            m_tagIsOpen = true;
+            return *this;
+        }
+
+        ScopedElement scopedElement( std::string const& name ) {
+            ScopedElement scoped( this );
+            startElement( name );
+            return scoped;
+        }
+
+        XmlWriter& endElement() {
+            newlineIfNecessary();
+            m_indent = m_indent.substr( 0, m_indent.size()-2 );
+            if( m_tagIsOpen ) {
+                stream() << "/>\n";
+                m_tagIsOpen = false;
+            }
+            else {
+                stream() << m_indent << "</" << m_tags.back() << ">\n";
+            }
+            m_tags.pop_back();
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+            if( !name.empty() && !attribute.empty() ) {
+                stream() << " " << name << "=\"";
+                writeEncodedText( attribute );
+                stream() << "\"";
+            }
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+            stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+            return *this;
+        }
+
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            if( !name.empty() )
+                stream() << " " << name << "=\"" << attribute << "\"";
+            return *this;
+        }
+
+        XmlWriter& writeText( std::string const& text, bool indent = true ) {
+            if( !text.empty() ){
+                bool tagWasOpen = m_tagIsOpen;
+                ensureTagClosed();
+                if( tagWasOpen && indent )
+                    stream() << m_indent;
+                writeEncodedText( text );
+                m_needsNewline = true;
+            }
+            return *this;
+        }
+
+        XmlWriter& writeComment( std::string const& text ) {
+            ensureTagClosed();
+            stream() << m_indent << "<!--" << text << "-->";
+            m_needsNewline = true;
+            return *this;
+        }
+
+        XmlWriter& writeBlankLine() {
+            ensureTagClosed();
+            stream() << "\n";
+            return *this;
+        }
+
+        void setStream( std::ostream& os ) {
+            m_os = &os;
+        }
+
+    private:
+        XmlWriter( XmlWriter const& );
+        void operator=( XmlWriter const& );
+
+        std::ostream& stream() {
+            return *m_os;
+        }
+
+        void ensureTagClosed() {
+            if( m_tagIsOpen ) {
+                stream() << ">\n";
+                m_tagIsOpen = false;
+            }
+        }
+
+        void newlineIfNecessary() {
+            if( m_needsNewline ) {
+                stream() << "\n";
+                m_needsNewline = false;
+            }
+        }
+
+        void writeEncodedText( std::string const& text ) {
+            static const char* charsToEncode = "<&\"";
+            std::string mtext = text;
+            std::string::size_type pos = mtext.find_first_of( charsToEncode );
+            while( pos != std::string::npos ) {
+                stream() << mtext.substr( 0, pos );
+
+                switch( mtext[pos] ) {
+                    case '<':
+                        stream() << "&lt;";
+                        break;
+                    case '&':
+                        stream() << "&amp;";
+                        break;
+                    case '\"':
+                        stream() << "&quot;";
+                        break;
+                }
+                mtext = mtext.substr( pos+1 );
+                pos = mtext.find_first_of( charsToEncode );
+            }
+            stream() << mtext;
+        }
+
+        bool m_tagIsOpen;
+        bool m_needsNewline;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream* m_os;
+    };
+
+}
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase {
+    public:
+        XmlReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_sectionDepth( 0 )
+        {}
+
+        virtual ~XmlReporter();
+
+        static std::string getDescription() {
+            return "Reports test results as an XML document";
+        }
+
+    public: // StreamingReporterBase
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = true;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& s ) {
+            StreamingReporterBase::noMatchingTestCases( s );
+        }
+
+        virtual void testRunStarting( TestRunInfo const& testInfo ) {
+            StreamingReporterBase::testRunStarting( testInfo );
+            m_xml.setStream( stream );
+            m_xml.startElement( "Catch" );
+            if( !m_config->name().empty() )
+                m_xml.writeAttribute( "name", m_config->name() );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+            StreamingReporterBase::testGroupStarting( groupInfo );
+            m_xml.startElement( "Group" )
+                .writeAttribute( "name", groupInfo.name );
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+            StreamingReporterBase::testCaseStarting(testInfo);
+            m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                m_testCaseTimer.start();
+        }
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+            StreamingReporterBase::sectionStarting( sectionInfo );
+            if( m_sectionDepth++ > 0 ) {
+                m_xml.startElement( "Section" )
+                    .writeAttribute( "name", trim( sectionInfo.name ) )
+                    .writeAttribute( "description", sectionInfo.description );
+            }
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) { }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            const AssertionResult& assertionResult = assertionStats.assertionResult;
+
+            // Print any info messages in <Info> tags.
+            if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+                for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                        it != itEnd;
+                        ++it ) {
+                    if( it->type == ResultWas::Info ) {
+                        m_xml.scopedElement( "Info" )
+                            .writeText( it->message );
+                    } else if ( it->type == ResultWas::Warning ) {
+                        m_xml.scopedElement( "Warning" )
+                            .writeText( it->message );
+                    }
+                }
+            }
+
+            // Drop out if result was successful but we're not printing them.
+            if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
+                return true;
+
+            // Print the expression if there is one.
+            if( assertionResult.hasExpression() ) {
+                m_xml.startElement( "Expression" )
+                    .writeAttribute( "success", assertionResult.succeeded() )
+					.writeAttribute( "type", assertionResult.getTestMacroName() )
+                    .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                    .writeAttribute( "line", assertionResult.getSourceInfo().line );
+
+                m_xml.scopedElement( "Original" )
+                    .writeText( assertionResult.getExpression() );
+                m_xml.scopedElement( "Expanded" )
+                    .writeText( assertionResult.getExpandedExpression() );
+            }
+
+            // And... Print a result applicable to each result type.
+            switch( assertionResult.getResultType() ) {
+                case ResultWas::ThrewException:
+                    m_xml.scopedElement( "Exception" )
+                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::FatalErrorCondition:
+                    m_xml.scopedElement( "Fatal Error Condition" )
+                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::Info:
+                    m_xml.scopedElement( "Info" )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::Warning:
+                    // Warning will already have been written
+                    break;
+                case ResultWas::ExplicitFailure:
+                    m_xml.scopedElement( "Failure" )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                default:
+                    break;
+            }
+
+            if( assertionResult.hasExpression() )
+                m_xml.endElement();
+
+            return true;
+        }
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) {
+            StreamingReporterBase::sectionEnded( sectionStats );
+            if( --m_sectionDepth > 0 ) {
+                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+                e.writeAttribute( "successes", sectionStats.assertions.passed );
+                e.writeAttribute( "failures", sectionStats.assertions.failed );
+                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+                if ( m_config->showDurations() == ShowDurations::Always )
+                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+                m_xml.endElement();
+            }
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            StreamingReporterBase::testCaseEnded( testCaseStats );
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+            m_xml.endElement();
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            StreamingReporterBase::testGroupEnded( testGroupStats );
+            // TODO: Check testGroupStats.aborting and act accordingly.
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+                .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+            StreamingReporterBase::testRunEnded( testRunStats );
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+                .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth;
+    };
+
+     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+    class JunitReporter : public CumulativeReporterBase {
+    public:
+        JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() )
+        {}
+
+        ~JunitReporter();
+
+        static std::string getDescription() {
+            return "Reports test results in an XML format that looks like Ant's junitreport target";
+        }
+
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = true;
+            return prefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& runInfo ) {
+            CumulativeReporterBase::testRunStarting( runInfo );
+            xml.startElement( "testsuites" );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+            suiteTimer.start();
+            stdOutForSuite.str("");
+            stdErrForSuite.str("");
+            unexpectedExceptions = 0;
+            CumulativeReporterBase::testGroupStarting( groupInfo );
+        }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+                unexpectedExceptions++;
+            return CumulativeReporterBase::assertionEnded( assertionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            stdOutForSuite << testCaseStats.stdOut;
+            stdErrForSuite << testCaseStats.stdErr;
+            CumulativeReporterBase::testCaseEnded( testCaseStats );
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            double suiteTime = suiteTimer.getElapsedSeconds();
+            CumulativeReporterBase::testGroupEnded( testGroupStats );
+            writeGroup( *m_testGroups.back(), suiteTime );
+        }
+
+        virtual void testRunEndedCumulative() {
+            xml.endElement();
+        }
+
+        void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+            TestGroupStats const& stats = groupNode.value;
+            xml.writeAttribute( "name", stats.groupInfo.name );
+            xml.writeAttribute( "errors", unexpectedExceptions );
+            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+            xml.writeAttribute( "tests", stats.totals.assertions.total() );
+            xml.writeAttribute( "hostname", "tbd" ); // !TBD
+            if( m_config->showDurations() == ShowDurations::Never )
+                xml.writeAttribute( "time", "" );
+            else
+                xml.writeAttribute( "time", suiteTime );
+            xml.writeAttribute( "timestamp", "tbd" ); // !TBD
+
+            // Write test cases
+            for( TestGroupNode::ChildNodes::const_iterator
+                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
+                    it != itEnd;
+                    ++it )
+                writeTestCase( **it );
+
+            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+        }
+
+        void writeTestCase( TestCaseNode const& testCaseNode ) {
+            TestCaseStats const& stats = testCaseNode.value;
+
+            // All test cases have exactly one section - which represents the
+            // test case itself. That section may have 0-n nested sections
+            assert( testCaseNode.children.size() == 1 );
+            SectionNode const& rootSection = *testCaseNode.children.front();
+
+            std::string className = stats.testInfo.className;
+
+            if( className.empty() ) {
+                if( rootSection.childSections.empty() )
+                    className = "global";
+            }
+            writeSection( className, "", rootSection );
+        }
+
+        void writeSection(  std::string const& className,
+                            std::string const& rootName,
+                            SectionNode const& sectionNode ) {
+            std::string name = trim( sectionNode.stats.sectionInfo.name );
+            if( !rootName.empty() )
+                name = rootName + "/" + name;
+
+            if( !sectionNode.assertions.empty() ||
+                !sectionNode.stdOut.empty() ||
+                !sectionNode.stdErr.empty() ) {
+                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+                if( className.empty() ) {
+                    xml.writeAttribute( "classname", name );
+                    xml.writeAttribute( "name", "root" );
+                }
+                else {
+                    xml.writeAttribute( "classname", className );
+                    xml.writeAttribute( "name", name );
+                }
+                xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+                writeAssertions( sectionNode );
+
+                if( !sectionNode.stdOut.empty() )
+                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+                if( !sectionNode.stdErr.empty() )
+                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+            }
+            for( SectionNode::ChildSections::const_iterator
+                    it = sectionNode.childSections.begin(),
+                    itEnd = sectionNode.childSections.end();
+                    it != itEnd;
+                    ++it )
+                if( className.empty() )
+                    writeSection( name, "", **it );
+                else
+                    writeSection( className, name, **it );
+        }
+
+        void writeAssertions( SectionNode const& sectionNode ) {
+            for( SectionNode::Assertions::const_iterator
+                    it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+                    it != itEnd;
+                    ++it )
+                writeAssertion( *it );
+        }
+        void writeAssertion( AssertionStats const& stats ) {
+            AssertionResult const& result = stats.assertionResult;
+            if( !result.isOk() ) {
+                std::string elementName;
+                switch( result.getResultType() ) {
+                    case ResultWas::ThrewException:
+                    case ResultWas::FatalErrorCondition:
+                        elementName = "error";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        elementName = "failure";
+                        break;
+
+                    // We should never see these here:
+                    case ResultWas::Info:
+                    case ResultWas::Warning:
+                    case ResultWas::Ok:
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        elementName = "internalError";
+                        break;
+                }
+
+                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+                xml.writeAttribute( "message", result.getExpandedExpression() );
+                xml.writeAttribute( "type", result.getTestMacroName() );
+
+                std::ostringstream oss;
+                if( !result.getMessage().empty() )
+                    oss << result.getMessage() << "\n";
+                for( std::vector<MessageInfo>::const_iterator
+                        it = stats.infoMessages.begin(),
+                        itEnd = stats.infoMessages.end();
+                            it != itEnd;
+                            ++it )
+                    if( it->type == ResultWas::Info )
+                        oss << it->message << "\n";
+
+                oss << "at " << result.getSourceInfo();
+                xml.writeText( oss.str(), false );
+            }
+        }
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::ostringstream stdOutForSuite;
+        std::ostringstream stdErrForSuite;
+        unsigned int unexpectedExceptions;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+namespace Catch {
+
+    struct ConsoleReporter : StreamingReporterBase {
+        ConsoleReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_headerPrinted( false )
+        {}
+
+        virtual ~ConsoleReporter();
+        static std::string getDescription() {
+            return "Reports test results as plain lines of text";
+        }
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << "'" << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            lazyPrint();
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+            m_headerPrinted = false;
+            StreamingReporterBase::sectionStarting( _sectionInfo );
+        }
+        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+            if( _sectionStats.missingAssertions ) {
+                lazyPrint();
+                Colour colour( Colour::ResultError );
+                if( m_sectionStack.size() > 1 )
+                    stream << "\nNo assertions in section";
+                else
+                    stream << "\nNo assertions in test case";
+                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+            }
+            if( m_headerPrinted ) {
+                if( m_config->showDurations() == ShowDurations::Always )
+                    stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+                m_headerPrinted = false;
+            }
+            else {
+                if( m_config->showDurations() == ShowDurations::Always )
+                    stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+            }
+            StreamingReporterBase::sectionEnded( _sectionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+            StreamingReporterBase::testCaseEnded( _testCaseStats );
+            m_headerPrinted = false;
+        }
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+            if( currentGroupInfo.used ) {
+                printSummaryDivider();
+                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+                printTotals( _testGroupStats.totals );
+                stream << "\n" << std::endl;
+            }
+            StreamingReporterBase::testGroupEnded( _testGroupStats );
+        }
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotalsDivider( _testRunStats.totals );
+            printTotals( _testRunStats.totals );
+            stream << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            :   stream( _stream ),
+                stats( _stats ),
+                result( _stats.assertionResult ),
+                colour( Colour::None ),
+                message( result.getMessage() ),
+                messages( _stats.infoMessages ),
+                printInfoMessages( _printInfoMessages )
+            {
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        colour = Colour::Success;
+                        passOrFail = "PASSED";
+                        //if( result.hasMessage() )
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() ) {
+                            colour = Colour::Success;
+                            passOrFail = "FAILED - but was ok";
+                        }
+                        else {
+                            colour = Colour::Error;
+                            passOrFail = "FAILED";
+                        }
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ThrewException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to unexpected exception with message";
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to a fatal error condition";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "because no exception was thrown where one was expected";
+                        break;
+                    case ResultWas::Info:
+                        messageLabel = "info";
+                        break;
+                    case ResultWas::Warning:
+                        messageLabel = "warning";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        passOrFail = "FAILED";
+                        colour = Colour::Error;
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "explicitly with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "explicitly with messages";
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        passOrFail = "** internal error **";
+                        colour = Colour::Error;
+                        break;
+                }
+            }
+
+            void print() const {
+                printSourceInfo();
+                if( stats.totals.assertions.total() > 0 ) {
+                    if( result.isOk() )
+                        stream << "\n";
+                    printResultType();
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                }
+                else {
+                    stream << "\n";
+                }
+                printMessage();
+            }
+
+        private:
+            void printResultType() const {
+                if( !passOrFail.empty() ) {
+                    Colour colourGuard( colour );
+                    stream << passOrFail << ":\n";
+                }
+            }
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    Colour colourGuard( Colour::OriginalExpression );
+                    stream  << "  ";
+                    stream << result.getExpressionInMacro();
+                    stream << "\n";
+                }
+            }
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    stream << "with expansion:\n";
+                    Colour colourGuard( Colour::ReconstructedExpression );
+                    stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
+                }
+            }
+            void printMessage() const {
+                if( !messageLabel.empty() )
+                    stream << messageLabel << ":" << "\n";
+                for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+                        it != itEnd;
+                        ++it ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || it->type != ResultWas::Info )
+                        stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
+                }
+            }
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ": ";
+            }
+
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            Colour::Code colour;
+            std::string passOrFail;
+            std::string messageLabel;
+            std::string message;
+            std::vector<MessageInfo> messages;
+            bool printInfoMessages;
+        };
+
+        void lazyPrint() {
+
+            if( !currentTestRunInfo.used )
+                lazyPrintRunInfo();
+            if( !currentGroupInfo.used )
+                lazyPrintGroupInfo();
+
+            if( !m_headerPrinted ) {
+                printTestCaseAndSectionHeader();
+                m_headerPrinted = true;
+            }
+        }
+        void lazyPrintRunInfo() {
+            stream  << "\n" << getLineOfChars<'~'>() << "\n";
+            Colour colour( Colour::SecondaryText );
+            stream  << currentTestRunInfo->name
+                    << " is a Catch v"  << libraryVersion.majorVersion << "."
+                    << libraryVersion.minorVersion << " b"
+                    << libraryVersion.buildNumber;
+            if( libraryVersion.branchName != std::string( "master" ) )
+                stream << " (" << libraryVersion.branchName << ")";
+            stream  << " host application.\n"
+                    << "Run with -? for options\n\n";
+
+            if( m_config->rngSeed() != 0 )
+                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+            currentTestRunInfo.used = true;
+        }
+        void lazyPrintGroupInfo() {
+            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+                printClosedHeader( "Group: " + currentGroupInfo->name );
+                currentGroupInfo.used = true;
+            }
+        }
+        void printTestCaseAndSectionHeader() {
+            assert( !m_sectionStack.empty() );
+            printOpenHeader( currentTestCaseInfo->name );
+
+            if( m_sectionStack.size() > 1 ) {
+                Colour colourGuard( Colour::Headers );
+
+                std::vector<SectionInfo>::const_iterator
+                    it = m_sectionStack.begin()+1, // Skip first section (test case)
+                    itEnd = m_sectionStack.end();
+                for( ; it != itEnd; ++it )
+                    printHeaderString( it->name, 2 );
+            }
+
+            SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+            if( !lineInfo.empty() ){
+                stream << getLineOfChars<'-'>() << "\n";
+                Colour colourGuard( Colour::FileName );
+                stream << lineInfo << "\n";
+            }
+            stream << getLineOfChars<'.'>() << "\n" << std::endl;
+        }
+
+        void printClosedHeader( std::string const& _name ) {
+            printOpenHeader( _name );
+            stream << getLineOfChars<'.'>() << "\n";
+        }
+        void printOpenHeader( std::string const& _name ) {
+            stream  << getLineOfChars<'-'>() << "\n";
+            {
+                Colour colourGuard( Colour::Headers );
+                printHeaderString( _name );
+            }
+        }
+
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+            std::size_t i = _string.find( ": " );
+            if( i != std::string::npos )
+                i+=2;
+            else
+                i = 0;
+            stream << Text( _string, TextAttributes()
+                                        .setIndent( indent+i)
+                                        .setInitialIndent( indent ) ) << "\n";
+        }
+
+        struct SummaryColumn {
+
+            SummaryColumn( std::string const& _label, Colour::Code _colour )
+            :   label( _label ),
+                colour( _colour )
+            {}
+            SummaryColumn addRow( std::size_t count ) {
+                std::ostringstream oss;
+                oss << count;
+                std::string row = oss.str();
+                for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+                    while( it->size() < row.size() )
+                        *it = " " + *it;
+                    while( it->size() > row.size() )
+                        row = " " + row;
+                }
+                rows.push_back( row );
+                return *this;
+            }
+
+            std::string label;
+            Colour::Code colour;
+            std::vector<std::string> rows;
+
+        };
+
+        void printTotals( Totals const& totals ) {
+            if( totals.testCases.total() == 0 ) {
+                stream << Colour( Colour::Warning ) << "No tests ran\n";
+            }
+            else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+                stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+                stream << " ("
+                        << pluralise( totals.assertions.passed, "assertion" ) << " in "
+                        << pluralise( totals.testCases.passed, "test case" ) << ")"
+                        << "\n";
+            }
+            else {
+
+                std::vector<SummaryColumn> columns;
+                columns.push_back( SummaryColumn( "", Colour::None )
+                                        .addRow( totals.testCases.total() )
+                                        .addRow( totals.assertions.total() ) );
+                columns.push_back( SummaryColumn( "passed", Colour::Success )
+                                        .addRow( totals.testCases.passed )
+                                        .addRow( totals.assertions.passed ) );
+                columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+                                        .addRow( totals.testCases.failed )
+                                        .addRow( totals.assertions.failed ) );
+                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+                                        .addRow( totals.testCases.failedButOk )
+                                        .addRow( totals.assertions.failedButOk ) );
+
+                printSummaryRow( "test cases", columns, 0 );
+                printSummaryRow( "assertions", columns, 1 );
+            }
+        }
+        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+            for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+                std::string value = it->rows[row];
+                if( it->label.empty() ) {
+                    stream << label << ": ";
+                    if( value != "0" )
+                        stream << value;
+                    else
+                        stream << Colour( Colour::Warning ) << "- none -";
+                }
+                else if( value != "0" ) {
+                    stream  << Colour( Colour::LightGrey ) << " | ";
+                    stream  << Colour( it->colour )
+                            << value << " " << it->label;
+                }
+            }
+            stream << "\n";
+        }
+
+        static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+            return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+        }
+        static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+            if( i > j && i > k )
+                return i;
+            else if( j > k )
+                return j;
+            else
+                return k;
+        }
+
+        void printTotalsDivider( Totals const& totals ) {
+            if( totals.testCases.total() > 0 ) {
+                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )++;
+                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+                if( totals.testCases.allPassed() )
+                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+                else
+                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+            }
+            else {
+                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+            }
+            stream << "\n";
+        }
+        void printSummaryDivider() {
+            stream << getLineOfChars<'-'>() << "\n";
+        }
+
+    private:
+        bool m_headerPrinted;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+    struct CompactReporter : StreamingReporterBase {
+
+        CompactReporter( ReporterConfig const& _config )
+        : StreamingReporterBase( _config )
+        {}
+
+        virtual ~CompactReporter();
+
+        static std::string getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << "'" << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( _testRunStats.totals );
+            stream << "\n" << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            : stream( _stream )
+            , stats( _stats )
+            , result( _stats.assertionResult )
+            , messages( _stats.infoMessages )
+            , itMessage( _stats.infoMessages.begin() )
+            , printInfoMessages( _printInfoMessages )
+            {}
+
+            void print() {
+                printSourceInfo();
+
+                itMessage = messages.begin();
+
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        printResultType( Colour::ResultSuccess, passedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        if ( ! result.hasExpression() )
+                            printRemainingMessages( Colour::None );
+                        else
+                            printRemainingMessages();
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() )
+                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+                        else
+                            printResultType( Colour::Error, failedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ThrewException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "unexpected exception with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "fatal error condition with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::DidntThrowException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "expected exception, got none" );
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Info:
+                        printResultType( Colour::None, "info" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Warning:
+                        printResultType( Colour::None, "warning" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "explicitly" );
+                        printRemainingMessages( Colour::None );
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        printResultType( Colour::Error, "** internal error **" );
+                        break;
+                }
+            }
+
+        private:
+            // Colour::LightGrey
+
+            static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+            static const char* failedString() { return "FAILED"; }
+            static const char* passedString() { return "PASSED"; }
+#else
+            static const char* failedString() { return "failed"; }
+            static const char* passedString() { return "passed"; }
+#endif
+
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ":";
+            }
+
+            void printResultType( Colour::Code colour, std::string passOrFail ) const {
+                if( !passOrFail.empty() ) {
+                    {
+                        Colour colourGuard( colour );
+                        stream << " " << passOrFail;
+                    }
+                    stream << ":";
+                }
+            }
+
+            void printIssue( std::string issue ) const {
+                stream << " " << issue;
+            }
+
+            void printExpressionWas() {
+                if( result.hasExpression() ) {
+                    stream << ";";
+                    {
+                        Colour colour( dimColour() );
+                        stream << " expression was:";
+                    }
+                    printOriginalExpression();
+                }
+            }
+
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    stream << " " << result.getExpression();
+                }
+            }
+
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    {
+                        Colour colour( dimColour() );
+                        stream << " for: ";
+                    }
+                    stream << result.getExpandedExpression();
+                }
+            }
+
+            void printMessage() {
+                if ( itMessage != messages.end() ) {
+                    stream << " '" << itMessage->message << "'";
+                    ++itMessage;
+                }
+            }
+
+            void printRemainingMessages( Colour::Code colour = dimColour() ) {
+                if ( itMessage == messages.end() )
+                    return;
+
+                // using messages.end() directly yields compilation error:
+                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+                {
+                    Colour colourGuard( colour );
+                    stream << " with " << pluralise( N, "message" ) << ":";
+                }
+
+                for(; itMessage != itEnd; ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+                        stream << " '" << itMessage->message << "'";
+                        if ( ++itMessage != itEnd ) {
+                            Colour colourGuard( dimColour() );
+                            stream << " and";
+                        }
+                    }
+                }
+            }
+
+        private:
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            std::vector<MessageInfo> messages;
+            std::vector<MessageInfo>::const_iterator itMessage;
+            bool printInfoMessages;
+        };
+
+        // Colour, message variants:
+        // - white: No tests ran.
+        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+        // - white: Passed [both/all] N test cases (no assertions).
+        // -   red: Failed N tests cases, failed M assertions.
+        // - green: Passed [both/all] N tests cases with M assertions.
+
+        std::string bothOrAll( std::size_t count ) const {
+            return count == 1 ? "" : count == 2 ? "both " : "all " ;
+        }
+
+        void printTotals( const Totals& totals ) const {
+            if( totals.testCases.total() == 0 ) {
+                stream << "No tests ran.";
+            }
+            else if( totals.testCases.failed == totals.testCases.total() ) {
+                Colour colour( Colour::ResultError );
+                const std::string qualify_assertions_failed =
+                    totals.assertions.failed == totals.assertions.total() ?
+                        bothOrAll( totals.assertions.failed ) : "";
+                stream <<
+                    "Failed " << bothOrAll( totals.testCases.failed )
+                              << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << qualify_assertions_failed <<
+                                 pluralise( totals.assertions.failed, "assertion" ) << ".";
+            }
+            else if( totals.assertions.total() == 0 ) {
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.total() )
+                              << pluralise( totals.testCases.total(), "test case" )
+                              << " (no assertions).";
+            }
+            else if( totals.assertions.failed ) {
+                Colour colour( Colour::ResultError );
+                stream <<
+                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
+            }
+            else {
+                Colour colour( Colour::ResultSuccess );
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.passed )
+                              << pluralise( totals.testCases.passed, "test case"  ) <<
+                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << ".";
+            }
+        }
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+    NonCopyable::~NonCopyable() {}
+    IShared::~IShared() {}
+    StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+    IContext::~IContext() {}
+    IResultCapture::~IResultCapture() {}
+    ITestCase::~ITestCase() {}
+    ITestCaseRegistry::~ITestCaseRegistry() {}
+    IRegistryHub::~IRegistryHub() {}
+    IMutableRegistryHub::~IMutableRegistryHub() {}
+    IExceptionTranslator::~IExceptionTranslator() {}
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+    IReporter::~IReporter() {}
+    IReporterFactory::~IReporterFactory() {}
+    IReporterRegistry::~IReporterRegistry() {}
+    IStreamingReporter::~IStreamingReporter() {}
+    AssertionStats::~AssertionStats() {}
+    SectionStats::~SectionStats() {}
+    TestCaseStats::~TestCaseStats() {}
+    TestGroupStats::~TestGroupStats() {}
+    TestRunStats::~TestRunStats() {}
+    CumulativeReporterBase::SectionNode::~SectionNode() {}
+    CumulativeReporterBase::~CumulativeReporterBase() {}
+
+    StreamingReporterBase::~StreamingReporterBase() {}
+    ConsoleReporter::~ConsoleReporter() {}
+    CompactReporter::~CompactReporter() {}
+    IRunner::~IRunner() {}
+    IMutableContext::~IMutableContext() {}
+    IConfig::~IConfig() {}
+    XmlReporter::~XmlReporter() {}
+    JunitReporter::~JunitReporter() {}
+    TestRegistry::~TestRegistry() {}
+    FreeFunctionTestCase::~FreeFunctionTestCase() {}
+    IGeneratorInfo::~IGeneratorInfo() {}
+    IGeneratorsForTest::~IGeneratorsForTest() {}
+    TestSpec::Pattern::~Pattern() {}
+    TestSpec::NamePattern::~NamePattern() {}
+    TestSpec::TagPattern::~TagPattern() {}
+    TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+    Matchers::Impl::StdString::Equals::~Equals() {}
+    Matchers::Impl::StdString::Contains::~Contains() {}
+    Matchers::Impl::StdString::StartsWith::~StartsWith() {}
+    Matchers::Impl::StdString::EndsWith::~EndsWith() {}
+
+    void Config::dummy() {}
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * const argv[]) {
+    return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    Catch::registerTestMethods();
+    int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+    [pool drain];
+#endif
+
+    return result;
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+#  undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+    #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+    #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
+#else
+    #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc )    CATCH_SECTION( "Given: " desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+    #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+    #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+    #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
+#else
+    #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
+#define WHEN( desc )     SECTION( "    When: " desc, "" )
+#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
+#define THEN( desc )     SECTION( "    Then: " desc, "" )
+#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/third_party/immer/tools/include/doctest.h b/third_party/immer/tools/include/doctest.h
new file mode 100644
index 0000000000..695c271b59
--- /dev/null
+++ b/third_party/immer/tools/include/doctest.h
@@ -0,0 +1,5696 @@
+// ======================================================================
+// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==
+// ======================================================================
+//
+// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
+//
+// Copyright (c) 2016-2017 Viktor Kirilov
+//
+// Distributed under the MIT Software License
+// See accompanying file LICENSE.txt or copy at
+// https://opensource.org/licenses/MIT
+//
+// The documentation can be found at the library's page:
+// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+//
+// The library is heavily influenced by Catch - https://github.com/philsquared/Catch
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/philsquared/Catch/blob/master/LICENSE_1_0.txt
+//
+// The concept of subcases (sections in Catch) and expression decomposition are from there.
+// Some parts of the code are taken directly:
+// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<>
+// - the Approx() helper class for floating point comparison
+// - colors in the console
+// - breaking into a debugger
+// - signal / SEH handling
+// - timer
+//
+// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/martinmoene/lest/blob/master/LICENSE_1_0.txt
+//
+// The type list and the foreach algorithm on it for C++98 are taken from Loki
+// - http://loki-lib.sourceforge.net/
+// - https://en.wikipedia.org/wiki/Loki_%28C%2B%2B%29
+// - https://github.com/snaewe/loki-lib
+// which uses the MIT Software License
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+
+// Suppress this globally (without push/pop) - there is no way to silence it in the
+// expression decomposition macros _Pragma() in macros doesn't work for the c++ front-end of g++
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543
+// Also the warning is completely worthless nowadays - http://stackoverflow.com/questions/14016993
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic ignored "-Waggregate-return"
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wdeprecated"
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Weffc++"
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#pragma GCC diagnostic ignored "-Winline"
+#pragma GCC diagnostic ignored "-Wlong-long"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif // > gcc 4.6
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7)
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#endif // > gcc 4.7
+#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3)
+#pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif // > gcc 5.3
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration
+#pragma warning(disable : 4706) // assignment within conditional expression
+#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated
+#pragma warning(disable : 4127) // conditional expression is constant
+#endif                          // _MSC_VER
+
+#ifndef DOCTEST_LIBRARY_INCLUDED
+#define DOCTEST_LIBRARY_INCLUDED
+
+#define DOCTEST_VERSION_MAJOR 1
+#define DOCTEST_VERSION_MINOR 2
+#define DOCTEST_VERSION_PATCH 1
+#define DOCTEST_VERSION_STR "1.2.1"
+
+#define DOCTEST_VERSION                                                                            \
+    (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
+
+// =================================================================================================
+// == FEATURE DETECTION ============================================================================
+// =================================================================================================
+
+#if __cplusplus >= 201103L
+#ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#ifndef DOCTEST_CONFIG_WITH_NULLPTR
+#define DOCTEST_CONFIG_WITH_NULLPTR
+#endif // DOCTEST_CONFIG_WITH_NULLPTR
+#ifndef DOCTEST_CONFIG_WITH_LONG_LONG
+#define DOCTEST_CONFIG_WITH_LONG_LONG
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#define DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // __cplusplus >= 201103L
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif // __has_feature
+
+// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx
+// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
+// MSVC version table:
+// MSVC++ 15.0 _MSC_VER == 1910 (Visual Studio 2017)
+// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+// MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
+// MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
+
+// deleted functions
+
+#ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+#define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // _MSC_VER
+#if defined(__clang__) && __has_feature(cxx_deleted_functions)
+#define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // __clang__
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) &&               \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // __GNUC__
+#endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+
+#if defined(DOCTEST_CONFIG_NO_DELETED_FUNCTIONS) && defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS)
+#undef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS
+#endif // DOCTEST_CONFIG_NO_DELETED_FUNCTIONS
+
+// rvalue references
+
+#ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#endif // _MSC_VER
+#if defined(__clang__) && __has_feature(cxx_rvalue_references)
+#define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#endif // __clang__
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4) &&               \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#endif // __GNUC__
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+
+#if defined(DOCTEST_CONFIG_NO_RVALUE_REFERENCES) && defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES)
+#undef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+#endif // DOCTEST_CONFIG_NO_RVALUE_REFERENCES
+
+// nullptr
+
+#ifndef DOCTEST_CONFIG_WITH_NULLPTR
+#if defined(__clang__) && __has_feature(cxx_nullptr)
+#define DOCTEST_CONFIG_WITH_NULLPTR
+#endif // __clang__
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) &&               \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_NULLPTR
+#endif                                      // __GNUC__
+#if defined(_MSC_VER) && (_MSC_VER >= 1600) // MSVC 2010
+#define DOCTEST_CONFIG_WITH_NULLPTR
+#endif // _MSC_VER
+#endif // DOCTEST_CONFIG_WITH_NULLPTR
+
+#if defined(DOCTEST_CONFIG_NO_NULLPTR) && defined(DOCTEST_CONFIG_WITH_NULLPTR)
+#undef DOCTEST_CONFIG_WITH_NULLPTR
+#endif // DOCTEST_CONFIG_NO_NULLPTR
+
+// variadic macros
+
+#ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#if defined(_MSC_VER) && _MSC_VER > 1400 && !defined(__EDGE__)
+#define DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // _MSC_VER
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4) &&               \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // __GNUC__ and clang
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#if defined(DOCTEST_CONFIG_NO_VARIADIC_MACROS) && defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS)
+#undef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // DOCTEST_CONFIG_NO_VARIADIC_MACROS
+
+// long long
+
+#ifndef DOCTEST_CONFIG_WITH_LONG_LONG
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define DOCTEST_CONFIG_WITH_LONG_LONG
+#endif // _MSC_VER
+#if(defined(__clang__) ||                                                                          \
+    (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 5) || __GNUC__ > 4))) &&            \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_LONG_LONG
+#endif // __GNUC__ and clang
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+#if defined(DOCTEST_CONFIG_NO_LONG_LONG) && defined(DOCTEST_CONFIG_WITH_LONG_LONG)
+#undef DOCTEST_CONFIG_WITH_LONG_LONG
+#endif // DOCTEST_CONFIG_NO_LONG_LONG
+
+// static_assert
+
+#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#if defined(__clang__) && __has_feature(cxx_static_assert)
+#define DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#endif // __clang__
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4) &&               \
+        defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#endif                                      // __GNUC__
+#if defined(_MSC_VER) && (_MSC_VER >= 1600) // MSVC 2010
+#define DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#endif // _MSC_VER
+#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT
+
+#if defined(DOCTEST_CONFIG_NO_STATIC_ASSERT) && defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT)
+#undef DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#endif // DOCTEST_CONFIG_NO_STATIC_ASSERT
+
+// other stuff...
+
+#if defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES) || defined(DOCTEST_CONFIG_WITH_LONG_LONG) ||    \
+        defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS) || defined(DOCTEST_CONFIG_WITH_NULLPTR) ||  \
+        defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS) || defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT)
+#define DOCTEST_NO_CPP11_COMPAT
+#endif // c++11 stuff
+
+#if defined(__clang__) && defined(DOCTEST_NO_CPP11_COMPAT)
+#pragma clang diagnostic ignored "-Wc++98-compat"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif // __clang__ && DOCTEST_NO_CPP11_COMPAT
+
+#if defined(_MSC_VER) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#define DOCTEST_CONFIG_WINDOWS_SEH
+#endif // _MSC_VER
+#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#undef DOCTEST_CONFIG_WINDOWS_SEH
+#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
+
+#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS)
+#define DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // _WIN32
+#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
+#undef DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#if defined(__GNUC__) && !defined(__EXCEPTIONS)
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // clang and gcc
+// in MSVC _HAS_EXCEPTIONS is defined in a header instead of as a project define
+// so we can't do the automatic detection for MSVC without including some header
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS)
+#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
+#define DOCTEST_CONFIG_IMPLEMENT
+#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+
+#if defined _WIN32 || defined __CYGWIN__
+#ifdef __GNUC__
+#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport))
+#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport))
+#else // __GNUC__
+#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport)
+#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport)
+#endif // __GNUC__
+#else  // _WIN32
+#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default")))
+#define DOCTEST_SYMBOL_IMPORT
+#endif // _WIN32
+
+#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#ifdef DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT
+#else // DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT
+#endif // DOCTEST_CONFIG_IMPLEMENT
+#else  // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#define DOCTEST_INTERFACE
+#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+
+#ifdef _MSC_VER
+#define DOCTEST_NOINLINE __declspec(noinline)
+#else // _MSC_VER
+#define DOCTEST_NOINLINE __attribute__((noinline))
+#endif // _MSC_VER
+
+#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5
+#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK
+
+// =================================================================================================
+// == FEATURE DETECTION END ========================================================================
+// =================================================================================================
+
+// internal macros for string concatenation and anonymous variable name generation
+#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
+#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
+#ifdef __COUNTER__ // not standard and may be missing for some compilers
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__)
+#else // __COUNTER__
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
+#endif // __COUNTER__
+
+// macro for making a string out of an identifier
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TOSTR_IMPL(...) #__VA_ARGS__
+#define DOCTEST_TOSTR(...) DOCTEST_TOSTR_IMPL(__VA_ARGS__)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+// for concatenating literals and making the result a string
+#define DOCTEST_STR_CONCAT_TOSTR(s1, s2) DOCTEST_TOSTR(s1) DOCTEST_TOSTR(s2)
+
+// counts the number of elements in a C string
+#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
+
+#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x&
+#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x
+#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+
+// not using __APPLE__ because... this is how Catch does it
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_IPHONE
+#elif defined(_WIN32) || defined(_MSC_VER)
+#define DOCTEST_PLATFORM_WINDOWS
+#else
+#define DOCTEST_PLATFORM_LINUX
+#endif
+
+#if defined(__clang__)
+#define DOCTEST_GLOBAL_NO_WARNINGS(var)                                                            \
+    _Pragma("clang diagnostic push")                                                               \
+            _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") static int var
+#define DOCTEST_GLOBAL_NO_WARNINGS_END() _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+#define DOCTEST_GLOBAL_NO_WARNINGS(var) static int var __attribute__((unused))
+#define DOCTEST_GLOBAL_NO_WARNINGS_END()
+#else // MSVC / other
+#define DOCTEST_GLOBAL_NO_WARNINGS(var) static int var
+#define DOCTEST_GLOBAL_NO_WARNINGS_END()
+#endif // MSVC / other
+
+// should probably take a look at https://github.com/scottt/debugbreak
+#ifdef DOCTEST_PLATFORM_MAC
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :)
+#elif defined(_MSC_VER)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
+#elif defined(__MINGW32__)
+extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak()
+#else // linux
+#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0)
+#endif // linux
+
+#ifdef __clang__
+// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier)
+#include <ciso646>
+#endif // __clang__
+
+#ifdef _LIBCPP_VERSION
+// not forward declaring ostream for libc++ because I had some problems (inline namespaces vs c++98)
+// so the <iosfwd> header is used - also it is very light and doesn't drag a ton of stuff
+#include <iosfwd>
+#else // _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_IOSFWD
+namespace std
+{
+template <class charT>
+struct char_traits;
+template <>
+struct char_traits<char>;
+template <class charT, class traits>
+class basic_ostream;
+typedef basic_ostream<char, char_traits<char> > ostream;
+} // namespace std
+#else // DOCTEST_CONFIG_USE_IOSFWD
+#include <iosfwd>
+#endif // DOCTEST_CONFIG_USE_IOSFWD
+#endif // _LIBCPP_VERSION
+
+// static assert macro - because of the c++98 support requires that the message is an
+// identifier (no spaces and not a C string) - example without quotes: I_am_a_message
+// taken from here: http://stackoverflow.com/a/1980156/3162383
+#ifdef DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#define DOCTEST_STATIC_ASSERT(expression, message) static_assert(expression, #message)
+#else // DOCTEST_CONFIG_WITH_STATIC_ASSERT
+#define DOCTEST_STATIC_ASSERT(expression, message)                                                 \
+    struct DOCTEST_CAT(__static_assertion_at_line_, __LINE__)                                      \
+    {                                                                                              \
+        doctest::detail::static_assert_impl::StaticAssertion<static_cast<bool>((expression))>      \
+                DOCTEST_CAT(DOCTEST_CAT(DOCTEST_CAT(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__),   \
+                                        _),                                                        \
+                            message);                                                              \
+    };                                                                                             \
+    typedef doctest::detail::static_assert_impl::StaticAssertionTest<static_cast<int>(             \
+            sizeof(DOCTEST_CAT(__static_assertion_at_line_, __LINE__)))>                           \
+            DOCTEST_CAT(__static_assertion_test_at_line_, __LINE__)
+#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT
+
+#ifdef DOCTEST_CONFIG_WITH_NULLPTR
+#ifdef _LIBCPP_VERSION
+#include <cstddef>
+#else  // _LIBCPP_VERSION
+namespace std
+{ typedef decltype(nullptr) nullptr_t; }
+#endif // _LIBCPP_VERSION
+#endif // DOCTEST_CONFIG_WITH_NULLPTR
+
+#ifndef DOCTEST_CONFIG_DISABLE
+namespace doctest
+{
+namespace detail
+{
+    struct TestSuite
+    {
+        const char* m_test_suite;
+        const char* m_description;
+        bool        m_skip;
+        bool        m_may_fail;
+        bool        m_should_fail;
+        int         m_expected_failures;
+        double      m_timeout;
+
+        TestSuite& operator*(const char* in) {
+            m_test_suite = in;
+            // clear state
+            m_description       = 0;
+            m_skip              = false;
+            m_may_fail          = false;
+            m_should_fail       = false;
+            m_expected_failures = 0;
+            m_timeout           = 0;
+            return *this;
+        }
+
+        template <typename T>
+        TestSuite& operator*(const T& in) {
+            in.fill(*this);
+            return *this;
+        }
+    };
+} // namespace detail
+} // namespace doctest
+
+// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro
+// introduces an anonymous namespace in which getCurrentTestSuite gets overridden
+namespace doctest_detail_test_suite_ns
+{
+DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite();
+} // namespace doctest_detail_test_suite_ns
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+namespace doctest
+{
+// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
+// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
+// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
+// - if small - capacity left before going on the heap - using the lowest 5 bits
+// - if small - 2 bits are left unused - the second and third highest ones
+// - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator)
+//              and the "is small" bit remains "0" ("as well as the capacity left") so its OK
+// Idea taken from this lecture about the string implementation of facebook/folly - fbstring
+// https://www.youtube.com/watch?v=kPR8h4-qZdk
+// TODO:
+// - optimizations - like not deleting memory unnecessarily in operator= and etc.
+// - resize/reserve/clear
+// - substr
+// - replace
+// - back/front
+// - iterator stuff
+// - find & friends
+// - push_back/pop_back
+// - assign/insert/erase
+// - relational operators as free functions - taking const char* as one of the params
+class DOCTEST_INTERFACE String
+{
+    static const unsigned len  = 24;      //!OCLINT avoid private static members
+    static const unsigned last = len - 1; //!OCLINT avoid private static members
+
+    struct view // len should be more than sizeof(view) - because of the final byte for flags
+    {
+        char*    ptr;
+        unsigned size;
+        unsigned capacity;
+    };
+
+    union
+    {
+        char buf[len];
+        view data;
+    };
+
+    void copy(const String& other);
+
+    void setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
+    void setLast(unsigned in = last) { buf[last] = char(in); }
+
+public:
+    String() {
+        buf[0] = '\0';
+        setLast();
+    }
+
+    String(const char* in);
+
+    String(const String& other) { copy(other); }
+
+    ~String() {
+        if(!isOnStack())
+            delete[] data.ptr;
+    }
+
+    // GCC 4.9/5/6 report Wstrict-overflow when optimizations are ON and it got inlined in the vector class somewhere...
+    // see commit 574ef95f0cd379118be5011704664e4b5351f1e0 and build https://travis-ci.org/onqtam/doctest/builds/230671611
+    DOCTEST_NOINLINE String& operator=(const String& other) {
+        if(!isOnStack())
+            delete[] data.ptr;
+
+        copy(other);
+
+        return *this;
+    }
+    String& operator+=(const String& other);
+
+    String operator+(const String& other) const { return String(*this) += other; }
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+    String(String&& other);
+    String& operator=(String&& other);
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+
+    bool isOnStack() const { return (buf[last] & 128) == 0; }
+
+    char operator[](unsigned i) const { return const_cast<String*>(this)->operator[](i); } // NOLINT
+    char& operator[](unsigned i) {
+        if(isOnStack())
+            return reinterpret_cast<char*>(buf)[i];
+        return data.ptr[i];
+    }
+
+    const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT
+    char*       c_str() {
+        if(isOnStack())
+            return reinterpret_cast<char*>(buf);
+        return data.ptr;
+    }
+
+    unsigned size() const {
+        if(isOnStack())
+            return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32
+        return data.size;
+    }
+
+    unsigned capacity() const {
+        if(isOnStack())
+            return len;
+        return data.capacity;
+    }
+
+    int compare(const char* other, bool no_case = false) const;
+    int compare(const String& other, bool no_case = false) const;
+};
+
+// clang-format off
+inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
+inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
+inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
+inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
+inline bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; }
+inline bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; }
+// clang-format on
+
+DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& stream, const String& in);
+
+namespace detail
+{
+#ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT
+    namespace static_assert_impl
+    {
+        template <bool>
+        struct StaticAssertion;
+
+        template <>
+        struct StaticAssertion<true>
+        {};
+
+        template <int i>
+        struct StaticAssertionTest
+        {};
+    }  // namespace static_assert_impl
+#endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT
+
+    namespace traits
+    {
+        template <typename T>
+        struct remove_const
+        { typedef T type; };
+
+        template <typename T>
+        struct remove_const<const T>
+        { typedef T type; };
+
+        template <typename T>
+        struct remove_volatile
+        { typedef T type; };
+
+        template <typename T>
+        struct remove_volatile<volatile T>
+        { typedef T type; };
+
+        template <typename T>
+        struct remove_cv
+        { typedef typename remove_volatile<typename remove_const<T>::type>::type type; };
+
+        template <typename T>
+        struct is_pointer_helper
+        { static const bool value = false; };
+
+        template <typename T>
+        struct is_pointer_helper<T*>
+        // cppcheck-suppress unusedStructMember
+        { static const bool value = true; };
+
+        template <typename T>
+        struct is_pointer
+        // cppcheck-suppress unusedStructMember
+        { static const bool value = is_pointer_helper<typename remove_cv<T>::type>::value; };
+
+        template <bool CONDITION, typename TYPE = void>
+        struct enable_if
+        {};
+
+        template <typename TYPE>
+        struct enable_if<true, TYPE>
+        { typedef TYPE type; };
+
+        template <typename T>
+        struct remove_reference
+        { typedef T type; };
+
+        template <typename T>
+        struct remove_reference<T&>
+        { typedef T type; };
+
+        template <typename T, typename AT_1 = void>
+        class is_constructible_impl
+        {
+        private:
+            template <typename C_T, typename C_AT_1>
+            static bool test(typename enable_if< //!OCLINT avoid private static members
+                             sizeof(C_T) ==
+                             sizeof(C_T(static_cast<C_AT_1>(
+                                     *static_cast<typename remove_reference<C_AT_1>::type*>(
+                                             0))))>::type*);
+
+            template <typename, typename>
+            static int test(...); //!OCLINT avoid private static members
+
+        public:
+            static const bool value = sizeof(test<T, AT_1>(0)) == sizeof(bool);
+        };
+
+        template <typename T>
+        class is_constructible_impl<T, void>
+        {
+        private:
+            template <typename C_T>
+            static C_T testFun(C_T); //!OCLINT avoid private static members
+
+            template <typename C_T>
+            static bool test(typename enable_if< //!OCLINT avoid private static members
+                             sizeof(C_T) == sizeof(testFun(C_T()))>::type*);
+
+            template <typename>
+            static int test(...); //!OCLINT avoid private static members
+
+        public:
+            static const bool value = sizeof(test<T>(0)) == sizeof(bool);
+        };
+
+// is_constructible<> taken from here: http://stackoverflow.com/a/40443701/3162383
+// for GCC/Clang gives the same results as std::is_constructible<> - see here: https://wandbox.org/permlink/bNWr7Ii2fuz4Vf7A
+// modifications:
+// - reworked to support only 1 argument (mainly because of MSVC...)
+// - removed pointer support
+// MSVC support:
+// - for versions before 2012 read the CAUTION comment below
+// currently intended for use only in the Approx() helper for strong typedefs of double - see issue #62
+#ifndef _MSC_VER
+        template <typename T, typename AT_1 = void>
+        class is_constructible
+        {
+        public:
+            static const bool value = is_pointer<typename remove_reference<T>::type>::value ?
+                                              false :
+                                              is_constructible_impl<T, AT_1>::value;
+        };
+#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
+        template <typename T, typename AT_1>
+        struct is_constructible
+        { static const bool value = __is_constructible(T, AT_1); };
+#elif defined(_MSC_VER)
+        // !!! USE WITH CAUTION !!!
+        // will always return false - unable to implement this for versions of MSVC older than 2012 for now...
+        template <typename T, typename AT_1>
+        struct is_constructible
+        { static const bool value = false; };
+#endif // _MSC_VER
+    }  // namespace traits
+
+    template <typename T>
+    struct deferred_false
+    // cppcheck-suppress unusedStructMember
+    { static const bool value = false; };
+
+    // to silence the warning "-Wzero-as-null-pointer-constant" only for gcc 5 for the Approx template ctor - pragmas don't work for it...
+    inline void* getNull() { return 0; }
+
+    namespace has_insertion_operator_impl
+    {
+        typedef char no;
+        typedef char yes[2];
+
+        struct any_t
+        {
+            template <typename T>
+            // cppcheck-suppress noExplicitConstructor
+            any_t(const DOCTEST_REF_WRAP(T));
+        };
+
+        yes& testStreamable(std::ostream&);
+        no   testStreamable(no);
+
+        no operator<<(const std::ostream&, const any_t&);
+
+        template <typename T>
+        struct has_insertion_operator
+        {
+            static std::ostream& s;
+            static const DOCTEST_REF_WRAP(T) t;
+            static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
+        };
+    } // namespace has_insertion_operator_impl
+
+    template <typename T>
+    struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
+    {};
+
+    DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num);
+    DOCTEST_INTERFACE unsigned my_strlen(const char* in);
+
+    DOCTEST_INTERFACE std::ostream* createStream();
+    DOCTEST_INTERFACE String getStreamResult(std::ostream*);
+    DOCTEST_INTERFACE void   freeStream(std::ostream*);
+
+    template <bool C>
+    struct StringMakerBase
+    {
+        template <typename T>
+        static String convert(const DOCTEST_REF_WRAP(T)) {
+            return "{?}";
+        }
+    };
+
+    template <>
+    struct StringMakerBase<true>
+    {
+        template <typename T>
+        static String convert(const DOCTEST_REF_WRAP(T) in) {
+            std::ostream* stream = createStream();
+            *stream << in;
+            String result = getStreamResult(stream);
+            freeStream(stream);
+            return result;
+        }
+    };
+
+    DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size);
+
+    template <typename T>
+    String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) {
+        return rawMemoryToString(&object, sizeof(object));
+    }
+
+    class NullType
+    {};
+
+    template <class T, class U>
+    struct Typelist
+    {
+        typedef T Head;
+        typedef U Tail;
+    };
+
+    // type of recursive function
+    template <class TList, class Callable>
+    struct ForEachType;
+
+    // Recursion rule
+    template <class Head, class Tail, class Callable>
+    struct ForEachType<Typelist<Head, Tail>, Callable> : public ForEachType<Tail, Callable>
+    {
+        enum
+        {
+            value = 1 + ForEachType<Tail, Callable>::value
+        };
+
+        explicit ForEachType(Callable& callable)
+                : ForEachType<Tail, Callable>(callable) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+            callable.operator()<value, Head>();
+#else  // _MSC_VER
+            callable.template operator()<value, Head>();
+#endif // _MSC_VER
+        }
+    };
+
+    // Recursion end
+    template <class Head, class Callable>
+    struct ForEachType<Typelist<Head, NullType>, Callable>
+    {
+    public:
+        enum
+        {
+            value = 0
+        };
+
+        explicit ForEachType(Callable& callable) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+            callable.operator()<value, Head>();
+#else  // _MSC_VER
+            callable.template operator()<value, Head>();
+#endif // _MSC_VER
+        }
+    };
+
+    template <typename T>
+    const char* type_to_string() {
+        return "<>";
+    }
+} // namespace detail
+
+template <typename T1 = detail::NullType, typename T2 = detail::NullType,
+          typename T3 = detail::NullType, typename T4 = detail::NullType,
+          typename T5 = detail::NullType, typename T6 = detail::NullType,
+          typename T7 = detail::NullType, typename T8 = detail::NullType,
+          typename T9 = detail::NullType, typename T10 = detail::NullType,
+          typename T11 = detail::NullType, typename T12 = detail::NullType,
+          typename T13 = detail::NullType, typename T14 = detail::NullType,
+          typename T15 = detail::NullType, typename T16 = detail::NullType,
+          typename T17 = detail::NullType, typename T18 = detail::NullType,
+          typename T19 = detail::NullType, typename T20 = detail::NullType,
+          typename T21 = detail::NullType, typename T22 = detail::NullType,
+          typename T23 = detail::NullType, typename T24 = detail::NullType,
+          typename T25 = detail::NullType, typename T26 = detail::NullType,
+          typename T27 = detail::NullType, typename T28 = detail::NullType,
+          typename T29 = detail::NullType, typename T30 = detail::NullType,
+          typename T31 = detail::NullType, typename T32 = detail::NullType,
+          typename T33 = detail::NullType, typename T34 = detail::NullType,
+          typename T35 = detail::NullType, typename T36 = detail::NullType,
+          typename T37 = detail::NullType, typename T38 = detail::NullType,
+          typename T39 = detail::NullType, typename T40 = detail::NullType,
+          typename T41 = detail::NullType, typename T42 = detail::NullType,
+          typename T43 = detail::NullType, typename T44 = detail::NullType,
+          typename T45 = detail::NullType, typename T46 = detail::NullType,
+          typename T47 = detail::NullType, typename T48 = detail::NullType,
+          typename T49 = detail::NullType, typename T50 = detail::NullType,
+          typename T51 = detail::NullType, typename T52 = detail::NullType,
+          typename T53 = detail::NullType, typename T54 = detail::NullType,
+          typename T55 = detail::NullType, typename T56 = detail::NullType,
+          typename T57 = detail::NullType, typename T58 = detail::NullType,
+          typename T59 = detail::NullType, typename T60 = detail::NullType>
+struct Types
+{
+private:
+    typedef typename Types<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17,
+                           T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31,
+                           T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
+                           T46, T47, T48, T49, T50, T51, T52, T53, T54, T55, T56, T57, T58, T59,
+                           T60>::Result TailResult;
+
+public:
+    typedef detail::Typelist<T1, TailResult> Result;
+};
+
+template <>
+struct Types<>
+{ typedef detail::NullType Result; };
+
+template <typename T>
+struct StringMaker : detail::StringMakerBase<detail::has_insertion_operator<T>::value>
+{};
+
+template <typename T>
+struct StringMaker<T*>
+{
+    template <typename U>
+    static String convert(U* p) {
+        if(p)
+            return detail::rawMemoryToString(p);
+        return "NULL";
+    }
+};
+
+template <typename R, typename C>
+struct StringMaker<R C::*>
+{
+    static String convert(R C::*p) {
+        if(p)
+            return detail::rawMemoryToString(p);
+        return "NULL";
+    }
+};
+
+template <typename T>
+String toString(const DOCTEST_REF_WRAP(T) value) {
+    return StringMaker<T>::convert(value);
+}
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+DOCTEST_INTERFACE String toString(char* in);
+DOCTEST_INTERFACE String toString(const char* in);
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+DOCTEST_INTERFACE String toString(bool in);
+DOCTEST_INTERFACE String toString(float in);
+DOCTEST_INTERFACE String toString(double in);
+DOCTEST_INTERFACE String toString(double long in);
+
+DOCTEST_INTERFACE String toString(char in);
+DOCTEST_INTERFACE String toString(char signed in);
+DOCTEST_INTERFACE String toString(char unsigned in);
+DOCTEST_INTERFACE String toString(int short in);
+DOCTEST_INTERFACE String toString(int short unsigned in);
+DOCTEST_INTERFACE String toString(int in);
+DOCTEST_INTERFACE String toString(int unsigned in);
+DOCTEST_INTERFACE String toString(int long in);
+DOCTEST_INTERFACE String toString(int long unsigned in);
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+DOCTEST_INTERFACE String toString(int long long in);
+DOCTEST_INTERFACE String toString(int long long unsigned in);
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+#ifdef DOCTEST_CONFIG_WITH_NULLPTR
+DOCTEST_INTERFACE String toString(std::nullptr_t in);
+#endif // DOCTEST_CONFIG_WITH_NULLPTR
+
+class DOCTEST_INTERFACE Approx
+{
+public:
+    explicit Approx(double value);
+
+    Approx operator()(double value) const {
+        Approx approx(value);
+        approx.epsilon(m_epsilon);
+        approx.scale(m_scale);
+        return approx;
+    }
+
+    template <typename T>
+    explicit Approx(const T& value,
+                    typename detail::traits::enable_if<
+                            detail::traits::is_constructible<double, T>::value>::type* =
+                            static_cast<T*>(detail::getNull())) {
+        *this = Approx(static_cast<double>(value));
+    }
+
+    // clang-format off
+    // overloads for double - the first one is necessary as it is in the implementation part of doctest
+    // as for the others - keeping them for potentially faster compile times
+    DOCTEST_INTERFACE friend bool operator==(double lhs, Approx const& rhs);
+    friend bool operator==(Approx const& lhs, double rhs) { return operator==(rhs, lhs); }
+    friend bool operator!=(double lhs, Approx const& rhs) { return !operator==(lhs, rhs); }
+    friend bool operator!=(Approx const& lhs, double rhs) { return !operator==(rhs, lhs); }
+    friend bool operator<=(double lhs, Approx const& rhs) { return lhs < rhs.m_value || lhs == rhs; }
+    friend bool operator<=(Approx const& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; }
+    friend bool operator>=(double lhs, Approx const& rhs) { return lhs > rhs.m_value || lhs == rhs; }
+    friend bool operator>=(Approx const& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; }
+    friend bool operator< (double lhs, Approx const& rhs) { return lhs < rhs.m_value && lhs != rhs; }
+    friend bool operator< (Approx const& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; }
+    friend bool operator> (double lhs, Approx const& rhs) { return lhs > rhs.m_value && lhs != rhs; }
+    friend bool operator> (Approx const& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
+
+#define DOCTEST_APPROX_PREFIX \
+    template <typename T> friend typename detail::traits::enable_if<detail::traits::is_constructible<double, T>::value, bool>::type
+
+    DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); }
+    DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
+    DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
+    DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
+    DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; }
+    DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; }
+    DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; }
+    DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; }
+    DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; }
+    DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; }
+    DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; }
+    DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; }
+#undef DOCTEST_APPROX_PREFIX
+    // clang-format on
+
+    Approx& epsilon(double newEpsilon) {
+        m_epsilon = (newEpsilon);
+        return *this;
+    }
+
+    template <typename T>
+    typename detail::traits::enable_if<detail::traits::is_constructible<double, T>::value,
+                                       Approx&>::type
+    epsilon(const T& newEpsilon) {
+        m_epsilon = static_cast<double>(newEpsilon);
+        return *this;
+    }
+
+    Approx& scale(double newScale) {
+        m_scale = (newScale);
+        return *this;
+    }
+
+    template <typename T>
+    typename detail::traits::enable_if<detail::traits::is_constructible<double, T>::value,
+                                       Approx&>::type
+    scale(const T& newScale) {
+        m_scale = static_cast<double>(newScale);
+        return *this;
+    }
+
+    String toString() const;
+
+private:
+    double m_epsilon;
+    double m_scale;
+    double m_value;
+};
+
+template <>
+inline String toString<Approx>(const DOCTEST_REF_WRAP(Approx) value) {
+    return value.toString();
+}
+
+#if !defined(DOCTEST_CONFIG_DISABLE)
+
+namespace detail
+{
+    // the function type this library works with
+    typedef void (*funcType)();
+
+    namespace assertType
+    {
+        enum Enum
+        {
+            // macro traits
+
+            is_warn    = 1,
+            is_check   = 2,
+            is_require = 4,
+
+            is_throws    = 8,
+            is_throws_as = 16,
+            is_nothrow   = 32,
+
+            is_fast  = 64, // not checked anywhere - used just to distinguish the types
+            is_false = 128,
+            is_unary = 256,
+
+            is_eq = 512,
+            is_ne = 1024,
+
+            is_lt = 2048,
+            is_gt = 4096,
+
+            is_ge = 8192,
+            is_le = 16384,
+
+            // macro types
+
+            DT_WARN    = is_warn,
+            DT_CHECK   = is_check,
+            DT_REQUIRE = is_require,
+
+            DT_WARN_FALSE    = is_false | is_warn,
+            DT_CHECK_FALSE   = is_false | is_check,
+            DT_REQUIRE_FALSE = is_false | is_require,
+
+            DT_WARN_THROWS    = is_throws | is_warn,
+            DT_CHECK_THROWS   = is_throws | is_check,
+            DT_REQUIRE_THROWS = is_throws | is_require,
+
+            DT_WARN_THROWS_AS    = is_throws_as | is_warn,
+            DT_CHECK_THROWS_AS   = is_throws_as | is_check,
+            DT_REQUIRE_THROWS_AS = is_throws_as | is_require,
+
+            DT_WARN_NOTHROW    = is_nothrow | is_warn,
+            DT_CHECK_NOTHROW   = is_nothrow | is_check,
+            DT_REQUIRE_NOTHROW = is_nothrow | is_require,
+
+            DT_WARN_EQ    = is_eq | is_warn,
+            DT_CHECK_EQ   = is_eq | is_check,
+            DT_REQUIRE_EQ = is_eq | is_require,
+
+            DT_WARN_NE    = is_ne | is_warn,
+            DT_CHECK_NE   = is_ne | is_check,
+            DT_REQUIRE_NE = is_ne | is_require,
+
+            DT_WARN_GT    = is_gt | is_warn,
+            DT_CHECK_GT   = is_gt | is_check,
+            DT_REQUIRE_GT = is_gt | is_require,
+
+            DT_WARN_LT    = is_lt | is_warn,
+            DT_CHECK_LT   = is_lt | is_check,
+            DT_REQUIRE_LT = is_lt | is_require,
+
+            DT_WARN_GE    = is_ge | is_warn,
+            DT_CHECK_GE   = is_ge | is_check,
+            DT_REQUIRE_GE = is_ge | is_require,
+
+            DT_WARN_LE    = is_le | is_warn,
+            DT_CHECK_LE   = is_le | is_check,
+            DT_REQUIRE_LE = is_le | is_require,
+
+            DT_WARN_UNARY    = is_unary | is_warn,
+            DT_CHECK_UNARY   = is_unary | is_check,
+            DT_REQUIRE_UNARY = is_unary | is_require,
+
+            DT_WARN_UNARY_FALSE    = is_false | is_unary | is_warn,
+            DT_CHECK_UNARY_FALSE   = is_false | is_unary | is_check,
+            DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require,
+
+            DT_FAST_WARN_EQ    = is_fast | is_eq | is_warn,
+            DT_FAST_CHECK_EQ   = is_fast | is_eq | is_check,
+            DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require,
+
+            DT_FAST_WARN_NE    = is_fast | is_ne | is_warn,
+            DT_FAST_CHECK_NE   = is_fast | is_ne | is_check,
+            DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require,
+
+            DT_FAST_WARN_GT    = is_fast | is_gt | is_warn,
+            DT_FAST_CHECK_GT   = is_fast | is_gt | is_check,
+            DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require,
+
+            DT_FAST_WARN_LT    = is_fast | is_lt | is_warn,
+            DT_FAST_CHECK_LT   = is_fast | is_lt | is_check,
+            DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require,
+
+            DT_FAST_WARN_GE    = is_fast | is_ge | is_warn,
+            DT_FAST_CHECK_GE   = is_fast | is_ge | is_check,
+            DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require,
+
+            DT_FAST_WARN_LE    = is_fast | is_le | is_warn,
+            DT_FAST_CHECK_LE   = is_fast | is_le | is_check,
+            DT_FAST_REQUIRE_LE = is_fast | is_le | is_require,
+
+            DT_FAST_WARN_UNARY    = is_fast | is_unary | is_warn,
+            DT_FAST_CHECK_UNARY   = is_fast | is_unary | is_check,
+            DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require,
+
+            DT_FAST_WARN_UNARY_FALSE    = is_fast | is_false | is_unary | is_warn,
+            DT_FAST_CHECK_UNARY_FALSE   = is_fast | is_false | is_unary | is_check,
+            DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require
+        };
+    } // namespace assertType
+
+    DOCTEST_INTERFACE const char* getAssertString(assertType::Enum val);
+
+    // clang-format off
+    template<class T>               struct decay_array       { typedef T type; };
+    template<class T, unsigned N>   struct decay_array<T[N]> { typedef T* type; };
+    template<class T>               struct decay_array<T[]>  { typedef T* type; };
+
+    template<class T>   struct not_char_pointer              { enum { value = 1 }; };
+    template<>          struct not_char_pointer<char*>       { enum { value = 0 }; };
+    template<>          struct not_char_pointer<const char*> { enum { value = 0 }; };
+
+    template<class T> struct can_use_op : not_char_pointer<typename decay_array<T>::type> {};
+    // clang-format on
+
+    struct TestFailureException
+    {};
+
+    DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum assert_type);
+    DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags);
+    DOCTEST_INTERFACE void throwException();
+
+    struct TestAccessibleContextState
+    {
+        bool no_throw; // to skip exceptions-related assertion macros
+        bool success;  // include successful assertions in output
+    };
+
+    struct ContextState;
+
+    DOCTEST_INTERFACE TestAccessibleContextState* getTestsContextState();
+
+    struct DOCTEST_INTERFACE SubcaseSignature
+    {
+        const char* m_name;
+        const char* m_file;
+        int         m_line;
+
+        SubcaseSignature(const char* name, const char* file, int line)
+                : m_name(name)
+                , m_file(file)
+                , m_line(line) {}
+
+        bool operator<(const SubcaseSignature& other) const;
+    };
+
+    // cppcheck-suppress copyCtorAndEqOperator
+    struct DOCTEST_INTERFACE Subcase
+    {
+        SubcaseSignature m_signature;
+        bool             m_entered;
+
+        Subcase(const char* name, const char* file, int line);
+        Subcase(const Subcase& other);
+        ~Subcase();
+
+        operator bool() const { return m_entered; }
+    };
+
+    template <typename L, typename R>
+    String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
+                               const DOCTEST_REF_WRAP(R) rhs) {
+        return toString(lhs) + op + toString(rhs);
+    }
+
+    struct DOCTEST_INTERFACE Result
+    {
+        bool   m_passed;
+        String m_decomposition;
+
+        ~Result();
+
+        DOCTEST_NOINLINE Result(bool passed = false, const String& decomposition = String())
+                : m_passed(passed)
+                , m_decomposition(decomposition) {}
+
+        DOCTEST_NOINLINE Result(const Result& other)
+                : m_passed(other.m_passed)
+                , m_decomposition(other.m_decomposition) {}
+
+        Result& operator=(const Result& other);
+
+        operator bool() { return !m_passed; }
+
+        // clang-format off
+        // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence
+        template <typename R> Result& operator&  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator^  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator|  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator&& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator|| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator== (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator!= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator<  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator>  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator<= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator>= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator=  (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator+= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator-= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator*= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator/= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator%= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator<<=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator>>=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator&= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator^= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        template <typename R> Result& operator|= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false<R>::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; }
+        // clang-format on
+    };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wsign-compare"
+//#pragma clang diagnostic ignored "-Wdouble-promotion"
+//#pragma clang diagnostic ignored "-Wconversion"
+//#pragma clang diagnostic ignored "-Wfloat-equal"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
+//#pragma GCC diagnostic ignored "-Wdouble-promotion"
+#endif // > gcc 4.5
+//#pragma GCC diagnostic ignored "-Wconversion"
+//#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+// http://stackoverflow.com/questions/39479163 what's the difference between C4018 and C4389
+#pragma warning(disable : 4389) // 'operator' : signed/unsigned mismatch
+#pragma warning(disable : 4018) // 'expression' : signed/unsigned mismatch
+//#pragma warning(disable : 4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation
+#endif // _MSC_VER
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+// clang-format off
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE bool
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE typename traits::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
+    inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
+    inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
+    inline bool lt(const char* lhs, const char* rhs) { return String(lhs) <  String(rhs); }
+    inline bool gt(const char* lhs, const char* rhs) { return String(lhs) >  String(rhs); }
+    inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); }
+    inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE eq(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs == rhs; }
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE ne(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs != rhs; }
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE lt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs <  rhs; }
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE gt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs >  rhs; }
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE le(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs <= rhs; }
+    template <typename L, typename R> DOCTEST_COMPARISON_RETURN_TYPE ge(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs >= rhs; }
+// clang-format on
+
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) l == r
+#define DOCTEST_CMP_NE(l, r) l != r
+#define DOCTEST_CMP_GT(l, r) l > r
+#define DOCTEST_CMP_LT(l, r) l < r
+#define DOCTEST_CMP_GE(l, r) l >= r
+#define DOCTEST_CMP_LE(l, r) l <= r
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) eq(l, r)
+#define DOCTEST_CMP_NE(l, r) ne(l, r)
+#define DOCTEST_CMP_GT(l, r) gt(l, r)
+#define DOCTEST_CMP_LT(l, r) lt(l, r)
+#define DOCTEST_CMP_GE(l, r) ge(l, r)
+#define DOCTEST_CMP_LE(l, r) le(l, r)
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro)                              \
+    template <typename R>                                                                          \
+    DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) {                           \
+        bool res = op_macro(lhs, rhs);                                                             \
+        if(m_assert_type & assertType::is_false)                                                   \
+            res = !res;                                                                            \
+        if(!res || doctest::detail::getTestsContextState()->success)                               \
+            return Result(res, stringifyBinaryExpr(lhs, op_str, rhs));                             \
+        return Result(res);                                                                        \
+    }
+
+#define DOCTEST_FORBIT_EXPRESSION(op)                                                              \
+    template <typename R>                                                                          \
+    Expression_lhs& operator op(const R&) {                                                        \
+        DOCTEST_STATIC_ASSERT(deferred_false<R>::value,                                            \
+                              Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison);         \
+        return *this;                                                                              \
+    }
+
+    template <typename L>
+    // cppcheck-suppress copyCtorAndEqOperator
+    struct Expression_lhs
+    {
+        L                lhs;
+        assertType::Enum m_assert_type;
+
+        explicit Expression_lhs(L in, assertType::Enum assert_type)
+                : lhs(in)
+                , m_assert_type(assert_type) {}
+
+        Expression_lhs(const Expression_lhs& other)
+                : lhs(other.lhs) {}
+
+        DOCTEST_NOINLINE operator Result() {
+            bool res = !!lhs;
+            if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional
+                res = !res;
+
+            if(!res || getTestsContextState()->success)
+                return Result(res, toString(lhs));
+            return Result(res);
+        }
+
+        // clang-format off
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " >  ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " <  ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional
+        DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional
+        // clang-format on
+
+        // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence
+        DOCTEST_FORBIT_EXPRESSION(&)
+        DOCTEST_FORBIT_EXPRESSION (^)
+        DOCTEST_FORBIT_EXPRESSION(|)
+        DOCTEST_FORBIT_EXPRESSION(&&)
+        DOCTEST_FORBIT_EXPRESSION(||)
+        DOCTEST_FORBIT_EXPRESSION(=)
+        DOCTEST_FORBIT_EXPRESSION(+=)
+        DOCTEST_FORBIT_EXPRESSION(-=)
+        DOCTEST_FORBIT_EXPRESSION(*=)
+        DOCTEST_FORBIT_EXPRESSION(/=)
+        DOCTEST_FORBIT_EXPRESSION(%=)
+        DOCTEST_FORBIT_EXPRESSION(<<=)
+        DOCTEST_FORBIT_EXPRESSION(>>=)
+        DOCTEST_FORBIT_EXPRESSION(&=)
+        DOCTEST_FORBIT_EXPRESSION(^=)
+        DOCTEST_FORBIT_EXPRESSION(|=)
+        // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the
+        // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression...
+        DOCTEST_FORBIT_EXPRESSION(<<)
+        DOCTEST_FORBIT_EXPRESSION(>>)
+    };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+    struct ExpressionDecomposer
+    {
+        assertType::Enum m_assert_type;
+
+        ExpressionDecomposer(assertType::Enum assert_type)
+                : m_assert_type(assert_type) {}
+
+        // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table)
+        // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now...
+        // https://github.com/philsquared/Catch/issues/870
+        // https://github.com/philsquared/Catch/issues/565
+        template <typename L>
+        Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) {
+            return Expression_lhs<const DOCTEST_REF_WRAP(L)>(operand, m_assert_type);
+        }
+    };
+
+    struct DOCTEST_INTERFACE TestCase
+    {
+        // not used for determining uniqueness
+        funcType m_test;    // a function pointer to the test case
+        String m_full_name; // contains the name (only for templated test cases!) + the template type
+        const char* m_name;       // name of the test case
+        const char* m_type;       // for templated test cases - gets appended to the real name
+        const char* m_test_suite; // the test suite in which the test was added
+        const char* m_description;
+        bool        m_skip;
+        bool        m_may_fail;
+        bool        m_should_fail;
+        int         m_expected_failures;
+        double      m_timeout;
+
+        // fields by which uniqueness of test cases shall be determined
+        const char* m_file; // the file in which the test was registered
+        unsigned    m_line; // the line where the test was registered
+        int m_template_id; // an ID used to distinguish between the different versions of a templated test case
+
+        TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+                 const char* type = "", int template_id = -1);
+
+        // for gcc 4.7
+        DOCTEST_NOINLINE ~TestCase() {}
+
+        TestCase& operator*(const char* in);
+
+        template <typename T>
+        TestCase& operator*(const T& in) {
+            in.fill(*this);
+            return *this;
+        }
+
+        TestCase(const TestCase& other) { *this = other; }
+
+        TestCase& operator=(const TestCase& other);
+
+        bool operator<(const TestCase& other) const;
+    };
+
+    // forward declarations of functions used by the macros
+    DOCTEST_INTERFACE int regTest(const TestCase& tc);
+    DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts);
+
+    DOCTEST_INTERFACE void addFailedAssert(assertType::Enum assert_type);
+
+    DOCTEST_INTERFACE void logTestStart(const TestCase& tc);
+    DOCTEST_INTERFACE void logTestEnd();
+
+    DOCTEST_INTERFACE void logTestException(const String& what, bool crash = false);
+
+    DOCTEST_INTERFACE void logAssert(bool passed, const char* decomposition, bool threw,
+                                     const String& exception, const char* expr,
+                                     assertType::Enum assert_type, const char* file, int line);
+
+    DOCTEST_INTERFACE void logAssertThrows(bool threw, const char* expr,
+                                           assertType::Enum assert_type, const char* file,
+                                           int line);
+
+    DOCTEST_INTERFACE void logAssertThrowsAs(bool threw, bool threw_as, const char* as,
+                                             const String& exception, const char* expr,
+                                             assertType::Enum assert_type, const char* file,
+                                             int line);
+
+    DOCTEST_INTERFACE void logAssertNothrow(bool threw, const String& exception, const char* expr,
+                                            assertType::Enum assert_type, const char* file,
+                                            int line);
+
+    DOCTEST_INTERFACE bool isDebuggerActive();
+    DOCTEST_INTERFACE void writeToDebugConsole(const String&);
+
+    namespace binaryAssertComparison
+    {
+        enum Enum
+        {
+            eq = 0,
+            ne,
+            gt,
+            lt,
+            ge,
+            le
+        };
+    } // namespace binaryAssertComparison
+
+    // clang-format off
+    template <int, class L, class R> struct RelationalComparator     { bool operator()(const DOCTEST_REF_WRAP(L),     const DOCTEST_REF_WRAP(R)    ) const { return false;        } };
+    template <class L, class R> struct RelationalComparator<0, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return eq(lhs, rhs); } };
+    template <class L, class R> struct RelationalComparator<1, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ne(lhs, rhs); } };
+    template <class L, class R> struct RelationalComparator<2, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return gt(lhs, rhs); } };
+    template <class L, class R> struct RelationalComparator<3, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return lt(lhs, rhs); } };
+    template <class L, class R> struct RelationalComparator<4, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ge(lhs, rhs); } };
+    template <class L, class R> struct RelationalComparator<5, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return le(lhs, rhs); } };
+    // clang-format on
+
+    struct DOCTEST_INTERFACE ResultBuilder
+    {
+        assertType::Enum m_assert_type;
+        const char*      m_file;
+        int              m_line;
+        const char*      m_expr;
+        const char*      m_exception_type;
+
+        Result m_result;
+        bool   m_threw;
+        bool   m_threw_as;
+        bool   m_failed;
+        String m_exception;
+
+        ResultBuilder(assertType::Enum assert_type, const char* file, int line, const char* expr,
+                      const char* exception_type = "");
+
+        ~ResultBuilder();
+
+        void setResult(const Result& res) { m_result = res; }
+
+        template <int         comparison, typename L, typename R>
+        DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+                                            const DOCTEST_REF_WRAP(R) rhs) {
+            m_result.m_passed = RelationalComparator<comparison, L, R>()(lhs, rhs);
+            if(!m_result.m_passed || getTestsContextState()->success)
+                m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs);
+        }
+
+        template <typename L>
+        DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) {
+            m_result.m_passed = !!val;
+
+            if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional
+                m_result.m_passed = !m_result.m_passed;
+
+            if(!m_result.m_passed || getTestsContextState()->success)
+                m_result.m_decomposition = toString(val);
+        }
+
+        void unexpectedExceptionOccurred();
+
+        bool log();
+        void react() const;
+    };
+
+    namespace assertAction
+    {
+        enum Enum
+        {
+            nothing     = 0,
+            dbgbreak    = 1,
+            shouldthrow = 2
+        };
+    } // namespace assertAction
+
+    template <int        comparison, typename L, typename R>
+    DOCTEST_NOINLINE int fast_binary_assert(assertType::Enum assert_type, const char* file,
+                                            int line, const char* expr,
+                                            const DOCTEST_REF_WRAP(L) lhs,
+                                            const DOCTEST_REF_WRAP(R) rhs) {
+        ResultBuilder rb(assert_type, file, line, expr);
+
+        rb.m_result.m_passed = RelationalComparator<comparison, L, R>()(lhs, rhs);
+
+        if(!rb.m_result.m_passed || getTestsContextState()->success)
+            rb.m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs);
+
+        int res = 0;
+
+        if(rb.log())
+            res |= assertAction::dbgbreak;
+
+        if(rb.m_failed && checkIfShouldThrow(assert_type))
+            res |= assertAction::shouldthrow;
+
+#ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+        // #########################################################################################
+        // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION
+        // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+        // #########################################################################################
+        if(res & assertAction::dbgbreak)
+            DOCTEST_BREAK_INTO_DEBUGGER();
+        fastAssertThrowIfFlagSet(res);
+#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+        return res;
+    }
+
+    template <typename L>
+    DOCTEST_NOINLINE int fast_unary_assert(assertType::Enum assert_type, const char* file, int line,
+                                           const char* val_str, const DOCTEST_REF_WRAP(L) val) {
+        ResultBuilder rb(assert_type, file, line, val_str);
+
+        rb.m_result.m_passed = !!val;
+
+        if(assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional
+            rb.m_result.m_passed = !rb.m_result.m_passed;
+
+        if(!rb.m_result.m_passed || getTestsContextState()->success)
+            rb.m_result.m_decomposition = toString(val);
+
+        int res = 0;
+
+        if(rb.log())
+            res |= assertAction::dbgbreak;
+
+        if(rb.m_failed && checkIfShouldThrow(assert_type))
+            res |= assertAction::shouldthrow;
+
+#ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+        // #########################################################################################
+        // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION
+        // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+        // #########################################################################################
+        if(res & assertAction::dbgbreak)
+            DOCTEST_BREAK_INTO_DEBUGGER();
+        fastAssertThrowIfFlagSet(res);
+#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+        return res;
+    }
+
+    struct DOCTEST_INTERFACE IExceptionTranslator //!OCLINT destructor of virtual class
+    {
+        virtual ~IExceptionTranslator();
+        virtual bool translate(String&) const = 0;
+    };
+
+    template <typename T>
+    class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class
+    {
+    public:
+        explicit ExceptionTranslator(String (*translateFunction)(T))
+                : m_translateFunction(translateFunction) {}
+
+        bool translate(String& res) const {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+            try {
+                throw;
+                // cppcheck-suppress catchExceptionByValue
+            } catch(T ex) {                    // NOLINT
+                res = m_translateFunction(ex); //!OCLINT parameter reassignment
+                return true;
+            } catch(...) {} //!OCLINT -  empty catch statement
+#endif                      // DOCTEST_CONFIG_NO_EXCEPTIONS
+            ((void)res);    // to silence -Wunused-parameter
+            return false;
+        }
+
+    protected:
+        String (*m_translateFunction)(T);
+    };
+
+    DOCTEST_INTERFACE void registerExceptionTranslatorImpl(
+            const IExceptionTranslator* translateFunction);
+
+    // FIX FOR VISUAL STUDIO VERSIONS PRIOR TO 2015 - they failed to compile the call to operator<< with
+    // std::ostream passed as a reference noting that there is a use of an undefined type (which there isn't)
+    DOCTEST_INTERFACE void writeStringToStream(std::ostream* stream, const String& str);
+
+    template <bool C>
+    struct StringStreamBase
+    {
+        template <typename T>
+        static void convert(std::ostream* stream, const T& in) {
+            writeStringToStream(stream, toString(in));
+        }
+
+        // always treat char* as a string in this context - no matter
+        // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined
+        static void convert(std::ostream* stream, const char* in) {
+            writeStringToStream(stream, String(in));
+        }
+    };
+
+    template <>
+    struct StringStreamBase<true>
+    {
+        template <typename T>
+        static void convert(std::ostream* stream, const T& in) {
+            *stream << in;
+        }
+    };
+
+    template <typename T>
+    struct StringStream : StringStreamBase<has_insertion_operator<T>::value>
+    {};
+
+    template <typename T>
+    void toStream(std::ostream* stream, const T& value) {
+        StringStream<T>::convert(stream, value);
+    }
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, char* in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, const char* in);
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, bool in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, float in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, double in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, double long in);
+
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, char in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, char signed in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, char unsigned in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int short in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int short unsigned in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int unsigned in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int long in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int long unsigned in);
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int long long in);
+    DOCTEST_INTERFACE void toStream(std::ostream* stream, int long long unsigned in);
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+    struct IContextScope //!OCLINT destructor of virtual class
+    { virtual void build(std::ostream*) = 0; };
+
+    DOCTEST_INTERFACE void addToContexts(IContextScope* ptr);
+    DOCTEST_INTERFACE void popFromContexts();
+    DOCTEST_INTERFACE void useContextIfExceptionOccurred(IContextScope* ptr);
+
+    // cppcheck-suppress copyCtorAndEqOperator
+    class ContextBuilder
+    {
+        friend class ContextScope;
+
+        struct ICapture //!OCLINT destructor of virtual class
+        { virtual void toStream(std::ostream*) const = 0; };
+
+        template <typename T>
+        struct Capture : ICapture //!OCLINT destructor of virtual class
+        {
+            const T* capture;
+
+            explicit Capture(const T* in)
+                    : capture(in) {}
+            virtual void toStream(std::ostream* stream) const { // override
+                doctest::detail::toStream(stream, *capture);
+            }
+        };
+
+        struct Chunk
+        {
+            char buf[sizeof(Capture<char>)]; // place to construct a Capture<T>
+        };
+
+        struct Node
+        {
+            Chunk chunk;
+            Node* next;
+        };
+
+        Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK];
+        int   numCaptures;
+        Node* head;
+        Node* tail;
+
+        void build(std::ostream* stream) const {
+            int curr = 0;
+            // iterate over small buffer
+            while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)
+                reinterpret_cast<const ICapture*>(stackChunks[curr++].buf)->toStream(stream);
+            // iterate over list
+            Node* curr_elem = head;
+            while(curr < numCaptures) {
+                reinterpret_cast<const ICapture*>(curr_elem->chunk.buf)->toStream(stream);
+                curr_elem = curr_elem->next;
+                ++curr;
+            }
+        }
+
+        // steal the contents of the other - acting as a move constructor...
+        DOCTEST_NOINLINE ContextBuilder(ContextBuilder& other)
+                : numCaptures(other.numCaptures)
+                , head(other.head)
+                , tail(other.tail) {
+            other.numCaptures = 0;
+            other.head        = 0;
+            other.tail        = 0;
+            my_memcpy(stackChunks, other.stackChunks,
+                      unsigned(int(sizeof(Chunk)) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK));
+        }
+
+    public:
+        // cppcheck-suppress uninitMemberVar
+        DOCTEST_NOINLINE ContextBuilder() // NOLINT
+                : numCaptures(0)
+                , head(0)
+                , tail(0) {}
+
+        template <typename T>
+        DOCTEST_NOINLINE ContextBuilder& operator<<(T& in) {
+            Capture<T> temp(&in);
+
+            // construct either on stack or on heap
+            // copy the bytes for the whole object - including the vtable because we cant construct
+            // the object directly in the buffer using placement new - need the <new> header...
+            if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) {
+                my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk));
+            } else {
+                Node* curr = new Node;
+                curr->next = 0;
+                if(tail) {
+                    tail->next = curr;
+                    tail       = curr;
+                } else {
+                    head = tail = curr;
+                }
+
+                my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk));
+            }
+            ++numCaptures;
+            return *this;
+        }
+
+        DOCTEST_NOINLINE ~ContextBuilder() {
+            // free the linked list - the ones on the stack are left as-is
+            // no destructors are called at all - there is no need
+            while(head) {
+                Node* next = head->next;
+                delete head;
+                head = next;
+            }
+        }
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+        template <typename T>
+        ContextBuilder& operator<<(const T&&) {
+            DOCTEST_STATIC_ASSERT(
+                    deferred_false<T>::value,
+                    Cannot_pass_temporaries_or_rvalues_to_the_streaming_operator_because_it_caches_pointers_to_the_passed_objects_for_lazy_evaluation);
+            return *this;
+        }
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+    };
+
+    class ContextScope : public IContextScope //!OCLINT destructor of virtual class
+    {
+        ContextBuilder contextBuilder;
+        bool           built;
+
+    public:
+        DOCTEST_NOINLINE explicit ContextScope(ContextBuilder& temp)
+                : contextBuilder(temp)
+                , built(false) {
+            addToContexts(this);
+        }
+
+        DOCTEST_NOINLINE ~ContextScope() {
+            if(!built)
+                useContextIfExceptionOccurred(this);
+            popFromContexts();
+        }
+
+        void build(std::ostream* stream) {
+            built = true;
+            contextBuilder.build(stream);
+        }
+    };
+
+    class DOCTEST_INTERFACE MessageBuilder
+    {
+        std::ostream*                     m_stream;
+        const char*                       m_file;
+        int                               m_line;
+        doctest::detail::assertType::Enum m_severity;
+
+    public:
+        MessageBuilder(const char* file, int line, doctest::detail::assertType::Enum severity);
+        ~MessageBuilder();
+
+        template <typename T>
+        MessageBuilder& operator<<(const T& in) {
+            doctest::detail::toStream(m_stream, in);
+            return *this;
+        }
+
+        bool log();
+        void react();
+    };
+} // namespace detail
+
+struct test_suite
+{
+    const char* data;
+    test_suite(const char* in)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_test_suite = data; }
+    void fill(detail::TestSuite& state) const { state.m_test_suite = data; }
+};
+
+struct description
+{
+    const char* data;
+    description(const char* in)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_description = data; }
+    void fill(detail::TestSuite& state) const { state.m_description = data; }
+};
+
+struct skip
+{
+    bool data;
+    skip(bool in = true)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_skip = data; }
+    void fill(detail::TestSuite& state) const { state.m_skip = data; }
+};
+
+struct timeout
+{
+    double data;
+    timeout(double in)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_timeout = data; }
+    void fill(detail::TestSuite& state) const { state.m_timeout = data; }
+};
+
+struct may_fail
+{
+    bool data;
+    may_fail(bool in = true)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_may_fail = data; }
+    void fill(detail::TestSuite& state) const { state.m_may_fail = data; }
+};
+
+struct should_fail
+{
+    bool data;
+    should_fail(bool in = true)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_should_fail = data; }
+    void fill(detail::TestSuite& state) const { state.m_should_fail = data; }
+};
+
+struct expected_failures
+{
+    int data;
+    expected_failures(int in)
+            : data(in) {}
+    void fill(detail::TestCase& state) const { state.m_expected_failures = data; }
+    void fill(detail::TestSuite& state) const { state.m_expected_failures = data; }
+};
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+#ifndef DOCTEST_CONFIG_DISABLE
+template <typename T>
+int registerExceptionTranslator(String (*translateFunction)(T)) {
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif // __clang__
+    static detail::ExceptionTranslator<T> exceptionTranslator(translateFunction);
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+    detail::registerExceptionTranslatorImpl(&exceptionTranslator);
+    return 0;
+}
+
+#else  // DOCTEST_CONFIG_DISABLE
+template <typename T>
+int registerExceptionTranslator(String (*)(T)) {
+    return 0;
+}
+#endif // DOCTEST_CONFIG_DISABLE
+
+DOCTEST_INTERFACE bool isRunningInTest();
+
+// cppcheck-suppress noCopyConstructor
+class DOCTEST_INTERFACE Context
+{
+#if !defined(DOCTEST_CONFIG_DISABLE)
+    detail::ContextState* p;
+
+    void parseArgs(int argc, const char* const* argv, bool withDefaults = false);
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+public:
+    explicit Context(int argc = 0, const char* const* argv = 0);
+
+    ~Context();
+
+    void applyCommandLine(int argc, const char* const* argv);
+
+    void addFilter(const char* filter, const char* value);
+    void clearFilters();
+    void setOption(const char* option, int value);
+    void setOption(const char* option, const char* value);
+
+    bool shouldExit();
+
+    int run();
+};
+
+} // namespace doctest
+
+// if registering is not disabled
+#if !defined(DOCTEST_CONFIG_DISABLE)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_EXPAND_VA_ARGS(...) __VA_ARGS__
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_EXPAND_VA_ARGS
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_STRIP_PARENS(x) x
+#define DOCTEST_HANDLE_BRACED_VA_ARGS(expr) DOCTEST_STRIP_PARENS(DOCTEST_EXPAND_VA_ARGS expr)
+
+// registers the test by initializing a dummy var with a function
+#define DOCTEST_REGISTER_FUNCTION(f, decorators)                                                   \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = doctest::detail::regTest(  \
+            doctest::detail::TestCase(f, __FILE__, __LINE__,                                       \
+                                      doctest_detail_test_suite_ns::getCurrentTestSuite()) *       \
+            decorators);                                                                           \
+    DOCTEST_GLOBAL_NO_WARNINGS_END()
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators)                                     \
+    namespace                                                                                      \
+    {                                                                                              \
+        struct der : base                                                                          \
+        { void f(); };                                                                             \
+        static void func() {                                                                       \
+            der v;                                                                                 \
+            v.f();                                                                                 \
+        }                                                                                          \
+        DOCTEST_REGISTER_FUNCTION(func, decorators)                                                \
+    }                                                                                              \
+    inline DOCTEST_NOINLINE void der::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators)                                        \
+    static void f();                                                                               \
+    DOCTEST_REGISTER_FUNCTION(f, decorators)                                                       \
+    static void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(decorators)                                                              \
+    DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(c, decorators)                                                   \
+    DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c,                          \
+                              DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators)
+
+// for converting types to strings without the <typeinfo> header and demangling
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TYPE_TO_STRING_IMPL(...)                                                           \
+    template <>                                                                                    \
+    inline const char* type_to_string<__VA_ARGS__>() {                                             \
+        return "<" #__VA_ARGS__ ">";                                                               \
+    }
+#define DOCTEST_TYPE_TO_STRING(...)                                                                \
+    namespace doctest                                                                              \
+    {                                                                                              \
+        namespace detail                                                                           \
+        { DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) }                                               \
+    }                                                                                              \
+    typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TYPE_TO_STRING_IMPL(x)                                                             \
+    template <>                                                                                    \
+    inline const char* type_to_string<x>() {                                                       \
+        return "<" #x ">";                                                                         \
+    }
+#define DOCTEST_TYPE_TO_STRING(x)                                                                  \
+    namespace doctest                                                                              \
+    {                                                                                              \
+        namespace detail                                                                           \
+        { DOCTEST_TYPE_TO_STRING_IMPL(x) }                                                         \
+    }                                                                                              \
+    typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+// for typed tests
+#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, anon)                                \
+    template <typename T>                                                                          \
+    inline void anon();                                                                            \
+    struct DOCTEST_CAT(anon, FUNCTOR)                                                              \
+    {                                                                                              \
+        template <int Index, typename Type>                                                        \
+        void          operator()() {                                                               \
+            doctest::detail::regTest(                                                              \
+                    doctest::detail::TestCase(anon<Type>, __FILE__, __LINE__,                      \
+                                              doctest_detail_test_suite_ns::getCurrentTestSuite(), \
+                                              doctest::detail::type_to_string<Type>(), Index) *    \
+                    decorators);                                                                   \
+        }                                                                                          \
+    };                                                                                             \
+    inline int DOCTEST_CAT(anon, REG_FUNC)() {                                                     \
+        DOCTEST_CAT(anon, FUNCTOR) registrar;                                                      \
+        doctest::detail::ForEachType<DOCTEST_HANDLE_BRACED_VA_ARGS(types)::Result,                 \
+                                     DOCTEST_CAT(anon, FUNCTOR)>                                   \
+                doIt(registrar);                                                                   \
+        return 0;                                                                                  \
+    }                                                                                              \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)();          \
+    DOCTEST_GLOBAL_NO_WARNINGS_END()                                                               \
+    template <typename T>                                                                          \
+    inline void anon()
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, ...)                                             \
+    DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, (__VA_ARGS__),                                  \
+                                    DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, types)                                           \
+    DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, anon)                            \
+    template <typename T>                                                                          \
+    inline void anon();                                                                            \
+    struct DOCTEST_CAT(id, _FUNCTOR)                                                               \
+    {                                                                                              \
+        int m_line;                                                                                \
+        DOCTEST_CAT(id, _FUNCTOR)                                                                  \
+        (int line)                                                                                 \
+                : m_line(line) {}                                                                  \
+        template <int Index, typename Type>                                                        \
+        void          operator()() {                                                               \
+            doctest::detail::regTest(                                                              \
+                    doctest::detail::TestCase(anon<Type>, __FILE__, __LINE__,                      \
+                                              doctest_detail_test_suite_ns::getCurrentTestSuite(), \
+                                              doctest::detail::type_to_string<Type>(),             \
+                                              m_line * 1000 + Index) *                             \
+                    decorators);                                                                   \
+        }                                                                                          \
+    };                                                                                             \
+    template <typename T>                                                                          \
+    inline void anon()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(decorators, T, id)                                       \
+    DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, anon)                               \
+    static int DOCTEST_CAT(anon, REG_FUNC)() {                                                     \
+        DOCTEST_CAT(id, _FUNCTOR) registrar(__LINE__);                                             \
+        doctest::detail::ForEachType<DOCTEST_HANDLE_BRACED_VA_ARGS(types)::Result,                 \
+                                     DOCTEST_CAT(id, _FUNCTOR)>                                    \
+                doIt(registrar);                                                                   \
+        return 0;                                                                                  \
+    }                                                                                              \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)();          \
+    DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...)                                            \
+    DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, (__VA_ARGS__),                                 \
+                                                DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types)                                          \
+    DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_))
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+// for subcases
+#if defined(__GNUC__)
+#define DOCTEST_SUBCASE(name)                                                                      \
+    if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_)                  \
+                                                __attribute__((unused)) =                          \
+               doctest::detail::Subcase(name, __FILE__, __LINE__))
+#else // __GNUC__
+#define DOCTEST_SUBCASE(name)                                                                      \
+    if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) =                \
+               doctest::detail::Subcase(name, __FILE__, __LINE__))
+#endif // __GNUC__
+
+// for grouping tests in test suites by using code blocks
+#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name)                                               \
+    namespace ns_name                                                                              \
+    {                                                                                              \
+        namespace doctest_detail_test_suite_ns                                                     \
+        {                                                                                          \
+            inline DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() {            \
+                static doctest::detail::TestSuite data;                                            \
+                static bool                       inited = false;                                  \
+                if(!inited) {                                                                      \
+                    data* decorators;                                                              \
+                    inited = true;                                                                 \
+                }                                                                                  \
+                return data;                                                                       \
+            }                                                                                      \
+        }                                                                                          \
+    }                                                                                              \
+    namespace ns_name
+
+#define DOCTEST_TEST_SUITE(decorators)                                                             \
+    DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_))
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE_BEGIN(decorators)                                                       \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) =                            \
+            doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators);              \
+    DOCTEST_GLOBAL_NO_WARNINGS_END()                                                               \
+    typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END                                                                     \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) =                            \
+            doctest::detail::setTestSuite(doctest::detail::TestSuite() * "");                      \
+    DOCTEST_GLOBAL_NO_WARNINGS_END()                                                               \
+    typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+// for registering exception translators
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature)                      \
+    static doctest::String translatorName(signature);                                              \
+    DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) =                     \
+            doctest::registerExceptionTranslator(translatorName);                                  \
+    DOCTEST_GLOBAL_NO_WARNINGS_END()                                                               \
+    static doctest::String translatorName(signature)
+
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature)                                           \
+    DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_),       \
+                                               signature)
+
+// for logging
+#define DOCTEST_INFO(x)                                                                            \
+    doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)(                            \
+            doctest::detail::ContextBuilder() << x)
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
+
+#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x)                                               \
+    do {                                                                                           \
+        doctest::detail::MessageBuilder mb(file, line, doctest::detail::assertType::type);         \
+        mb << x;                                                                                   \
+        if(mb.log())                                                                               \
+            DOCTEST_BREAK_INTO_DEBUGGER();                                                         \
+        mb.react();                                                                                \
+    } while((void)0, 0)
+
+// clang-format off
+#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+// clang-format on
+
+#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x)
+#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x)
+#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x)
+
+#if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1910)
+template <class T, T x>
+constexpr T to_lvalue = x;
+#define DOCTEST_TO_LVALUE(...) to_lvalue<decltype(__VA_ARGS__), __VA_ARGS__>
+#else
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TO_LVALUE(...) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TO_LVALUE(x) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#endif // TO_LVALUE hack for logging macros like INFO()
+
+// common code in asserts - for convenience
+#define DOCTEST_ASSERT_LOG_AND_REACT(rb)                                                           \
+    if(rb.log())                                                                                   \
+        DOCTEST_BREAK_INTO_DEBUGGER();                                                             \
+    rb.react()
+
+#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x) x;
+#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x)                                                                     \
+    try {                                                                                          \
+        x;                                                                                         \
+    } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); }
+#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#define DOCTEST_ASSERT_IMPLEMENT_3(expr, assert_type)                                              \
+    doctest::detail::ResultBuilder _DOCTEST_RB(                                                    \
+            doctest::detail::assertType::assert_type, __FILE__, __LINE__,                          \
+            DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)));                                   \
+    DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult(                                                     \
+            doctest::detail::ExpressionDecomposer(doctest::detail::assertType::assert_type)        \
+            << DOCTEST_HANDLE_BRACED_VA_ARGS(expr)))                                               \
+    DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB)
+
+#if defined(__clang__)
+#define DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type)                                              \
+    _Pragma("clang diagnostic push")                                                               \
+            _Pragma("clang diagnostic ignored \"-Woverloaded-shift-op-parentheses\"")              \
+                    DOCTEST_ASSERT_IMPLEMENT_3(expr, assert_type);                                 \
+    _Pragma("clang diagnostic pop")
+#else // __clang__
+#define DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type) DOCTEST_ASSERT_IMPLEMENT_3(expr, assert_type);
+#endif // __clang__
+
+#define DOCTEST_ASSERT_IMPLEMENT_1(expr, assert_type)                                              \
+    do {                                                                                           \
+        DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type);                                             \
+    } while((void)0, 0)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN)
+#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK)
+#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE)
+#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN_FALSE)
+#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK_FALSE)
+#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE_FALSE)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN)
+#define DOCTEST_CHECK(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK)
+#define DOCTEST_REQUIRE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE)
+#define DOCTEST_WARN_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN_FALSE)
+#define DOCTEST_CHECK_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK_FALSE)
+#define DOCTEST_REQUIRE_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE_FALSE)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+// clang-format off
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN); } while((void)0, 0)
+#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK); } while((void)0, 0)
+#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE); } while((void)0, 0)
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN_FALSE); } while((void)0, 0)
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK_FALSE); } while((void)0, 0)
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE_FALSE); } while((void)0, 0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN); } while((void)0, 0)
+#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK); } while((void)0, 0)
+#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE); } while((void)0, 0)
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN_FALSE); } while((void)0, 0)
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK_FALSE); } while((void)0, 0)
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE_FALSE); } while((void)0, 0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+// clang-format on
+
+#define DOCTEST_ASSERT_THROWS(expr, assert_type)                                                   \
+    do {                                                                                           \
+        if(!doctest::detail::getTestsContextState()->no_throw) {                                   \
+            doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type,   \
+                                                       __FILE__, __LINE__, #expr);                 \
+            try {                                                                                  \
+                expr;                                                                              \
+            } catch(...) { _DOCTEST_RB.m_threw = true; }                                           \
+            DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                             \
+        }                                                                                          \
+    } while((void)0, 0)
+
+#define DOCTEST_ASSERT_THROWS_AS(expr, as, assert_type)                                            \
+    do {                                                                                           \
+        if(!doctest::detail::getTestsContextState()->no_throw) {                                   \
+            doctest::detail::ResultBuilder _DOCTEST_RB(                                            \
+                    doctest::detail::assertType::assert_type, __FILE__, __LINE__, #expr,           \
+                    DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(as)));                             \
+            try {                                                                                  \
+                expr;                                                                              \
+            } catch(DOCTEST_HANDLE_BRACED_VA_ARGS(as)) {                                           \
+                _DOCTEST_RB.m_threw    = true;                                                     \
+                _DOCTEST_RB.m_threw_as = true;                                                     \
+            } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); }                            \
+            DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                             \
+        }                                                                                          \
+    } while((void)0, 0)
+
+#define DOCTEST_ASSERT_NOTHROW(expr, assert_type)                                                  \
+    do {                                                                                           \
+        if(!doctest::detail::getTestsContextState()->no_throw) {                                   \
+            doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type,   \
+                                                       __FILE__, __LINE__, #expr);                 \
+            try {                                                                                  \
+                expr;                                                                              \
+            } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); }                            \
+            DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                             \
+        }                                                                                          \
+    } while((void)0, 0)
+
+#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_WARN_THROWS)
+#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_CHECK_THROWS)
+#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_REQUIRE_THROWS)
+
+// clang-format off
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_WARN_THROWS_AS)
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_CHECK_THROWS_AS)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_REQUIRE_THROWS_AS)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_WARN_THROWS_AS)
+#define DOCTEST_CHECK_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_CHECK_THROWS_AS)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_REQUIRE_THROWS_AS)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+// clang-format on
+
+#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW)
+#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW)
+#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW)
+
+// clang-format off
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0)
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0)
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0)
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0)
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0)
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0)
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0)
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0)
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0)
+// clang-format on
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_BINARY_ASSERT(assert_type, expr, comp)                                             \
+    do {                                                                                           \
+        doctest::detail::ResultBuilder _DOCTEST_RB(                                                \
+                doctest::detail::assertType::assert_type, __FILE__, __LINE__,                      \
+                DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)));                               \
+        DOCTEST_WRAP_IN_TRY(                                                                       \
+                _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>(          \
+                        DOCTEST_HANDLE_BRACED_VA_ARGS(expr)))                                      \
+        DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                                 \
+    } while((void)0, 0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_BINARY_ASSERT(assert_type, lhs, rhs, comp)                                         \
+    do {                                                                                           \
+        doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type,       \
+                                                   __FILE__, __LINE__, #lhs ", " #rhs);            \
+        DOCTEST_WRAP_IN_TRY(                                                                       \
+                _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>(lhs,      \
+                                                                                         rhs))     \
+        DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                                 \
+    } while((void)0, 0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_UNARY_ASSERT(assert_type, expr)                                                    \
+    do {                                                                                           \
+        doctest::detail::ResultBuilder _DOCTEST_RB(                                                \
+                doctest::detail::assertType::assert_type, __FILE__, __LINE__,                      \
+                DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)));                               \
+        DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)))         \
+        DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB);                                                 \
+    } while((void)0, 0)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, (__VA_ARGS__), ne)
+#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, (__VA_ARGS__), ne)
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, (__VA_ARGS__), ne)
+#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, (__VA_ARGS__), gt)
+#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, (__VA_ARGS__), gt)
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, (__VA_ARGS__), gt)
+#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, (__VA_ARGS__), lt)
+#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, (__VA_ARGS__), lt)
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, (__VA_ARGS__), lt)
+#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, (__VA_ARGS__), ge)
+#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, (__VA_ARGS__), ge)
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, (__VA_ARGS__), ge)
+#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, (__VA_ARGS__), le)
+#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, (__VA_ARGS__), le)
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, (__VA_ARGS__), le)
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, (__VA_ARGS__))
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, (__VA_ARGS__))
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, (__VA_ARGS__))
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, (__VA_ARGS__))
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, (__VA_ARGS__))
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, (__VA_ARGS__))
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, lhs, rhs, eq)
+#define DOCTEST_CHECK_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, lhs, rhs, eq)
+#define DOCTEST_REQUIRE_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, lhs, rhs, eq)
+#define DOCTEST_WARN_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_NE, lhs, rhs, ne)
+#define DOCTEST_CHECK_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, lhs, rhs, ne)
+#define DOCTEST_REQUIRE_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, lhs, rhs, ne)
+#define DOCTEST_WARN_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GT, lhs, rhs, gt)
+#define DOCTEST_CHECK_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, lhs, rhs, gt)
+#define DOCTEST_REQUIRE_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, lhs, rhs, gt)
+#define DOCTEST_WARN_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lhs, rhs, lt)
+#define DOCTEST_CHECK_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lhs, rhs, lt)
+#define DOCTEST_REQUIRE_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lhs, rhs, lt)
+#define DOCTEST_WARN_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GE, lhs, rhs, ge)
+#define DOCTEST_CHECK_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, lhs, rhs, ge)
+#define DOCTEST_REQUIRE_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, lhs, rhs, ge)
+#define DOCTEST_WARN_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LE, lhs, rhs, le)
+#define DOCTEST_CHECK_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, lhs, rhs, le)
+#define DOCTEST_REQUIRE_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, lhs, rhs, le)
+
+#define DOCTEST_WARN_UNARY(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, v)
+#define DOCTEST_CHECK_UNARY(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, v)
+#define DOCTEST_REQUIRE_UNARY(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, v)
+#define DOCTEST_WARN_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, v)
+#define DOCTEST_CHECK_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, v)
+#define DOCTEST_REQUIRE_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, v)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison)                                  \
+    do {                                                                                           \
+        int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert<                               \
+                doctest::detail::binaryAssertComparison::comparison>(                              \
+                doctest::detail::assertType::assert_type, __FILE__, __LINE__,                      \
+                DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)),                                \
+                DOCTEST_HANDLE_BRACED_VA_ARGS(expr));                                              \
+        if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak)                            \
+            DOCTEST_BREAK_INTO_DEBUGGER();                                                         \
+        doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES);                              \
+    } while((void)0, 0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison)                              \
+    do {                                                                                           \
+        int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert<                               \
+                doctest::detail::binaryAssertComparison::comparison>(                              \
+                doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs, \
+                rhs);                                                                              \
+        if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak)                            \
+            DOCTEST_BREAK_INTO_DEBUGGER();                                                         \
+        doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES);                              \
+    } while((void)0, 0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr)                                               \
+    do {                                                                                           \
+        int _DOCTEST_FAST_RES = doctest::detail::fast_unary_assert(                                \
+                doctest::detail::assertType::assert_type, __FILE__, __LINE__,                      \
+                DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)),                                \
+                DOCTEST_HANDLE_BRACED_VA_ARGS(expr));                                              \
+        if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak)                            \
+            DOCTEST_BREAK_INTO_DEBUGGER();                                                         \
+        doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES);                              \
+    } while((void)0, 0)
+
+#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison)                                  \
+    doctest::detail::fast_binary_assert<doctest::detail::binaryAssertComparison::comparison>(      \
+            doctest::detail::assertType::assert_type, __FILE__, __LINE__,                          \
+            DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)),                                    \
+            DOCTEST_HANDLE_BRACED_VA_ARGS(expr))
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison)                              \
+    doctest::detail::fast_binary_assert<doctest::detail::binaryAssertComparison::comparison>(      \
+            doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs,     \
+            rhs)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr)                                               \
+    doctest::detail::fast_unary_assert(doctest::detail::assertType::assert_type, __FILE__,         \
+                                       __LINE__,                                                   \
+                                       DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)),         \
+                                       DOCTEST_HANDLE_BRACED_VA_ARGS(expr))
+
+#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+// clang-format off
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_WARN_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_FAST_CHECK_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_FAST_REQUIRE_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, (__VA_ARGS__), eq)
+#define DOCTEST_FAST_WARN_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, (__VA_ARGS__), ne)
+#define DOCTEST_FAST_CHECK_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, (__VA_ARGS__), ne)
+#define DOCTEST_FAST_REQUIRE_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, (__VA_ARGS__), ne)
+#define DOCTEST_FAST_WARN_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, (__VA_ARGS__), gt)
+#define DOCTEST_FAST_CHECK_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, (__VA_ARGS__), gt)
+#define DOCTEST_FAST_REQUIRE_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, (__VA_ARGS__), gt)
+#define DOCTEST_FAST_WARN_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, (__VA_ARGS__), lt)
+#define DOCTEST_FAST_CHECK_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, (__VA_ARGS__), lt)
+#define DOCTEST_FAST_REQUIRE_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, (__VA_ARGS__), lt)
+#define DOCTEST_FAST_WARN_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, (__VA_ARGS__), ge)
+#define DOCTEST_FAST_CHECK_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, (__VA_ARGS__), ge)
+#define DOCTEST_FAST_REQUIRE_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, (__VA_ARGS__), ge)
+#define DOCTEST_FAST_WARN_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, (__VA_ARGS__), le)
+#define DOCTEST_FAST_CHECK_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, (__VA_ARGS__), le)
+#define DOCTEST_FAST_REQUIRE_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, (__VA_ARGS__), le)
+
+#define DOCTEST_FAST_WARN_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, (__VA_ARGS__))
+#define DOCTEST_FAST_CHECK_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, (__VA_ARGS__))
+#define DOCTEST_FAST_REQUIRE_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, (__VA_ARGS__))
+#define DOCTEST_FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, (__VA_ARGS__))
+#define DOCTEST_FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, (__VA_ARGS__))
+#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, (__VA_ARGS__))
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_FAST_WARN_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, l, r, eq)
+#define DOCTEST_FAST_CHECK_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, l, r, eq)
+#define DOCTEST_FAST_REQUIRE_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, l, r, eq)
+#define DOCTEST_FAST_WARN_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, l, r, ne)
+#define DOCTEST_FAST_CHECK_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, l, r, ne)
+#define DOCTEST_FAST_REQUIRE_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, l, r, ne)
+#define DOCTEST_FAST_WARN_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, l, r, gt)
+#define DOCTEST_FAST_CHECK_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, l, r, gt)
+#define DOCTEST_FAST_REQUIRE_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, l, r, gt)
+#define DOCTEST_FAST_WARN_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, l, r, lt)
+#define DOCTEST_FAST_CHECK_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, l, r, lt)
+#define DOCTEST_FAST_REQUIRE_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, l, r, lt)
+#define DOCTEST_FAST_WARN_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, l, r, ge)
+#define DOCTEST_FAST_CHECK_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, l, r, ge)
+#define DOCTEST_FAST_REQUIRE_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, l, r, ge)
+#define DOCTEST_FAST_WARN_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, l, r, le)
+#define DOCTEST_FAST_CHECK_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, l, r, le)
+#define DOCTEST_FAST_REQUIRE_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, l, r, le)
+
+#define DOCTEST_FAST_WARN_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, v)
+#define DOCTEST_FAST_CHECK_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, v)
+#define DOCTEST_FAST_REQUIRE_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, v)
+#define DOCTEST_FAST_WARN_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, v)
+#define DOCTEST_FAST_CHECK_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, v)
+#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, v)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+// clang-format on
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#undef DOCTEST_WARN_THROWS
+#undef DOCTEST_CHECK_THROWS
+#undef DOCTEST_REQUIRE_THROWS
+#undef DOCTEST_WARN_THROWS_AS
+#undef DOCTEST_CHECK_THROWS_AS
+#undef DOCTEST_REQUIRE_THROWS_AS
+#undef DOCTEST_WARN_NOTHROW
+#undef DOCTEST_CHECK_NOTHROW
+#undef DOCTEST_REQUIRE_NOTHROW
+
+#undef DOCTEST_WARN_THROWS_MESSAGE
+#undef DOCTEST_CHECK_THROWS_MESSAGE
+#undef DOCTEST_REQUIRE_THROWS_MESSAGE
+#undef DOCTEST_WARN_THROWS_AS_MESSAGE
+#undef DOCTEST_CHECK_THROWS_AS_MESSAGE
+#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE
+#undef DOCTEST_WARN_NOTHROW_MESSAGE
+#undef DOCTEST_CHECK_NOTHROW_MESSAGE
+#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#define DOCTEST_WARN_THROWS(expr) ((void)0)
+#define DOCTEST_CHECK_THROWS(expr) ((void)0)
+#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
+#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
+#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
+
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#undef DOCTEST_REQUIRE
+#undef DOCTEST_REQUIRE_FALSE
+#undef DOCTEST_REQUIRE_MESSAGE
+#undef DOCTEST_REQUIRE_FALSE_MESSAGE
+#undef DOCTEST_REQUIRE_EQ
+#undef DOCTEST_REQUIRE_NE
+#undef DOCTEST_REQUIRE_GT
+#undef DOCTEST_REQUIRE_LT
+#undef DOCTEST_REQUIRE_GE
+#undef DOCTEST_REQUIRE_LE
+#undef DOCTEST_REQUIRE_UNARY
+#undef DOCTEST_REQUIRE_UNARY_FALSE
+#undef DOCTEST_FAST_REQUIRE_EQ
+#undef DOCTEST_FAST_REQUIRE_NE
+#undef DOCTEST_FAST_REQUIRE_GT
+#undef DOCTEST_FAST_REQUIRE_LT
+#undef DOCTEST_FAST_REQUIRE_GE
+#undef DOCTEST_FAST_REQUIRE_LE
+#undef DOCTEST_FAST_REQUIRE_UNARY
+#undef DOCTEST_FAST_REQUIRE_UNARY_FALSE
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+// =================================================================================================
+// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING!                      ==
+// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY!                            ==
+// =================================================================================================
+#else // DOCTEST_CONFIG_DISABLE
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name)                                           \
+    namespace                                                                                      \
+    {                                                                                              \
+        template <typename T>                                                                      \
+        struct der : base                                                                          \
+        { void f(); };                                                                             \
+    }                                                                                              \
+    template <typename T>                                                                          \
+    inline void der<T>::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name)                                              \
+    template <typename T>                                                                          \
+    static inline void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(name)                                                                    \
+    DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(x, name)                                                         \
+    DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x,                          \
+                              DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name)
+
+// for converting types to strings without the <typeinfo> header and demangling
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TYPE_TO_STRING_IMPL(...)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_TYPE_TO_STRING(x) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+#define DOCTEST_TYPE_TO_STRING_IMPL(x)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+// for typed tests
+#define DOCTEST_TEST_CASE_TEMPLATE(name, type, types)                                              \
+    template <typename type>                                                                       \
+    inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id)                                          \
+    template <typename type>                                                                       \
+    inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types)                                          \
+    typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+// for subcases
+#define DOCTEST_SUBCASE(name)
+
+// for a testsuite block
+#define DOCTEST_TEST_SUITE(name) namespace
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
+
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature)                                           \
+    template <typename T>                                                                          \
+    static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature)
+
+#define DOCTEST_INFO(x) ((void)0)
+#define DOCTEST_CAPTURE(x) ((void)0)
+#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0)
+#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0)
+#define DOCTEST_MESSAGE(x) ((void)0)
+#define DOCTEST_FAIL_CHECK(x) ((void)0)
+#define DOCTEST_FAIL(x) ((void)0)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN(...) ((void)0)
+#define DOCTEST_CHECK(...) ((void)0)
+#define DOCTEST_REQUIRE(...) ((void)0)
+#define DOCTEST_WARN_FALSE(...) ((void)0)
+#define DOCTEST_CHECK_FALSE(...) ((void)0)
+#define DOCTEST_REQUIRE_FALSE(...) ((void)0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN(expr) ((void)0)
+#define DOCTEST_CHECK(expr) ((void)0)
+#define DOCTEST_REQUIRE(expr) ((void)0)
+#define DOCTEST_WARN_FALSE(expr) ((void)0)
+#define DOCTEST_CHECK_FALSE(expr) ((void)0)
+#define DOCTEST_REQUIRE_FALSE(expr) ((void)0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0)
+#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0)
+#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0)
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0)
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0)
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0)
+
+#define DOCTEST_WARN_THROWS(expr) ((void)0)
+#define DOCTEST_CHECK_THROWS(expr) ((void)0)
+#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0)
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
+#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
+#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
+
+#ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_WARN_EQ(...) ((void)0)
+#define DOCTEST_CHECK_EQ(...) ((void)0)
+#define DOCTEST_REQUIRE_EQ(...) ((void)0)
+#define DOCTEST_WARN_NE(...) ((void)0)
+#define DOCTEST_CHECK_NE(...) ((void)0)
+#define DOCTEST_REQUIRE_NE(...) ((void)0)
+#define DOCTEST_WARN_GT(...) ((void)0)
+#define DOCTEST_CHECK_GT(...) ((void)0)
+#define DOCTEST_REQUIRE_GT(...) ((void)0)
+#define DOCTEST_WARN_LT(...) ((void)0)
+#define DOCTEST_CHECK_LT(...) ((void)0)
+#define DOCTEST_REQUIRE_LT(...) ((void)0)
+#define DOCTEST_WARN_GE(...) ((void)0)
+#define DOCTEST_CHECK_GE(...) ((void)0)
+#define DOCTEST_REQUIRE_GE(...) ((void)0)
+#define DOCTEST_WARN_LE(...) ((void)0)
+#define DOCTEST_CHECK_LE(...) ((void)0)
+#define DOCTEST_REQUIRE_LE(...) ((void)0)
+
+#define DOCTEST_WARN_UNARY(...) ((void)0)
+#define DOCTEST_CHECK_UNARY(...) ((void)0)
+#define DOCTEST_REQUIRE_UNARY(...) ((void)0)
+#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0)
+#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0)
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0)
+
+#define DOCTEST_FAST_WARN_EQ(...) ((void)0)
+#define DOCTEST_FAST_CHECK_EQ(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_EQ(...) ((void)0)
+#define DOCTEST_FAST_WARN_NE(...) ((void)0)
+#define DOCTEST_FAST_CHECK_NE(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_NE(...) ((void)0)
+#define DOCTEST_FAST_WARN_GT(...) ((void)0)
+#define DOCTEST_FAST_CHECK_GT(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_GT(...) ((void)0)
+#define DOCTEST_FAST_WARN_LT(...) ((void)0)
+#define DOCTEST_FAST_CHECK_LT(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_LT(...) ((void)0)
+#define DOCTEST_FAST_WARN_GE(...) ((void)0)
+#define DOCTEST_FAST_CHECK_GE(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_GE(...) ((void)0)
+#define DOCTEST_FAST_WARN_LE(...) ((void)0)
+#define DOCTEST_FAST_CHECK_LE(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_LE(...) ((void)0)
+
+#define DOCTEST_FAST_WARN_UNARY(...) ((void)0)
+#define DOCTEST_FAST_CHECK_UNARY(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_UNARY(...) ((void)0)
+#define DOCTEST_FAST_WARN_UNARY_FALSE(...) ((void)0)
+#define DOCTEST_FAST_CHECK_UNARY_FALSE(...) ((void)0)
+#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) ((void)0)
+
+#else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#define DOCTEST_WARN_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_WARN_NE(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_NE(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_NE(lhs, rhs) ((void)0)
+#define DOCTEST_WARN_GT(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_GT(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_GT(lhs, rhs) ((void)0)
+#define DOCTEST_WARN_LT(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_LT(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_LT(lhs, rhs) ((void)0)
+#define DOCTEST_WARN_GE(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_GE(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_GE(lhs, rhs) ((void)0)
+#define DOCTEST_WARN_LE(lhs, rhs) ((void)0)
+#define DOCTEST_CHECK_LE(lhs, rhs) ((void)0)
+#define DOCTEST_REQUIRE_LE(lhs, rhs) ((void)0)
+
+#define DOCTEST_WARN_UNARY(val) ((void)0)
+#define DOCTEST_CHECK_UNARY(val) ((void)0)
+#define DOCTEST_REQUIRE_UNARY(val) ((void)0)
+#define DOCTEST_WARN_UNARY_FALSE(val) ((void)0)
+#define DOCTEST_CHECK_UNARY_FALSE(val) ((void)0)
+#define DOCTEST_REQUIRE_UNARY_FALSE(val) ((void)0)
+
+#define DOCTEST_FAST_WARN_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_EQ(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_WARN_NE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_NE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_NE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_WARN_GT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_GT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_GT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_WARN_LT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_LT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_LT(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_WARN_GE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_GE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_GE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_WARN_LE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_CHECK_LE(lhs, rhs) ((void)0)
+#define DOCTEST_FAST_REQUIRE_LE(lhs, rhs) ((void)0)
+
+#define DOCTEST_FAST_WARN_UNARY(val) ((void)0)
+#define DOCTEST_FAST_CHECK_UNARY(val) ((void)0)
+#define DOCTEST_FAST_REQUIRE_UNARY(val) ((void)0)
+#define DOCTEST_FAST_WARN_UNARY_FALSE(val) ((void)0)
+#define DOCTEST_FAST_CHECK_UNARY_FALSE(val) ((void)0)
+#define DOCTEST_FAST_REQUIRE_UNARY_FALSE(val) ((void)0)
+
+#endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+// BDD style macros
+// clang-format off
+#define DOCTEST_SCENARIO(name)  TEST_CASE("  Scenario: " name)
+#define DOCTEST_GIVEN(name)     SUBCASE("   Given: " name)
+#define DOCTEST_WHEN(name)      SUBCASE("    When: " name)
+#define DOCTEST_AND_WHEN(name)  SUBCASE("And when: " name)
+#define DOCTEST_THEN(name)      SUBCASE("    Then: " name)
+#define DOCTEST_AND_THEN(name)  SUBCASE("     And: " name)
+// clang-format on
+
+// == SHORT VERSIONS OF THE MACROS
+#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
+
+#define TEST_CASE DOCTEST_TEST_CASE
+#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE
+#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING
+#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE
+#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE
+#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE
+#define SUBCASE DOCTEST_SUBCASE
+#define TEST_SUITE DOCTEST_TEST_SUITE
+#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
+#define TEST_SUITE_END DOCTEST_TEST_SUITE_END
+#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
+#define INFO DOCTEST_INFO
+#define CAPTURE DOCTEST_CAPTURE
+#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT
+#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT
+#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT
+#define MESSAGE DOCTEST_MESSAGE
+#define FAIL_CHECK DOCTEST_FAIL_CHECK
+#define FAIL DOCTEST_FAIL
+#define TO_LVALUE DOCTEST_TO_LVALUE
+
+#define WARN DOCTEST_WARN
+#define WARN_FALSE DOCTEST_WARN_FALSE
+#define WARN_THROWS DOCTEST_WARN_THROWS
+#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS
+#define WARN_NOTHROW DOCTEST_WARN_NOTHROW
+#define CHECK DOCTEST_CHECK
+#define CHECK_FALSE DOCTEST_CHECK_FALSE
+#define CHECK_THROWS DOCTEST_CHECK_THROWS
+#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS
+#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW
+#define REQUIRE DOCTEST_REQUIRE
+#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE
+#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS
+#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS
+#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW
+
+#define WARN_MESSAGE DOCTEST_WARN_MESSAGE
+#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE
+#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE
+#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE
+#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE
+#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE
+#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE
+#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE
+#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE
+#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE
+#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE
+#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE
+#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE
+#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE
+#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE
+
+#define SCENARIO DOCTEST_SCENARIO
+#define GIVEN DOCTEST_GIVEN
+#define WHEN DOCTEST_WHEN
+#define AND_WHEN DOCTEST_AND_WHEN
+#define THEN DOCTEST_THEN
+#define AND_THEN DOCTEST_AND_THEN
+
+#define WARN_EQ DOCTEST_WARN_EQ
+#define CHECK_EQ DOCTEST_CHECK_EQ
+#define REQUIRE_EQ DOCTEST_REQUIRE_EQ
+#define WARN_NE DOCTEST_WARN_NE
+#define CHECK_NE DOCTEST_CHECK_NE
+#define REQUIRE_NE DOCTEST_REQUIRE_NE
+#define WARN_GT DOCTEST_WARN_GT
+#define CHECK_GT DOCTEST_CHECK_GT
+#define REQUIRE_GT DOCTEST_REQUIRE_GT
+#define WARN_LT DOCTEST_WARN_LT
+#define CHECK_LT DOCTEST_CHECK_LT
+#define REQUIRE_LT DOCTEST_REQUIRE_LT
+#define WARN_GE DOCTEST_WARN_GE
+#define CHECK_GE DOCTEST_CHECK_GE
+#define REQUIRE_GE DOCTEST_REQUIRE_GE
+#define WARN_LE DOCTEST_WARN_LE
+#define CHECK_LE DOCTEST_CHECK_LE
+#define REQUIRE_LE DOCTEST_REQUIRE_LE
+#define WARN_UNARY DOCTEST_WARN_UNARY
+#define CHECK_UNARY DOCTEST_CHECK_UNARY
+#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY
+#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE
+#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
+#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
+
+#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ
+#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ
+#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ
+#define FAST_WARN_NE DOCTEST_FAST_WARN_NE
+#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE
+#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE
+#define FAST_WARN_GT DOCTEST_FAST_WARN_GT
+#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT
+#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT
+#define FAST_WARN_LT DOCTEST_FAST_WARN_LT
+#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT
+#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT
+#define FAST_WARN_GE DOCTEST_FAST_WARN_GE
+#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE
+#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE
+#define FAST_WARN_LE DOCTEST_FAST_WARN_LE
+#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE
+#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE
+#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY
+#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY
+#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY
+#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE
+#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE
+#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE
+
+#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
+
+// this is here to clear the 'current test suite' for the current translation unit - at the top
+DOCTEST_TEST_SUITE_END();
+
+// add stringification for primitive/fundamental types
+namespace doctest
+{
+namespace detail
+{
+    DOCTEST_TYPE_TO_STRING_IMPL(bool)
+    DOCTEST_TYPE_TO_STRING_IMPL(float)
+    DOCTEST_TYPE_TO_STRING_IMPL(double)
+    DOCTEST_TYPE_TO_STRING_IMPL(long double)
+    DOCTEST_TYPE_TO_STRING_IMPL(char)
+    DOCTEST_TYPE_TO_STRING_IMPL(signed char)
+    DOCTEST_TYPE_TO_STRING_IMPL(unsigned char)
+    DOCTEST_TYPE_TO_STRING_IMPL(wchar_t)
+    DOCTEST_TYPE_TO_STRING_IMPL(short int)
+    DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int)
+    DOCTEST_TYPE_TO_STRING_IMPL(int)
+    DOCTEST_TYPE_TO_STRING_IMPL(unsigned int)
+    DOCTEST_TYPE_TO_STRING_IMPL(long int)
+    DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int)
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+    DOCTEST_TYPE_TO_STRING_IMPL(long long int)
+    DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int)
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+} // namespace detail
+} // namespace doctest
+
+#endif // DOCTEST_LIBRARY_INCLUDED
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#ifndef DOCTEST_SINGLE_HEADER
+#define DOCTEST_SINGLE_HEADER
+#endif // DOCTEST_SINGLE_HEADER
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wpadded"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
+#pragma clang diagnostic ignored "-Wswitch"
+#pragma clang diagnostic ignored "-Wswitch-enum"
+#pragma clang diagnostic ignored "-Wcovered-switch-default"
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic push
+#endif // > gcc 4.6
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Weffc++"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#pragma GCC diagnostic ignored "-Wmissing-braces"
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#pragma GCC diagnostic ignored "-Winline"
+#pragma GCC diagnostic ignored "-Wswitch"
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#pragma GCC diagnostic ignored "-Wswitch-default"
+#pragma GCC diagnostic ignored "-Wunsafe-loop-optimizations"
+#pragma GCC diagnostic ignored "-Wlong-long"
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif // > gcc 4.6
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7)
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#endif // > gcc 4.7
+#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3)
+#pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif // > gcc 5.3
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // The compiler encountered a deprecated declaration
+#pragma warning(disable : 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
+#pragma warning(disable : 4706) // assignment within conditional expression
+#pragma warning(disable : 4512) // 'class' : assignment operator could not be generated
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4530) // C++ exception handler used, but unwind semantics are not enabled
+#pragma warning(disable : 4577) // 'noexcept' used with no exception handling mode specified
+#endif                          // _MSC_VER
+
+#if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER)
+#ifndef DOCTEST_LIBRARY_IMPLEMENTATION
+#define DOCTEST_LIBRARY_IMPLEMENTATION
+
+#ifndef DOCTEST_SINGLE_HEADER
+#include "doctest_fwd.h"
+#endif // DOCTEST_SINGLE_HEADER
+
+#if defined(__clang__) && defined(DOCTEST_NO_CPP11_COMPAT)
+#pragma clang diagnostic ignored "-Wc++98-compat"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif // __clang__ && DOCTEST_NO_CPP11_COMPAT
+
+// snprintf() not in the C++98 standard
+#ifdef _MSC_VER
+#define DOCTEST_SNPRINTF _snprintf
+#else
+#define DOCTEST_SNPRINTF std::snprintf
+#endif
+
+#define DOCTEST_LOG_START()                                                                        \
+    do {                                                                                           \
+        if(!contextState->hasLoggedCurrentTestStart) {                                             \
+            doctest::detail::logTestStart(*contextState->currentTest);                             \
+            contextState->hasLoggedCurrentTestStart = true;                                        \
+        }                                                                                          \
+    } while(false)
+
+// required includes - will go only in one translation unit!
+#include <ctime>
+#include <cmath>
+// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37
+#ifdef __BORLANDC__
+#include <math.h>
+#endif // __BORLANDC__
+#include <new>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <utility>
+#include <sstream>
+#include <iomanip>
+#include <vector>
+#include <set>
+#include <exception>
+#include <stdexcept>
+#include <csignal>
+#include <cfloat>
+#ifndef _MSC_VER
+#include <stdint.h>
+#endif // _MSC_VER
+
+namespace doctest
+{
+namespace detail
+{
+    // lowers ascii letters
+    char tolower(const char c) { return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c; }
+
+    template <typename T>
+    T my_max(const T& lhs, const T& rhs) {
+        return lhs > rhs ? lhs : rhs;
+    }
+
+    // case insensitive strcmp
+    int stricmp(char const* a, char const* b) {
+        for(;; a++, b++) {
+            int d = tolower(*a) - tolower(*b);
+            if(d != 0 || !*a)
+                return d;
+        }
+    }
+
+    void my_memcpy(void* dest, const void* src, unsigned num) {
+        const char* csrc  = static_cast<const char*>(src);
+        char*       cdest = static_cast<char*>(dest);
+        for(unsigned i = 0; i < num; ++i)
+            cdest[i]   = csrc[i];
+    }
+
+    // not using std::strlen() because of valgrind errors when optimizations are turned on
+    // 'Invalid read of size 4' when the test suite len (with '\0') is not a multiple of 4
+    // for details see http://stackoverflow.com/questions/35671155
+    unsigned my_strlen(const char* in) {
+        const char* temp = in;
+        while(temp && *temp)
+            ++temp;
+        return unsigned(temp - in);
+    }
+
+    template <typename T>
+    String fpToString(T value, int precision) {
+        std::ostringstream oss;
+        oss << std::setprecision(precision) << std::fixed << value;
+        std::string d = oss.str();
+        size_t      i = d.find_last_not_of('0');
+        if(i != std::string::npos && i != d.size() - 1) {
+            if(d[i] == '.')
+                i++;
+            d = d.substr(0, i + 1);
+        }
+        return d.c_str();
+    }
+
+    struct Endianness
+    {
+        enum Arch
+        {
+            Big,
+            Little
+        };
+
+        static Arch which() {
+            union _
+            {
+                int  asInt;
+                char asChar[sizeof(int)];
+            } u;
+
+            u.asInt = 1;                                            // NOLINT
+            return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; // NOLINT
+        }
+    };
+
+    String rawMemoryToString(const void* object, unsigned size) {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>(size), inc = 1;
+        if(Endianness::which() == Endianness::Little) {
+            i   = end - 1;
+            end = inc = -1;
+        }
+
+        unsigned char const* bytes = static_cast<unsigned char const*>(object);
+        std::ostringstream   os;
+        os << "0x" << std::setfill('0') << std::hex;
+        for(; i != end; i += inc)
+            os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+        return os.str().c_str();
+    }
+
+    std::ostream* createStream() { return new std::ostringstream(); }
+    String getStreamResult(std::ostream* in) {
+        return static_cast<std::ostringstream*>(in)->str().c_str(); // NOLINT
+    }
+    void freeStream(std::ostream* in) { delete in; }
+
+#ifndef DOCTEST_CONFIG_DISABLE
+
+    // this holds both parameters for the command line and runtime data for tests
+    struct ContextState : TestAccessibleContextState //!OCLINT too many fields
+    {
+        // == parameters from the command line
+
+        std::vector<std::vector<String> > filters;
+
+        String   order_by;  // how tests should be ordered
+        unsigned rand_seed; // the seed for rand ordering
+
+        unsigned first; // the first (matching) test to be executed
+        unsigned last;  // the last (matching) test to be executed
+
+        int  abort_after;           // stop tests after this many failed assertions
+        int  subcase_filter_levels; // apply the subcase filters for the first N levels
+        bool case_sensitive;        // if filtering should be case sensitive
+        bool exit;         // if the program should be exited after the tests are ran/whatever
+        bool duration;     // print the time duration of each test case
+        bool no_exitcode;  // if the framework should return 0 as the exitcode
+        bool no_run;       // to not run the tests at all (can be done with an "*" exclude)
+        bool no_version;   // to not print the version of the framework
+        bool no_colors;    // if output to the console should be colorized
+        bool force_colors; // forces the use of colors even when a tty cannot be detected
+        bool no_breaks;    // to not break into the debugger
+        bool no_skip;      // don't skip test cases which are marked to be skipped
+        bool no_path_in_filenames; // if the path to files should be removed from the output
+        bool no_line_numbers;      // if source code line numbers should be omitted from the output
+        bool no_skipped_summary;   // don't print "skipped" in the summary !!! UNDOCUMENTED !!!
+
+        bool help;             // to print the help
+        bool version;          // to print the version
+        bool count;            // if only the count of matching tests is to be retreived
+        bool list_test_cases;  // to list all tests matching the filters
+        bool list_test_suites; // to list all suites matching the filters
+
+        // == data for the tests being ran
+
+        unsigned        numTestsPassingFilters;
+        unsigned        numTestSuitesPassingFilters;
+        unsigned        numFailed;
+        const TestCase* currentTest;
+        bool            hasLoggedCurrentTestStart;
+        int             numAssertionsForCurrentTestcase;
+        int             numAssertions;
+        int             numFailedAssertionsForCurrentTestcase;
+        int             numFailedAssertions;
+        bool            hasCurrentTestFailed;
+
+        std::vector<IContextScope*> contexts;            // for logging with INFO() and friends
+        std::vector<std::string>    exceptionalContexts; // logging from INFO() due to an exception
+
+        // stuff for subcases
+        std::set<SubcaseSignature> subcasesPassed;
+        std::set<int>              subcasesEnteredLevels;
+        std::vector<Subcase>       subcasesStack;
+        int                        subcasesCurrentLevel;
+        bool                       subcasesHasSkipped;
+
+        void resetRunData() {
+            numTestsPassingFilters                = 0;
+            numTestSuitesPassingFilters           = 0;
+            numFailed                             = 0;
+            numAssertions                         = 0;
+            numFailedAssertions                   = 0;
+            numFailedAssertionsForCurrentTestcase = 0;
+        }
+
+        // cppcheck-suppress uninitMemberVar
+        ContextState()
+                : filters(8) // 8 different filters total
+        {
+            resetRunData();
+        }
+    };
+
+    ContextState* contextState = 0;
+#endif // DOCTEST_CONFIG_DISABLE
+} // namespace detail
+
+void String::copy(const String& other) {
+    if(other.isOnStack()) {
+        detail::my_memcpy(buf, other.buf, len);
+    } else {
+        setOnHeap();
+        data.size     = other.data.size;
+        data.capacity = data.size + 1;
+        data.ptr      = new char[data.capacity];
+        detail::my_memcpy(data.ptr, other.data.ptr, data.size + 1);
+    }
+}
+
+String::String(const char* in) {
+    unsigned in_len = detail::my_strlen(in);
+    if(in_len <= last) {
+        detail::my_memcpy(buf, in, in_len + 1);
+        setLast(last - in_len);
+    } else {
+        setOnHeap();
+        data.size     = in_len;
+        data.capacity = data.size + 1;
+        data.ptr      = new char[data.capacity];
+        detail::my_memcpy(data.ptr, in, in_len + 1);
+    }
+}
+
+String& String::operator+=(const String& other) {
+    unsigned my_old_size = size();
+    unsigned other_size  = other.size();
+    unsigned total_size  = my_old_size + other_size;
+    if(isOnStack()) {
+        if(total_size < len) {
+            // append to the current stack space
+            detail::my_memcpy(buf + my_old_size, other.c_str(), other_size + 1);
+            setLast(last - total_size);
+        } else {
+            // alloc new chunk
+            char* temp = new char[total_size + 1];
+            // copy current data to new location before writing in the union
+            detail::my_memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed
+            // update data in union
+            setOnHeap();
+            data.size     = total_size;
+            data.capacity = data.size + 1;
+            data.ptr      = temp;
+            // transfer the rest of the data
+            detail::my_memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+        }
+    } else {
+        if(data.capacity > total_size) {
+            // append to the current heap block
+            data.size = total_size;
+            detail::my_memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+        } else {
+            // resize
+            data.capacity *= 2;
+            if(data.capacity <= total_size)
+                data.capacity = total_size + 1;
+            // alloc new chunk
+            char* temp = new char[data.capacity];
+            // copy current data to new location before releasing it
+            detail::my_memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed
+            // release old chunk
+            delete[] data.ptr;
+            // update the rest of the union members
+            data.size = total_size;
+            data.ptr  = temp;
+            // transfer the rest of the data
+            detail::my_memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+        }
+    }
+
+    return *this;
+}
+
+#ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+String::String(String&& other) {
+    detail::my_memcpy(buf, other.buf, len);
+    other.buf[0] = '\0';
+    other.setLast();
+}
+
+String& String::operator=(String&& other) {
+    if(!isOnStack())
+        delete[] data.ptr;
+    detail::my_memcpy(buf, other.buf, len);
+    other.buf[0] = '\0';
+    other.setLast();
+    return *this;
+}
+#endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES
+
+int String::compare(const char* other, bool no_case) const {
+    if(no_case)
+        return detail::stricmp(c_str(), other);
+    return std::strcmp(c_str(), other);
+}
+
+int String::compare(const String& other, bool no_case) const {
+    return compare(other.c_str(), no_case);
+}
+
+std::ostream& operator<<(std::ostream& stream, const String& in) {
+    stream << in.c_str();
+    return stream;
+}
+
+Approx::Approx(double value)
+        : m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100)
+        , m_scale(1.0)
+        , m_value(value) {}
+
+bool operator==(double lhs, Approx const& rhs) {
+    // Thanks to Richard Harris for his help refining this formula
+    return std::fabs(lhs - rhs.m_value) <
+           rhs.m_epsilon * (rhs.m_scale + detail::my_max(std::fabs(lhs), std::fabs(rhs.m_value)));
+}
+
+String Approx::toString() const { return String("Approx( ") + doctest::toString(m_value) + " )"; }
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+String toString(char* in) { return toString(static_cast<const char*>(in)); }
+String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+String toString(bool in) { return in ? "true" : "false"; }
+String toString(float in) { return detail::fpToString(in, 5) + "f"; }
+String toString(double in) { return detail::fpToString(in, 10); }
+String toString(double long in) { return detail::fpToString(in, 15); }
+
+String toString(char in) {
+    char buf[64];
+    std::sprintf(buf, "%d", in);
+    return buf;
+}
+
+String toString(char signed in) {
+    char buf[64];
+    std::sprintf(buf, "%d", in);
+    return buf;
+}
+
+String toString(char unsigned in) {
+    char buf[64];
+    std::sprintf(buf, "%ud", in);
+    return buf;
+}
+
+String toString(int short in) {
+    char buf[64];
+    std::sprintf(buf, "%d", in);
+    return buf;
+}
+
+String toString(int short unsigned in) {
+    char buf[64];
+    std::sprintf(buf, "%u", in);
+    return buf;
+}
+
+String toString(int in) {
+    char buf[64];
+    std::sprintf(buf, "%d", in);
+    return buf;
+}
+
+String toString(int unsigned in) {
+    char buf[64];
+    std::sprintf(buf, "%u", in);
+    return buf;
+}
+
+String toString(int long in) {
+    char buf[64];
+    std::sprintf(buf, "%ld", in);
+    return buf;
+}
+
+String toString(int long unsigned in) {
+    char buf[64];
+    std::sprintf(buf, "%lu", in);
+    return buf;
+}
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+String toString(int long long in) {
+    char buf[64];
+    std::sprintf(buf, "%lld", in);
+    return buf;
+}
+String toString(int long long unsigned in) {
+    char buf[64];
+    std::sprintf(buf, "%llu", in);
+    return buf;
+}
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+#ifdef DOCTEST_CONFIG_WITH_NULLPTR
+String toString(std::nullptr_t) { return "nullptr"; }
+#endif // DOCTEST_CONFIG_WITH_NULLPTR
+
+} // namespace doctest
+
+#ifdef DOCTEST_CONFIG_DISABLE
+namespace doctest
+{
+bool isRunningInTest() { return false; }
+Context::Context(int, const char* const*) {}
+Context::~Context() {}
+void Context::applyCommandLine(int, const char* const*) {}
+void Context::addFilter(const char*, const char*) {}
+void Context::clearFilters() {}
+void Context::setOption(const char*, int) {}
+void Context::setOption(const char*, const char*) {}
+bool Context::shouldExit() { return false; }
+int  Context::run() { return 0; }
+} // namespace doctest
+#else // DOCTEST_CONFIG_DISABLE
+
+#if !defined(DOCTEST_CONFIG_COLORS_NONE)
+#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI)
+#ifdef DOCTEST_PLATFORM_WINDOWS
+#define DOCTEST_CONFIG_COLORS_WINDOWS
+#else // linux
+#define DOCTEST_CONFIG_COLORS_ANSI
+#endif // platform
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI
+#endif // DOCTEST_CONFIG_COLORS_NONE
+
+#define DOCTEST_PRINTF_COLORED(buffer, color)                                                      \
+    do {                                                                                           \
+        doctest::detail::Color col(color);                                                         \
+        std::printf("%s", buffer);                                                                 \
+    } while((void)0, 0)
+
+// the buffer size used for snprintf() calls
+#if !defined(DOCTEST_SNPRINTF_BUFFER_LENGTH)
+#define DOCTEST_SNPRINTF_BUFFER_LENGTH 1024
+#endif // DOCTEST_SNPRINTF_BUFFER_LENGTH
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#if defined(_MSC_VER) && _MSC_VER >= 1700
+#define DOCTEST_WINDOWS_SAL_IN_OPT _In_opt_
+#else // _MSC_VER
+#define DOCTEST_WINDOWS_SAL_IN_OPT
+#endif // _MSC_VER
+extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(
+        DOCTEST_WINDOWS_SAL_IN_OPT const char*);
+extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+#endif // _MSC_VER || __MINGW32__
+
+#ifdef DOCTEST_CONFIG_COLORS_ANSI
+#include <unistd.h>
+#endif // DOCTEST_CONFIG_COLORS_ANSI
+
+#ifdef _WIN32
+
+// defines for a leaner windows.h
+#ifndef WIN32_MEAN_AND_LEAN
+#define WIN32_MEAN_AND_LEAN
+#endif // WIN32_MEAN_AND_LEAN
+#ifndef VC_EXTRA_LEAN
+#define VC_EXTRA_LEAN
+#endif // VC_EXTRA_LEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+// not sure what AfxWin.h is for - here I do what Catch does
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+#include <io.h>
+
+#else // _WIN32
+
+#include <sys/time.h>
+
+#endif // _WIN32
+
+namespace doctest_detail_test_suite_ns
+{
+// holds the current test suite
+doctest::detail::TestSuite& getCurrentTestSuite() {
+    static doctest::detail::TestSuite data;
+    return data;
+}
+} // namespace doctest_detail_test_suite_ns
+
+namespace doctest
+{
+namespace detail
+{
+    TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+                       const char* type, int template_id)
+            : m_test(test)
+            , m_name(0)
+            , m_type(type)
+            , m_test_suite(test_suite.m_test_suite)
+            , m_description(test_suite.m_description)
+            , m_skip(test_suite.m_skip)
+            , m_may_fail(test_suite.m_may_fail)
+            , m_should_fail(test_suite.m_should_fail)
+            , m_expected_failures(test_suite.m_expected_failures)
+            , m_timeout(test_suite.m_timeout)
+            , m_file(file)
+            , m_line(line)
+            , m_template_id(template_id) {}
+
+    TestCase& TestCase::operator*(const char* in) {
+        m_name = in;
+        // make a new name with an appended type for templated test case
+        if(m_template_id != -1) {
+            m_full_name = String(m_name) + m_type;
+            // redirect the name to point to the newly constructed full name
+            m_name = m_full_name.c_str();
+        }
+        return *this;
+    }
+
+    TestCase& TestCase::operator=(const TestCase& other) {
+        m_test              = other.m_test;
+        m_full_name         = other.m_full_name;
+        m_name              = other.m_name;
+        m_type              = other.m_type;
+        m_test_suite        = other.m_test_suite;
+        m_description       = other.m_description;
+        m_skip              = other.m_skip;
+        m_may_fail          = other.m_may_fail;
+        m_should_fail       = other.m_should_fail;
+        m_expected_failures = other.m_expected_failures;
+        m_timeout           = other.m_timeout;
+        m_file              = other.m_file;
+        m_line              = other.m_line;
+        m_template_id       = other.m_template_id;
+
+        if(m_template_id != -1)
+            m_name = m_full_name.c_str();
+        return *this;
+    }
+
+    bool TestCase::operator<(const TestCase& other) const {
+        if(m_line != other.m_line)
+            return m_line < other.m_line;
+        int file_cmp = std::strcmp(m_file, other.m_file);
+        if(file_cmp != 0)
+            return file_cmp < 0;
+        return m_template_id < other.m_template_id;
+    }
+
+    const char* getAssertString(assertType::Enum val) {
+        switch(val) { //!OCLINT missing default in switch statements
+            // clang-format off
+            case assertType::DT_WARN                    : return "WARN";
+            case assertType::DT_CHECK                   : return "CHECK";
+            case assertType::DT_REQUIRE                 : return "REQUIRE";
+
+            case assertType::DT_WARN_FALSE              : return "WARN_FALSE";
+            case assertType::DT_CHECK_FALSE             : return "CHECK_FALSE";
+            case assertType::DT_REQUIRE_FALSE           : return "REQUIRE_FALSE";
+
+            case assertType::DT_WARN_THROWS             : return "WARN_THROWS";
+            case assertType::DT_CHECK_THROWS            : return "CHECK_THROWS";
+            case assertType::DT_REQUIRE_THROWS          : return "REQUIRE_THROWS";
+
+            case assertType::DT_WARN_THROWS_AS          : return "WARN_THROWS_AS";
+            case assertType::DT_CHECK_THROWS_AS         : return "CHECK_THROWS_AS";
+            case assertType::DT_REQUIRE_THROWS_AS       : return "REQUIRE_THROWS_AS";
+
+            case assertType::DT_WARN_NOTHROW            : return "WARN_NOTHROW";
+            case assertType::DT_CHECK_NOTHROW           : return "CHECK_NOTHROW";
+            case assertType::DT_REQUIRE_NOTHROW         : return "REQUIRE_NOTHROW";
+
+            case assertType::DT_WARN_EQ                 : return "WARN_EQ";
+            case assertType::DT_CHECK_EQ                : return "CHECK_EQ";
+            case assertType::DT_REQUIRE_EQ              : return "REQUIRE_EQ";
+            case assertType::DT_WARN_NE                 : return "WARN_NE";
+            case assertType::DT_CHECK_NE                : return "CHECK_NE";
+            case assertType::DT_REQUIRE_NE              : return "REQUIRE_NE";
+            case assertType::DT_WARN_GT                 : return "WARN_GT";
+            case assertType::DT_CHECK_GT                : return "CHECK_GT";
+            case assertType::DT_REQUIRE_GT              : return "REQUIRE_GT";
+            case assertType::DT_WARN_LT                 : return "WARN_LT";
+            case assertType::DT_CHECK_LT                : return "CHECK_LT";
+            case assertType::DT_REQUIRE_LT              : return "REQUIRE_LT";
+            case assertType::DT_WARN_GE                 : return "WARN_GE";
+            case assertType::DT_CHECK_GE                : return "CHECK_GE";
+            case assertType::DT_REQUIRE_GE              : return "REQUIRE_GE";
+            case assertType::DT_WARN_LE                 : return "WARN_LE";
+            case assertType::DT_CHECK_LE                : return "CHECK_LE";
+            case assertType::DT_REQUIRE_LE              : return "REQUIRE_LE";
+
+            case assertType::DT_WARN_UNARY              : return "WARN_UNARY";
+            case assertType::DT_CHECK_UNARY             : return "CHECK_UNARY";
+            case assertType::DT_REQUIRE_UNARY           : return "REQUIRE_UNARY";
+            case assertType::DT_WARN_UNARY_FALSE        : return "WARN_UNARY_FALSE";
+            case assertType::DT_CHECK_UNARY_FALSE       : return "CHECK_UNARY_FALSE";
+            case assertType::DT_REQUIRE_UNARY_FALSE     : return "REQUIRE_UNARY_FALSE";
+
+            case assertType::DT_FAST_WARN_EQ            : return "FAST_WARN_EQ";
+            case assertType::DT_FAST_CHECK_EQ           : return "FAST_CHECK_EQ";
+            case assertType::DT_FAST_REQUIRE_EQ         : return "FAST_REQUIRE_EQ";
+            case assertType::DT_FAST_WARN_NE            : return "FAST_WARN_NE";
+            case assertType::DT_FAST_CHECK_NE           : return "FAST_CHECK_NE";
+            case assertType::DT_FAST_REQUIRE_NE         : return "FAST_REQUIRE_NE";
+            case assertType::DT_FAST_WARN_GT            : return "FAST_WARN_GT";
+            case assertType::DT_FAST_CHECK_GT           : return "FAST_CHECK_GT";
+            case assertType::DT_FAST_REQUIRE_GT         : return "FAST_REQUIRE_GT";
+            case assertType::DT_FAST_WARN_LT            : return "FAST_WARN_LT";
+            case assertType::DT_FAST_CHECK_LT           : return "FAST_CHECK_LT";
+            case assertType::DT_FAST_REQUIRE_LT         : return "FAST_REQUIRE_LT";
+            case assertType::DT_FAST_WARN_GE            : return "FAST_WARN_GE";
+            case assertType::DT_FAST_CHECK_GE           : return "FAST_CHECK_GE";
+            case assertType::DT_FAST_REQUIRE_GE         : return "FAST_REQUIRE_GE";
+            case assertType::DT_FAST_WARN_LE            : return "FAST_WARN_LE";
+            case assertType::DT_FAST_CHECK_LE           : return "FAST_CHECK_LE";
+            case assertType::DT_FAST_REQUIRE_LE         : return "FAST_REQUIRE_LE";
+
+            case assertType::DT_FAST_WARN_UNARY         : return "FAST_WARN_UNARY";
+            case assertType::DT_FAST_CHECK_UNARY        : return "FAST_CHECK_UNARY";
+            case assertType::DT_FAST_REQUIRE_UNARY      : return "FAST_REQUIRE_UNARY";
+            case assertType::DT_FAST_WARN_UNARY_FALSE   : return "FAST_WARN_UNARY_FALSE";
+            case assertType::DT_FAST_CHECK_UNARY_FALSE  : return "FAST_CHECK_UNARY_FALSE";
+            case assertType::DT_FAST_REQUIRE_UNARY_FALSE: return "FAST_REQUIRE_UNARY_FALSE";
+                // clang-format on
+        }
+        return "";
+    }
+
+    bool checkIfShouldThrow(assertType::Enum assert_type) {
+        if(assert_type & assertType::is_require) //!OCLINT bitwise operator in conditional
+            return true;
+
+        if((assert_type & assertType::is_check) //!OCLINT bitwise operator in conditional
+           && contextState->abort_after > 0 &&
+           contextState->numFailedAssertions >= contextState->abort_after)
+            return true;
+
+        return false;
+    }
+    void fastAssertThrowIfFlagSet(int flags) {
+        if(flags & assertAction::shouldthrow) //!OCLINT bitwise operator in conditional
+            throwException();
+    }
+    void throwException() {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+        throw TestFailureException();
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+    }
+
+    // matching of a string against a wildcard mask (case sensitivity configurable) taken from
+    // http://www.emoticode.net/c/simple-wildcard-string-compare-globbing-function.html
+    int wildcmp(const char* str, const char* wild, bool caseSensitive) {
+        const char* cp = 0;
+        const char* mp = 0;
+
+        // rolled my own tolower() to not include more headers
+        while((*str) && (*wild != '*')) {
+            if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
+               (*wild != '?')) {
+                return 0;
+            }
+            wild++;
+            str++;
+        }
+
+        while(*str) {
+            if(*wild == '*') {
+                if(!*++wild) {
+                    return 1;
+                }
+                mp = wild;
+                cp = str + 1;
+            } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) ||
+                      (*wild == '?')) {
+                wild++;
+                str++;
+            } else {
+                wild = mp;   //!OCLINT parameter reassignment
+                str  = cp++; //!OCLINT parameter reassignment
+            }
+        }
+
+        while(*wild == '*') {
+            wild++;
+        }
+        return !*wild;
+    }
+
+    //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
+    //unsigned hashStr(unsigned const char* str) {
+    //    unsigned long hash = 5381;
+    //    char          c;
+    //    while((c = *str++))
+    //        hash = ((hash << 5) + hash) + c; // hash * 33 + c
+    //    return hash;
+    //}
+
+    // checks if the name matches any of the filters (and can be configured what to do when empty)
+    bool matchesAny(const char* name, const std::vector<String>& filters, int matchEmpty,
+                    bool caseSensitive) {
+        if(filters.empty() && matchEmpty)
+            return true;
+        for(unsigned i = 0; i < filters.size(); ++i)
+            if(wildcmp(name, filters[i].c_str(), caseSensitive))
+                return true;
+        return false;
+    }
+
+#ifdef _WIN32
+
+    typedef unsigned long long UInt64;
+
+    UInt64 getCurrentTicks() {
+        static UInt64 hz = 0, hzo = 0;
+        if(!hz) {
+            QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&hz));
+            QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&hzo));
+        }
+        UInt64 t;
+        QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t));
+        return ((t - hzo) * 1000000) / hz;
+    }
+#else  // _WIN32
+
+    typedef uint64_t UInt64;
+
+    UInt64 getCurrentTicks() {
+        timeval t;
+        gettimeofday(&t, 0);
+        return static_cast<UInt64>(t.tv_sec) * 1000000 + static_cast<UInt64>(t.tv_usec);
+    }
+#endif // _WIN32
+
+    class Timer
+    {
+    public:
+        Timer()
+                : m_ticks(0) {}
+        void         start() { m_ticks = getCurrentTicks(); }
+        unsigned int getElapsedMicroseconds() const {
+            return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+        }
+        unsigned int getElapsedMilliseconds() const {
+            return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
+        }
+        double getElapsedSeconds() const { return getElapsedMicroseconds() / 1000000.0; }
+
+    private:
+        UInt64 m_ticks;
+    };
+
+    TestAccessibleContextState* getTestsContextState() { return contextState; }
+
+    bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
+        if(m_line != other.m_line)
+            return m_line < other.m_line;
+        if(std::strcmp(m_file, other.m_file) != 0)
+            return std::strcmp(m_file, other.m_file) < 0;
+        return std::strcmp(m_name, other.m_name) < 0;
+    }
+
+    Subcase::Subcase(const char* name, const char* file, int line)
+            : m_signature(name, file, line)
+            , m_entered(false) {
+        ContextState* s = contextState;
+
+        // if we have already completed it
+        if(s->subcasesPassed.count(m_signature) != 0)
+            return;
+
+        // check subcase filters
+        if(s->subcasesCurrentLevel < s->subcase_filter_levels) {
+            if(!matchesAny(m_signature.m_name, s->filters[6], 1, s->case_sensitive))
+                return;
+            if(matchesAny(m_signature.m_name, s->filters[7], 0, s->case_sensitive))
+                return;
+        }
+
+        // if a Subcase on the same level has already been entered
+        if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) {
+            s->subcasesHasSkipped = true;
+            return;
+        }
+
+        s->subcasesStack.push_back(*this);
+        if(s->hasLoggedCurrentTestStart)
+            logTestEnd();
+        s->hasLoggedCurrentTestStart = false;
+
+        s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++);
+        m_entered = true;
+    }
+
+    Subcase::Subcase(const Subcase& other)
+            : m_signature(other.m_signature.m_name, other.m_signature.m_file,
+                          other.m_signature.m_line)
+            , m_entered(other.m_entered) {}
+
+    Subcase::~Subcase() {
+        if(m_entered) {
+            ContextState* s = contextState;
+
+            s->subcasesCurrentLevel--;
+            // only mark the subcase as passed if no subcases have been skipped
+            if(s->subcasesHasSkipped == false)
+                s->subcasesPassed.insert(m_signature);
+
+            if(!s->subcasesStack.empty())
+                s->subcasesStack.pop_back();
+            if(s->hasLoggedCurrentTestStart)
+                logTestEnd();
+            s->hasLoggedCurrentTestStart = false;
+        }
+    }
+
+    Result::~Result() {}
+
+    Result& Result::operator=(const Result& other) {
+        m_passed        = other.m_passed;
+        m_decomposition = other.m_decomposition;
+
+        return *this;
+    }
+
+    // for sorting tests by file/line
+    int fileOrderComparator(const void* a, const void* b) {
+        const TestCase* lhs = *static_cast<TestCase* const*>(a);
+        const TestCase* rhs = *static_cast<TestCase* const*>(b);
+#ifdef _MSC_VER
+        // this is needed because MSVC gives different case for drive letters
+        // for __FILE__ when evaluated in a header and a source file
+        int res = stricmp(lhs->m_file, rhs->m_file);
+#else  // _MSC_VER
+        int res = std::strcmp(lhs->m_file, rhs->m_file);
+#endif // _MSC_VER
+        if(res != 0)
+            return res;
+        return static_cast<int>(lhs->m_line - rhs->m_line);
+    }
+
+    // for sorting tests by suite/file/line
+    int suiteOrderComparator(const void* a, const void* b) {
+        const TestCase* lhs = *static_cast<TestCase* const*>(a);
+        const TestCase* rhs = *static_cast<TestCase* const*>(b);
+
+        int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite);
+        if(res != 0)
+            return res;
+        return fileOrderComparator(a, b);
+    }
+
+    // for sorting tests by name/suite/file/line
+    int nameOrderComparator(const void* a, const void* b) {
+        const TestCase* lhs = *static_cast<TestCase* const*>(a);
+        const TestCase* rhs = *static_cast<TestCase* const*>(b);
+
+        int res_name = std::strcmp(lhs->m_name, rhs->m_name);
+        if(res_name != 0)
+            return res_name;
+        return suiteOrderComparator(a, b);
+    }
+
+    // sets the current test suite
+    int setTestSuite(const TestSuite& ts) {
+        doctest_detail_test_suite_ns::getCurrentTestSuite() = ts;
+        return 0;
+    }
+
+    // all the registered tests
+    std::set<TestCase>& getRegisteredTests() {
+        static std::set<TestCase> data;
+        return data;
+    }
+
+    // used by the macros for registering tests
+    int regTest(const TestCase& tc) {
+        getRegisteredTests().insert(tc);
+        return 0;
+    }
+
+    struct Color
+    {
+        enum Code
+        {
+            None = 0,
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed   = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey   = Bright | Grey,
+            BrightWhite = Bright | White
+        };
+        explicit Color(Code code) { use(code); }
+        ~Color() { use(None); }
+
+        static void use(Code code);
+        static void init();
+    };
+
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+    HANDLE g_stdoutHandle;
+    WORD   g_originalForegroundAttributes;
+    WORD   g_originalBackgroundAttributes;
+    bool   g_attrsInitted = false;
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+
+    void Color::init() {
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+        if(!g_attrsInitted) {
+            g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+            g_attrsInitted = true;
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo);
+            g_originalForegroundAttributes =
+                    csbiInfo.wAttributes &
+                    ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+            g_originalBackgroundAttributes =
+                    csbiInfo.wAttributes &
+                    ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+        }
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+    }
+
+    void Color::use(Code
+#ifndef DOCTEST_CONFIG_COLORS_NONE
+                            code
+#endif // DOCTEST_CONFIG_COLORS_NONE
+                    ) {
+        const ContextState* p = contextState;
+        if(p->no_colors)
+            return;
+#ifdef DOCTEST_CONFIG_COLORS_ANSI
+        if(isatty(STDOUT_FILENO) == false && p->force_colors == false)
+            return;
+
+        const char* col = "";
+        // clang-format off
+        switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement
+            case Color::Red:         col = "[0;31m"; break;
+            case Color::Green:       col = "[0;32m"; break;
+            case Color::Blue:        col = "[0;34m"; break;
+            case Color::Cyan:        col = "[0;36m"; break;
+            case Color::Yellow:      col = "[0;33m"; break;
+            case Color::Grey:        col = "[1;30m"; break;
+            case Color::LightGrey:   col = "[0;37m"; break;
+            case Color::BrightRed:   col = "[1;31m"; break;
+            case Color::BrightGreen: col = "[1;32m"; break;
+            case Color::BrightWhite: col = "[1;37m"; break;
+            case Color::Bright: // invalid
+            case Color::None:
+            case Color::White:
+            default:                 col = "[0m";
+        }
+        // clang-format on
+        std::printf("\033%s", col);
+#endif // DOCTEST_CONFIG_COLORS_ANSI
+
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+        if(isatty(fileno(stdout)) == false && p->force_colors == false)
+            return;
+
+#define DOCTEST_SET_ATTR(x)                                                                        \
+    SetConsoleTextAttribute(g_stdoutHandle, x | g_originalBackgroundAttributes)
+
+        // clang-format off
+        switch (code) {
+            case Color::White:       DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+            case Color::Red:         DOCTEST_SET_ATTR(FOREGROUND_RED);                                      break;
+            case Color::Green:       DOCTEST_SET_ATTR(FOREGROUND_GREEN);                                    break;
+            case Color::Blue:        DOCTEST_SET_ATTR(FOREGROUND_BLUE);                                     break;
+            case Color::Cyan:        DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN);                  break;
+            case Color::Yellow:      DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN);                   break;
+            case Color::Grey:        DOCTEST_SET_ATTR(0);                                                   break;
+            case Color::LightGrey:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY);                                break;
+            case Color::BrightRed:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED);               break;
+            case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN);             break;
+            case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+            case Color::None:
+            case Color::Bright: // invalid
+            default:                 DOCTEST_SET_ATTR(g_originalForegroundAttributes);
+        }
+// clang-format on
+#undef DOCTEST_SET_ATTR
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+    }
+
+    IExceptionTranslator::~IExceptionTranslator() {}
+
+    std::vector<const IExceptionTranslator*>& getExceptionTranslators() {
+        static std::vector<const IExceptionTranslator*> data;
+        return data;
+    }
+
+    void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction) {
+        getExceptionTranslators().push_back(translateFunction);
+    }
+
+    String translateActiveException() {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+        String                                    res;
+        std::vector<const IExceptionTranslator*>& translators = getExceptionTranslators();
+        for(size_t i = 0; i < translators.size(); ++i)
+            if(translators[i]->translate(res))
+                return res;
+        // clang-format off
+        try {
+            throw;
+        } catch(std::exception& ex) {
+            return ex.what();
+        } catch(std::string& msg) {
+            return msg.c_str();
+        } catch(const char* msg) {
+            return msg;
+        } catch(...) {
+            return "unknown exception";
+        }
+// clang-format on
+#else  // DOCTEST_CONFIG_NO_EXCEPTIONS
+        return "";
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+    }
+
+    void writeStringToStream(std::ostream* stream, const String& str) { *stream << str; }
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+    void toStream(std::ostream* stream, char* in) { *stream << in; }
+    void toStream(std::ostream* stream, const char* in) { *stream << in; }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+    void toStream(std::ostream* stream, bool in) {
+        *stream << std::boolalpha << in << std::noboolalpha;
+    }
+    void toStream(std::ostream* stream, float in) { *stream << in; }
+    void toStream(std::ostream* stream, double in) { *stream << in; }
+    void toStream(std::ostream* stream, double long in) { *stream << in; }
+
+    void toStream(std::ostream* stream, char in) { *stream << in; }
+    void toStream(std::ostream* stream, char signed in) { *stream << in; }
+    void toStream(std::ostream* stream, char unsigned in) { *stream << in; }
+    void toStream(std::ostream* stream, int short in) { *stream << in; }
+    void toStream(std::ostream* stream, int short unsigned in) { *stream << in; }
+    void toStream(std::ostream* stream, int in) { *stream << in; }
+    void toStream(std::ostream* stream, int unsigned in) { *stream << in; }
+    void toStream(std::ostream* stream, int long in) { *stream << in; }
+    void toStream(std::ostream* stream, int long unsigned in) { *stream << in; }
+
+#ifdef DOCTEST_CONFIG_WITH_LONG_LONG
+    void toStream(std::ostream* stream, int long long in) { *stream << in; }
+    void toStream(std::ostream* stream, int long long unsigned in) { *stream << in; }
+#endif // DOCTEST_CONFIG_WITH_LONG_LONG
+
+    void addToContexts(IContextScope* ptr) { contextState->contexts.push_back(ptr); }
+    void                              popFromContexts() { contextState->contexts.pop_back(); }
+    void useContextIfExceptionOccurred(IContextScope* ptr) {
+        if(std::uncaught_exception()) {
+            std::ostringstream stream;
+            ptr->build(&stream);
+            contextState->exceptionalContexts.push_back(stream.str());
+        }
+    }
+
+    void printSummary();
+
+#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+    void reportFatal(const std::string&) {}
+    struct FatalConditionHandler
+    {
+        void reset() {}
+    };
+#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+
+    void reportFatal(const std::string& message) {
+        DOCTEST_LOG_START();
+
+        contextState->numAssertions += contextState->numAssertionsForCurrentTestcase;
+        logTestException(message.c_str(), true);
+        logTestEnd();
+        contextState->numFailed++;
+
+        printSummary();
+    }
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+
+    struct SignalDefs
+    {
+        DWORD       id;
+        const char* name;
+    };
+    // There is no 1-1 mapping between signals and windows exceptions.
+    // Windows can easily distinguish between SO and SigSegV,
+    // but SigInt, SigTerm, etc are handled differently.
+    SignalDefs signalDefs[] = {
+            {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"},
+            {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"},
+            {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"},
+            {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"},
+    };
+
+    struct FatalConditionHandler
+    {
+        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+            for(size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+                    reportFatal(signalDefs[i].name);
+                }
+            }
+            // If its not an exception we care about, pass it along.
+            // This stops us from eating debugger breaks etc.
+            return EXCEPTION_CONTINUE_SEARCH;
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            // 32k seems enough for doctest to handle stack overflow,
+            // but the value was found experimentally, so there is no strong guarantee
+            guaranteeSize          = 32 * 1024;
+            exceptionHandlerHandle = 0;
+            // Register as first handler in current chain
+            exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+            // Pass in guarantee size to be filled
+            SetThreadStackGuarantee(&guaranteeSize);
+        }
+
+        static void reset() {
+            if(isSet) {
+                // Unregister handler and restore the old guarantee
+                RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+                SetThreadStackGuarantee(&guaranteeSize);
+                exceptionHandlerHandle = 0;
+                isSet                  = false;
+            }
+        }
+
+        ~FatalConditionHandler() { reset(); }
+
+    private:
+        static bool  isSet;
+        static ULONG guaranteeSize;
+        static PVOID exceptionHandlerHandle;
+    };
+
+    bool  FatalConditionHandler::isSet                  = false;
+    ULONG FatalConditionHandler::guaranteeSize          = 0;
+    PVOID FatalConditionHandler::exceptionHandlerHandle = 0;
+
+#else // DOCTEST_PLATFORM_WINDOWS
+
+    struct SignalDefs
+    {
+        int         id;
+        const char* name;
+    };
+    SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"},
+                               {SIGILL, "SIGILL - Illegal instruction signal"},
+                               {SIGFPE, "SIGFPE - Floating point error signal"},
+                               {SIGSEGV, "SIGSEGV - Segmentation violation signal"},
+                               {SIGTERM, "SIGTERM - Termination request signal"},
+                               {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}};
+
+    struct FatalConditionHandler
+    {
+        static bool             isSet;
+        static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)];
+        static stack_t          oldSigStack;
+        static char             altStackMem[SIGSTKSZ];
+
+        static void handleSignal(int sig) {
+            std::string name = "<unknown signal>";
+            for(std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                SignalDefs& def = signalDefs[i];
+                if(sig == def.id) {
+                    name = def.name;
+                    break;
+                }
+            }
+            reset();
+            reportFatal(name);
+            raise(sig);
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            stack_t sigStack;
+            sigStack.ss_sp    = altStackMem;
+            sigStack.ss_size  = SIGSTKSZ;
+            sigStack.ss_flags = 0;
+            sigaltstack(&sigStack, &oldSigStack);
+            struct sigaction sa = {0};
+
+            sa.sa_handler = handleSignal; // NOLINT
+            sa.sa_flags   = SA_ONSTACK;
+            for(std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+            }
+        }
+
+        ~FatalConditionHandler() { reset(); }
+        static void reset() {
+            if(isSet) {
+                // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+                for(std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                    sigaction(signalDefs[i].id, &oldSigActions[i], 0);
+                }
+                // Return the old stack
+                sigaltstack(&oldSigStack, 0);
+                isSet = false;
+            }
+        }
+    };
+
+    bool             FatalConditionHandler::isSet = false;
+    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)] =
+            {};
+    stack_t FatalConditionHandler::oldSigStack           = {};
+    char    FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+#endif // DOCTEST_PLATFORM_WINDOWS
+#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+
+    // depending on the current options this will remove the path of filenames
+    const char* fileForOutput(const char* file) {
+        if(contextState->no_path_in_filenames) {
+            const char* back    = std::strrchr(file, '\\');
+            const char* forward = std::strrchr(file, '/');
+            if(back || forward) {
+                if(back > forward)
+                    forward = back;
+                return forward + 1;
+            }
+        }
+        return file;
+    }
+
+    // depending on the current options this will substitute the line numbers with 0
+    int lineForOutput(int line) {
+        if(contextState->no_line_numbers)
+            return 0;
+        return line;
+    }
+
+#ifdef DOCTEST_PLATFORM_MAC
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+    // The following function is taken directly from the following technical note:
+    // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+    // Returns true if the current process is being debugged (either
+    // running under the debugger or has a debugger attached post facto).
+    bool isDebuggerActive() {
+        int        mib[4];
+        kinfo_proc info;
+        size_t     size;
+        // Initialize the flags so that, if sysctl fails for some bizarre
+        // reason, we get a predictable result.
+        info.kp_proc.p_flag = 0;
+        // Initialize mib, which tells sysctl the info we want, in this case
+        // we're looking for information about a specific process ID.
+        mib[0] = CTL_KERN;
+        mib[1] = KERN_PROC;
+        mib[2] = KERN_PROC_PID;
+        mib[3] = getpid();
+        // Call sysctl.
+        size = sizeof(info);
+        if(sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, 0, 0) != 0) {
+            fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is "
+                            "active **\n\n");
+            return false;
+        }
+        // We're being debugged if the P_TRACED flag is set.
+        return ((info.kp_proc.p_flag & P_TRACED) != 0);
+    }
+#elif defined(_MSC_VER) || defined(__MINGW32__)
+    bool  isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
+#else
+    bool isDebuggerActive() { return false; }
+#endif // Platform
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+    void myOutputDebugString(const String& text) { ::OutputDebugStringA(text.c_str()); }
+#else
+    // TODO: integration with XCode and other IDEs
+    void myOutputDebugString(const String&) {}
+#endif // Platform
+
+    const char* getSeparator() {
+        return "===============================================================================\n";
+    }
+
+    void printToDebugConsole(const String& text) {
+        if(isDebuggerActive())
+            myOutputDebugString(text.c_str());
+    }
+
+    void addFailedAssert(assertType::Enum assert_type) {
+        if((assert_type & assertType::is_warn) == 0) { //!OCLINT bitwise operator in conditional
+            contextState->numFailedAssertions++;
+            contextState->numFailedAssertionsForCurrentTestcase++;
+            contextState->hasCurrentTestFailed = true;
+        }
+    }
+
+    void logTestStart(const TestCase& tc) {
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)\n", fileForOutput(tc.m_file),
+                         lineForOutput(tc.m_line));
+
+        char ts1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(ts1, DOCTEST_COUNTOF(ts1), "TEST SUITE: ");
+        char ts2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(ts2, DOCTEST_COUNTOF(ts2), "%s\n", tc.m_test_suite);
+        char n1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(n1, DOCTEST_COUNTOF(n1), "TEST CASE:  ");
+        char n2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(n2, DOCTEST_COUNTOF(n2), "%s\n", tc.m_name);
+        char d1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(d1, DOCTEST_COUNTOF(d1), "DESCRIPTION: ");
+        char d2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(d2, DOCTEST_COUNTOF(d2), "%s\n", tc.m_description);
+
+        // hack for BDD style of macros - to not print "TEST CASE:"
+        char scenario[] = "  Scenario:";
+        if(std::string(tc.m_name).substr(0, DOCTEST_COUNTOF(scenario) - 1) == scenario)
+            n1[0] = '\0';
+
+        DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+
+        String forDebugConsole;
+        if(tc.m_description) {
+            DOCTEST_PRINTF_COLORED(d1, Color::Yellow);
+            DOCTEST_PRINTF_COLORED(d2, Color::None);
+            forDebugConsole += d1;
+            forDebugConsole += d2;
+        }
+        if(tc.m_test_suite[0] != '\0') {
+            DOCTEST_PRINTF_COLORED(ts1, Color::Yellow);
+            DOCTEST_PRINTF_COLORED(ts2, Color::None);
+            forDebugConsole += ts1;
+            forDebugConsole += ts2;
+        }
+        DOCTEST_PRINTF_COLORED(n1, Color::Yellow);
+        DOCTEST_PRINTF_COLORED(n2, Color::None);
+
+        String                subcaseStuff;
+        std::vector<Subcase>& subcasesStack = contextState->subcasesStack;
+        for(unsigned i = 0; i < subcasesStack.size(); ++i) {
+            if(subcasesStack[i].m_signature.m_name[0] != '\0') {
+                char subcase[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+                DOCTEST_SNPRINTF(subcase, DOCTEST_COUNTOF(loc), "  %s\n",
+                                 subcasesStack[i].m_signature.m_name);
+                DOCTEST_PRINTF_COLORED(subcase, Color::None);
+                subcaseStuff += subcase;
+            }
+        }
+
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(getSeparator()) + loc + forDebugConsole.c_str() + n1 + n2 +
+                            subcaseStuff.c_str() + "\n");
+    }
+
+    void logTestEnd() {}
+
+    void logTestException(const String& what, bool crash) {
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), "TEST CASE FAILED!\n");
+
+        char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        info1[0] = 0;
+        info2[0] = 0;
+        DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1),
+                         crash ? "crashed:\n" : "threw exception:\n");
+        DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "  %s\n", what.c_str());
+
+        std::string contextStr;
+
+        if(!contextState->exceptionalContexts.empty()) {
+            contextStr += "with context:\n";
+            for(size_t i = contextState->exceptionalContexts.size(); i > 0; --i) {
+                contextStr += "  ";
+                contextStr += contextState->exceptionalContexts[i - 1];
+                contextStr += "\n";
+            }
+        }
+
+        DOCTEST_PRINTF_COLORED(msg, Color::Red);
+        DOCTEST_PRINTF_COLORED(info1, Color::None);
+        DOCTEST_PRINTF_COLORED(info2, Color::Cyan);
+        DOCTEST_PRINTF_COLORED(contextStr.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(msg) + info1 + info2 + contextStr.c_str() + "\n");
+    }
+
+    String logContext() {
+        std::ostringstream           stream;
+        std::vector<IContextScope*>& contexts = contextState->contexts;
+        if(!contexts.empty())
+            stream << "with context:\n";
+        for(size_t i = 0; i < contexts.size(); ++i) {
+            stream << "  ";
+            contexts[i]->build(&stream);
+            stream << "\n";
+        }
+        return stream.str().c_str();
+    }
+
+    const char* getFailString(assertType::Enum assert_type) {
+        if(assert_type & assertType::is_warn) //!OCLINT bitwise operator in conditional
+            return "WARNING";
+        if(assert_type & assertType::is_check) //!OCLINT bitwise operator in conditional
+            return "ERROR";
+        if(assert_type & assertType::is_require) //!OCLINT bitwise operator in conditional
+            return "FATAL ERROR";
+        return "";
+    }
+
+    void logAssert(bool passed, const char* decomposition, bool threw, const String& exception,
+                   const char* expr, assertType::Enum assert_type, const char* file, int line) {
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file),
+                         lineForOutput(line));
+
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " %s!\n",
+                         passed ? "PASSED" : getFailString(assert_type));
+
+        char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), "  %s( %s )\n",
+                         getAssertString(assert_type), expr);
+
+        char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        char info3[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        info2[0] = 0;
+        info3[0] = 0;
+        if(threw) {
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "threw exception:\n");
+            DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), "  %s\n", exception.c_str());
+        } else {
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "with expansion:\n");
+            DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), "  %s( %s )\n",
+                             getAssertString(assert_type), decomposition);
+        }
+
+        bool isWarn = assert_type & assertType::is_warn;
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+        DOCTEST_PRINTF_COLORED(msg,
+                               passed ? Color::BrightGreen : isWarn ? Color::Yellow : Color::Red);
+        DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
+        DOCTEST_PRINTF_COLORED(info2, Color::None);
+        DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
+    }
+
+    void logAssertThrows(bool threw, const char* expr, assertType::Enum assert_type,
+                         const char* file, int line) {
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file),
+                         lineForOutput(line));
+
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " %s!\n",
+                         threw ? "PASSED" : getFailString(assert_type));
+
+        char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), "  %s( %s )\n",
+                         getAssertString(assert_type), expr);
+
+        char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        info2[0] = 0;
+
+        if(!threw)
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "didn't throw at all\n");
+
+        bool isWarn = assert_type & assertType::is_warn;
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+        DOCTEST_PRINTF_COLORED(msg,
+                               threw ? Color::BrightGreen : isWarn ? Color::Yellow : Color::Red);
+        DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
+        DOCTEST_PRINTF_COLORED(info2, Color::None);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(loc) + msg + info1 + info2 + context.c_str() + "\n");
+    }
+
+    void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const String& exception,
+                           const char* expr, assertType::Enum assert_type, const char* file,
+                           int line) {
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file),
+                         lineForOutput(line));
+
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " %s!\n",
+                         threw_as ? "PASSED" : getFailString(assert_type));
+
+        char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), "  %s( %s, %s )\n",
+                         getAssertString(assert_type), expr, as);
+
+        char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        char info3[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        info2[0] = 0;
+        info3[0] = 0;
+
+        if(!threw) { //!OCLINT inverted logic
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "didn't throw at all\n");
+        } else if(!threw_as) {
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "threw a different exception:\n");
+            DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), "  %s\n", exception.c_str());
+        }
+
+        bool isWarn = assert_type & assertType::is_warn;
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+        DOCTEST_PRINTF_COLORED(msg,
+                               threw_as ? Color::BrightGreen : isWarn ? Color::Yellow : Color::Red);
+        DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
+        DOCTEST_PRINTF_COLORED(info2, Color::None);
+        DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
+    }
+
+    void logAssertNothrow(bool threw, const String& exception, const char* expr,
+                          assertType::Enum assert_type, const char* file, int line) {
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(file),
+                         lineForOutput(line));
+
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " %s!\n",
+                         threw ? getFailString(assert_type) : "PASSED");
+
+        char info1[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(info1, DOCTEST_COUNTOF(info1), "  %s( %s )\n",
+                         getAssertString(assert_type), expr);
+
+        char info2[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        char info3[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        info2[0] = 0;
+        info3[0] = 0;
+        if(threw) {
+            DOCTEST_SNPRINTF(info2, DOCTEST_COUNTOF(info2), "threw exception:\n");
+            DOCTEST_SNPRINTF(info3, DOCTEST_COUNTOF(info3), "  %s\n", exception.c_str());
+        }
+
+        bool isWarn = assert_type & assertType::is_warn;
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+        DOCTEST_PRINTF_COLORED(msg,
+                               threw ? isWarn ? Color::Yellow : Color::Red : Color::BrightGreen);
+        DOCTEST_PRINTF_COLORED(info1, Color::Cyan);
+        DOCTEST_PRINTF_COLORED(info2, Color::None);
+        DOCTEST_PRINTF_COLORED(info3, Color::Cyan);
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(loc) + msg + info1 + info2 + info3 + context.c_str() + "\n");
+    }
+
+    ResultBuilder::ResultBuilder(assertType::Enum assert_type, const char* file, int line,
+                                 const char* expr, const char* exception_type)
+            : m_assert_type(assert_type)
+            , m_file(file)
+            , m_line(line)
+            , m_expr(expr)
+            , m_exception_type(exception_type)
+            , m_threw(false)
+            , m_threw_as(false)
+            , m_failed(false) {
+#ifdef _MSC_VER
+        if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
+            ++m_expr;
+#endif // _MSC_VER
+    }
+
+    ResultBuilder::~ResultBuilder() {}
+
+    void ResultBuilder::unexpectedExceptionOccurred() {
+        m_threw = true;
+
+        m_exception = translateActiveException();
+    }
+
+    bool ResultBuilder::log() {
+        if((m_assert_type & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional
+            contextState->numAssertionsForCurrentTestcase++;
+
+        if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+            m_failed = !m_threw;
+        } else if(m_assert_type & //!OCLINT bitwise operator in conditional
+                  assertType::is_throws_as) {
+            m_failed = !m_threw_as;
+        } else if(m_assert_type & //!OCLINT bitwise operator in conditional
+                  assertType::is_nothrow) {
+            m_failed = m_threw;
+        } else {
+            m_failed = m_result;
+        }
+
+        if(m_failed || contextState->success) {
+            DOCTEST_LOG_START();
+
+            if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+                logAssertThrows(m_threw, m_expr, m_assert_type, m_file, m_line);
+            } else if(m_assert_type & //!OCLINT bitwise operator in conditional
+                      assertType::is_throws_as) {
+                logAssertThrowsAs(m_threw, m_threw_as, m_exception_type, m_exception, m_expr,
+                                  m_assert_type, m_file, m_line);
+            } else if(m_assert_type & //!OCLINT bitwise operator in conditional
+                      assertType::is_nothrow) {
+                logAssertNothrow(m_threw, m_exception, m_expr, m_assert_type, m_file, m_line);
+            } else {
+                logAssert(m_result.m_passed, m_result.m_decomposition.c_str(), m_threw, m_exception,
+                          m_expr, m_assert_type, m_file, m_line);
+            }
+        }
+
+        if(m_failed)
+            addFailedAssert(m_assert_type);
+
+        return m_failed && isDebuggerActive() && !contextState->no_breaks; // break into debugger
+    }
+
+    void ResultBuilder::react() const {
+        if(m_failed && checkIfShouldThrow(m_assert_type))
+            throwException();
+    }
+
+    MessageBuilder::MessageBuilder(const char* file, int line,
+                                   doctest::detail::assertType::Enum severity)
+            : m_stream(createStream())
+            , m_file(file)
+            , m_line(line)
+            , m_severity(severity) {}
+
+    bool MessageBuilder::log() {
+        DOCTEST_LOG_START();
+
+        bool is_warn = m_severity & doctest::detail::assertType::is_warn;
+
+        // warn is just a message in this context so we dont treat it as an assert
+        if(!is_warn) {
+            contextState->numAssertionsForCurrentTestcase++;
+            addFailedAssert(m_severity);
+        }
+
+        char loc[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(loc, DOCTEST_COUNTOF(loc), "%s(%d)", fileForOutput(m_file),
+                         lineForOutput(m_line));
+        char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+        DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), " %s!\n",
+                         is_warn ? "MESSAGE" : getFailString(m_severity));
+
+        DOCTEST_PRINTF_COLORED(loc, Color::LightGrey);
+        DOCTEST_PRINTF_COLORED(msg, is_warn ? Color::Yellow : Color::Red);
+
+        String info = getStreamResult(m_stream);
+        if(info.size()) {
+            DOCTEST_PRINTF_COLORED("  ", Color::None);
+            DOCTEST_PRINTF_COLORED(info.c_str(), Color::None);
+            DOCTEST_PRINTF_COLORED("\n", Color::None);
+        }
+        String context = logContext();
+        DOCTEST_PRINTF_COLORED(context.c_str(), Color::None);
+        DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+        printToDebugConsole(String(loc) + msg + "  " + info.c_str() + "\n" + context.c_str() +
+                            "\n");
+
+        return isDebuggerActive() && !contextState->no_breaks && !is_warn; // break into debugger
+    }
+
+    void MessageBuilder::react() {
+        if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional
+            throwException();
+    }
+
+    MessageBuilder::~MessageBuilder() { freeStream(m_stream); }
+
+    // the implementation of parseFlag()
+    bool parseFlagImpl(int argc, const char* const* argv, const char* pattern) {
+        for(int i = argc - 1; i >= 0; --i) {
+            const char* temp = std::strstr(argv[i], pattern);
+            if(temp && my_strlen(temp) == my_strlen(pattern)) {
+                // eliminate strings in which the chars before the option are not '-'
+                bool noBadCharsFound = true; //!OCLINT prefer early exits and continue
+                while(temp != argv[i]) {
+                    if(*--temp != '-') {
+                        noBadCharsFound = false;
+                        break;
+                    }
+                }
+                if(noBadCharsFound && argv[i][0] == '-')
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    // locates a flag on the command line
+    bool parseFlag(int argc, const char* const* argv, const char* pattern) {
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+        if(!parseFlagImpl(argc, argv, pattern))
+            return parseFlagImpl(argc, argv, pattern + 3); // 3 for "dt-"
+        return true;
+#else  // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+        return parseFlagImpl(argc, argv, pattern);
+#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+    }
+
+    // the implementation of parseOption()
+    bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String& res) {
+        for(int i = argc - 1; i >= 0; --i) {
+            const char* temp = std::strstr(argv[i], pattern);
+            if(temp) { //!OCLINT prefer early exits and continue
+                // eliminate matches in which the chars before the option are not '-'
+                bool        noBadCharsFound = true;
+                const char* curr            = argv[i];
+                while(curr != temp) {
+                    if(*curr++ != '-') {
+                        noBadCharsFound = false;
+                        break;
+                    }
+                }
+                if(noBadCharsFound && argv[i][0] == '-') {
+                    temp += my_strlen(pattern);
+                    unsigned len = my_strlen(temp);
+                    if(len) {
+                        res = temp;
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    // parses an option and returns the string after the '=' character
+    bool parseOption(int argc, const char* const* argv, const char* pattern, String& res,
+                     const String& defaultVal = String()) {
+        res = defaultVal;
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+        if(!parseOptionImpl(argc, argv, pattern, res))
+            return parseOptionImpl(argc, argv, pattern + 3, res); // 3 for "dt-"
+        return true;
+#else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+        return parseOptionImpl(argc, argv, pattern, res);
+#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+    }
+
+    // parses a comma separated list of words after a pattern in one of the arguments in argv
+    bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern,
+                           std::vector<String>& res) {
+        String filtersString;
+        if(parseOption(argc, argv, pattern, filtersString)) {
+            // tokenize with "," as a separator
+            // cppcheck-suppress strtokCalled
+            char* pch = std::strtok(filtersString.c_str(), ","); // modifies the string
+            while(pch != 0) {
+                if(my_strlen(pch))
+                    res.push_back(pch);
+                // uses the strtok() internal state to go to the next token
+                // cppcheck-suppress strtokCalled
+                pch = std::strtok(0, ",");
+            }
+            return true;
+        }
+        return false;
+    }
+
+    enum optionType
+    {
+        option_bool,
+        option_int
+    };
+
+    // parses an int/bool option from the command line
+    bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type,
+                        int& res) {
+        String parsedValue;
+        if(!parseOption(argc, argv, pattern, parsedValue))
+            return false;
+
+        if(type == 0) {
+            // boolean
+            const char positive[][5] = {"1", "true", "on", "yes"};  // 5 - strlen("true") + 1
+            const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1
+
+            // if the value matches any of the positive/negative possibilities
+            for(unsigned i = 0; i < 4; i++) {
+                if(parsedValue.compare(positive[i], true) == 0) {
+                    res = 1; //!OCLINT parameter reassignment
+                    return true;
+                }
+                if(parsedValue.compare(negative[i], true) == 0) {
+                    res = 0; //!OCLINT parameter reassignment
+                    return true;
+                }
+            }
+        } else {
+            // integer
+            int theInt = std::atoi(parsedValue.c_str()); // NOLINT
+            if(theInt != 0) {
+                res = theInt; //!OCLINT parameter reassignment
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void printVersion() {
+        if(contextState->no_version == false) {
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+            std::printf("doctest version is \"%s\"\n", DOCTEST_VERSION_STR);
+        }
+    }
+
+    void printHelp() {
+        printVersion();
+        // clang-format off
+        DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n");
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("filter  values: \"str1,str2,str3\" (comma separated strings)\n");
+        DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("filters use wildcards for matching strings\n");
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("something passes a filter if any of the strings in a filter matches\n");
+        DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"dt-\" PREFIX!!!\n");
+        DOCTEST_PRINTF_COLORED("[doctest]\n", Color::Cyan);
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("Query flags - the program quits after them. Available:\n\n");
+        std::printf(" -?,   --help, -h                      prints this message\n");
+        std::printf(" -v,   --version                       prints the version\n");
+        std::printf(" -c,   --count                         prints the number of matching tests\n");
+        std::printf(" -ltc, --list-test-cases               lists all matching tests by name\n");
+        std::printf(" -lts, --list-test-suites              lists all matching test suites\n\n");
+        // ========================================================================================= << 79
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("The available <int>/<string> options/filters are:\n\n");
+        std::printf(" -tc,  --test-case=<filters>           filters     tests by their name\n");
+        std::printf(" -tce, --test-case-exclude=<filters>   filters OUT tests by their name\n");
+        std::printf(" -sf,  --source-file=<filters>         filters     tests by their file\n");
+        std::printf(" -sfe, --source-file-exclude=<filters> filters OUT tests by their file\n");
+        std::printf(" -ts,  --test-suite=<filters>          filters     tests by their test suite\n");
+        std::printf(" -tse, --test-suite-exclude=<filters>  filters OUT tests by their test suite\n");
+        std::printf(" -sc,  --subcase=<filters>             filters     subcases by their name\n");
+        std::printf(" -sce, --subcase-exclude=<filters>     filters OUT subcases by their name\n");
+        std::printf(" -ob,  --order-by=<string>             how the tests should be ordered\n");
+        std::printf("                                       <string> - by [file/suite/name/rand]\n");
+        std::printf(" -rs,  --rand-seed=<int>               seed for random ordering\n");
+        std::printf(" -f,   --first=<int>                   the first test passing the filters to\n");
+        std::printf("                                       execute - for range-based execution\n");
+        std::printf(" -l,   --last=<int>                    the last test passing the filters to\n");
+        std::printf("                                       execute - for range-based execution\n");
+        std::printf(" -aa,  --abort-after=<int>             stop after <int> failed assertions\n");
+        std::printf(" -scfl,--subcase-filter-levels=<int>   apply filters for the first <int> levels\n");
+        DOCTEST_PRINTF_COLORED("\n[doctest] ", Color::Cyan);
+        std::printf("Bool options - can be used like flags and true is assumed. Available:\n\n");
+        std::printf(" -s,   --success=<bool>                include successful assertions in output\n");
+        std::printf(" -cs,  --case-sensitive=<bool>         filters being treated as case sensitive\n");
+        std::printf(" -e,   --exit=<bool>                   exits after the tests finish\n");
+        std::printf(" -d,   --duration=<bool>               prints the time duration of each test\n");
+        std::printf(" -nt,  --no-throw=<bool>               skips exceptions-related assert checks\n");
+        std::printf(" -ne,  --no-exitcode=<bool>            returns (or exits) always with success\n");
+        std::printf(" -nr,  --no-run=<bool>                 skips all runtime doctest operations\n");
+        std::printf(" -nv,  --no-version=<bool>             omit the framework version in the output\n");
+        std::printf(" -nc,  --no-colors=<bool>              disables colors in output\n");
+        std::printf(" -fc,  --force-colors=<bool>           use colors even when not in a tty\n");
+        std::printf(" -nb,  --no-breaks=<bool>              disables breakpoints in debuggers\n");
+        std::printf(" -ns,  --no-skip=<bool>                don't skip test cases marked as skip\n");
+        std::printf(" -npf, --no-path-filenames=<bool>      only filenames and no paths in output\n");
+        std::printf(" -nln, --no-line-numbers=<bool>        0 instead of real line numbers in output\n");
+        // ========================================================================================= << 79
+        // clang-format on
+
+        DOCTEST_PRINTF_COLORED("\n[doctest] ", Color::Cyan);
+        std::printf("for more information visit the project documentation\n\n");
+    }
+
+    void printSummary() {
+        const ContextState* p = contextState;
+
+        DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+        if(p->count || p->list_test_cases) {
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+            std::printf("unskipped test cases passing the current filters: %u\n",
+                        p->numTestsPassingFilters);
+        } else if(p->list_test_suites) {
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+            std::printf("unskipped test cases passing the current filters: %u\n",
+                        p->numTestsPassingFilters);
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+            std::printf("test suites with unskipped test cases passing the current filters: %u\n",
+                        p->numTestSuitesPassingFilters);
+        } else {
+            bool anythingFailed = p->numFailed > 0 || p->numFailedAssertions > 0;
+
+            char buff[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "test cases: %6u",
+                             p->numTestsPassingFilters);
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%6d passed",
+                             p->numTestsPassingFilters - p->numFailed);
+            DOCTEST_PRINTF_COLORED(buff,
+                                   (p->numTestsPassingFilters == 0 || anythingFailed) ?
+                                           Color::None :
+                                           Color::Green);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%6u failed", p->numFailed);
+            DOCTEST_PRINTF_COLORED(buff, p->numFailed > 0 ? Color::Red : Color::None);
+
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            if(p->no_skipped_summary == false) {
+                int numSkipped = static_cast<unsigned>(getRegisteredTests().size()) -
+                                 p->numTestsPassingFilters;
+                DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%6d skipped", numSkipped);
+                DOCTEST_PRINTF_COLORED(buff, numSkipped == 0 ? Color::None : Color::Yellow);
+            }
+            DOCTEST_PRINTF_COLORED("\n", Color::None);
+
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "assertions: %6d", p->numAssertions);
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%6d passed",
+                             p->numAssertions - p->numFailedAssertions);
+            DOCTEST_PRINTF_COLORED(
+                    buff, (p->numAssertions == 0 || anythingFailed) ? Color::None : Color::Green);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " | ");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), "%6d failed", p->numFailedAssertions);
+            DOCTEST_PRINTF_COLORED(buff, p->numFailedAssertions > 0 ? Color::Red : Color::None);
+
+            DOCTEST_SNPRINTF(buff, DOCTEST_COUNTOF(buff), " |\n");
+            DOCTEST_PRINTF_COLORED(buff, Color::None);
+
+            DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+            DOCTEST_PRINTF_COLORED("Status: ", Color::None);
+            const char* result = (p->numFailed > 0) ? "FAILURE!\n" : "SUCCESS!\n";
+            DOCTEST_PRINTF_COLORED(result, p->numFailed > 0 ? Color::Red : Color::Green);
+        }
+
+        // remove any coloring
+        DOCTEST_PRINTF_COLORED("", Color::None);
+    }
+} // namespace detail
+
+bool isRunningInTest() { return detail::contextState != 0; }
+
+Context::Context(int argc, const char* const* argv)
+        : p(new detail::ContextState) {
+    parseArgs(argc, argv, true);
+}
+
+Context::~Context() { delete p; }
+
+void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); }
+
+// parses args
+void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
+    using namespace detail;
+
+    // clang-format off
+    parseCommaSepArgs(argc, argv, "dt-source-file=",        p->filters[0]);
+    parseCommaSepArgs(argc, argv, "dt-sf=",                 p->filters[0]);
+    parseCommaSepArgs(argc, argv, "dt-source-file-exclude=",p->filters[1]);
+    parseCommaSepArgs(argc, argv, "dt-sfe=",                p->filters[1]);
+    parseCommaSepArgs(argc, argv, "dt-test-suite=",         p->filters[2]);
+    parseCommaSepArgs(argc, argv, "dt-ts=",                 p->filters[2]);
+    parseCommaSepArgs(argc, argv, "dt-test-suite-exclude=", p->filters[3]);
+    parseCommaSepArgs(argc, argv, "dt-tse=",                p->filters[3]);
+    parseCommaSepArgs(argc, argv, "dt-test-case=",          p->filters[4]);
+    parseCommaSepArgs(argc, argv, "dt-tc=",                 p->filters[4]);
+    parseCommaSepArgs(argc, argv, "dt-test-case-exclude=",  p->filters[5]);
+    parseCommaSepArgs(argc, argv, "dt-tce=",                p->filters[5]);
+    parseCommaSepArgs(argc, argv, "dt-subcase=",            p->filters[6]);
+    parseCommaSepArgs(argc, argv, "dt-sc=",                 p->filters[6]);
+    parseCommaSepArgs(argc, argv, "dt-subcase-exclude=",    p->filters[7]);
+    parseCommaSepArgs(argc, argv, "dt-sce=",                p->filters[7]);
+    // clang-format on
+
+    int    intRes = 0;
+    String strRes;
+
+#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default)                                   \
+    if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_bool, intRes) ||       \
+       parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_bool, intRes))        \
+        p->var = !!intRes;                                                                         \
+    else if(parseFlag(argc, argv, #name) || parseFlag(argc, argv, #sname))                         \
+        p->var = true;                                                                             \
+    else if(withDefaults)                                                                          \
+    p->var = default
+
+#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default)                                        \
+    if(parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), option_int, intRes) ||        \
+       parseIntOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), option_int, intRes))         \
+        p->var = intRes;                                                                           \
+    else if(withDefaults)                                                                          \
+    p->var = default
+
+#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default)                                        \
+    if(parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(name, =), strRes, default) ||              \
+       parseOption(argc, argv, DOCTEST_STR_CONCAT_TOSTR(sname, =), strRes, default) ||             \
+       withDefaults)                                                                               \
+    p->var = strRes
+
+    // clang-format off
+    DOCTEST_PARSE_STR_OPTION(dt-order-by, dt-ob, order_by, "file");
+    DOCTEST_PARSE_INT_OPTION(dt-rand-seed, dt-rs, rand_seed, 0);
+
+    DOCTEST_PARSE_INT_OPTION(dt-first, dt-f, first, 1);
+    DOCTEST_PARSE_INT_OPTION(dt-last, dt-l, last, 0);
+
+    DOCTEST_PARSE_INT_OPTION(dt-abort-after, dt-aa, abort_after, 0);
+    DOCTEST_PARSE_INT_OPTION(dt-subcase-filter-levels, dt-scfl, subcase_filter_levels, 2000000000);
+
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-success, dt-s, success, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-case-sensitive, dt-cs, case_sensitive, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-exit, dt-e, exit, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-duration, dt-d, duration, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-throw, dt-nt, no_throw, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-exitcode, dt-ne, no_exitcode, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-run, dt-nr, no_run, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-version, dt-nv, no_version, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-colors, dt-nc, no_colors, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-force-colors, dt-fc, force_colors, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-breaks, dt-nb, no_breaks, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-skip, dt-ns, no_skip, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-path-filenames, dt-npf, no_path_in_filenames, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-line-numbers, dt-nln, no_line_numbers, false);
+    DOCTEST_PARSE_AS_BOOL_OR_FLAG(dt-no-skipped-summary, dt-nss, no_skipped_summary, false);
+// clang-format on
+
+#undef DOCTEST_PARSE_STR_OPTION
+#undef DOCTEST_PARSE_INT_OPTION
+#undef DOCTEST_PARSE_AS_BOOL_OR_FLAG
+
+    if(withDefaults) {
+        p->help             = false;
+        p->version          = false;
+        p->count            = false;
+        p->list_test_cases  = false;
+        p->list_test_suites = false;
+    }
+    if(parseFlag(argc, argv, "dt-help") || parseFlag(argc, argv, "dt-h") ||
+       parseFlag(argc, argv, "dt-?")) {
+        p->help = true;
+        p->exit = true;
+    }
+    if(parseFlag(argc, argv, "dt-version") || parseFlag(argc, argv, "dt-v")) {
+        p->version = true;
+        p->exit    = true;
+    }
+    if(parseFlag(argc, argv, "dt-count") || parseFlag(argc, argv, "dt-c")) {
+        p->count = true;
+        p->exit  = true;
+    }
+    if(parseFlag(argc, argv, "dt-list-test-cases") || parseFlag(argc, argv, "dt-ltc")) {
+        p->list_test_cases = true;
+        p->exit            = true;
+    }
+    if(parseFlag(argc, argv, "dt-list-test-suites") || parseFlag(argc, argv, "dt-lts")) {
+        p->list_test_suites = true;
+        p->exit             = true;
+    }
+}
+
+// allows the user to add procedurally to the filters from the command line
+void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); }
+
+// allows the user to clear all filters from the command line
+void Context::clearFilters() {
+    for(unsigned i = 0; i < p->filters.size(); ++i)
+        p->filters[i].clear();
+}
+
+// allows the user to override procedurally the int/bool options from the command line
+void Context::setOption(const char* option, int value) {
+    setOption(option, toString(value).c_str());
+}
+
+// allows the user to override procedurally the string options from the command line
+void Context::setOption(const char* option, const char* value) {
+    String      argv   = String("-") + option + "=" + value;
+    const char* lvalue = argv.c_str();
+    parseArgs(1, &lvalue);
+}
+
+// users should query this in their main() and exit the program if true
+bool Context::shouldExit() { return p->exit; }
+
+// the main function that does all the filtering and test running
+int Context::run() {
+    using namespace detail;
+
+    Color::init();
+
+    contextState = p;
+    p->resetRunData();
+
+    // handle version, help and no_run
+    if(p->no_run || p->version || p->help) {
+        if(p->version)
+            printVersion();
+        if(p->help)
+            printHelp();
+
+        contextState = 0;
+
+        return EXIT_SUCCESS;
+    }
+
+    printVersion();
+    DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+    std::printf("run with \"--help\" for options\n");
+
+    unsigned i = 0; // counter used for loops - here for VC6
+
+    std::set<TestCase>& registeredTests = getRegisteredTests();
+
+    std::vector<const TestCase*> testArray;
+    for(std::set<TestCase>::iterator it = registeredTests.begin(); it != registeredTests.end();
+        ++it)
+        testArray.push_back(&(*it));
+
+    // sort the collected records
+    if(!testArray.empty()) {
+        if(p->order_by.compare("file", true) == 0) {
+            std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), fileOrderComparator);
+        } else if(p->order_by.compare("suite", true) == 0) {
+            std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), suiteOrderComparator);
+        } else if(p->order_by.compare("name", true) == 0) {
+            std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), nameOrderComparator);
+        } else if(p->order_by.compare("rand", true) == 0) {
+            std::srand(p->rand_seed);
+
+            // random_shuffle implementation
+            const TestCase** first = &testArray[0];
+            for(i = testArray.size() - 1; i > 0; --i) {
+                int idxToSwap = std::rand() % (i + 1); // NOLINT
+
+                const TestCase* temp = first[i];
+
+                first[i]         = first[idxToSwap];
+                first[idxToSwap] = temp;
+            }
+        }
+    }
+
+    if(p->list_test_cases) {
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("listing all test case names\n");
+        DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+    }
+
+    std::set<String> testSuitesPassingFilters;
+    if(p->list_test_suites) {
+        DOCTEST_PRINTF_COLORED("[doctest] ", Color::Cyan);
+        std::printf("listing all test suites\n");
+        DOCTEST_PRINTF_COLORED(getSeparator(), Color::Yellow);
+    }
+
+    // invoke the registered functions if they match the filter criteria (or just count them)
+    for(i = 0; i < testArray.size(); i++) {
+        const TestCase& data = *testArray[i];
+
+        if(data.m_skip && !p->no_skip)
+            continue;
+
+        if(!matchesAny(data.m_file, p->filters[0], 1, p->case_sensitive))
+            continue;
+        if(matchesAny(data.m_file, p->filters[1], 0, p->case_sensitive))
+            continue;
+        if(!matchesAny(data.m_test_suite, p->filters[2], 1, p->case_sensitive))
+            continue;
+        if(matchesAny(data.m_test_suite, p->filters[3], 0, p->case_sensitive))
+            continue;
+        if(!matchesAny(data.m_name, p->filters[4], 1, p->case_sensitive))
+            continue;
+        if(matchesAny(data.m_name, p->filters[5], 0, p->case_sensitive))
+            continue;
+
+        p->numTestsPassingFilters++;
+
+        // do not execute the test if we are to only count the number of filter passing tests
+        if(p->count)
+            continue;
+
+        // print the name of the test and don't execute it
+        if(p->list_test_cases) {
+            std::printf("%s\n", data.m_name);
+            continue;
+        }
+
+        // print the name of the test suite if not done already and don't execute it
+        if(p->list_test_suites) {
+            if((testSuitesPassingFilters.count(data.m_test_suite) == 0) &&
+               data.m_test_suite[0] != '\0') {
+                std::printf("%s\n", data.m_test_suite);
+                testSuitesPassingFilters.insert(data.m_test_suite);
+                p->numTestSuitesPassingFilters++;
+            }
+            continue;
+        }
+
+        // skip the test if it is not in the execution range
+        if((p->last < p->numTestsPassingFilters && p->first <= p->last) ||
+           (p->first > p->numTestsPassingFilters))
+            continue;
+
+        // execute the test if it passes all the filtering
+        {
+            p->currentTest = &data;
+
+            bool failed                              = false;
+            p->hasLoggedCurrentTestStart             = false;
+            p->numFailedAssertionsForCurrentTestcase = 0;
+            p->subcasesPassed.clear();
+            double duration = 0;
+            Timer  timer;
+            timer.start();
+            do {
+                // if the start has been logged from a previous iteration of this loop
+                if(p->hasLoggedCurrentTestStart)
+                    logTestEnd();
+                p->hasLoggedCurrentTestStart = false;
+
+                // if logging successful tests - force the start log
+                if(p->success)
+                    DOCTEST_LOG_START();
+
+                // reset the assertion state
+                p->numAssertionsForCurrentTestcase = 0;
+                p->hasCurrentTestFailed            = false;
+
+                // reset some of the fields for subcases (except for the set of fully passed ones)
+                p->subcasesHasSkipped   = false;
+                p->subcasesCurrentLevel = 0;
+                p->subcasesEnteredLevels.clear();
+
+                // reset stuff for logging with INFO()
+                p->exceptionalContexts.clear();
+
+// execute the test
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+                try {
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+                    FatalConditionHandler fatalConditionHandler; // Handle signals
+                    data.m_test();
+                    fatalConditionHandler.reset();
+                    if(contextState->hasCurrentTestFailed)
+                        failed = true;
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+                } catch(const TestFailureException&) { failed = true; } catch(...) {
+                    DOCTEST_LOG_START();
+                    logTestException(translateActiveException());
+                    failed = true;
+                }
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+                p->numAssertions += p->numAssertionsForCurrentTestcase;
+
+                // exit this loop if enough assertions have failed
+                if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) {
+                    p->subcasesHasSkipped = false;
+                    DOCTEST_PRINTF_COLORED("Aborting - too many failed asserts!\n", Color::Red);
+                }
+
+            } while(p->subcasesHasSkipped == true);
+
+            duration = timer.getElapsedSeconds();
+
+            if(Approx(p->currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 &&
+               Approx(duration).epsilon(DBL_EPSILON) > p->currentTest->m_timeout) {
+                failed = true;
+                DOCTEST_LOG_START();
+                char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+                DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg),
+                                 "Test case exceeded time limit of %.6f!\n",
+                                 p->currentTest->m_timeout);
+                DOCTEST_PRINTF_COLORED(msg, Color::Red);
+            }
+
+            if(p->duration) {
+                char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+                DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg), "%.6f s: %s\n", duration,
+                                 p->currentTest->m_name);
+                DOCTEST_PRINTF_COLORED(msg, Color::None);
+            }
+
+            if(data.m_should_fail) {
+                DOCTEST_LOG_START();
+                if(failed) {
+                    failed = false;
+                    DOCTEST_PRINTF_COLORED("Failed as expected so marking it as not failed\n",
+                                           Color::Yellow);
+                } else {
+                    failed = true;
+                    DOCTEST_PRINTF_COLORED("Should have failed but didn't! Marking it as failed!\n",
+                                           Color::Red);
+                }
+            } else if(failed && data.m_may_fail) {
+                DOCTEST_LOG_START();
+                failed = false;
+                DOCTEST_PRINTF_COLORED("Allowed to fail so marking it as not failed\n",
+                                       Color::Yellow);
+            } else if(data.m_expected_failures > 0) {
+                DOCTEST_LOG_START();
+                char msg[DOCTEST_SNPRINTF_BUFFER_LENGTH];
+                if(p->numFailedAssertionsForCurrentTestcase == data.m_expected_failures) {
+                    failed = false;
+                    DOCTEST_SNPRINTF(
+                            msg, DOCTEST_COUNTOF(msg),
+                            "Failed exactly %d times as expected so marking it as not failed!\n",
+                            data.m_expected_failures);
+                    DOCTEST_PRINTF_COLORED(msg, Color::Yellow);
+                } else {
+                    failed = true;
+                    DOCTEST_SNPRINTF(msg, DOCTEST_COUNTOF(msg),
+                                     "Didn't fail exactly %d times so marking it as failed!\n",
+                                     data.m_expected_failures);
+                    DOCTEST_PRINTF_COLORED(msg, Color::Red);
+                }
+            }
+
+            if(p->hasLoggedCurrentTestStart)
+                logTestEnd();
+
+            if(failed) // if any subcase has failed - the whole test case has failed
+                p->numFailed++;
+
+            // stop executing tests if enough assertions have failed
+            if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after)
+                break;
+        }
+    }
+
+    printSummary();
+
+    contextState = 0;
+
+    if(p->numFailed && !p->no_exitcode)
+        return EXIT_FAILURE;
+    return EXIT_SUCCESS;
+}
+} // namespace doctest
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); }
+#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+
+#endif // DOCTEST_LIBRARY_IMPLEMENTATION
+#endif // DOCTEST_CONFIG_IMPLEMENT
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // __clang__
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6)
+#pragma GCC diagnostic pop
+#endif // > gcc 4.6
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
diff --git a/third_party/immer/tools/include/nonius.h++ b/third_party/immer/tools/include/nonius.h++
new file mode 100644
index 0000000000..2fb3758dd8
--- /dev/null
+++ b/third_party/immer/tools/include/nonius.h++
@@ -0,0 +1,4292 @@
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014 by Martinho Fernandes <martinho.fernandes@gmail.com>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// This file was automatically generated on 2017-07-26T10:25:52.043838Z
+// Do not edit it directly
+
+#ifndef NONIUS_SINGLE_INCLUDE_HPP
+#define NONIUS_SINGLE_INCLUDE_HPP
+
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Single header root
+
+// #included from: nonius.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Main header
+
+#ifndef NONIUS_HPP
+#define NONIUS_HPP
+
+// #included from: clock.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Clocks
+
+#define NONIUS_CLOCK_HPP
+
+// #included from: detail/compiler.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Compiler detection macros
+
+#define NONIUS_DETAIL_COMPILER_HPP
+
+#if defined(__clang__)
+#   define NONIUS_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
+#elif defined(__GNUC__)
+#   define NONIUS_GCC   (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#elif defined(_MSC_VER)
+#   define NONIUS_MSVC  _MSC_VER
+#endif
+
+// MSVC <chrono> is borken and had little to no testing done before shipping (Dev14/VS15 CTP fixes it)
+#if defined(NONIUS_MSVC) && NONIUS_MSVC < 1900
+#   ifndef NONIUS_USE_BOOST_CHRONO
+#       define NONIUS_USE_BOOST_CHRONO
+#   endif
+#endif
+
+#ifdef NONIUS_USE_BOOST_CHRONO
+#   include <boost/chrono.hpp>
+#else
+#   include <chrono>
+#   include <ratio>
+#endif
+
+namespace nonius {
+#ifdef NONIUS_USE_BOOST_CHRONO
+    namespace chrono = boost::chrono;
+    template <unsigned Num, unsigned Den = 1>
+    using ratio = boost::ratio<Num, Den>;
+#else
+    namespace chrono = std::chrono;
+    template <unsigned Num, unsigned Den = 1>
+    using ratio = std::ratio<Num, Den>;
+#endif
+    using milli = ratio<1,       1000>;
+    using micro = ratio<1,    1000000>;
+    using nano  = ratio<1, 1000000000>;
+
+    template <typename Clock>
+    using Duration = typename Clock::duration;
+    template <typename Clock>
+    using FloatDuration = chrono::duration<double, typename Clock::period>;
+
+    template <typename Clock>
+    using TimePoint = typename Clock::time_point;
+
+    using default_clock = chrono::high_resolution_clock;
+
+    template <typename Clock>
+    struct now {
+        TimePoint<Clock> operator()() const {
+            return Clock::now();
+        }
+    };
+
+    using fp_seconds = chrono::duration<double, ratio<1>>;
+} // namespace nonius
+
+// #included from: benchmark.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Benchmark
+
+#define NONIUS_BENCHMARK_HPP
+
+// #included from: configuration.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Run configuration
+
+#define NONIUS_CONFIGURATION_HPP
+
+// #included from: param.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014 by Martinho Fernandes <martinho.fernandes@gmail.com>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// User-facing param
+
+#define NONIUS_PARAM_HPP
+
+// #included from: detail/meta.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014 by Martinho Fernandes <martinho.fernandes@gmail.com>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Invoke with a special case for void
+
+#define NONIUS_META_HPP
+
+#include <type_traits>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename> struct true_given : std::true_type {};
+        struct is_callable_tester {
+            template <typename Fun, typename... Args>
+            true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
+            template <typename...>
+            std::false_type static test(...);
+        };
+        template <typename T>
+        struct is_callable;
+        template <typename Fun, typename... Args>
+        struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
+    } // namespace detail
+} // namespace nonius
+
+// #included from: detail/noexcept.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Cross-compiler noexcept support
+
+#define NONIUS_DETAIL_NOEXCEPT_HPP
+
+#if defined(NONIUS_MSVC) && NONIUS_MSVC < 1900
+#   define NONIUS_NOEXCEPT
+#else
+#   define NONIUS_NOEXCEPT noexcept
+#endif
+
+#include <boost/lexical_cast.hpp>
+#include <unordered_map>
+#include <typeinfo>
+#include <memory>
+
+namespace nonius {
+
+struct param_bad_operation : std::exception {
+    char const* what() const NONIUS_NOEXCEPT override {
+        return "operation not supported for this parameter type";
+    }
+};
+
+namespace detail {
+
+struct eq_fn {
+    template <typename T>
+    auto operator() (T x, T y) -> decltype(x == y) { return x == y; }
+};
+
+struct plus_fn {
+    template <typename T>
+    auto operator() (T x, T y) -> decltype(x + y) { return x + y; }
+};
+
+struct mult_fn {
+    template <typename T>
+    auto operator() (T x, T y) -> decltype(x * y) { return x * y; }
+};
+
+} // namespace detail
+
+class param {
+    struct interface {
+        virtual ~interface() = default;
+        virtual std::istream& pull(std::istream&) = 0;
+        virtual std::ostream& push(std::ostream&) const = 0;
+        virtual bool eq(param const&) const = 0;
+        virtual param plus(param const&) const = 0;
+        virtual param mult(param const&) const = 0;
+        virtual param clone() const = 0;
+    };
+
+    template <typename T>
+    struct model : interface {
+        T value;
+
+        model(T v) : value(std::move(v)) {}
+        std::istream& pull(std::istream& is) override { is >> value; return is; }
+        std::ostream& push(std::ostream& os) const override { os << value; return os; }
+        bool eq(param const& y) const override { return operate<bool>(detail::eq_fn{}, value, y.as<T>()); }
+        param plus(param const& y) const override { return operate<T>(detail::plus_fn{}, value, y.as<T>()); }
+        param mult(param const& y) const override { return operate<T>(detail::mult_fn{}, value, y.as<T>()); }
+        param clone() const override { return value; }
+
+        template <typename R, typename Op, typename ...Args>
+        auto operate(Op&& op, Args&& ...xs) const
+            -> typename std::enable_if<detail::is_callable<Op&&(Args&&...)>::value, R>::type {
+            return std::forward<Op>(op)(std::forward<Args>(xs)...);
+        }
+
+        template <typename R, typename Op, typename ...Args>
+        auto operate(Op&&, Args&&...) const
+            -> typename std::enable_if<!detail::is_callable<Op&&(Args&&...)>::value, R>::type {
+            throw param_bad_operation{};
+        }
+    };
+
+public:
+    template <typename T,
+              typename std::enable_if<!std::is_base_of<param, typename std::decay<T>::type>::value, int>::type = 0>
+    param(T&& v)
+        : impl_(new model<typename std::decay<T>::type>{std::forward<T>(v)}) {}
+
+    template <typename T>
+    const T& as() const& { return dynamic_cast<model<T> const&>(*impl_).value; }
+
+    friend std::istream& operator>>(std::istream& is, param& x) { x.writable_(); return x.impl_->pull(is); }
+    friend std::ostream& operator<<(std::ostream& os, const param& x) { return x.impl_->push(os); }
+    friend param operator+(const param& x, const param& y) { return x.impl_->plus(y); }
+    friend param operator*(const param& x, const param& y) { return x.impl_->mult(y); }
+    friend bool operator==(const param& x, const param& y) { return x.impl_->eq(y); }
+
+    param parse(std::string const& s) const {
+        auto ss = std::stringstream{s};
+        auto cpy = *this;
+        ss.exceptions(std::ios::failbit);
+        ss >> cpy;
+        return cpy;
+    }
+
+private:
+    void writable_() {
+        if (impl_.use_count() > 1) {
+        }
+    }
+
+    std::shared_ptr<interface> impl_;
+};
+
+struct parameters : std::unordered_map<std::string, param> {
+    using base_t = std::unordered_map<std::string, param>;
+    using base_t::base_t;
+
+    template <typename Tag>
+    auto get() const -> typename Tag::type {
+        return at(Tag::name()).template as<typename Tag::type>();
+    }
+
+    parameters merged(parameters m) const& {
+        m.insert(begin(), end());
+        return m;
+    }
+    parameters merged(parameters m) && {
+        m.insert(std::make_move_iterator(begin()), std::make_move_iterator(end()));
+        return m;
+    }
+
+    friend std::ostream& operator<< (std::ostream& os, const parameters& m) {
+        for(auto&& p : m) os << "  " << p.first << " = " << p.second << "\n";
+        return os;
+    }
+};
+
+struct param_registry {
+    parameters const& defaults() const { return defaults_; }
+    void add(std::string name, param v) { defaults_.emplace(name, v); }
+    void remove(std::string name) { defaults_.erase(name); }
+
+private:
+    parameters defaults_;
+};
+
+inline param_registry& global_param_registry() {
+    static param_registry instance;
+    return instance;
+}
+
+template <typename Tag>
+struct param_declaration {
+    using type = typename Tag::type;
+    param_registry& registry;
+
+    param_declaration(type v, param_registry& r = global_param_registry())
+        : registry{r}
+    {
+        r.add(Tag::name(), param{type{v}});
+    }
+};
+
+template <typename Tag>
+struct scoped_param_declaration : param_declaration<Tag> {
+    using param_declaration<Tag>::param_declaration;
+
+    ~scoped_param_declaration() {
+        this->registry.remove(Tag::name());
+    }
+};
+
+} /* namespace nonius */
+
+#define NONIUS_PARAM(name_, ...)                                        \
+    namespace {                                                         \
+    struct name_ {                                                      \
+        using type = decltype(__VA_ARGS__);                             \
+        static const char* name() { return #name_; }                    \
+    };                                                                  \
+    static auto NONIUS_DETAIL_UNIQUE_NAME(param_declaration) =          \
+                   ::nonius::param_declaration<name_>{ (__VA_ARGS__) }; \
+    }                                                                   \
+    //
+
+#include <boost/optional.hpp>
+#include <string>
+#include <vector>
+
+namespace nonius {
+    struct run_configuration {
+        std::string name;
+        std::string op;
+        param init;
+        param step;
+        std::size_t count;
+    };
+
+    struct param_configuration {
+        parameters map;
+        boost::optional<run_configuration> run;
+    };
+
+    struct configuration {
+    public:
+        int samples = 100;
+        double confidence_interval = 0.95;
+        int resamples = 100000;
+        std::string title = "benchmarks";
+        std::string output_file;
+        std::string reporter;
+        std::string filter_pattern = ".*";
+        bool list_benchmarks = false;
+        bool list_params = false;
+        bool list_reporters = false;
+        bool no_analysis = false;
+        bool verbose = false;
+        bool summary = false;
+        bool help = false;
+        param_configuration params;
+    };
+} // namespace nonius
+
+// #included from: environment.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Environment information
+
+#define NONIUS_ENVIRONMENT_HPP
+
+// #included from: outlier_classification.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Outlier information
+
+#define NONIUS_OUTLIERS_HPP
+
+namespace nonius {
+    struct outlier_classification {
+        int samples_seen = 0;
+        int low_severe = 0;     // more than 3 times IQR below Q1
+        int low_mild = 0;       // 1.5 to 3 times IQR below Q1
+        int high_mild = 0;      // 1.5 to 3 times IQR above Q3
+        int high_severe = 0;    // more than 3 times IQR above Q3
+
+        int total() const {
+            return low_severe + low_mild + high_mild + high_severe;
+        }
+    };
+} // namespace nonius
+
+namespace nonius {
+    template <typename Duration>
+    struct environment_estimate {
+        Duration mean;
+        outlier_classification outliers;
+
+        template <typename Duration2>
+        operator environment_estimate<Duration2>() const {
+            return { mean, outliers };
+        }
+    };
+    template <typename Clock>
+    struct environment {
+        using clock_type = Clock;
+        environment_estimate<FloatDuration<Clock>> clock_resolution;
+        environment_estimate<FloatDuration<Clock>> clock_cost;
+        //estimate function_cost;
+    };
+} // namespace nonius
+
+// #included from: execution_plan.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Execution plan
+
+#define NONIUS_EXECUTION_PLAN_HPP
+
+// #included from: optimizer.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2016 by Martinho Fernandes <rmf@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Hinting the optimizer
+
+#define NONIUS_OPTIMIZER_HPP
+
+#if defined(NONIUS_MSVC)
+#   include <atomic> // atomic_thread_fence
+#endif
+
+namespace nonius {
+#if defined(NONIUS_GCC) || defined(NONIUS_CLANG)
+    template <typename T>
+    inline void keep_memory(T* p) {
+        asm volatile("" : : "g"(p) : "memory");
+    }
+    inline void keep_memory() {
+        asm volatile("" : : : "memory");
+    }
+
+    namespace detail {
+        inline void optimizer_barrier() { keep_memory(); }
+    } // namespace detail
+#elif defined(NONIUS_MSVC)
+
+#pragma optimize("", off)
+    template <typename T>
+    inline void keep_memory(T* p) {
+        // thanks @milleniumbug
+    }
+    // TODO equivalent keep_memory()
+#pragma optimize("", on)
+
+    namespace detail {
+        inline void optimizer_barrier() {
+            std::atomic_thread_fence(std::memory_order_seq_cst);
+        }
+    } // namespace detail
+
+#endif
+} // namespace nonius
+
+// #included from: detail/benchmark_function.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Dumb std::function implementation for consistent call overhead
+
+#define NONIUS_DETAIL_BENCHMARK_FUNCTION_HPP
+
+// #included from: chronometer.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// User-facing chronometer
+
+#define NONIUS_CHRONOMETER_HPP
+
+// #included from: detail/complete_invoke.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Invoke with a special case for void
+
+#define NONIUS_DETAIL_COMPLETE_INVOKE_HPP
+
+#include <type_traits>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename T>
+        struct complete_type { using type = T; };
+        template <>
+        struct complete_type<void> { struct type {}; };
+
+        template <typename T>
+        using CompleteType = typename complete_type<T>::type;
+
+        template <typename Result>
+        struct complete_invoker {
+            template <typename Fun, typename... Args>
+            static Result invoke(Fun&& fun, Args&&... args) {
+                return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+            }
+        };
+        template <>
+        struct complete_invoker<void> {
+            template <typename Fun, typename... Args>
+            static CompleteType<void> invoke(Fun&& fun, Args&&... args) {
+                std::forward<Fun>(fun)(std::forward<Args>(args)...);
+                return {};
+            }
+        };
+        template <typename Sig>
+        using ResultOf = typename std::result_of<Sig>::type;
+
+        // invoke and not return void :(
+        template <typename Fun, typename... Args>
+        CompleteType<ResultOf<Fun(Args...)>> complete_invoke(Fun&& fun, Args&&... args) {
+            return complete_invoker<ResultOf<Fun(Args...)>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <boost/lexical_cast.hpp>
+
+namespace nonius {
+    namespace detail {
+        struct chronometer_concept {
+            virtual void start() = 0;
+            virtual void finish() = 0;
+            virtual ~chronometer_concept() = default;
+        };
+        template <typename Clock>
+        struct chronometer_model final : public chronometer_concept {
+            void start() override { started = Clock::now(); }
+            void finish() override { finished = Clock::now(); }
+
+            Duration<Clock> elapsed() const { return finished - started; }
+
+            TimePoint<Clock> started;
+            TimePoint<Clock> finished;
+        };
+    } // namespace detail
+
+    struct chronometer {
+    public:
+        template <typename Fun>
+        void measure(Fun&& fun) { measure(std::forward<Fun>(fun), detail::is_callable<Fun(int)>()); }
+
+        int runs() const { return k; }
+
+        chronometer(detail::chronometer_concept& meter, int k, const parameters& p)
+            : impl(&meter)
+            , k(k)
+            , params(&p)
+        {}
+
+        template <typename Tag>
+        auto param() const -> typename Tag::type {
+            return params->get<Tag>();
+        }
+
+    private:
+        template <typename Fun>
+        void measure(Fun&& fun, std::false_type) {
+            measure([&fun](int) { fun(); });
+        }
+        template <typename Fun>
+        void measure(Fun&& fun, std::true_type) {
+            impl->start();
+            for(int i = 0; i < k; ++i) fun(i);
+            impl->finish();
+        }
+
+        detail::chronometer_concept* impl;
+        int k;
+        const parameters* params;
+    };
+} // namespace nonius
+
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace nonius {
+    namespace detail {
+        template <typename T>
+        using Decay = typename std::decay<T>::type;
+        template <typename T, typename U>
+        struct is_related
+        : std::is_same<Decay<T>, Decay<U>> {};
+
+        /// We need to reinvent std::function because every piece of code that might add overhead
+        /// in a measurement context needs to have consistent performance characteristics so that we
+        /// can account for it in the measurement.
+        /// Implementations of std::function with optimizations that aren't always applicable, like
+        /// small buffer optimizations, are not uncommon.
+        /// This is effectively an implementation of std::function without any such optimizations;
+        /// it may be slow, but it is consistently slow.
+        struct benchmark_function {
+        private:
+            struct concept {
+                virtual benchmark_function call(parameters params) const = 0;
+                virtual void call(chronometer meter) const = 0;
+                virtual concept* clone() const = 0;
+                virtual ~concept() = default;
+            };
+            template <typename Fun>
+            struct model : public concept {
+                model(Fun&& fun) : fun(std::move(fun)) {}
+                model(Fun const& fun) : fun(fun) {}
+
+                model<Fun>* clone() const override { return new model<Fun>(*this); }
+
+                benchmark_function call(parameters params) const override {
+                    return call(params, is_callable<Fun(parameters)>());
+                }
+                benchmark_function call(parameters params, std::true_type) const {
+                    return fun(params);
+                }
+                benchmark_function call(parameters, std::false_type) const {
+                    return this->clone();
+                }
+
+                void call(chronometer meter) const override {
+                    call(meter, is_callable<Fun(chronometer)>(), is_callable<Fun(parameters)>());
+                }
+                void call(chronometer meter, std::true_type, std::false_type) const {
+                    fun(meter);
+                }
+                void call(chronometer meter, std::false_type, std::false_type) const {
+                    meter.measure(fun);
+                }
+                template <typename T>
+                void call(chronometer, T, std::true_type) const {
+                    // the function should be prepared first
+                    assert(false);
+                }
+
+                Fun fun;
+            };
+
+            struct do_nothing { void operator()() const {} };
+
+            template <typename T>
+            benchmark_function(model<T>* c) : f(c) {}
+
+        public:
+            benchmark_function()
+            : f(new model<do_nothing>{{}})
+            {}
+
+            template <typename Fun,
+                      typename std::enable_if<!is_related<Fun, benchmark_function>::value, int>::type = 0>
+            benchmark_function(Fun&& fun)
+            : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+
+            benchmark_function(benchmark_function&& that)
+            : f(std::move(that.f)) {}
+
+            benchmark_function(benchmark_function const& that)
+            : f(that.f->clone()) {}
+
+            benchmark_function& operator=(benchmark_function&& that) {
+                f = std::move(that.f);
+                return *this;
+            }
+
+            benchmark_function& operator=(benchmark_function const& that) {
+                f.reset(that.f->clone());
+                return *this;
+            }
+
+            benchmark_function operator()(parameters params) const { return f->call(params); }
+            void operator()(chronometer meter) const { f->call(meter); }
+        private:
+            std::unique_ptr<concept> f;
+        };
+    } // namespace detail
+} // namespace nonius
+
+// #included from: detail/repeat.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// repeat algorithm
+
+#define NONIUS_DETAIL_REPEAT_HPP
+
+#include <type_traits>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename Fun>
+        struct repeater {
+            void operator()(int k) const {
+                for(int i = 0; i < k; ++i) {
+                    fun();
+                }
+            }
+            Fun fun;
+        };
+        template <typename Fun>
+        repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
+            return { std::forward<Fun>(fun) };
+        }
+    } // namespace detail
+} // namespace nonius
+
+// #included from: detail/run_for_at_least.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Run a function for a minimum amount of time
+
+#define NONIUS_RUN_FOR_AT_LEAST_HPP
+
+// #included from: detail/measure.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Measure
+
+#define NONIUS_DETAIL_MEASURE_HPP
+
+// #included from: detail/timing.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Timing
+
+#define NONIUS_DETAIL_TIMING_HPP
+
+#include <tuple>
+#include <type_traits>
+
+namespace nonius {
+    template <typename Duration, typename Result>
+    struct timing {
+        Duration elapsed;
+        Result result;
+        int iterations;
+    };
+    template <typename Clock, typename Sig>
+    using TimingOf = timing<Duration<Clock>, detail::CompleteType<detail::ResultOf<Sig>>>;
+} // namespace nonius
+
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename Clock, typename Fun, typename... Args>
+        TimingOf<Clock, Fun(Args...)> measure(Fun&& fun, Args&&... args) {
+            auto start = Clock::now();
+            auto&& r = detail::complete_invoke(fun, std::forward<Args>(args)...);
+            auto end = Clock::now();
+            auto delta = end - start;
+            return { delta, std::forward<decltype(r)>(r), 1 };
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <utility>
+#include <type_traits>
+
+namespace nonius {
+    namespace detail {
+        template <typename Clock, typename Fun>
+        TimingOf<Clock, Fun(int)> measure_one(Fun&& fun, int iters, const parameters&, std::false_type) {
+            return detail::measure<Clock>(fun, iters);
+        }
+        template <typename Clock, typename Fun>
+        TimingOf<Clock, Fun(chronometer)> measure_one(Fun&& fun, int iters, const parameters& params, std::true_type) {
+            detail::chronometer_model<Clock> meter;
+            auto&& result = detail::complete_invoke(fun, chronometer(meter, iters, params));
+
+            return { meter.elapsed(), std::move(result), iters };
+        }
+
+        template <typename Clock, typename Fun>
+        using run_for_at_least_argument_t = typename std::conditional<detail::is_callable<Fun(chronometer)>::value, chronometer, int>::type;
+
+        struct optimized_away_error : std::exception {
+            const char* what() const NONIUS_NOEXCEPT override {
+                return "could not measure benchmark, maybe it was optimized away";
+            }
+        };
+
+        template <typename Clock, typename Fun>
+        TimingOf<Clock, Fun(run_for_at_least_argument_t<Clock, Fun>)> run_for_at_least(const parameters& params, Duration<Clock> how_long, int seed, Fun&& fun) {
+            auto iters = seed;
+            while(iters < (1 << 30)) {
+                auto&& timing = measure_one<Clock>(fun, iters, params, detail::is_callable<Fun(chronometer)>());
+
+                if(timing.elapsed >= how_long) {
+                    return { timing.elapsed, std::move(timing.result), iters };
+                }
+                iters *= 2;
+            }
+            throw optimized_away_error{};
+        }
+    } // namespace detail
+} // namespace nonius
+
+namespace nonius {
+    template <typename Duration>
+    struct execution_plan {
+        int iterations_per_sample;
+        Duration estimated_duration;
+        parameters params;
+        detail::benchmark_function benchmark;
+        Duration warmup_time;
+        int warmup_iterations;
+
+        template <typename Duration2>
+        operator execution_plan<Duration2>() const {
+            return { iterations_per_sample, estimated_duration, params, benchmark, warmup_time, warmup_iterations };
+        }
+
+        template <typename Clock>
+        std::vector<FloatDuration<Clock>> run(configuration cfg, environment<FloatDuration<Clock>> env) const {
+            // warmup a bit
+            detail::run_for_at_least<Clock>(params, chrono::duration_cast<nonius::Duration<Clock>>(warmup_time), warmup_iterations, detail::repeat(now<Clock>{}));
+
+            std::vector<FloatDuration<Clock>> times;
+            times.reserve(cfg.samples);
+            std::generate_n(std::back_inserter(times), cfg.samples, [this, env]{
+                    detail::chronometer_model<Clock> model;
+                    detail::optimizer_barrier();
+                    this->benchmark(chronometer(model, iterations_per_sample, params));
+                    detail::optimizer_barrier();
+                    auto sample_time = model.elapsed() - env.clock_cost.mean;
+                    if(sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
+                    return sample_time / iterations_per_sample;
+            });
+            return times;
+        }
+    };
+} // namespace nonius
+
+// #included from: detail/unique_name.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Unique name generator macro
+
+#define NONIUS_DETAIL_UNIQUE_NAME_HPP
+
+#define NONIUS_DETAIL_UNIQUE_NAME_LINE_CAT(name, id) NONIUS_ ## name ## _ ## id
+#define NONIUS_DETAIL_UNIQUE_NAME_LINE(name, id) NONIUS_DETAIL_UNIQUE_NAME_LINE_CAT(name, id)
+#ifdef __COUNTER__
+#define NONIUS_DETAIL_UNIQUE_NAME(name) NONIUS_DETAIL_UNIQUE_NAME_LINE(name, __COUNTER__)
+#else // __COUNTER__
+#define NONIUS_DETAIL_UNIQUE_NAME(name) NONIUS_DETAIL_UNIQUE_NAME_LINE(name, __LINE__)
+#endif // __COUNTER__
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
+
+namespace nonius {
+    namespace detail {
+        const auto warmup_iterations = 10000;
+        const auto warmup_time = chrono::milliseconds(100);
+        const auto minimum_ticks = 1000;
+    } // namespace detail
+
+    struct benchmark {
+        benchmark(std::string name, detail::benchmark_function fun)
+        : name(std::move(name)), fun(std::move(fun)) {}
+
+        template <typename Clock>
+        execution_plan<FloatDuration<Clock>> prepare(configuration cfg, parameters params, environment<FloatDuration<Clock>> env) const {
+            auto bench = fun(params);
+            auto min_time = env.clock_resolution.mean * detail::minimum_ticks;
+            auto run_time = std::max(min_time, chrono::duration_cast<decltype(min_time)>(detail::warmup_time));
+            auto&& test = detail::run_for_at_least<Clock>(params, chrono::duration_cast<Duration<Clock>>(run_time), 1, bench);
+            int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
+            return { new_iters, test.elapsed / test.iterations * new_iters * cfg.samples, params, bench, chrono::duration_cast<FloatDuration<Clock>>(detail::warmup_time), detail::warmup_iterations };
+        }
+
+        std::string name;
+        detail::benchmark_function fun;
+    };
+
+    using benchmark_registry = std::vector<benchmark>;
+
+    inline benchmark_registry& global_benchmark_registry() {
+        static benchmark_registry registry;
+        return registry;
+    }
+
+    struct benchmark_registrar {
+        template <typename Fun>
+        benchmark_registrar(benchmark_registry& registry, std::string name, Fun&& registrant) {
+            registry.emplace_back(std::move(name), std::forward<Fun>(registrant));
+        }
+    };
+} // namespace nonius
+
+#define NONIUS_BENCHMARK(name, ...) \
+    namespace { static ::nonius::benchmark_registrar NONIUS_DETAIL_UNIQUE_NAME(benchmark_registrar) (::nonius::global_benchmark_registry(), name, __VA_ARGS__); }
+
+// #included from: constructor.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Constructor and destructor helpers
+
+#define NONIUS_CONSTRUCTOR_HPP
+
+#include <type_traits>
+
+namespace nonius {
+    namespace detail {
+        template <typename T, bool Destruct>
+        struct object_storage
+        {
+            typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type TStorage;
+
+            object_storage() : data() {}
+
+            object_storage(const object_storage& other)
+            {
+                new(&data) T(other.stored_object());
+            }
+
+            object_storage(object_storage&& other)
+            {
+                new(&data) T(std::move(other.stored_object()));
+            }
+
+            ~object_storage() { destruct_on_exit<T>(); }
+
+            template <typename... Args>
+            void construct(Args&&... args)
+            {
+                new (&data) T(std::forward<Args>(args)...);
+            }
+
+            template <bool AllowManualDestruction = !Destruct>
+            typename std::enable_if<AllowManualDestruction>::type destruct()
+            {
+                stored_object().~T();
+            }
+
+        private:
+            // If this is a constructor benchmark, destruct the underlying object
+            template <typename U>
+            void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+            // Otherwise, don't
+            template <typename U>
+            void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+
+            T& stored_object()
+            {
+                return *static_cast<T*>(static_cast<void*>(&data));
+            }
+
+            TStorage data;
+        };
+    }
+
+    template <typename T>
+    using storage_for = detail::object_storage<T, true>;
+
+    template <typename T>
+    using destructable_object = detail::object_storage<T, false>;
+}
+
+// #included from: go.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Runner entry point
+
+#define NONIUS_GO_HPP
+
+// #included from: reporter.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Reporter interface
+
+#define NONIUS_REPORTER_HPP
+
+// #included from: sample_analysis.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Benchmark results
+
+#define NONIUS_BENCHMARK_RESULTS_HPP
+
+// #included from: estimate.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Statistics estimates
+
+#define NONIUS_ESTIMATE_HPP
+
+namespace nonius {
+    template <typename Duration>
+    struct estimate {
+        Duration point;
+        Duration lower_bound;
+        Duration upper_bound;
+        double confidence_interval;
+
+        template <typename Duration2>
+        operator estimate<Duration2>() const {
+            return { point, lower_bound, upper_bound, confidence_interval };
+        }
+    };
+} // namespace nonius
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <iterator>
+
+namespace nonius {
+    template <typename Duration>
+    struct sample_analysis {
+        std::vector<Duration> samples;
+        estimate<Duration> mean;
+        estimate<Duration> standard_deviation;
+        outlier_classification outliers;
+        double outlier_variance;
+
+        template <typename Duration2>
+        operator sample_analysis<Duration2>() const {
+            std::vector<Duration2> samples2;
+            samples2.reserve(samples.size());
+            std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
+            return {
+                std::move(samples2),
+                mean,
+                standard_deviation,
+                outliers,
+                outlier_variance,
+            };
+        }
+    };
+} // namespace nonius
+
+#include <vector>
+#include <string>
+#include <ios>
+#include <ostream>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <unordered_map>
+#include <exception>
+
+namespace nonius {
+    struct bad_stream : virtual std::exception {
+        char const* what() const NONIUS_NOEXCEPT override {
+            return "failed to open file";
+        }
+    };
+
+    struct reporter {
+    public:
+        virtual ~reporter() = default;
+
+        void configure(configuration& cfg) {
+            if(cfg.output_file.empty()) {
+                os = [&]() -> std::ostream& { return std::cout; };
+            } else {
+                auto ofs = std::make_shared<std::ofstream>(cfg.output_file);
+                os = [ofs]() -> std::ostream& { return *ofs; };
+            }
+            report_stream().exceptions(std::ios::failbit);
+            if(!report_stream()) throw bad_stream();
+            do_configure(cfg);
+        }
+
+        void warmup_start() {
+            do_warmup_start();
+        }
+        void warmup_end(int iterations) {
+            do_warmup_end(iterations);
+        }
+        void estimate_clock_resolution_start() {
+            do_estimate_clock_resolution_start();
+        }
+        void estimate_clock_resolution_complete(environment_estimate<fp_seconds> estimate) {
+            do_estimate_clock_resolution_complete(estimate);
+        }
+
+        void estimate_clock_cost_start() {
+            do_estimate_clock_cost_start();
+        }
+        void estimate_clock_cost_complete(environment_estimate<fp_seconds> estimate) {
+            do_estimate_clock_cost_complete(estimate);
+        }
+
+        void suite_start() {
+            do_suite_start();
+        }
+        void params_start(parameters const& params) {
+            do_params_start(params);
+        }
+        void benchmark_start(std::string const& name) {
+            do_benchmark_start(name);
+        }
+
+        void measurement_start(execution_plan<fp_seconds> plan) {
+            do_measurement_start(plan);
+        }
+        void measurement_complete(std::vector<fp_seconds> const& samples) {
+            do_measurement_complete(samples);
+        }
+
+        void analysis_start() {
+            do_analysis_start();
+        }
+        void analysis_complete(sample_analysis<fp_seconds> const& analysis) {
+            do_analysis_complete(analysis);
+        }
+
+        void benchmark_failure(std::exception_ptr error) {
+            do_benchmark_failure(error);
+        }
+        void benchmark_complete() {
+            do_benchmark_complete();
+        }
+
+        void params_complete() {
+            do_params_complete();
+        }
+        void suite_complete() {
+            do_suite_complete();
+        }
+
+        virtual std::string description() = 0;
+
+    private:
+        virtual void do_configure(configuration& /*cfg*/) {}
+
+        virtual void do_warmup_start() {}
+        virtual void do_warmup_end(int /*iterations*/) {}
+
+        virtual void do_estimate_clock_resolution_start() {}
+        virtual void do_estimate_clock_resolution_complete(environment_estimate<fp_seconds> /*estimate*/) {}
+
+        virtual void do_estimate_clock_cost_start() {}
+        virtual void do_estimate_clock_cost_complete(environment_estimate<fp_seconds> /*estimate*/) {}
+
+        virtual void do_suite_start() {}
+        virtual void do_params_start(parameters const& /*params*/) {}
+        virtual void do_benchmark_start(std::string const& /*name*/) {}
+
+        virtual void do_measurement_start(execution_plan<fp_seconds> /*plan*/) {}
+        virtual void do_measurement_complete(std::vector<fp_seconds> const& /*samples*/) {}
+
+        virtual void do_analysis_start() {} // TODO make generic?
+        virtual void do_analysis_complete(sample_analysis<fp_seconds> const& /*analysis*/) {}
+
+        virtual void do_benchmark_failure(std::exception_ptr /*error*/) {}
+        virtual void do_benchmark_complete() {}
+        virtual void do_params_complete() {}
+        virtual void do_suite_complete() {}
+
+    protected:
+        std::ostream& progress_stream() {
+            return std::cout;
+        }
+        std::ostream& error_stream() {
+            return std::cerr;
+        }
+        std::ostream& report_stream() {
+            return os();
+        }
+
+    private:
+        std::function<std::ostream&()> os;
+    };
+
+    using reporter_registry = std::unordered_map<std::string, std::unique_ptr<reporter>>;
+
+    inline reporter_registry& global_reporter_registry() {
+        static reporter_registry registry;
+        return registry;
+    }
+
+    struct reporter_registrar {
+        reporter_registrar(reporter_registry& registry, std::string name, reporter* registrant) {
+            registry.emplace(std::move(name), std::unique_ptr<reporter>(registrant));
+        }
+    };
+} // namespace nonius
+
+#define NONIUS_REPORTER(name, ...) \
+    namespace { static ::nonius::reporter_registrar NONIUS_DETAIL_UNIQUE_NAME(reporter_registrar) (::nonius::global_reporter_registry(), name, new __VA_ARGS__()); } \
+    static_assert(true, "")
+
+// #included from: reporters/standard_reporter.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Standard reporter
+
+#define NONIUS_REPORTERS_STANDARD_REPORTER_HPP
+
+// #included from: detail/pretty_print.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Pretty printing routines
+
+#define NONIUS_DETAIL_PRETTY_PRINT_HPP
+
+#include <ratio>
+#include <ios>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <memory>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        inline double get_magnitude(fp_seconds secs) {
+            if(secs.count() >= 2.e0) {
+                return 1.e0;
+            } else if(secs.count() >= 2.e-3) {
+                return 1.e3;
+            } else if(secs.count() >= 2.e-6) {
+                return 1.e6;
+            } else {
+                return 1.e9;
+            }
+        }
+        inline std::string units_for_magnitude(double magnitude) {
+            if(magnitude <= 1.e0) return "s";
+            else if(magnitude <= 1.e3) return "ms";
+            else if(magnitude <= 1.e6) return "μs";
+            else return "ns";
+        }
+        inline std::string pretty_duration(fp_seconds secs) {
+            auto magnitude = get_magnitude(secs);
+            auto units = units_for_magnitude(magnitude);
+#ifdef NONIUS_MSVC
+            if(units == "μs") units = "us";
+#endif
+            std::ostringstream ss;
+            ss << std::setprecision(ss.precision());
+            ss << (secs.count() * magnitude) << ' ' << units;
+            return ss.str();
+        }
+        inline std::string percentage(double d) {
+            std::ostringstream ss;
+            ss << std::setprecision(3);
+            if(d != 0 && d < 1e-5) {
+                ss << std::fixed;
+                ss << 0.0001 << "%";
+            } else {
+                ss.unsetf(std::ios::floatfield);
+                ss << (100. * d) << "%";
+            }
+            return ss.str();
+        }
+        inline std::string percentage_ratio(double part, double whole) {
+            return percentage(part / whole);
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <ratio>
+#include <ios>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <memory>
+
+namespace nonius {
+    struct standard_reporter : reporter {
+    private:
+        std::string description() override {
+            return "the standard reporter";
+        }
+
+        void do_configure(configuration& cfg) override {
+            n_samples = cfg.samples;
+            verbose = cfg.verbose;
+            summary = cfg.summary;
+            n_resamples = cfg.resamples;
+            if(!summary && !cfg.params.map.empty()) report_stream() << "parameters\n" << cfg.params.map;
+        }
+
+        void do_warmup_start() override {
+            if(verbose) report_stream() << "warming up\n";
+        }
+        void do_estimate_clock_resolution_start() override {
+            if(verbose) report_stream() << "estimating clock resolution\n";
+        }
+        void do_estimate_clock_resolution_complete(environment_estimate<fp_seconds> estimate) override {
+            if(!summary) {
+                if(!verbose) report_stream() << "clock resolution: ";
+                print_environment_estimate(estimate, estimate.outliers.samples_seen + 2);
+            }
+        }
+
+        void do_estimate_clock_cost_start() override {
+            if(verbose) report_stream() << "estimating cost of a clock call\n";
+        }
+        void do_estimate_clock_cost_complete(environment_estimate<fp_seconds> estimate) override {
+            if(verbose) print_environment_estimate(estimate, estimate.outliers.samples_seen);
+        }
+
+        void do_params_start(parameters const& params) override {
+            if(!summary && !params.empty()) report_stream() << "\n\nnew round for parameters\n" << params;
+        }
+        void do_benchmark_start(std::string const& name) override {
+            report_stream() << '\n';
+            if(!summary) report_stream() << "benchmarking ";
+            report_stream() << name << "\n";
+            current = name;
+        }
+
+        void do_measurement_start(execution_plan<fp_seconds> plan) override {
+            report_stream() << std::setprecision(7);
+            report_stream().unsetf(std::ios::floatfield);
+            if(!summary) report_stream() << "collecting " << n_samples << " samples, " << plan.iterations_per_sample << " iterations each, in estimated " << detail::pretty_duration(plan.estimated_duration) << "\n";
+        }
+        void do_analysis_start() override {
+            if(verbose) report_stream() << "bootstrapping with " << n_resamples << " resamples\n";
+        }
+        void do_benchmark_failure(std::exception_ptr eptr) override {
+            error_stream() << current << " failed to run successfully\n";
+            if(!summary) {
+                try {
+                    std::rethrow_exception(eptr);
+                } catch(std::exception& ex) {
+                    error_stream() << "error: " << ex.what();
+                } catch(...) {
+                    error_stream() << "unknown error";
+                }
+            }
+            report_stream() << "\nbenchmark aborted\n";
+        }
+        void do_analysis_complete(sample_analysis<fp_seconds> const& analysis) override {
+            print_statistic_estimate("mean", analysis.mean);
+            print_statistic_estimate("std dev", analysis.standard_deviation);
+            if(!summary) print_outliers(analysis.outliers);
+            if(verbose) report_stream() << "variance introduced by outliers: " << detail::percentage(analysis.outlier_variance) << "\n";
+            const char* effect;
+            if(analysis.outlier_variance < 0.01) {
+                effect = "unaffected";
+            } else if(analysis.outlier_variance < 0.1) {
+                effect = "slightly inflated";
+            } else if(analysis.outlier_variance < 0.5) {
+                effect = "moderately inflated";
+            } else {
+                effect = "severely inflated";
+            }
+            report_stream() << "variance is " << effect << " by outliers\n";
+        }
+
+        void print_environment_estimate(environment_estimate<fp_seconds> e, int iterations) {
+            report_stream() << std::setprecision(7);
+            report_stream().unsetf(std::ios::floatfield);
+            report_stream() << "mean is " << detail::pretty_duration(e.mean) << " (" << iterations << " iterations)\n";
+            if(verbose) print_outliers(e.outliers);
+        }
+        void print_outlier_count(const char* description, int count, int total) {
+            if(count > 0) report_stream() << "  " << count << " (" << detail::percentage_ratio(count, total) << ") " << description << "\n";
+        }
+        void print_outliers(outlier_classification o) {
+            report_stream() << "found " << o.total() << " outliers among " << o.samples_seen << " samples (" << detail::percentage_ratio(o.total(), o.samples_seen) << ")\n";
+            if(verbose) {
+                print_outlier_count("low severe", o.low_severe, o.samples_seen);
+                print_outlier_count("low mild", o.low_mild, o.samples_seen);
+                print_outlier_count("high mild", o.high_mild, o.samples_seen);
+                print_outlier_count("high severe", o.high_severe, o.samples_seen);
+            }
+        }
+        void print_statistic_estimate(const char* name, estimate<fp_seconds> estimate) {
+            report_stream() << std::setprecision(7);
+            report_stream().unsetf(std::ios::floatfield);
+            report_stream() << name << ": " << detail::pretty_duration(estimate.point);
+            if(!summary) {
+                report_stream() << ", lb " << detail::pretty_duration(estimate.lower_bound)
+                         << ", ub " << detail::pretty_duration(estimate.upper_bound)
+                         << ", ci " << std::setprecision(3) << estimate.confidence_interval;
+            }
+            report_stream() << "\n";
+        }
+
+        int n_samples = 0;
+        int n_resamples = 0;
+        bool verbose = false;
+        bool summary = false;
+
+        std::string current;
+    };
+
+    NONIUS_REPORTER("", standard_reporter);
+    NONIUS_REPORTER("standard", standard_reporter);
+} // namespace nonius
+
+// #included from: detail/estimate_clock.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Environment measurement
+
+#define NONIUS_DETAIL_ENVIRONMENT_HPP
+
+// #included from: detail/stats.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Statistical analysis tools
+
+#define NONIUS_DETAIL_ANALYSIS_HPP
+
+#include <boost/math/distributions/normal.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <vector>
+#include <array>
+#include <random>
+#include <numeric>
+#include <tuple>
+#include <cmath>
+#include <utility>
+#include <future>
+#include <cstddef>
+
+namespace nonius {
+    namespace detail {
+        using sample = std::vector<double>;
+
+        template <typename Iterator>
+        double weighted_average_quantile(int k, int q, Iterator first, Iterator last) {
+            auto count = last - first;
+            double idx = (count - 1) * k /static_cast<double>(q);
+            int j = static_cast<int>(idx);
+            double g = idx - j;
+            std::nth_element(first, first+j, last);
+            auto xj = first[j];
+            if(g == 0) return xj;
+
+            auto xj1 = *std::min_element(first+(j+1), last);
+            return xj + g * (xj1 - xj);
+        }
+
+        template <typename Iterator>
+        outlier_classification classify_outliers(Iterator first, Iterator last) {
+            std::vector<double> copy(first, last);
+
+            auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
+            auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
+            auto iqr = q3 - q1;
+            auto los = q1 - (iqr * 3.);
+            auto lom = q1 - (iqr * 1.5);
+            auto him = q3 + (iqr * 1.5);
+            auto his = q3 + (iqr * 3.);
+
+            outlier_classification o;
+            for(; first != last; ++first) {
+                auto&& t = *first;
+                if(t < los) ++o.low_severe;
+                else if(t < lom) ++o.low_mild;
+                else if(t > his) ++o.high_severe;
+                else if(t > him) ++o.high_mild;
+                ++o.samples_seen;
+            }
+            return o;
+        }
+
+        template <typename Iterator>
+        double mean(Iterator first, Iterator last) {
+            auto count = last - first;
+            double sum = std::accumulate(first, last, 0.);
+            return sum / count;
+        }
+
+        template <typename Iterator>
+        double standard_deviation(Iterator first, Iterator last) {
+            auto m = mean(first, last);
+            double variance = std::accumulate(first, last, 0., [m](double a, double b) {
+                        double diff = b - m;
+                        return a + diff*diff;
+                    }) / (last - first);
+            return std::sqrt(variance);
+        }
+
+        template <typename URng, typename Iterator, typename Estimator>
+        sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
+            auto n = last - first;
+            std::uniform_int_distribution<decltype(n)> dist(0, n-1);
+
+            sample out;
+            out.reserve(resamples);
+            std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
+                std::vector<double> resampled;
+                resampled.reserve(n);
+                std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
+                return estimator(resampled.begin(), resampled.end());
+            });
+            std::sort(out.begin(), out.end());
+            return out;
+        }
+
+        template <typename Estimator, typename Iterator>
+        sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
+            auto n = last - first;
+            auto second = std::next(first);
+            sample results;
+            results.reserve(n);
+
+            for(auto it = first; it != last; ++it) {
+                std::iter_swap(it, first);
+                results.push_back(estimator(second, last));
+            }
+
+            return results;
+        }
+
+        template <typename Iterator, typename Estimator>
+        estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
+            namespace bm = boost::math;
+
+            auto n_samples = last - first;
+
+            double point = estimator(first, last);
+            // Degenerate case with a single sample
+            if(n_samples == 1) return { point, point, point, confidence_level };
+
+            sample jack = jackknife(estimator, first, last);
+            double jack_mean = mean(jack.begin(), jack.end());
+            double sum_squares, sum_cubes;
+            std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
+                        auto d = jack_mean - x;
+                        auto d2 = d * d;
+                        auto d3 = d2 * d;
+                        return { sqcb.first + d2, sqcb.second + d3 };
+                    });
+
+            double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
+            int n = static_cast<int>(resample.size());
+            double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) /(double) n;
+            // degenerate case with uniform samples
+            if(prob_n == 0) return { point, point, point, confidence_level };
+
+            double bias = bm::quantile(bm::normal{}, prob_n);
+            double z1 = bm::quantile(bm::normal{}, (1. - confidence_level) / 2.);
+
+            auto cumn = [n](double x) -> int { return std::lround(bm::cdf(bm::normal{}, x) * n); };
+            auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
+            double b1 = bias + z1;
+            double b2 = bias - z1;
+            double a1 = a(b1);
+            double a2 = a(b2);
+            auto lo = std::max(cumn(a1), 0);
+            auto hi = std::min(cumn(a2), n - 1);
+
+            return { point, resample[lo], resample[hi], confidence_level };
+        }
+
+        inline double outlier_variance(estimate<double> mean, estimate<double> stddev, int n) {
+            double sb = stddev.point;
+            double mn = mean.point / n;
+            double mg_min = mn / 2.;
+            double sg = std::min(mg_min / 4., sb / std::sqrt(n));
+            double sg2 = sg * sg;
+            double sb2 = sb * sb;
+
+            auto c_max = [n, mn, sb2, sg2](double x) -> double {
+                double k = mn - x;
+                double d = k * k;
+                double nd = n * d;
+                double k0 = -n * nd;
+                double k1 = sb2 - n * sg2 + nd;
+                double det = k1 * k1 - 4 * sg2 * k0;
+                return (int)(-2. * k0 / (k1 + std::sqrt(det)));
+            };
+
+            auto var_out = [n, sb2, sg2](double c) {
+                double nc = n - c;
+                return (nc / n) * (sb2 - nc * sg2);
+            };
+
+            return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
+        }
+
+        struct bootstrap_analysis {
+            estimate<double> mean;
+            estimate<double> standard_deviation;
+            double outlier_variance;
+        };
+
+        template <typename Iterator>
+        bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, Iterator first, Iterator last) {
+            static std::random_device entropy;
+
+            auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
+
+            auto mean = &detail::mean<Iterator>;
+            auto stddev = &detail::standard_deviation<Iterator>;
+
+            auto estimate = [=](double(*f)(Iterator, Iterator)) {
+                auto seed = entropy();
+                return std::async(std::launch::async, [=]{
+                    std::mt19937 rng(seed);
+                    auto resampled = resample(rng, n_resamples, first, last, f);
+                    return bootstrap(confidence_level, first, last, resampled, f);
+                });
+            };
+
+            auto mean_future = estimate(mean);
+            auto stddev_future = estimate(stddev);
+
+            auto mean_estimate = mean_future.get();
+            auto stddev_estimate = stddev_future.get();
+
+            double outlier_variance = detail::outlier_variance(mean_estimate, stddev_estimate, n);
+
+            return { mean_estimate, stddev_estimate, outlier_variance };
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <algorithm>
+#include <iterator>
+#include <tuple>
+#include <vector>
+#include <cmath>
+
+namespace nonius {
+    namespace detail {
+        template <typename Clock>
+        std::vector<double> resolution(int k) {
+            std::vector<TimePoint<Clock>> times;
+            times.reserve(k+1);
+            std::generate_n(std::back_inserter(times), k+1, now<Clock>{});
+
+            std::vector<double> deltas;
+            deltas.reserve(k);
+            std::transform(std::next(times.begin()), times.end(), times.begin(),
+                              std::back_inserter(deltas),
+                              [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
+
+            return deltas;
+        }
+
+        const auto warmup_seed = 10000;
+        const auto clock_resolution_estimation_time = chrono::milliseconds(500);
+        const auto clock_cost_estimation_time_limit = chrono::seconds(1);
+        const auto clock_cost_estimation_tick_limit = 100000;
+        const auto clock_cost_estimation_time = chrono::milliseconds(10);
+        const auto clock_cost_estimation_iterations = 10000;
+
+        template <typename Clock>
+        int warmup() {
+            return run_for_at_least<Clock>({}, chrono::duration_cast<Duration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
+                    .iterations;
+        }
+        template <typename Clock>
+        environment_estimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
+            auto r = run_for_at_least<Clock>({}, chrono::duration_cast<Duration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
+                    .result;
+            return {
+                FloatDuration<Clock>(mean(r.begin(), r.end())),
+                classify_outliers(r.begin(), r.end()),
+            };
+        }
+        template <typename Clock>
+        environment_estimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
+            auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
+            auto time_clock = [](int k) {
+                return detail::measure<Clock>([k]{
+                    for(int i = 0; i < k; ++i) {
+                        volatile auto ignored = Clock::now();
+                        (void)ignored;
+                    }
+                }).elapsed;
+            };
+            time_clock(1);
+            int iters = clock_cost_estimation_iterations;
+            auto&& r = run_for_at_least<Clock>({}, chrono::duration_cast<Duration<Clock>>(clock_cost_estimation_time), iters, time_clock);
+            std::vector<double> times;
+            int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
+            times.reserve(nsamples);
+            std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r]{
+                        return (time_clock(r.iterations) / r.iterations).count();
+                    });
+            return {
+                FloatDuration<Clock>(mean(times.begin(), times.end())),
+                classify_outliers(times.begin(), times.end()),
+            };
+        }
+    } // namespace detail
+} // namespace nonius
+
+// #included from: detail/analyse.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Run and analyse one benchmark
+
+#define NONIUS_DETAIL_ANALYSE_HPP
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace nonius {
+    namespace detail {
+        template <typename Duration, typename Iterator>
+        sample_analysis<Duration> analyse(configuration cfg, environment<Duration>, Iterator first, Iterator last) {
+            std::vector<double> samples;
+            samples.reserve(last - first);
+            std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
+
+            auto analysis = nonius::detail::analyse_samples(cfg.confidence_interval, cfg.resamples, samples.begin(), samples.end());
+            auto outliers = nonius::detail::classify_outliers(samples.begin(), samples.end());
+
+            auto wrap_estimate = [](estimate<double> e) {
+                return estimate<Duration> {
+                    Duration(e.point),
+                    Duration(e.lower_bound),
+                    Duration(e.upper_bound),
+                    e.confidence_interval,
+                };
+            };
+            std::vector<Duration> samples2;
+            samples2.reserve(samples.size());
+            std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
+            return {
+                std::move(samples2),
+                wrap_estimate(analysis.mean),
+                wrap_estimate(analysis.standard_deviation),
+                outliers,
+                analysis.outlier_variance,
+            };
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <algorithm>
+#include <set>
+#include <exception>
+#include <utility>
+#include <regex>
+
+namespace nonius {
+    namespace detail {
+        template <typename Clock>
+        environment<FloatDuration<Clock>> measure_environment(reporter& rep) {
+            rep.warmup_start();
+            auto iters = detail::warmup<Clock>();
+            rep.warmup_end(iters);
+
+            rep.estimate_clock_resolution_start();
+            auto resolution = detail::estimate_clock_resolution<Clock>(iters);
+            rep.estimate_clock_resolution_complete(resolution);
+
+            rep.estimate_clock_cost_start();
+            auto cost = detail::estimate_clock_cost<Clock>(resolution.mean);
+            rep.estimate_clock_cost_complete(cost);
+
+            return { resolution, cost };
+        }
+    } // namespace detail
+
+    struct benchmark_user_error : virtual std::exception {
+        char const* what() const NONIUS_NOEXCEPT override {
+            return "a benchmark failed to run successfully";
+        }
+    };
+
+    struct skip_error : virtual std::exception {
+        const char* what() const NONIUS_NOEXCEPT override {
+            return "benchmark was skipped";
+        }
+    };
+
+    inline void skip() {
+        throw skip_error{};
+    }
+
+    inline std::vector<parameters> generate_params(param_configuration cfg) {
+        auto params = global_param_registry().defaults().merged(cfg.map);
+        if (!cfg.run) {
+            return {params};
+        } else {
+            using operation_t = std::function<param(param, param)>;
+
+            auto&& run = *cfg.run;
+            auto step = run.step;
+            auto oper = std::unordered_map<std::string, operation_t> {
+                {"+", std::plus<param>{}},
+                {"*", std::multiplies<param>{}},
+            }.at(run.op);
+
+            auto r = std::vector<parameters>{};
+            auto next = run.init;
+            std::generate_n(std::back_inserter(r), std::max(run.count, std::size_t{1}), [&] {
+                auto last = next;
+                next = oper(next, step);
+                return params.merged(parameters{{run.name, last}});
+            });
+
+            return r;
+        }
+    }
+
+    template <typename Iterator>
+    std::vector<benchmark> filter_benchmarks(Iterator first, Iterator last, std::string pattern) {
+        auto r = std::vector<benchmark>{};
+        auto matcher = std::regex{pattern};
+        std::copy_if(first, last, std::back_inserter(r), [&] (benchmark const& b) {
+            return std::regex_match(b.name, matcher);
+        });
+        return r;
+    }
+
+    template <typename Clock = default_clock, typename Iterator>
+    void go(configuration cfg, Iterator first, Iterator last, reporter& rep) {
+        rep.configure(cfg);
+
+        auto env = detail::measure_environment<Clock>(rep);
+        rep.suite_start();
+
+        auto benchmarks = filter_benchmarks(first, last, cfg.filter_pattern);
+        auto all_params = generate_params(cfg.params);
+
+        for (auto&& params : all_params) {
+            rep.params_start(params);
+            for (auto&& bench : benchmarks) {
+                rep.benchmark_start(bench.name);
+                try {
+                    auto plan = bench.template prepare<Clock>(cfg, params, env);
+
+                    rep.measurement_start(plan);
+                    auto samples = plan.template run<Clock>(cfg, env);
+                    rep.measurement_complete(std::vector<fp_seconds>(samples.begin(), samples.end()));
+
+                    if(!cfg.no_analysis) {
+                        rep.analysis_start();
+                        auto analysis = detail::analyse(cfg, env, samples.begin(), samples.end());
+                        rep.analysis_complete(analysis);
+                    }
+                    rep.benchmark_complete();
+                } catch (...) {
+                    rep.benchmark_failure(std::current_exception());
+                }
+            }
+            rep.params_complete();
+        }
+
+        rep.suite_complete();
+    }
+    struct duplicate_benchmarks : virtual std::exception {
+        char const* what() const NONIUS_NOEXCEPT override {
+            return "two or more benchmarks with the same name were registered";
+        }
+    };
+    template <typename Clock = default_clock, typename Iterator>
+    void validate_benchmarks(Iterator first, Iterator last) {
+        struct strings_lt_through_pointer {
+            bool operator()(std::string* a, std::string* b) const { return *a < *b; };
+        };
+        std::set<std::string*, strings_lt_through_pointer> names;
+        for(; first != last; ++first) {
+            if(!names.insert(&first->name).second)
+                throw duplicate_benchmarks();
+        }
+    }
+    template <typename Clock = default_clock, typename Iterator>
+    void go(configuration cfg, Iterator first, Iterator last, reporter&& rep) {
+        go<Clock>(cfg, first, last, rep);
+    }
+    struct no_such_reporter : virtual std::exception {
+        char const* what() const NONIUS_NOEXCEPT override {
+            return "reporter could not be found";
+        }
+    };
+    template <typename Clock = default_clock>
+    void go(configuration cfg, benchmark_registry& benchmarks = global_benchmark_registry(), reporter_registry& reporters = global_reporter_registry()) {
+        auto it = reporters.find(cfg.reporter);
+        if(it == reporters.end()) throw no_such_reporter();
+        validate_benchmarks<Clock>(benchmarks.begin(), benchmarks.end());
+        go<Clock>(cfg, benchmarks.begin(), benchmarks.end(), *it->second);
+    }
+} // namespace nonius
+
+#ifndef NONIUS_DISABLE_EXTRA_REPORTERS
+#ifndef NONIUS_DISABLE_CSV_REPORTER
+// #included from: reporters/csv_reporter.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// CSV raw data reporter
+
+#define NONIUS_REPORTERS_CSV_REPORTER_HPP
+
+#include <ios>
+#include <iomanip>
+#include <algorithm>
+#include <string>
+#include <iterator>
+#include <limits>
+#include <unordered_map>
+#include <vector>
+
+namespace nonius {
+    struct csv_reporter : reporter {
+    private:
+        std::string description() override {
+            return "outputs samples to a CSV file";
+        }
+
+        void do_configure(configuration& cfg) override {
+            cfg.no_analysis = true;
+            n_samples = cfg.samples;
+            verbose = cfg.verbose;
+        }
+
+        void do_warmup_start() override {
+            if(verbose) progress_stream() << "warming up\n";
+        }
+        void do_estimate_clock_resolution_start() override {
+            if(verbose) progress_stream() << "estimating clock resolution\n";
+        }
+        void do_estimate_clock_cost_start() override {
+            if(verbose) progress_stream() << "estimating cost of a clock call\n";
+        }
+
+        void do_benchmark_start(std::string const& name) override {
+            if(verbose) progress_stream() << "\nbenchmarking " << name << "\n";
+            current = name;
+        }
+
+        void do_measurement_start(execution_plan<fp_seconds> plan) override {
+            report_stream() << std::setprecision(7);
+            report_stream().unsetf(std::ios::floatfield);
+            if(verbose) progress_stream() << "collecting " << n_samples << " samples, " << plan.iterations_per_sample << " iterations each, in estimated " << detail::pretty_duration(plan.estimated_duration) << "\n";
+        }
+        void do_measurement_complete(std::vector<fp_seconds> const& samples) override {
+            data[current] = samples;
+        }
+
+        void do_benchmark_failure(std::exception_ptr) override {
+            error_stream() << current << " failed to run successfully\n";
+        }
+
+        void do_suite_complete() override {
+            if(verbose) progress_stream() << "\ngenerating CSV report\n";
+            report_stream() << std::fixed;
+            report_stream().precision(std::numeric_limits<double>::digits10);
+            bool first = true;
+            for(auto&& kv : data) {
+                if(!first) report_stream() << ",";
+                report_stream() << "\"" << escape(kv.first) << "\""; // TODO escape
+                first = false;
+            }
+            report_stream() << "\n";
+            for(int i = 0; i < n_samples; ++i) {
+                first = true;
+                for(auto&& kv : data) {
+                    if(!first) report_stream() << ",";
+                    report_stream() << kv.second[i].count();
+                    first = false;
+                }
+                report_stream() << "\n";
+            }
+            if(verbose) progress_stream() << "done\n";
+        }
+
+    private:
+        static std::string escape(std::string const& source) {
+            auto first = source.begin();
+            auto last = source.end();
+
+            auto quotes = std::count(first, last, '"');
+            if(quotes == 0) return source;
+
+            std::string escaped;
+            escaped.reserve(source.size() + quotes);
+
+            while(first != last) {
+                auto next_quote = std::find(first, last, '"');
+                std::copy(first, next_quote, std::back_inserter(escaped));
+                first = next_quote;
+                if(first != last) {
+                    ++first;
+                    escaped.push_back('"');
+                    escaped.push_back('"');
+                }
+            }
+
+            return escaped;
+        }
+
+        int n_samples;
+        bool verbose;
+        std::string current;
+        std::unordered_map<std::string, std::vector<fp_seconds>> data;
+    };
+
+    NONIUS_REPORTER("csv", csv_reporter);
+} // namespace nonius
+
+#endif // NONIUS_DISABLE_CSV_REPORTER
+#ifndef NONIUS_DISABLE_JUNIT_REPORTER
+// #included from: reporters/junit_reporter.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// JUnit reporter
+
+#define NONIUS_REPORTERS_JUNIT_REPORTER_HPP
+
+// #included from: detail/escape.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// General escaping routines
+
+#define NONIUS_DETAIL_ESCAPE_HPP
+
+#include <string>
+#include <algorithm>
+#include <iterator>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        inline std::string escape(std::string const& source, std::unordered_map<char, std::string> const& escapes) {
+            std::string magic;
+            magic.reserve(escapes.size());
+            std::transform(escapes.begin(), escapes.end(), std::back_inserter(magic), [](std::pair<char const, std::string> const& p) { return p.first; });
+
+            auto first = source.begin();
+            auto last = source.end();
+
+            auto n_magic = std::count_if(first, last, [&magic](char c) { return magic.find(c) != std::string::npos; });
+
+            std::string escaped;
+            escaped.reserve(source.size() + n_magic*6);
+
+            while(first != last) {
+                auto next_magic = std::find_first_of(first, last, magic.begin(), magic.end());
+                std::copy(first, next_magic, std::back_inserter(escaped));
+                first = next_magic;
+                if(first != last) {
+                    auto it = escapes.find(*first);
+                    if(it != escapes.end()) {
+                        escaped += it->second;
+                    }
+                    ++first;
+                }
+            }
+            return escaped;
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <ios>
+#include <iomanip>
+#include <algorithm>
+#include <string>
+#include <iterator>
+#include <limits>
+#include <unordered_map>
+#include <vector>
+#include <exception>
+
+namespace nonius {
+    struct junit_reporter : reporter {
+    private:
+        std::string description() override {
+            return "outputs results to a JUnit-compatible XML file";
+        }
+
+        void do_configure(configuration& cfg) override {
+            n_samples = cfg.samples;
+            confidence_interval = cfg.confidence_interval;
+            resamples = cfg.resamples;
+            verbose = cfg.verbose;
+            title = cfg.title;
+        }
+
+        struct result {
+            sample_analysis<fp_seconds> analysis;
+            std::exception_ptr failure;
+        };
+
+        void do_warmup_start() override {
+            if(verbose) progress_stream() << "warming up\n";
+        }
+        void do_estimate_clock_resolution_start() override {
+            if(verbose) progress_stream() << "estimating clock resolution\n";
+        }
+        void do_estimate_clock_cost_start() override {
+            if(verbose) progress_stream() << "estimating cost of a clock call\n";
+        }
+
+        void do_benchmark_start(std::string const& name) override {
+            if(verbose) progress_stream() << "\nbenchmarking " << name << "\n";
+            current = name;
+        }
+
+        void do_measurement_start(execution_plan<fp_seconds> plan) override {
+            report_stream() << std::setprecision(7);
+            report_stream().unsetf(std::ios::floatfield);
+            if(verbose) progress_stream() << "collecting " << n_samples << " samples, " << plan.iterations_per_sample << " iterations each, in estimated " << detail::pretty_duration(plan.estimated_duration) << "\n";
+        }
+
+        void do_analysis_start() override {
+            if(verbose) report_stream() << "analysing samples\n";
+        }
+        void do_analysis_complete(sample_analysis<fp_seconds> const& analysis) override {
+            data[current] = { analysis, nullptr };
+        }
+
+        void do_benchmark_failure(std::exception_ptr e) override {
+            data[current] = { sample_analysis<fp_seconds>(), e };
+            error_stream() << current << " failed to run successfully\n";
+        }
+
+        void do_suite_complete() override {
+            if(verbose) progress_stream() << "\ngenerating JUnit report\n";
+
+            report_stream() << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+            report_stream() << "<testsuite name=\"" << escape(title) << "\" tests=\"" << data.size() << "\"";
+            auto failures = std::count_if(data.begin(), data.end(),
+                    [](std::pair<std::string const&, result> const& p) {
+                        return static_cast<bool>(p.second.failure);
+                    });
+            if(failures > 0) report_stream() << " errors=\"" << failures << "\"";
+            report_stream() << ">\n";
+
+            report_stream() << " <properties>\n";
+            report_stream() << "  <property name=\"samples\" value=\"" << n_samples << "\">\n";
+            report_stream() << "  <property name=\"confidence_interval\" value=\"" << std::setprecision(3) << confidence_interval << "\">\n";
+            report_stream() << "  <property name=\"resamples\" value=\"" << resamples << "\">\n";
+            report_stream() << " </properties>\n";
+
+            for(auto tc : data) {
+                report_stream() << " <testcase name=\"" << escape(tc.first) << "\"";
+                if(tc.second.failure) {
+                    report_stream() << ">\n";
+                    try {
+                        std::rethrow_exception(tc.second.failure);
+                    } catch(std::exception const& e) {
+                        report_stream() << "  <error message=\"" << escape(e.what()) << "\" />\n";
+                    } catch(...) {
+                        report_stream() << "  <error message=\"unknown error\" />\n";
+                    }
+                    report_stream() << " </testcase>\n";
+                } else {
+                    report_stream() << std::fixed;
+                    report_stream().precision(std::numeric_limits<double>::digits10);
+                    report_stream() << " time=\"" << tc.second.analysis.mean.point.count() << "\" />\n";
+                }
+            }
+
+            report_stream() << "</testsuite>\n";
+
+            report_stream() << std::flush;
+
+            if(verbose) progress_stream() << "done\n";
+        }
+
+        static std::string escape(std::string const& source) {
+            static const std::unordered_map<char, std::string> escapes {
+                { '\'', "&apos;" },
+                { '"',  "&quot;" },
+                { '<',  "&lt;"   },
+                { '>',  "&gt;"   },
+                { '&',  "&amp;"  },
+            };
+            return detail::escape(source, escapes);
+        }
+
+        int n_samples;
+        double confidence_interval;
+        int resamples;
+        bool verbose;
+        std::string title;
+
+        std::string current;
+        std::unordered_map<std::string, result> data;
+    };
+
+    NONIUS_REPORTER("junit", junit_reporter);
+} // namespace nonius
+
+#endif // NONIUS_DISABLE_JUNIT_REPORTER
+#ifndef NONIUS_DISABLE_HTML_REPORTER
+// #included from: reporters/html_reporter.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// HTML single-chart reporter
+
+#define NONIUS_REPORTERS_HTML_ALL_REPORTER_HPP
+
+// #included from: detail/cpptempl.h
+// cpptempl
+// =================
+// This is a template engine for C++.
+//
+// Syntax
+// =================
+// Variables: {$variable_name}
+// Loops: {% for person in people %}Name: {$person.name}{% endfor %}
+// If: {% for person.name == "Bob" %}Full name: Robert{% endif %}
+//
+// Copyright
+// ==================
+// Copyright (c) Ryan Ginstrom
+//
+// 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.
+//
+// Modified by: Martinho Fernandes
+//
+// Usage
+// =======================
+//     std::string text = "{% if item %}{$item}{% endif %}\n"
+//      "{% if thing %}{$thing}{% endif %}" ;
+//  cpptempl::data_map data ;
+//  data["item"] = cpptempl::make_data("aaa") ;
+//  data["thing"] = cpptempl::make_data("bbb") ;
+//
+//     std::string result = cpptempl::parse(text, data) ;
+//
+// Handy Functions
+// ========================
+// make_data() : Feed it a string, data_map, or data_list to create a data entry.
+// Example:
+//  data_map person ;
+//  person["name"] = make_data("Bob") ;
+//  person["occupation"] = make_data("Plumber") ;
+//  data_map data ;
+//  data["person"] = make_data(person) ;
+//     std::string result = parse(templ_text, data) ;
+
+#ifndef CPPTEMPL_H
+#define CPPTEMPL_H
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4996 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
+#pragma warning( disable : 4512 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
+#endif
+
+#include <string>
+#include <vector>
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <boost/lexical_cast.hpp>
+
+#include <ostream>
+
+#include <sstream>
+#include <boost/algorithm/string.hpp>
+
+namespace cpptempl
+{
+    // various typedefs
+
+    // data classes
+    class Data ;
+    class DataValue ;
+    class DataList ;
+    class DataMap ;
+
+    class data_ptr {
+    public:
+        data_ptr() {}
+        template<typename T> data_ptr(const T& data) {
+            this->operator =(data);
+        }
+        data_ptr(DataValue* data);
+        data_ptr(DataList* data);
+        data_ptr(DataMap* data);
+        data_ptr(const data_ptr& data) {
+            ptr = data.ptr;
+        }
+        template<typename T> void operator = (const T& data);
+        void push_back(const data_ptr& data);
+        virtual ~data_ptr() {}
+        Data* operator ->() {
+            return ptr.get();
+        }
+    private:
+        std::shared_ptr<Data> ptr;
+    };
+    typedef std::vector<data_ptr> data_list ;
+
+    class data_map {
+    public:
+        data_ptr& operator [](const std::string& key);
+        bool empty();
+        bool has(const std::string& key);
+    private:
+        std::unordered_map<std::string, data_ptr> data;
+    };
+
+    template<> inline void data_ptr::operator = (const data_ptr& data);
+    template<> void data_ptr::operator = (const std::string& data);
+    template<> void data_ptr::operator = (const std::string& data);
+    template<> void data_ptr::operator = (const data_map& data);
+    template<typename T>
+    void data_ptr::operator = (const T& data) {
+        std::string data_str = boost::lexical_cast<std::string>(data);
+        this->operator =(data_str);
+    }
+
+    // token classes
+    class Token ;
+    typedef std::shared_ptr<Token> token_ptr ;
+    typedef std::vector<token_ptr> token_vector ;
+
+    // Custom exception class for library errors
+    class TemplateException : public std::exception
+    {
+    public:
+        TemplateException(std::string reason) : m_reason(std::move(reason)){}
+        char const* what() const NONIUS_NOEXCEPT override {
+            return m_reason.c_str();
+        }
+    private:
+        std::string m_reason;
+    };
+
+    // Data types used in templates
+    class Data
+    {
+    public:
+        virtual bool empty() = 0 ;
+        virtual std::string getvalue();
+        virtual data_list& getlist();
+        virtual data_map& getmap() ;
+    };
+
+    class DataValue : public Data
+    {
+        std::string m_value ;
+    public:
+        DataValue(std::string value) : m_value(value){}
+        std::string getvalue();
+        bool empty();
+    };
+
+    class DataList : public Data
+    {
+        data_list m_items ;
+    public:
+        DataList(const data_list &items) : m_items(items){}
+        data_list& getlist() ;
+        bool empty();
+    };
+
+    class DataMap : public Data
+    {
+        data_map m_items ;
+    public:
+        DataMap(const data_map &items) : m_items(items){}
+        data_map& getmap();
+        bool empty();
+    };
+
+    inline data_ptr::data_ptr(DataValue* data) : ptr(data) {}
+    inline data_ptr::data_ptr(DataList* data) : ptr(data) {}
+    inline data_ptr::data_ptr(DataMap* data) : ptr(data) {}
+
+    // convenience functions for making data objects
+    inline data_ptr make_data(std::string val)
+    {
+        return data_ptr(new DataValue(val)) ;
+    }
+    inline data_ptr make_data(data_list &val)
+    {
+        return data_ptr(new DataList(val)) ;
+    }
+    inline data_ptr make_data(data_map &val)
+    {
+        return data_ptr(new DataMap(val)) ;
+    }
+    // get a data value from a data map
+    // e.g. foo.bar => data["foo"]["bar"]
+    data_ptr parse_val(std::string key, data_map &data) ;
+
+    typedef enum
+    {
+        TOKEN_TYPE_NONE,
+        TOKEN_TYPE_TEXT,
+        TOKEN_TYPE_VAR,
+        TOKEN_TYPE_IF,
+        TOKEN_TYPE_FOR,
+        TOKEN_TYPE_ENDIF,
+        TOKEN_TYPE_ENDFOR,
+    } TokenType;
+
+    // Template tokens
+    // base class for all token types
+    class Token
+    {
+    public:
+        virtual TokenType gettype() = 0 ;
+        virtual void gettext(std::ostream &stream, data_map &data) = 0 ;
+        virtual void set_children(token_vector &children);
+        virtual token_vector & get_children();
+    };
+
+    // normal text
+    class TokenText : public Token
+    {
+        std::string m_text ;
+    public:
+        TokenText(std::string text) : m_text(text){}
+        TokenType gettype();
+        void gettext(std::ostream &stream, data_map &data);
+    };
+
+    // variable
+    class TokenVar : public Token
+    {
+        std::string m_key ;
+    public:
+        TokenVar(std::string key) : m_key(key){}
+        TokenType gettype();
+        void gettext(std::ostream &stream, data_map &data);
+    };
+
+    // for block
+    class TokenFor : public Token
+    {
+    public:
+        std::string m_key ;
+        std::string m_val ;
+        token_vector m_children ;
+        TokenFor(std::string expr);
+        TokenType gettype();
+        void gettext(std::ostream &stream, data_map &data);
+        void set_children(token_vector &children);
+        token_vector &get_children();
+    };
+
+    // if block
+    class TokenIf : public Token
+    {
+    public:
+        std::string m_expr ;
+        token_vector m_children ;
+        TokenIf(std::string expr) : m_expr(expr){}
+        TokenType gettype();
+        void gettext(std::ostream &stream, data_map &data);
+        bool is_true(std::string expr, data_map &data);
+        void set_children(token_vector &children);
+        token_vector &get_children();
+    };
+
+    // end of block
+    class TokenEnd : public Token // end of control block
+    {
+        std::string m_type ;
+    public:
+        TokenEnd(std::string text) : m_type(text){}
+        TokenType gettype();
+        void gettext(std::ostream &stream, data_map &data);
+    };
+
+    std::string gettext(token_ptr token, data_map &data) ;
+
+    void parse_tree(token_vector &tokens, token_vector &tree, TokenType until=TOKEN_TYPE_NONE) ;
+    token_vector & tokenize(std::string text, token_vector &tokens) ;
+
+    // The big daddy. Pass in the template and data,
+    // and get out a completed doc.
+    void parse(std::ostream &stream, std::string templ_text, data_map &data) ;
+    std::string parse(std::string templ_text, data_map &data);
+    std::string parse(std::string templ_text, data_map &data);
+
+// *********** Implementation ************
+
+    //////////////////////////////////////////////////////////////////////////
+    // Data classes
+    //////////////////////////////////////////////////////////////////////////
+
+    // data_map
+    inline data_ptr& data_map::operator [](const std::string& key) {
+        return data[key];
+    }
+    inline bool data_map::empty() {
+        return data.empty();
+    }
+    inline bool data_map::has(const std::string& key) {
+        return data.find(key) != data.end();
+    }
+
+    // data_ptr
+    template<>
+    inline void data_ptr::operator = (const data_ptr& data) {
+        ptr = data.ptr;
+    }
+
+    template<>
+    inline void data_ptr::operator = (const std::string& data) {
+        ptr.reset(new DataValue(data));
+    }
+
+    template<>
+    inline void data_ptr::operator = (const data_map& data) {
+        ptr.reset(new DataMap(data));
+    }
+
+    inline void data_ptr::push_back(const data_ptr& data) {
+        if (!ptr) {
+            ptr.reset(new DataList(data_list()));
+        }
+        data_list& list = ptr->getlist();
+        list.push_back(data);
+    }
+
+    // base data
+    inline std::string Data::getvalue()
+    {
+        throw TemplateException("Data item is not a value") ;
+    }
+
+    inline data_list& Data::getlist()
+    {
+        throw TemplateException("Data item is not a list") ;
+    }
+    inline data_map& Data::getmap()
+    {
+        throw TemplateException("Data item is not a dictionary") ;
+    }
+    // data value
+    inline std::string DataValue::getvalue()
+    {
+        return m_value ;
+    }
+    inline bool DataValue::empty()
+    {
+        return m_value.empty();
+    }
+    // data list
+    inline data_list& DataList::getlist()
+    {
+        return m_items ;
+    }
+
+    inline bool DataList::empty()
+    {
+        return m_items.empty();
+    }
+    // data map
+    inline data_map& DataMap:: getmap()
+    {
+        return m_items ;
+    }
+    inline bool DataMap::empty()
+    {
+        return m_items.empty();
+    }
+    //////////////////////////////////////////////////////////////////////////
+    // parse_val
+    //////////////////////////////////////////////////////////////////////////
+    inline data_ptr parse_val(std::string key, data_map &data)
+    {
+        // quoted string
+        if (key[0] == '\"')
+        {
+			return make_data(boost::trim_copy_if(key, [](char c){ return c == '"'; }));
+        }
+        // check for dotted notation, i.e [foo.bar]
+        size_t index = key.find(".") ;
+        if (index == std::string::npos)
+        {
+            if (!data.has(key))
+            {
+                return make_data("{$" + key + "}") ;
+            }
+            return data[key] ;
+        }
+
+        std::string sub_key = key.substr(0, index) ;
+        if (!data.has(sub_key))
+        {
+            return make_data("{$" + key + "}") ;
+        }
+        data_ptr item = data[sub_key] ;
+        return parse_val(key.substr(index+1), item->getmap()) ;
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // Token classes
+    //////////////////////////////////////////////////////////////////////////
+
+    // defaults, overridden by subclasses with children
+    inline void Token::set_children( token_vector & )
+    {
+        throw TemplateException("This token type cannot have children") ;
+    }
+
+    inline token_vector & Token::get_children()
+    {
+        throw TemplateException("This token type cannot have children") ;
+    }
+
+    // TokenText
+    inline TokenType TokenText::gettype()
+    {
+        return TOKEN_TYPE_TEXT ;
+    }
+
+    inline void TokenText::gettext( std::ostream &stream, data_map & )
+    {
+        stream << m_text ;
+    }
+
+    // TokenVar
+    inline TokenType TokenVar::gettype()
+    {
+        return TOKEN_TYPE_VAR ;
+    }
+
+    inline void TokenVar::gettext( std::ostream &stream, data_map &data )
+    {
+        stream << parse_val(m_key, data)->getvalue() ;
+    }
+
+    // TokenFor
+    inline TokenFor::TokenFor(std::string expr)
+    {
+        std::vector<std::string> elements ;
+        boost::split(elements, expr, boost::is_space()) ;
+        if (elements.size() != 4u)
+        {
+            throw TemplateException("Invalid syntax in for statement") ;
+        }
+        m_val = elements[1] ;
+        m_key = elements[3] ;
+    }
+
+    inline TokenType TokenFor::gettype()
+    {
+        return TOKEN_TYPE_FOR ;
+    }
+
+    inline void TokenFor::gettext( std::ostream &stream, data_map &data )
+    {
+        data_ptr value = parse_val(m_key, data) ;
+        data_list &items = value->getlist() ;
+        for (size_t i = 0 ; i < items.size() ; ++i)
+        {
+            data_map loop ;
+            loop["index"] = make_data(boost::lexical_cast<std::string>(i+1)) ;
+            loop["index0"] = make_data(boost::lexical_cast<std::string>(i)) ;
+            data["loop"] = make_data(loop);
+            data[m_val] = items[i] ;
+            for(size_t j = 0 ; j < m_children.size() ; ++j)
+            {
+                m_children[j]->gettext(stream, data) ;
+            }
+        }
+    }
+
+    inline void TokenFor::set_children( token_vector &children )
+    {
+        m_children.assign(children.begin(), children.end()) ;
+    }
+
+    inline token_vector & TokenFor::get_children()
+    {
+        return m_children;
+    }
+
+    // TokenIf
+    inline TokenType TokenIf::gettype()
+    {
+        return TOKEN_TYPE_IF ;
+    }
+
+    inline void TokenIf::gettext( std::ostream &stream, data_map &data )
+    {
+        if (is_true(m_expr, data))
+        {
+            for(size_t j = 0 ; j < m_children.size() ; ++j)
+            {
+                m_children[j]->gettext(stream, data) ;
+            }
+        }
+    }
+
+    inline bool TokenIf::is_true( std::string expr, data_map &data )
+    {
+        std::vector<std::string> elements ;
+        boost::split(elements, expr, boost::is_space()) ;
+
+        if (elements[1] == "not")
+        {
+            return parse_val(elements[2], data)->empty() ;
+        }
+        if (elements.size() == 2)
+        {
+            return ! parse_val(elements[1], data)->empty() ;
+        }
+        data_ptr lhs = parse_val(elements[1], data) ;
+        data_ptr rhs = parse_val(elements[3], data) ;
+        if (elements[2] == "==")
+        {
+            return lhs->getvalue() == rhs->getvalue() ;
+        }
+        return lhs->getvalue() != rhs->getvalue() ;
+    }
+
+    inline void TokenIf::set_children( token_vector &children )
+    {
+        m_children.assign(children.begin(), children.end()) ;
+    }
+
+    inline token_vector & TokenIf::get_children()
+    {
+        return m_children;
+    }
+
+    // TokenEnd
+    inline TokenType TokenEnd::gettype()
+    {
+        return m_type == "endfor" ? TOKEN_TYPE_ENDFOR : TOKEN_TYPE_ENDIF ;
+    }
+
+    inline void TokenEnd::gettext( std::ostream &, data_map &)
+    {
+        throw TemplateException("End-of-control statements have no associated text") ;
+    }
+
+    // gettext
+    // generic helper for getting text from tokens.
+
+    inline std::string gettext(token_ptr token, data_map &data)
+    {
+        std::ostringstream stream ;
+        token->gettext(stream, data) ;
+        return stream.str() ;
+    }
+    //////////////////////////////////////////////////////////////////////////
+    // parse_tree
+    // recursively parses list of tokens into a tree
+    //////////////////////////////////////////////////////////////////////////
+    inline void parse_tree(token_vector &tokens, token_vector &tree, TokenType until)
+    {
+        while(! tokens.empty())
+        {
+            // 'pops' first item off list
+            token_ptr token = tokens[0] ;
+            tokens.erase(tokens.begin()) ;
+
+            if (token->gettype() == TOKEN_TYPE_FOR)
+            {
+                token_vector children ;
+                parse_tree(tokens, children, TOKEN_TYPE_ENDFOR) ;
+                token->set_children(children) ;
+            }
+            else if (token->gettype() == TOKEN_TYPE_IF)
+            {
+                token_vector children ;
+                parse_tree(tokens, children, TOKEN_TYPE_ENDIF) ;
+                token->set_children(children) ;
+            }
+            else if (token->gettype() == until)
+            {
+                return ;
+            }
+            tree.push_back(token) ;
+        }
+    }
+    //////////////////////////////////////////////////////////////////////////
+    // tokenize
+    // parses a template into tokens (text, for, if, variable)
+    //////////////////////////////////////////////////////////////////////////
+    inline token_vector & tokenize(std::string text, token_vector &tokens)
+    {
+        while(! text.empty())
+        {
+            size_t pos = text.find("{") ;
+            if (pos == std::string::npos)
+            {
+                if (! text.empty())
+                {
+                    tokens.push_back(token_ptr(new TokenText(text))) ;
+                }
+                return tokens ;
+            }
+            std::string pre_text = text.substr(0, pos) ;
+            if (! pre_text.empty())
+            {
+                tokens.push_back(token_ptr(new TokenText(pre_text))) ;
+            }
+            text = text.substr(pos+1) ;
+            if (text.empty())
+            {
+                tokens.push_back(token_ptr(new TokenText("{"))) ;
+                return tokens ;
+            }
+
+            // variable
+            if (text[0] == '$')
+            {
+                pos = text.find("}") ;
+                if (pos != std::string::npos)
+                {
+                    tokens.push_back(token_ptr (new TokenVar(text.substr(1, pos-1)))) ;
+                    text = text.substr(pos+1) ;
+                }
+            }
+            // control statement
+            else if (text[0] == '%')
+            {
+                pos = text.find("}") ;
+                if (pos != std::string::npos)
+                {
+                    std::string expression = boost::trim_copy(text.substr(1, pos-2)) ;
+                    text = text.substr(pos+1) ;
+                    if (boost::starts_with(expression, "for"))
+                    {
+                        tokens.push_back(token_ptr (new TokenFor(expression))) ;
+                    }
+                    else if (boost::starts_with(expression, "if"))
+                    {
+                        tokens.push_back(token_ptr (new TokenIf(expression))) ;
+                    }
+                    else
+                    {
+                        tokens.push_back(token_ptr (new TokenEnd(boost::trim_copy(expression)))) ;
+                    }
+                }
+            }
+            else
+            {
+                tokens.push_back(token_ptr(new TokenText("{"))) ;
+            }
+        }
+        return tokens ;
+    }
+
+    inline std::string parse(std::string templ_text, data_map &data)
+    {
+        std::ostringstream stream ;
+        parse(stream, templ_text, data) ;
+        return stream.str() ;
+    }
+    inline void parse(std::ostream &stream, std::string templ_text, data_map &data)
+    {
+        token_vector tokens ;
+        tokenize(templ_text, tokens) ;
+        token_vector tree ;
+        parse_tree(tokens, tree) ;
+
+        for (size_t i = 0 ; i < tree.size() ; ++i)
+        {
+            // Recursively calls gettext on each node in the tree.
+            // gettext returns the appropriate text for that node.
+            // for text, itself;
+            // for variable, substitution;
+            // for control statement, recursively gets kids
+            tree[i]->gettext(stream, data) ;
+        }
+    }
+}
+
+#endif // CPPTEMPL_H
+#include <ios>
+#include <iomanip>
+#include <algorithm>
+#include <string>
+#include <iterator>
+#include <limits>
+#include <unordered_map>
+#include <vector>
+
+#include <fstream>
+
+namespace nonius {
+    struct html_reporter : reporter {
+    private:
+        static std::string const& template_string() {
+            static char const* template_parts[] = {
+// generated content broken into pieces because MSVC is in the 1990s.
+// #included from: detail/html_report_template.g.h++
+"<!DOCTYPE html>\n"
+"<html>\n"
+" <head>\n"
+"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
+"  <title>{$title} - nonius report</title>\n"
+"  <style type=\"text/css\"> body {\n"
+"    left: 0;\n"
+"    right: 0;\n"
+"    top: 0;\n"
+"    bottom: 0;\n"
+"    margin: 0;\n"
+"}\n"
+"\n"
+".plotly .modebar {\n"
+"    margin-top: 2em !important;\n"
+"}\n"
+"\n"
+"#header {\n"
+"    background-color: black;\n"
+"    z-index: 2;\n"
+"    position: absolute;\n"
+"    left: 0;\n"
+"    right: 0;\n"
+"    color: white;\n"
+"    font-family: monospace;\n"
+"    padding-left: 0;\n"
+"    text-align: center;\n"
+"    font-size: 1.2em;\n"
+"    font-weight: bold;\n"
+"    line-height: 2em;\n"
+"}\n"
+"\n"
+".select {\n"
+"    position: relative;\n"
+"    display: inline-block;\n"
+"    font-size: 1em;\n"
+"    font-weight: bold;\n"
+"    font-size: 1em;\n"
+"}\n"
+"\n"
+".select .arr {\n"
+"    background: #000;\n"
+"    bottom: 5px;\n"
+"    position: absolute;\n"
+"    right: 5px;\n"
+"    top: 5px;\n"
+"    width: 0px;\n"
+"    pointer-events: none;\n"
+"}\n"
+"\n"
+".select .arr:before {\n"
+"    content: '';\n"
+"    position: absolute;\n"
+"    top: 50%;\n"
+"    right: 14px;\n"
+"    margin-top: -5px;\n"
+"    pointer-events: none;\n"
+"    border-top: 10px solid white;\n"
+"    border-left: 10px solid transparent;\n"
+"    border-right: 10px solid transparent;\n"
+"}\n"
+"\n"
+".select .arr:after {\n"
+"    content: '';\n"
+"    position: absolute;\n"
+"    top: 50%;\n"
+"    right: 18px;\n"
+"    margin-top: -5px;\n"
+"    pointer-events: none;\n"
+"    border-top: 6px solid black;\n"
+"    border-left: 6px solid transparent;\n"
+"    border-right: 6px solid transparent;\n"
+"}\n"
+"\n"
+".select select {\n"
+"    outline: none;\n"
+"    -webkit-appearance: none;\n"
+"    -moz-appearance: none;\n"
+"    display: block;\n"
+"    padding: 0 3em 0 1.5em;\n"
+"    margin: 0.3em;\n"
+"    height: 2em;\n"
+"\n"
+"    transition: border-color 0.2s;\n"
+"    border: 2px solid #aaa;\n"
+"    border-radius: 0px;\n"
+"\n"
+"    background: black;\n"
+"    color: white;\n"
+"    font-family: inherit;\n"
+"    font-size: inherit;\n"
+"    line-height: inherit;\n"
+"    font-weight: inherit;\n"
+"}\n"
+"\n"
+".select select:focus {\n"
+"    border: 2px solid white;\n"
+"    color: white;\n"
+"}\n"
+"\n"
+"div.is-sorted {\n"
+"    position: absolute;\n"
+"    top: 0em;\n"
+"    right: 1em;\n"
+"    line-height: 2.8em;\n"
+"}\n"
+"\n"
+"div.is-sorted input {\n"
+"    position: relative;\n"
+"    top: 2px;\n"
+"}\n"
+"\n"
+"#plot {\n"
+"    position: absolute;\n"
+"    min-width: 300px;\n"
+"    min-height: 200px;\n"
+"    left: 0;\n"
+"    right: 0;\n"
+"    top: 2em;\n"
+"    bottom: 1em;\n"
+"}\n"
+"\n"
+"#footer {\n"
+"    position: absolute;\n"
+"    bottom: 0;\n"
+"    left: 0;\n"
+"    right: 0;\n"
+"    font-family: monospace;\n"
+"    font-size: 0.9em;\n"
+"    text-align: center;\n"
+"    text-transform: lowercase;\n"
+"    background-color: #bbb;\n"
+"    line-height: 2em;\n"
+"}\n"
+" </style>\n"
+"  <script type=\"text/javascript\"> /**\n"
+"* plotly.js (basic - minified) v1.15.0\n"
+"* Copyright 2012-2016, Plotly, Inc.\n"
+"* All rights reserved.\n"
+"* Licensed under the MIT license\n"
+"*/\n"
+,
+"!function(t){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=t();else if(\"function\"==typeof define&&define.amd)define([],t);else{var e;e=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this,e.Plotly=t()}}(function(){var t;return function e(t,n,r){function a(i,l){if(!n[i]){if(!t[i]){var s=\"function\"==typeof require&&require;if(!l&&s)return s(i,!0);if(o)return o(i,!0);var c=new Error(\"Cannot find module '\"+i+\"'\");throw c.code=\"MODULE_NOT_FOUND\",c}var u=n[i]={exports:{}};t[i][0].call(u.exports,function(e){var n=t[i][1][e];return a(n?n:e)},u,u.exports,e,t,n,r)}return n[i].exports}for(var o=\"function\"==typeof require&&require,i=0;i<r.length;i++)a(r[i]);return a}({1:[function(t,e,n){\"use strict\";var r=t(\"../src/plotly\"),a={\"X,X div\":\"font-family:'Open Sans', verdana, arial, sans-serif;margin:0;padding:0;\",\"X input,X button\":\"font-family:'Open Sans', verdana, arial, sans-serif;\",\"X input:focus,X button:focus\":\"outline:none;\",\"X a\":\"text-decoration:none;\",\"X a:hover\":\"text-decoration:none;\",\"X .crisp\":\"shape-rendering:crispEdges;\",\"X .user-select-none\":\"-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;\",\"X svg\":\"overflow:hidden;\",\"X svg a\":\"fill:#447adb;\",\"X svg a:hover\":\"fill:#3c6dc5;\",\"X .main-svg\":\"position:absolute;top:0;left:0;pointer-events:none;\",\"X .main-svg .draglayer\":\"pointer-events:all;\",\"X .cursor-pointer\":\"cursor:pointer;\",\"X .cursor-crosshair\":\"cursor:crosshair;\",\"X .cursor-move\":\"cursor:move;\",\"X .cursor-col-resize\":\"cursor:col-resize;\",\"X .cursor-row-resize\":\"cursor:row-resize;\",\"X .cursor-ns-resize\":\"cursor:ns-resize;\",\"X .cursor-ew-resize\":\"cursor:ew-resize;\",\"X .cursor-sw-resize\":\"cursor:sw-resize;\",\"X .cursor-s-resize\":\"cursor:s-resize;\",\"X .cursor-se-resize\":\"cursor:se-resize;\",\"X .cursor-w-resize\":\"cursor:w-resize;\",\"X .cursor-e-resize\":\"cursor:e-resize;\",\"X .cursor-nw-resize\":\"cursor:nw-resize;\",\"X .cursor-n-resize\":\"cursor:n-resize;\",\"X .cursor-ne-resize\":\"cursor:ne-resize;\",\"X .modebar\":\"position:absolute;top:2px;right:2px;z-index:1001;background:rgba(255,255,255,0.7);\",\"X .modebar--hover\":\"opacity:0;-webkit-transition:opacity 0.3s ease 0s;-moz-transition:opacity 0.3s ease 0s;-ms-transition:opacity 0.3s ease 0s;-o-transition:opacity 0.3s ease 0s;transition:opacity 0.3s ease 0s;\",\"X:hover .modebar--hover\":\"opacity:1;\",\"X .modebar-group\":\"float:left;display:inline-block;box-sizing:border-box;margin-left:8px;position:relative;vertical-align:middle;white-space:nowrap;\",\"X .modebar-group:first-child\":\"margin-left:0px;\",\"X .modebar-btn\":\"position:relative;font-size:16px;padding:3px 4px;cursor:pointer;line-height:normal;box-sizing:border-box;\",\"X .modebar-btn svg\":\"position:relative;top:2px;\",\"X .modebar-btn path\":\"fill:rgba(0,31,95,0.3);\",\"X .modebar-btn.active path,X .modebar-btn:hover path\":\"fill:rgba(0,22,72,0.5);\",\"X .modebar-btn.modebar-btn--logo\":\"padding:3px 1px;\",\"X .modebar-btn.modebar-btn--logo path\":\"fill:#447adb !important;\",\"X [data-title]:before,X [data-title]:after\":\"position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);display:none;opacity:0;z-index:1001;pointer-events:none;top:110%;right:50%;\",\"X [data-title]:hover:before,X [data-title]:hover:after\":\"display:block;opacity:1;\",\"X [data-title]:before\":\"content:'';position:absolute;background:transparent;border:6px solid transparent;z-index:1002;margin-top:-12px;border-bottom-color:#69738a;margin-right:-6px;\",\"X [data-title]:after\":\"content:attr(data-title);background:#69738a;color:white;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;margin-right:-18px;border-radius:2px;\",\"X .select-outline\":\"fill:none;stroke-width:1;shape-rendering:crispEdges;\",\"X .select-outline-1\":\"stroke:white;\",\"X .select-outline-2\":\"stroke:black;stroke-dasharray:2px 2px;\",Y:\"font-family:'Open Sans';position:fixed;top:50px;right:20px;z-index:10000;font-size:10pt;max-width:180px;\",\"Y p\":\"margin:0;\",\"Y .notifier-note\":\"min-width:180px;max-width:250px;border:1px solid #fff;z-index:3000;margin:0;background-color:#8c97af;background-color:rgba(140,151,175,0.9);color:#fff;padding:10px;\",\"Y .notifier-close\":\"color:#fff;opacity:0.8;float:right;padding:0 5px;background:none;border:none;font-size:20px;font-weight:bold;line-height:20px;\",\"Y .notifier-close:hover\":\"color:#444;text-decoration:none;cursor:pointer;\"};for(var o in a){var i=o.replace(/^,/,\" ,\").replace(/X/g,\".js-plotly-plot .plotly\").replace(/Y/g,\".plotly-notifier\");r.Lib.addStyleRule(i,a[o])}},{\"../src/plotly\":107}],2:[function(t,e,n){\"use strict\";e.exports={undo:{width:857.1,path:\"m857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z\",ascent:850,descent:-150},home:{width:928.6,path:\"m786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z\",ascent:850,descent:-150},\"camera-retro\":{width:1e3,path:\"m518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z\",ascent:850,descent:-150},zoombox:{width:1e3,path:\"m1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z\",ascent:850,descent:-150},pan:{width:1e3,path:\"m1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z\",ascent:850,descent:-150},zoom_plus:{width:1e3,path:\"m1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z\",ascent:850,descent:-150},zoom_minus:{width:1e3,path:\"m0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z\",ascent:850,descent:-150},autoscale:{width:1e3,path:\"m250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z\",ascent:850,descent:-150},tooltip_basic:{width:1500,path:\"m375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z\",ascent:850,descent:-150},tooltip_compare:{width:1125,path:\"m187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z\",ascent:850,descent:-150},plotlylogo:{width:1542,path:\"m0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z\",ascent:850,descent:-150},\"z-axis\":{width:1e3,path:\"m833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z\",ascent:850,descent:-150},\"3d_rotate\":{width:1e3,path:\"m922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z\",ascent:850,descent:-150},camera:{width:1e3,path:\"m500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z\",ascent:850,descent:-150},movie:{width:1e3,path:\"m938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z\",ascent:850,descent:-150},question:{width:857.1,path:\"m500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-2"
+,
+"07-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z\",ascent:850,descent:-150},disk:{width:857.1,path:\"m214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z\",ascent:850,descent:-150},lasso:{width:1031,path:\"m1018 538c-36 207-290 336-568 286-277-48-473-256-436-463 10-57 36-108 76-151-13-66 11-137 68-183 34-28 75-41 114-42l-55-70 0 0c-2-1-3-2-4-3-10-14-8-34 5-45 14-11 34-8 45 4 1 1 2 3 2 5l0 0 113 140c16 11 31 24 45 40 4 3 6 7 8 11 48-3 100 0 151 9 278 48 473 255 436 462z m-624-379c-80 14-149 48-197 96 42 42 109 47 156 9 33-26 47-66 41-105z m-187-74c-19 16-33 37-39 60 50-32 109-55 174-68-42-25-95-24-135 8z m360 75c-34-7-69-9-102-8 8 62-16 128-68 170-73 59-175 54-244-5-9 20-16 40-20 61-28 159 121 317 333 354s407-60 434-217c28-159-121-318-333-355z\",ascent:850,descent:-150},selectbox:{width:1e3,path:\"m0 850l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-285l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z\",ascent:850,descent:-150}}},{}],3:[function(t,e,n){e.exports=t(\"../src/traces/bar\")},{\"../src/traces/bar\":148}],4:[function(t,e,n){e.exports=t(\"../src/core\")},{\"../src/core\":83}],5:[function(t,e,n){\"use strict\";var r=t(\"./core\");r.register([t(\"./bar\"),t(\"./pie\")]),e.exports=r},{\"./bar\":3,\"./core\":4,\"./pie\":6}],6:[function(t,e,n){e.exports=t(\"../src/traces/pie\")},{\"../src/traces/pie\":160}],7:[function(t,e,n){function r(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function a(t){return\"function\"==typeof t}function o(t){return\"number\"==typeof t}function i(t){return\"object\"==typeof t&&null!==t}function l(t){return void 0===t}e.exports=r,r.EventEmitter=r,r.prototype._events=void 0,r.prototype._maxListeners=void 0,r.defaultMaxListeners=10,r.prototype.setMaxListeners=function(t){if(!o(t)||0>t||isNaN(t))throw TypeError(\"n must be a positive number\");return this._maxListeners=t,this},r.prototype.emit=function(t){var e,n,r,o,s,c;if(this._events||(this._events={}),\"error\"===t&&(!this._events.error||i(this._events.error)&&!this._events.error.length)){if(e=arguments[1],e instanceof Error)throw e;throw TypeError('Uncaught, unspecified \"error\" event.')}if(n=this._events[t],l(n))return!1;if(a(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:o=Array.prototype.slice.call(arguments,1),n.apply(this,o)}else if(i(n))for(o=Array.prototype.slice.call(arguments,1),c=n.slice(),r=c.length,s=0;r>s;s++)c[s].apply(this,o);return!0},r.prototype.addListener=function(t,e){var n;if(!a(e))throw TypeError(\"listener must be a function\");return this._events||(this._events={}),this._events.newListener&&this.emit(\"newListener\",t,a(e.listener)?e.listener:e),this._events[t]?i(this._events[t])?this._events[t].push(e):this._events[t]=[this._events[t],e]:this._events[t]=e,i(this._events[t])&&!this._events[t].warned&&(n=l(this._maxListeners)?r.defaultMaxListeners:this._maxListeners,n&&n>0&&this._events[t].length>n&&(this._events[t].warned=!0,console.error(\"(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.\",this._events[t].length),\"function\"==typeof console.trace&&console.trace())),this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(t,e){function n(){this.removeListener(t,n),r||(r=!0,e.apply(this,arguments))}if(!a(e))throw TypeError(\"listener must be a function\");var r=!1;return n.listener=e,this.on(t,n),this},r.prototype.removeListener=function(t,e){var n,r,o,l;if(!a(e))throw TypeError(\"listener must be a function\");if(!this._events||!this._events[t])return this;if(n=this._events[t],o=n.length,r=-1,n===e||a(n.listener)&&n.listener===e)delete this._events[t],this._events.removeListener&&this.emit(\"removeListener\",t,e);else if(i(n)){for(l=o;l-- >0;)if(n[l]===e||n[l].listener&&n[l].listener===e){r=l;break}if(0>r)return this;1===n.length?(n.length=0,delete this._events[t]):n.splice(r,1),this._events.removeListener&&this.emit(\"removeListener\",t,e)}return this},r.prototype.removeAllListeners=function(t){var e,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[t]&&delete this._events[t],this;if(0===arguments.length){for(e in this._events)\"removeListener\"!==e&&this.removeAllListeners(e);return this.removeAllListeners(\"removeListener\"),this._events={},this}if(n=this._events[t],a(n))this.removeListener(t,n);else if(n)for(;n.length;)this.removeListener(t,n[n.length-1]);return delete this._events[t],this},r.prototype.listeners=function(t){var e;return e=this._events&&this._events[t]?a(this._events[t])?[this._events[t]]:this._events[t].slice():[]},r.prototype.listenerCount=function(t){if(this._events){var e=this._events[t];if(a(e))return 1;if(e)return e.length}return 0},r.listenerCount=function(t,e){return t.listenerCount(e)}},{}],8:[function(t,e,n){function r(){u=!1,l.length?c=l.concat(c):f=-1,c.length&&a()}function a(){if(!u){var t=setTimeout(r);u=!0;for(var e=c.length;e;){for(l=c,c=[];++f<e;)l&&l[f].run();f=-1,e=c.length}l=null,u=!1,clearTimeout(t)}}function o(t,e){this.fun=t,this.array=e}function i(){}var l,s=e.exports={},c=[],u=!1,f=-1;s.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];c.push(new o(t,e)),1!==c.length||u||setTimeout(a,0)},o.prototype.run=function(){this.fun.apply(null,this.array)},s.title=\"browser\",s.browser=!0,s.env={},s.argv=[],s.version=\"\",s.versions={},s.on=i,s.addListener=i,s.once=i,s.off=i,s.removeListener=i,s.removeAllListeners=i,s.emit=i,s.binding=function(t){throw new Error(\"process.binding is not supported\")},s.cwd=function(){return\"/\"},s.chdir=function(t){throw new Error(\"process.chdir is not supported\")},s.umask=function(){return 0}},{}],9:[function(e,n,r){!function(){function e(t){return t&&(t.ownerDocument||t.document||t).documentElement}function r(t){return t&&(t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView)}function a(t,e){return e>t?-1:t>e?1:t>=e?0:NaN}function o(t){return null===t?NaN:+t}function i(t){return!isNaN(t)}function l(t){return{left:function(e,n,r,a){for(arguments.length<3&&(r=0),arguments.length<4&&(a=e.length);a>r;){var o=r+a>>>1;t(e[o],n)<0?r=o+1:a=o}return r},right:function(e,n,r,a){for(arguments.length<3&&(r=0),arguments.length<4&&(a=e.length);a>r;){var o=r+a>>>1;t(e[o],n)>0?a=o:r=o+1}return r}}}function s(t){return t.length}function c(t){for(var e=1;t*e%1;)e*=10;return e}function u(t,e){for(var n in e)Object.defineProperty(t.prototype,n,{value:e[n],enumerable:!1})}function f(){this._=Object.create(null)}function d(t){return(t+=\"\")===ki||t[0]===Mi?Mi+t:t}function h(t){return(t+=\"\")[0]===Mi?t.slice(1):t}function p(t){return d(t)in this._}function g(t){return(t=d(t))in this._&&delete this._[t]}function v(){var t=[];for(var e in this._)t.push(h(e));return t}function m(){var t=0;for(var e in this._)++t;return t}function y(){for(var t in this._)return!1;return!0}function x(){this._=Object.create(null)}function b(t){return t}function _(t,e,n){return function(){var r=n.apply(e,arguments);return r===e?t:r}}function w(t,e){if(e in t)return e;e=e.charAt(0).toUpperCase()+e.slice(1);for(var n=0,r=Ai.length;r>n;++n){var a=Ai[n]+e;if(a in t)return a}}function k(){}function M(){}function A(t){function e(){for(var e,r=n,a=-1,o=r.length;++a<o;)(e=r[a].on)&&e.apply(this,arguments);return t}var n=[],r=new f;return e.on=function(e,a){var o,i=r.get(e);return arguments.length<2?i&&i.on:(i&&(i.on=null,n=n.slice(0,o=n.indexOf(i)).concat(n.slice(o+1)),r.remove(e)),a&&n.push(r.set(e,{on:a})),t)},e}function L(){ui.event.preventDefault()}function T(){for(var t,e=ui.event;t=e.sourceEvent;)e=t;return e}function z(t){for(var e=new M,n=0,r=arguments.length;++n<r;)e[arguments[n]]=A(e);return e.of=function(n,r){return function(a){try{var o=a.sourceEvent=ui.event;a.target=t,ui.event=a,e[a.type].apply(n,r)}finally{ui.event=o}}},e}function S(t){return Ti(t,Ci),t}function E(t){return\"function\"==typeof t?t:function(){return zi(t,this)}}function C(t){return\"function\"==typeof t?t:function(){return Si(t,this)}}function O(t,e){function n(){this.removeAttribute(t)}function r(){this.removeAttributeNS(t.space,t.local)}function a(){this.setAttribute(t,e)}function o(){this.setAttributeNS(t.space,t.local,e)}function i(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}function l(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}return t=ui.ns.qualify(t),null==e?t.local?r:n:\"function\"==typeof e?t.local?l:i:t.local?o:a}function P(t){return t.trim().replace(/\\s+/g,\" \")}function N(t){return new RegExp(\"(?:^|\\\\s+)\"+ui.requote(t)+\"(?:\\\\s+|$)\",\"g\")}function D(t){return(t+\"\").trim().split(/^|\\s+/)}function I(t,e){function n(){for(var n=-1;++n<a;)t[n](this,e)}function r(){for(var n=-1,r=e.apply(this,arguments);++n<a;)t[n](this,r)}t=D(t).map(R);var a=t.length;return\"function\"==typeof e?r:n}function R(t){var e=N(t);return function(n,r){if(a=n.classList)return r?a.add(t):a.remove(t);var"
+,
+" a=n.getAttribute(\"class\")||\"\";r?(e.lastIndex=0,e.test(a)||n.setAttribute(\"class\",P(a+\" \"+t))):n.setAttribute(\"class\",P(a.replace(e,\" \")))}}function j(t,e,n){function r(){this.style.removeProperty(t)}function a(){this.style.setProperty(t,e,n)}function o(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}return null==e?r:\"function\"==typeof e?o:a}function q(t,e){function n(){delete this[t]}function r(){this[t]=e}function a(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}return null==e?n:\"function\"==typeof e?a:r}function F(t){function e(){var e=this.ownerDocument,n=this.namespaceURI;return n===Oi&&e.documentElement.namespaceURI===Oi?e.createElement(t):e.createElementNS(n,t)}function n(){return this.ownerDocument.createElementNS(t.space,t.local)}return\"function\"==typeof t?t:(t=ui.ns.qualify(t)).local?n:e}function B(){var t=this.parentNode;t&&t.removeChild(this)}function H(t){return{__data__:t}}function V(t){return function(){return Ei(this,t)}}function Z(t){return arguments.length||(t=a),function(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}}function Y(t,e){for(var n=0,r=t.length;r>n;n++)for(var a,o=t[n],i=0,l=o.length;l>i;i++)(a=o[i])&&e(a,i,n);return t}function U(t){return Ti(t,Ni),t}function X(t){var e,n;return function(r,a,o){var i,l=t[o].update,s=l.length;for(o!=n&&(n=o,e=0),a>=e&&(e=a+1);!(i=l[e])&&++e<s;);return i}}function G(t,e,n){function r(){var e=this[i];e&&(this.removeEventListener(t,e,e.$),delete this[i])}function a(){var a=s(e,di(arguments));r.call(this),this.addEventListener(t,this[i]=a,a.$=n),a._=e}function o(){var e,n=new RegExp(\"^__on([^.]+)\"+ui.requote(t)+\"$\");for(var r in this)if(e=r.match(n)){var a=this[r];this.removeEventListener(e[1],a,a.$),delete this[r]}}var i=\"__on\"+t,l=t.indexOf(\".\"),s=$;l>0&&(t=t.slice(0,l));var c=Di.get(t);return c&&(t=c,s=Q),l?e?a:r:e?k:o}function $(t,e){return function(n){var r=ui.event;ui.event=n,e[0]=this.__data__;try{t.apply(this,e)}finally{ui.event=r}}}function Q(t,e){var n=$(t,e);return function(t){var e=this,r=t.relatedTarget;r&&(r===e||8&r.compareDocumentPosition(e))||n.call(e,t)}}function W(t){var n=\".dragsuppress-\"+ ++Ri,a=\"click\"+n,o=ui.select(r(t)).on(\"touchmove\"+n,L).on(\"dragstart\"+n,L).on(\"selectstart\"+n,L);if(null==Ii&&(Ii=\"onselectstart\"in t?!1:w(t.style,\"userSelect\")),Ii){var i=e(t).style,l=i[Ii];i[Ii]=\"none\"}return function(t){if(o.on(n,null),Ii&&(i[Ii]=l),t){var e=function(){o.on(a,null)};o.on(a,function(){L(),e()},!0),setTimeout(e,0)}}}function J(t,e){e.changedTouches&&(e=e.changedTouches[0]);var n=t.ownerSVGElement||t;if(n.createSVGPoint){var a=n.createSVGPoint();if(0>ji){var o=r(t);if(o.scrollX||o.scrollY){n=ui.select(\"body\").append(\"svg\").style({position:\"absolute\",top:0,left:0,margin:0,padding:0,border:\"none\"},\"important\");var i=n[0][0].getScreenCTM();ji=!(i.f||i.e),n.remove()}}return ji?(a.x=e.pageX,a.y=e.pageY):(a.x=e.clientX,a.y=e.clientY),a=a.matrixTransform(t.getScreenCTM().inverse()),[a.x,a.y]}var l=t.getBoundingClientRect();return[e.clientX-l.left-t.clientLeft,e.clientY-l.top-t.clientTop]}function K(){return ui.event.changedTouches[0].identifier}function tt(t){return t>0?1:0>t?-1:0}function et(t,e,n){return(e[0]-t[0])*(n[1]-t[1])-(e[1]-t[1])*(n[0]-t[0])}function nt(t){return t>1?0:-1>t?Bi:Math.acos(t)}function rt(t){return t>1?Zi:-1>t?-Zi:Math.asin(t)}function at(t){return((t=Math.exp(t))-1/t)/2}function ot(t){return((t=Math.exp(t))+1/t)/2}function it(t){return((t=Math.exp(2*t))-1)/(t+1)}function lt(t){return(t=Math.sin(t/2))*t}function st(){}function ct(t,e,n){return this instanceof ct?(this.h=+t,this.s=+e,void(this.l=+n)):arguments.length<2?t instanceof ct?new ct(t.h,t.s,t.l):kt(\"\"+t,Mt,ct):new ct(t,e,n)}function ut(t,e,n){function r(t){return t>360?t-=360:0>t&&(t+=360),60>t?o+(i-o)*t/60:180>t?i:240>t?o+(i-o)*(240-t)/60:o}function a(t){return Math.round(255*r(t))}var o,i;return t=isNaN(t)?0:(t%=360)<0?t+360:t,e=isNaN(e)?0:0>e?0:e>1?1:e,n=0>n?0:n>1?1:n,i=.5>=n?n*(1+e):n+e-n*e,o=2*n-i,new xt(a(t+120),a(t),a(t-120))}function ft(t,e,n){return this instanceof ft?(this.h=+t,this.c=+e,void(this.l=+n)):arguments.length<2?t instanceof ft?new ft(t.h,t.c,t.l):t instanceof ht?gt(t.l,t.a,t.b):gt((t=At((t=ui.rgb(t)).r,t.g,t.b)).l,t.a,t.b):new ft(t,e,n)}function dt(t,e,n){return isNaN(t)&&(t=0),isNaN(e)&&(e=0),new ht(n,Math.cos(t*=Yi)*e,Math.sin(t)*e)}function ht(t,e,n){return this instanceof ht?(this.l=+t,this.a=+e,void(this.b=+n)):arguments.length<2?t instanceof ht?new ht(t.l,t.a,t.b):t instanceof ft?dt(t.h,t.c,t.l):At((t=xt(t)).r,t.g,t.b):new ht(t,e,n)}function pt(t,e,n){var r=(t+16)/116,a=r+e/500,o=r-n/200;return a=vt(a)*nl,r=vt(r)*rl,o=vt(o)*al,new xt(yt(3.2404542*a-1.5371385*r-.4985314*o),yt(-.969266*a+1.8760108*r+.041556*o),yt(.0556434*a-.2040259*r+1.0572252*o))}function gt(t,e,n){return t>0?new ft(Math.atan2(n,e)*Ui,Math.sqrt(e*e+n*n),t):new ft(NaN,NaN,t)}function vt(t){return t>.206893034?t*t*t:(t-4/29)/7.787037}function mt(t){return t>.008856?Math.pow(t,1/3):7.787037*t+4/29}function yt(t){return Math.round(255*(.00304>=t?12.92*t:1.055*Math.pow(t,1/2.4)-.055))}function xt(t,e,n){return this instanceof xt?(this.r=~~t,this.g=~~e,void(this.b=~~n)):arguments.length<2?t instanceof xt?new xt(t.r,t.g,t.b):kt(\"\"+t,xt,ut):new xt(t,e,n)}function bt(t){return new xt(t>>16,t>>8&255,255&t)}function _t(t){return bt(t)+\"\"}function wt(t){return 16>t?\"0\"+Math.max(0,t).toString(16):Math.min(255,t).toString(16)}function kt(t,e,n){var r,a,o,i=0,l=0,s=0;if(r=/([a-z]+)\\((.*)\\)/.exec(t=t.toLowerCase()))switch(a=r[2].split(\",\"),r[1]){case\"hsl\":return n(parseFloat(a[0]),parseFloat(a[1])/100,parseFloat(a[2])/100);case\"rgb\":return e(Tt(a[0]),Tt(a[1]),Tt(a[2]))}return(o=ll.get(t))?e(o.r,o.g,o.b):(null==t||\"#\"!==t.charAt(0)||isNaN(o=parseInt(t.slice(1),16))||(4===t.length?(i=(3840&o)>>4,i=i>>4|i,l=240&o,l=l>>4|l,s=15&o,s=s<<4|s):7===t.length&&(i=(16711680&o)>>16,l=(65280&o)>>8,s=255&o)),e(i,l,s))}function Mt(t,e,n){var r,a,o=Math.min(t/=255,e/=255,n/=255),i=Math.max(t,e,n),l=i-o,s=(i+o)/2;return l?(a=.5>s?l/(i+o):l/(2-i-o),r=t==i?(e-n)/l+(n>e?6:0):e==i?(n-t)/l+2:(t-e)/l+4,r*=60):(r=NaN,a=s>0&&1>s?0:r),new ct(r,a,s)}function At(t,e,n){t=Lt(t),e=Lt(e),n=Lt(n);var r=mt((.4124564*t+.3575761*e+.1804375*n)/nl),a=mt((.2126729*t+.7151522*e+.072175*n)/rl),o=mt((.0193339*t+.119192*e+.9503041*n)/al);return ht(116*a-16,500*(r-a),200*(a-o))}function Lt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Tt(t){var e=parseFloat(t);return\"%\"===t.charAt(t.length-1)?Math.round(2.55*e):e}function zt(t){return\"function\"==typeof t?t:function(){return t}}function St(t){return function(e,n,r){return 2===arguments.length&&\"function\"==typeof n&&(r=n,n=null),Et(e,n,t,r)}}function Et(t,e,n,r){function a(){var t,e=s.status;if(!e&&Ot(s)||e>=200&&300>e||304===e){try{t=n.call(o,s)}catch(r){return void i.error.call(o,r)}i.load.call(o,t)}else i.error.call(o,s)}var o={},i=ui.dispatch(\"beforesend\",\"progress\",\"load\",\"error\"),l={},s=new XMLHttpRequest,c=null;return!this.XDomainRequest||\"withCredentials\"in s||!/^(http(s)?:)?\\/\\//.test(t)||(s=new XDomainRequest),\"onload\"in s?s.onload=s.onerror=a:s.onreadystatechange=function(){s.readyState>3&&a()},s.onprogress=function(t){var e=ui.event;ui.event=t;try{i.progress.call(o,s)}finally{ui.event=e}},o.header=function(t,e){return t=(t+\"\").toLowerCase(),arguments.length<2?l[t]:(null==e?delete l[t]:l[t]=e+\"\",o)},o.mimeType=function(t){return arguments.length?(e=null==t?null:t+\"\",o):e},o.responseType=function(t){return arguments.length?(c=t,o):c},o.response=function(t){return n=t,o},[\"get\",\"post\"].forEach(function(t){o[t]=function(){return o.send.apply(o,[t].concat(di(arguments)))}}),o.send=function(n,r,a){if(2===arguments.length&&\"function\"==typeof r&&(a=r,r=null),s.open(n,t,!0),null==e||\"accept\"in l||(l.accept=e+\",*/*\"),s.setRequestHeader)for(var u in l)s.setRequestHeader(u,l[u]);return null!=e&&s.overrideMimeType&&s.overrideMimeType(e),null!=c&&(s.responseType=c),null!=a&&o.on(\"error\",a).on(\"load\",function(t){a(null,t)}),i.beforesend.call(o,s),s.send(null==r?null:r),o},o.abort=function(){return s.abort(),o},ui.rebind(o,i,\"on\"),null==r?o:o.get(Ct(r))}function Ct(t){return 1===t.length?function(e,n){t(null==e?n:null)}:t}function Ot(t){var e=t.responseType;return e&&\"text\"!==e?t.response:t.responseText}function Pt(t,e,n){var r=arguments.length;2>r&&(e=0),3>r&&(n=Date.now());var a=n+e,o={c:t,t:a,n:null};return cl?cl.n=o:sl=o,cl=o,ul||(fl=clearTimeout(fl),ul=1,dl(Nt)),o}function Nt(){var t=Dt(),e=It()-t;e>24?(isFinite(e)&&(clearTimeout(fl),fl=setTimeout(Nt,e)),ul=0):(ul=1,dl(Nt))}function Dt(){for(var t=Date.now(),e=sl;e;)t>=e.t&&e.c(t-e.t)&&(e.c=null),e=e.n;return t}function It(){for(var t,e=sl,n=1/0;e;)e.c?(e.t<n&&(n=e.t),e=(t=e).n):e=t?t.n=e.n:sl=e.n;return cl=t,n}function Rt(t,e){return e-(t?Math.ceil(Math.log(t)/Math.LN10):1)}function jt(t,e){var n=Math.pow(10,3*wi(8-e));return{scale:e>8?function(t){return t/n}:function(t){return t*n},symbol:t}}function qt(t){var e=t.decimal,n=t.thousands,r=t.grouping,a=t.currency,o=r&&n?function(t,e){for(var a=t.length,o=[],i=0,l=r[0],s=0;a>0&&l>0&&(s+l+1>e&&(l=Math.max(1,e-s)),o.push(t.substring(a-=l,a+l)),!((s+=l+1)>e));)l=r[i=(i+1)%r.length];return o.reverse().join(n)}:b;return function(t){var n=pl.exec(t),r=n[1]||\" \",i=n[2]||\">\",l=n[3]||\"-\",s=n[4]||\"\",c=n[5],u=+n[6],f=n[7],d=n[8],h=n[9],p=1,g=\"\",v=\"\",m=!1,y=!0;switch(d&&(d=+d.substring(1)),(c||\"0\"===r&&\"=\"===i)&&(c=r=\"0\",i=\"=\"),h){case\"n\":f=!0,h=\"g\";break;case\"%\":p=100,v=\"%\",h=\"f\";break;case\"p\":p=100,v=\"%\",h=\"r\";break;case\"b\":case\"o\":case\"x\":case\"X\":\"#\"===s&&(g=\"0\"+h.toLowerCase());case\"c\":y=!1;case\"d\":m=!0,d=0;break;case\"s\":p=-1,h=\"r\"}\"$\"===s&&(g=a[0],v=a[1]),\"r\"!=h||d||(h=\"g\"),null!=d&&(\"g\"==h?d=Math.max(1,Math.min(21,d)):\"e\"!=h&&\"f\"!=h||(d=Math.max(0,Math.min(20,d)))),h=gl.get(h)||Ft;var x=c&&f;return function(t){var n=v;if(m&&t%1)return\"\";var a=0>t||0===t&&0>1/t?(t=-t,\"-\"):\"-\"===l?\"\":l;if(0>p){var s=ui.formatPrefix(t,d);t=s.scale(t),n=s.symbol+v}else t*=p;t=h(t,d);var b,_,w=t.lastIndex"
+,
+"Of(\".\");if(0>w){var k=y?t.lastIndexOf(\"e\"):-1;0>k?(b=t,_=\"\"):(b=t.substring(0,k),_=t.substring(k))}else b=t.substring(0,w),_=e+t.substring(w+1);!c&&f&&(b=o(b,1/0));var M=g.length+b.length+_.length+(x?0:a.length),A=u>M?new Array(M=u-M+1).join(r):\"\";return x&&(b=o(A+b,A.length?u-_.length:1/0)),a+=g,t=b+_,(\"<\"===i?a+t+A:\">\"===i?A+a+t:\"^\"===i?A.substring(0,M>>=1)+a+t+A.substring(M):a+(x?t:A+t))+n}}}function Ft(t){return t+\"\"}function Bt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ht(t,e,n){function r(e){var n=t(e),r=o(n,1);return r-e>e-n?n:r}function a(n){return e(n=t(new ml(n-1)),1),n}function o(t,n){return e(t=new ml(+t),n),t}function i(t,r,o){var i=a(t),l=[];if(o>1)for(;r>i;)n(i)%o||l.push(new Date(+i)),e(i,1);else for(;r>i;)l.push(new Date(+i)),e(i,1);return l}function l(t,e,n){try{ml=Bt;var r=new Bt;return r._=t,i(r,e,n)}finally{ml=Date}}t.floor=t,t.round=r,t.ceil=a,t.offset=o,t.range=i;var s=t.utc=Vt(t);return s.floor=s,s.round=Vt(r),s.ceil=Vt(a),s.offset=Vt(o),s.range=l,t}function Vt(t){return function(e,n){try{ml=Bt;var r=new Bt;return r._=e,t(r,n)._}finally{ml=Date}}}function Zt(t){function e(t){function e(e){for(var n,a,o,i=[],l=-1,s=0;++l<r;)37===t.charCodeAt(l)&&(i.push(t.slice(s,l)),null!=(a=xl[n=t.charAt(++l)])&&(n=t.charAt(++l)),(o=z[n])&&(n=o(e,null==a?\"e\"===n?\" \":\"0\":a)),i.push(n),s=l+1);return i.push(t.slice(s,l)),i.join(\"\")}var r=t.length;return e.parse=function(e){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},a=n(r,t,e,0);if(a!=e.length)return null;\"p\"in r&&(r.H=r.H%12+12*r.p);var o=null!=r.Z&&ml!==Bt,i=new(o?Bt:ml);return\"j\"in r?i.setFullYear(r.y,0,r.j):\"W\"in r||\"U\"in r?(\"w\"in r||(r.w=\"W\"in r?1:0),i.setFullYear(r.y,0,1),i.setFullYear(r.y,0,\"W\"in r?(r.w+6)%7+7*r.W-(i.getDay()+5)%7:r.w+7*r.U-(i.getDay()+6)%7)):i.setFullYear(r.y,r.m,r.d),i.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),o?i._:i},e.toString=function(){return t},e}function n(t,e,n,r){for(var a,o,i,l=0,s=e.length,c=n.length;s>l;){if(r>=c)return-1;\n"
+,
+"if(a=e.charCodeAt(l++),37===a){if(i=e.charAt(l++),o=S[i in xl?e.charAt(l++):i],!o||(r=o(t,n,r))<0)return-1}else if(a!=n.charCodeAt(r++))return-1}return r}function r(t,e,n){w.lastIndex=0;var r=w.exec(e.slice(n));return r?(t.w=k.get(r[0].toLowerCase()),n+r[0].length):-1}function a(t,e,n){b.lastIndex=0;var r=b.exec(e.slice(n));return r?(t.w=_.get(r[0].toLowerCase()),n+r[0].length):-1}function o(t,e,n){L.lastIndex=0;var r=L.exec(e.slice(n));return r?(t.m=T.get(r[0].toLowerCase()),n+r[0].length):-1}function i(t,e,n){M.lastIndex=0;var r=M.exec(e.slice(n));return r?(t.m=A.get(r[0].toLowerCase()),n+r[0].length):-1}function l(t,e,r){return n(t,z.c.toString(),e,r)}function s(t,e,r){return n(t,z.x.toString(),e,r)}function c(t,e,r){return n(t,z.X.toString(),e,r)}function u(t,e,n){var r=x.get(e.slice(n,n+=2).toLowerCase());return null==r?-1:(t.p=r,n)}var f=t.dateTime,d=t.date,h=t.time,p=t.periods,g=t.days,v=t.shortDays,m=t.months,y=t.shortMonths;e.utc=function(t){function n(t){try{ml=Bt;var e=new ml;return e._=t,r(e)}finally{ml=Date}}var r=e(t);return n.parse=function(t){try{ml=Bt;var e=r.parse(t);return e&&e._}finally{ml=Date}},n.toString=r.toString,n},e.multi=e.utc.multi=ue;var x=ui.map(),b=Ut(g),_=Xt(g),w=Ut(v),k=Xt(v),M=Ut(m),A=Xt(m),L=Ut(y),T=Xt(y);p.forEach(function(t,e){x.set(t.toLowerCase(),e)});var z={a:function(t){return v[t.getDay()]},A:function(t){return g[t.getDay()]},b:function(t){return y[t.getMonth()]},B:function(t){return m[t.getMonth()]},c:e(f),d:function(t,e){return Yt(t.getDate(),e,2)},e:function(t,e){return Yt(t.getDate(),e,2)},H:function(t,e){return Yt(t.getHours(),e,2)},I:function(t,e){return Yt(t.getHours()%12||12,e,2)},j:function(t,e){return Yt(1+vl.dayOfYear(t),e,3)},L:function(t,e){return Yt(t.getMilliseconds(),e,3)},m:function(t,e){return Yt(t.getMonth()+1,e,2)},M:function(t,e){return Yt(t.getMinutes(),e,2)},p:function(t){return p[+(t.getHours()>=12)]},S:function(t,e){return Yt(t.getSeconds(),e,2)},U:function(t,e){return Yt(vl.sundayOfYear(t),e,2)},w:function(t){return t.getDay()},W:function(t,e){return Yt(vl.mondayOfYear(t),e,2)},x:e(d),X:e(h),y:function(t,e){return Yt(t.getFullYear()%100,e,2)},Y:function(t,e){return Yt(t.getFullYear()%1e4,e,4)},Z:se,\"%\":function(){return\"%\"}},S={a:r,A:a,b:o,B:i,c:l,d:ne,e:ne,H:ae,I:ae,j:re,L:le,m:ee,M:oe,p:u,S:ie,U:$t,w:Gt,W:Qt,x:s,X:c,y:Jt,Y:Wt,Z:Kt,\"%\":ce};return e}function Yt(t,e,n){var r=0>t?\"-\":\"\",a=(r?-t:t)+\"\",o=a.length;return r+(n>o?new Array(n-o+1).join(e)+a:a)}function Ut(t){return new RegExp(\"^(?:\"+t.map(ui.requote).join(\"|\")+\")\",\"i\")}function Xt(t){for(var e=new f,n=-1,r=t.length;++n<r;)e.set(t[n].toLowerCase(),n);return e}function Gt(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function $t(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n));return r?(t.U=+r[0],n+r[0].length):-1}function Qt(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n));return r?(t.W=+r[0],n+r[0].length):-1}function Wt(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Jt(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.y=te(+r[0]),n+r[0].length):-1}function Kt(t,e,n){return/^[+-]\\d{4}$/.test(e=e.slice(n,n+5))?(t.Z=-e,n+5):-1}function te(t){return t+(t>68?1900:2e3)}function ee(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function ne(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function re(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+3));return r?(t.j=+r[0],n+r[0].length):-1}function ae(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function oe(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function ie(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function le(t,e,n){bl.lastIndex=0;var r=bl.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function se(t){var e=t.getTimezoneOffset(),n=e>0?\"-\":\"+\",r=wi(e)/60|0,a=wi(e)%60;return n+Yt(r,\"0\",2)+Yt(a,\"0\",2)}function ce(t,e,n){_l.lastIndex=0;var r=_l.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function ue(t){for(var e=t.length,n=-1;++n<e;)t[n][0]=this(t[n][0]);return function(e){for(var n=0,r=t[n];!r[1](e);)r=t[++n];return r[0](e)}}function fe(){}function de(t,e,n){var r=n.s=t+e,a=r-t,o=r-a;n.t=t-o+(e-a)}function he(t,e){t&&Al.hasOwnProperty(t.type)&&Al[t.type](t,e)}function pe(t,e,n){var r,a=-1,o=t.length-n;for(e.lineStart();++a<o;)r=t[a],e.point(r[0],r[1],r[2]);e.lineEnd()}function ge(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)pe(t[n],e,1);e.polygonEnd()}function ve(){function t(t,e){t*=Yi,e=e*Yi/2+Bi/4;var n=t-r,i=n>=0?1:-1,l=i*n,s=Math.cos(e),c=Math.sin(e),u=o*c,f=a*s+u*Math.cos(l),d=u*i*Math.sin(l);Tl.add(Math.atan2(d,f)),r=t,a=s,o=c}var e,n,r,a,o;zl.point=function(i,l){zl.point=t,r=(e=i)*Yi,a=Math.cos(l=(n=l)*Yi/2+Bi/4),o=Math.sin(l)},zl.lineEnd=function(){t(e,n)}}function me(t){var e=t[0],n=t[1],r=Math.cos(n);return[r*Math.cos(e),r*Math.sin(e),Math.sin(n)]}function ye(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function xe(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function be(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function _e(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function we(t){var e=Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}function ke(t){return[Math.atan2(t[1],t[0]),rt(t[2])]}function Me(t,e){return wi(t[0]-e[0])<qi&&wi(t[1]-e[1])<qi}function Ae(t,e){t*=Yi;var n=Math.cos(e*=Yi);Le(n*Math.cos(t),n*Math.sin(t),Math.sin(e))}function Le(t,e,n){++Sl,Cl+=(t-Cl)/Sl,Ol+=(e-Ol)/Sl,Pl+=(n-Pl)/Sl}function Te(){function t(t,a){t*=Yi;var o=Math.cos(a*=Yi),i=o*Math.cos(t),l=o*Math.sin(t),s=Math.sin(a),c=Math.atan2(Math.sqrt((c=n*s-r*l)*c+(c=r*i-e*s)*c+(c=e*l-n*i)*c),e*i+n*l+r*s);El+=c,Nl+=c*(e+(e=i)),Dl+=c*(n+(n=l)),Il+=c*(r+(r=s)),Le(e,n,r)}var e,n,r;Fl.point=function(a,o){a*=Yi;var i=Math.cos(o*=Yi);e=i*Math.cos(a),n=i*Math.sin(a),r=Math.sin(o),Fl.point=t,Le(e,n,r)}}function ze(){Fl.point=Ae}function Se(){function t(t,e){t*=Yi;var n=Math.cos(e*=Yi),i=n*Math.cos(t),l=n*Math.sin(t),s=Math.sin(e),c=a*s-o*l,u=o*i-r*s,f=r*l-a*i,d=Math.sqrt(c*c+u*u+f*f),h=r*i+a*l+o*s,p=d&&-nt(h)/d,g=Math.atan2(d,h);Rl+=p*c,jl+=p*u,ql+=p*f,El+=g,Nl+=g*(r+(r=i)),Dl+=g*(a+(a=l)),Il+=g*(o+(o=s)),Le(r,a,o)}var e,n,r,a,o;Fl.point=function(i,l){e=i,n=l,Fl.point=t,i*=Yi;var s=Math.cos(l*=Yi);r=s*Math.cos(i),a=s*Math.sin(i),o=Math.sin(l),Le(r,a,o)},Fl.lineEnd=function(){t(e,n),Fl.lineEnd=ze,Fl.point=Ae}}function Ee(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.invert&&e.invert&&(n.invert=function(n,r){return n=e.invert(n,r),n&&t.invert(n[0],n[1])}),n}function Ce(){return!0}function Oe(t,e,n,r,a){var o=[],i=[];if(t.forEach(function(t){if(!((e=t.length-1)<=0)){var e,n=t[0],r=t[e];if(Me(n,r)){a.lineStart();for(var l=0;e>l;++l)a.point((n=t[l])[0],n[1]);return void a.lineEnd()}var s=new Ne(n,t,null,!0),c=new Ne(n,null,s,!1);s.o=c,o.push(s),i.push(c),s=new Ne(r,t,null,!1),c=new Ne(r,null,s,!0),s.o=c,o.push(s),i.push(c)}}),i.sort(e),Pe(o),Pe(i),o.length){for(var l=0,s=n,c=i.length;c>l;++l)i[l].e=s=!s;for(var u,f,d=o[0];;){for(var h=d,p=!0;h.v;)if((h=h.n)===d)return;u=h.z,a.lineStart();do{if(h.v=h.o.v=!0,h.e){if(p)for(var l=0,c=u.length;c>l;++l)a.point((f=u[l])[0],f[1]);else r(h.x,h.n.x,1,a);h=h.n}else{if(p){u=h.p.z;for(var l=u.length-1;l>=0;--l)a.point((f=u[l])[0],f[1])}else r(h.x,h.p.x,-1,a);h=h.p}h=h.o,u=h.z,p=!p}while(!h.v);a.lineEnd()}}}function Pe(t){if(e=t.length){for(var e,n,r=0,a=t[0];++r<e;)a.n=n=t[r],n.p=a,a=n;a.n=n=t[0],n.p=a}}function Ne(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this.n=this.p=null}function De(t,e,n,r){return function(a,o){function i(e,n){var r=a(e,n);t(e=r[0],n=r[1])&&o.point(e,n)}function l(t,e){var n=a(t,e);v.point(n[0],n[1])}function s(){y.point=l,v.lineStart()}function c(){y.point=i,v.lineEnd()}function u(t,e){g.push([t,e]);var n=a(t,e);b.point(n[0],n[1])}function f(){b.lineStart(),g=[]}function d(){u(g[0][0],g[0][1]),b.lineEnd();var t,e=b.clean(),n=x.buffer(),r=n.length;if(g.pop(),p.push(g),g=null,r)if(1&e){t=n[0];var a,r=t.length-1,i=-1;if(r>0){for(_||(o.polygonStart(),_=!0),o.lineStart();++i<r;)o.point((a=t[i])[0],a[1]);o.lineEnd()}}else r>1&&2&e&&n.push(n.pop().concat(n.shift())),h.push(n.filter(Ie))}var h,p,g,v=e(o),m=a.invert(r[0],r[1]),y={point:i,lineStart:s,lineEnd:c,polygonStart:function(){y.point=u,y.lineStart=f,y.lineEnd=d,h=[],p=[]},polygonEnd:function(){y.point=i,y.lineStart=s,y.lineEnd=c,h=ui.merge(h);var t=He(m,p);h.length?(_||(o.polygonStart(),_=!0),Oe(h,je,t,n,o)):t&&(_||(o.polygonStart(),_=!0),o.lineStart(),n(null,null,1,o),o.lineEnd()),_&&(o.polygonEnd(),_=!1),h=p=null},sphere:function(){o.polygonStart(),o.lineStart(),n(null,null,1,o),o.lineEnd(),o.polygonEnd()}},x=Re(),b=e(x),_=!1;return y}}function Ie(t){return t.length>1}function Re(){var t,e=[];return{lineStart:function(){e.push(t=[])},point:function(e,n){t.push([e,n])},lineEnd:k,buffer:function(){var n=e;return e=[],t=null,n},rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))}}}function je(t,e){return((t=t.x)[0]<0?t[1]-Zi-qi:Zi-t[1])-((e=e.x)[0]<0?e[1]-Zi-qi:Zi-e[1])}function qe(t){var e,n=NaN,r=NaN,a=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(o,i){var l=o>0?Bi:-Bi,s=wi(o-n);wi(s-Bi)<qi?(t.point(n,r=(r+i)/2>0?Zi:-Zi),t.point(a,r),t.lineEnd(),t.lineStart(),t.point(l,r),t.point(o,r),e=0):a!==l&&s>=Bi&&(wi(n-a)<qi&&(n-=a*qi),wi(o-l)<qi&&(o-=l*qi),r=Fe(n,r,o,i),t.point(a,r),t.lineEnd(),t.lineStart(),t.point(l,r),e=0),t.point(n=o,r=i),a=l},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}function Fe(t,e,n,r){var a,o,i=Math.sin(t-n);return wi(i)>qi?Math.atan((Math.sin(e)*(o=Math.cos(r))*Math.sin(n)-Math.sin(r)*(a=Math.cos(e))*Math.sin(t))/(a*o*i)):(e+r)/2}function Be(t,e,n,r){var a;if(null==t)a=n*Zi,r.point(-Bi,a),r.point(0,a),r.point(Bi,a),r.point(Bi,0),r.point(Bi,-a),r.point(0,-a),r.point(-Bi,-a),r.point(-B"
+,
+"i,0),r.point(-Bi,a);else if(wi(t[0]-e[0])>qi){var o=t[0]<e[0]?Bi:-Bi;a=n*o/2,r.point(-o,a),r.point(0,a),r.point(o,a)}else r.point(e[0],e[1])}function He(t,e){var n=t[0],r=t[1],a=[Math.sin(n),-Math.cos(n),0],o=0,i=0;Tl.reset();for(var l=0,s=e.length;s>l;++l){var c=e[l],u=c.length;if(u)for(var f=c[0],d=f[0],h=f[1]/2+Bi/4,p=Math.sin(h),g=Math.cos(h),v=1;;){v===u&&(v=0),t=c[v];var m=t[0],y=t[1]/2+Bi/4,x=Math.sin(y),b=Math.cos(y),_=m-d,w=_>=0?1:-1,k=w*_,M=k>Bi,A=p*x;if(Tl.add(Math.atan2(A*w*Math.sin(k),g*b+A*Math.cos(k))),o+=M?_+w*Hi:_,M^d>=n^m>=n){var L=xe(me(f),me(t));we(L);var T=xe(a,L);we(T);var z=(M^_>=0?-1:1)*rt(T[2]);(r>z||r===z&&(L[0]||L[1]))&&(i+=M^_>=0?1:-1)}if(!v++)break;d=m,p=x,g=b,f=t}}return(-qi>o||qi>o&&0>Tl)^1&i}function Ve(t){function e(t,e){return Math.cos(t)*Math.cos(e)>o}function n(t){var n,o,s,c,u;return{lineStart:function(){c=s=!1,u=1},point:function(f,d){var h,p=[f,d],g=e(f,d),v=i?g?0:a(f,d):g?a(f+(0>f?Bi:-Bi),d):0;if(!n&&(c=s=g)&&t.lineStart(),g!==s&&(h=r(n,p),(Me(n,h)||Me(p,h))&&(p[0]+=qi,p[1]+=qi,g=e(p[0],p[1]))),g!==s)u=0,g?(t.lineStart(),h=r(p,n),t.point(h[0],h[1])):(h=r(n,p),t.point(h[0],h[1]),t.lineEnd()),n=h;else if(l&&n&&i^g){var m;v&o||!(m=r(p,n,!0))||(u=0,i?(t.lineStart(),t.point(m[0][0],m[0][1]),t.point(m[1][0],m[1][1]),t.lineEnd()):(t.point(m[1][0],m[1][1]),t.lineEnd(),t.lineStart(),t.point(m[0][0],m[0][1])))}!g||n&&Me(n,p)||t.point(p[0],p[1]),n=p,s=g,o=v},lineEnd:function(){s&&t.lineEnd(),n=null},clean:function(){return u|(c&&s)<<1}}}function r(t,e,n){var r=me(t),a=me(e),i=[1,0,0],l=xe(r,a),s=ye(l,l),c=l[0],u=s-c*c;if(!u)return!n&&t;var f=o*s/u,d=-o*c/u,h=xe(i,l),p=_e(i,f),g=_e(l,d);be(p,g);var v=h,m=ye(p,v),y=ye(v,v),x=m*m-y*(ye(p,p)-1);if(!(0>x)){var b=Math.sqrt(x),_=_e(v,(-m-b)/y);if(be(_,p),_=ke(_),!n)return _;var w,k=t[0],M=e[0],A=t[1],L=e[1];k>M&&(w=k,k=M,M=w);var T=M-k,z=wi(T-Bi)<qi,S=z||qi>T;if(!z&&A>L&&(w=A,A=L,L=w),S?z?A+L>0^_[1]<(wi(_[0]-k)<qi?A:L):A<=_[1]&&_[1]<=L:T>Bi^(k<=_[0]&&_[0]<=M)){var E=_e(v,(-m+b)/y);return be(E,p),[_,ke(E)]}}}function a(e,n){var r=i?t:Bi-t,a=0;return-r>e?a|=1:e>r&&(a|=2),-r>n?a|=4:n>r&&(a|=8),a}var o=Math.cos(t),i=o>0,l=wi(o)>qi,s=mn(t,6*Yi);return De(e,n,s,i?[0,-t]:[-Bi,t-Bi])}function Ze(t,e,n,r){return function(a){var o,i=a.a,l=a.b,s=i.x,c=i.y,u=l.x,f=l.y,d=0,h=1,p=u-s,g=f-c;if(o=t-s,p||!(o>0)){if(o/=p,0>p){if(d>o)return;h>o&&(h=o)}else if(p>0){if(o>h)return;o>d&&(d=o)}if(o=n-s,p||!(0>o)){if(o/=p,0>p){if(o>h)return;o>d&&(d=o)}else if(p>0){if(d>o)return;h>o&&(h=o)}if(o=e-c,g||!(o>0)){if(o/=g,0>g){if(d>o)return;h>o&&(h=o)}else if(g>0){if(o>h)return;o>d&&(d=o)}if(o=r-c,g||!(0>o)){if(o/=g,0>g){if(o>h)return;o>d&&(d=o)}else if(g>0){if(d>o)return;h>o&&(h=o)}return d>0&&(a.a={x:s+d*p,y:c+d*g}),1>h&&(a.b={x:s+h*p,y:c+h*g}),a}}}}}}function Ye(t,e,n,r){function a(r,a){return wi(r[0]-t)<qi?a>0?0:3:wi(r[0]-n)<qi?a>0?2:1:wi(r[1]-e)<qi?a>0?1:0:a>0?3:2}function o(t,e){return i(t.x,e.x)}function i(t,e){var n=a(t,1),r=a(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(l){function s(t){for(var e=0,n=v.length,r=t[1],a=0;n>a;++a)for(var o,i=1,l=v[a],s=l.length,c=l[0];s>i;++i)o=l[i],c[1]<=r?o[1]>r&&et(c,o,t)>0&&++e:o[1]<=r&&et(c,o,t)<0&&--e,c=o;return 0!==e}function c(o,l,s,c){var u=0,f=0;if(null==o||(u=a(o,s))!==(f=a(l,s))||i(o,l)<0^s>0){do c.point(0===u||3===u?t:n,u>1?r:e);while((u=(u+s+4)%4)!==f)}else c.point(l[0],l[1])}function u(a,o){return a>=t&&n>=a&&o>=e&&r>=o}function f(t,e){u(t,e)&&l.point(t,e)}function d(){S.point=p,v&&v.push(m=[]),M=!0,k=!1,_=w=NaN}function h(){g&&(p(y,x),b&&k&&T.rejoin(),g.push(T.buffer())),S.point=f,k&&l.lineEnd()}function p(t,e){t=Math.max(-Hl,Math.min(Hl,t)),e=Math.max(-Hl,Math.min(Hl,e));var n=u(t,e);if(v&&m.push([t,e]),M)y=t,x=e,b=n,M=!1,n&&(l.lineStart(),l.point(t,e));else if(n&&k)l.point(t,e);else{var r={a:{x:_,y:w},b:{x:t,y:e}};z(r)?(k||(l.lineStart(),l.point(r.a.x,r.a.y)),l.point(r.b.x,r.b.y),n||l.lineEnd(),A=!1):n&&(l.lineStart(),l.point(t,e),A=!1)}_=t,w=e,k=n}var g,v,m,y,x,b,_,w,k,M,A,L=l,T=Re(),z=Ze(t,e,n,r),S={point:f,lineStart:d,lineEnd:h,polygonStart:function(){l=T,g=[],v=[],A=!0},polygonEnd:function(){l=L,g=ui.merge(g);var e=s([t,r]),n=A&&e,a=g.length;(n||a)&&(l.polygonStart(),n&&(l.lineStart(),c(null,null,1,l),l.lineEnd()),a&&Oe(g,o,e,c,l),l.polygonEnd()),g=v=m=null}};return S}}function Ue(t){var e=0,n=Bi/3,r=cn(t),a=r(e,n);return a.parallels=function(t){return arguments.length?r(e=t[0]*Bi/180,n=t[1]*Bi/180):[e/Bi*180,n/Bi*180]},a}function Xe(t,e){function n(t,e){var n=Math.sqrt(o-2*a*Math.sin(e))/a;return[n*Math.sin(t*=a),i-n*Math.cos(t)]}var r=Math.sin(t),a=(r+Math.sin(e))/2,o=1+r*(2*a-r),i=Math.sqrt(o)/a;return n.invert=function(t,e){var n=i-e;return[Math.atan2(t,n)/a,rt((o-(t*t+n*n)*a*a)/(2*a))]},n}function Ge(){function t(t,e){Zl+=a*t-r*e,r=t,a=e}var e,n,r,a;$l.point=function(o,i){$l.point=t,e=r=o,n=a=i},$l.lineEnd=function(){t(e,n)}}function $e(t,e){Yl>t&&(Yl=t),t>Xl&&(Xl=t),Ul>e&&(Ul=e),e>Gl&&(Gl=e)}function Qe(){function t(t,e){i.push(\"M\",t,\",\",e,o)}function e(t,e){i.push(\"M\",t,\",\",e),l.point=n}function n(t,e){i.push(\"L\",t,\",\",e)}function r(){l.point=t}function a(){i.push(\"Z\")}var o=We(4.5),i=[],l={point:t,lineStart:function(){l.point=e},lineEnd:r,polygonStart:function(){l.lineEnd=a},polygonEnd:function(){l.lineEnd=r,l.point=t},pointRadius:function(t){return o=We(t),l},result:function(){if(i.length){var t=i.join(\"\");return i=[],t}}};return l}function We(t){return\"m0,\"+t+\"a\"+t+\",\"+t+\" 0 1,1 0,\"+-2*t+\"a\"+t+\",\"+t+\" 0 1,1 0,\"+2*t+\"z\"}function Je(t,e){Cl+=t,Ol+=e,++Pl}function Ke(){function t(t,r){var a=t-e,o=r-n,i=Math.sqrt(a*a+o*o);Nl+=i*(e+t)/2,Dl+=i*(n+r)/2,Il+=i,Je(e=t,n=r)}var e,n;Wl.point=function(r,a){Wl.point=t,Je(e=r,n=a)}}function tn(){Wl.point=Je}function en(){function t(t,e){var n=t-r,o=e-a,i=Math.sqrt(n*n+o*o);Nl+=i*(r+t)/2,Dl+=i*(a+e)/2,Il+=i,i=a*t-r*e,Rl+=i*(r+t),jl+=i*(a+e),ql+=3*i,Je(r=t,a=e)}var e,n,r,a;Wl.point=function(o,i){Wl.point=t,Je(e=r=o,n=a=i)},Wl.lineEnd=function(){t(e,n)}}function nn(t){function e(e,n){t.moveTo(e+i,n),t.arc(e,n,i,0,Hi)}function n(e,n){t.moveTo(e,n),l.point=r}function r(e,n){t.lineTo(e,n)}function a(){l.point=e}function o(){t.closePath()}var i=4.5,l={point:e,lineStart:function(){l.point=n},lineEnd:a,polygonStart:function(){l.lineEnd=o},polygonEnd:function(){l.lineEnd=a,l.point=e},pointRadius:function(t){return i=t,l},result:k};return l}function rn(t){function e(t){return(l?r:n)(t)}function n(e){return ln(e,function(n,r){n=t(n,r),e.point(n[0],n[1])})}function r(e){function n(n,r){n=t(n,r),e.point(n[0],n[1])}function r(){x=NaN,M.point=o,e.lineStart()}function o(n,r){var o=me([n,r]),i=t(n,r);a(x,b,y,_,w,k,x=i[0],b=i[1],y=n,_=o[0],w=o[1],k=o[2],l,e),e.point(x,b)}function i(){M.point=n,e.lineEnd()}function s(){r(),M.point=c,M.lineEnd=u}function c(t,e){o(f=t,d=e),h=x,p=b,g=_,v=w,m=k,M.point=o}function u(){a(x,b,y,_,w,k,h,p,f,g,v,m,l,e),M.lineEnd=i,i()}var f,d,h,p,g,v,m,y,x,b,_,w,k,M={point:n,lineStart:r,lineEnd:i,polygonStart:function(){e.polygonStart(),M.lineStart=s},polygonEnd:function(){e.polygonEnd(),M.lineStart=r}};return M}function a(e,n,r,l,s,c,u,f,d,h,p,g,v,m){var y=u-e,x=f-n,b=y*y+x*x;if(b>4*o&&v--){var _=l+h,w=s+p,k=c+g,M=Math.sqrt(_*_+w*w+k*k),A=Math.asin(k/=M),L=wi(wi(k)-1)<qi||wi(r-d)<qi?(r+d)/2:Math.atan2(w,_),T=t(L,A),z=T[0],S=T[1],E=z-e,C=S-n,O=x*E-y*C;(O*O/b>o||wi((y*E+x*C)/b-.5)>.3||i>l*h+s*p+c*g)&&(a(e,n,r,l,s,c,z,S,L,_/=M,w/=M,k,v,m),m.point(z,S),a(z,S,L,_,w,k,u,f,d,h,p,g,v,m))}}var o=.5,i=Math.cos(30*Yi),l=16;return e.precision=function(t){return arguments.length?(l=(o=t*t)>0&&16,e):Math.sqrt(o)},e}function an(t){var e=rn(function(e,n){return t([e*Ui,n*Ui])});return function(t){return un(e(t))}}function on(t){this.stream=t}function ln(t,e){return{point:e,sphere:function(){t.sphere()},lineStart:function(){t.lineStart()},lineEnd:function(){t.lineEnd()},polygonStart:function(){t.polygonStart()},polygonEnd:function(){t.polygonEnd()}}}function sn(t){return cn(function(){return t})()}function cn(t){function e(t){return t=l(t[0]*Yi,t[1]*Yi),[t[0]*d+s,c-t[1]*d]}function n(t){return t=l.invert((t[0]-s)/d,(c-t[1])/d),t&&[t[0]*Ui,t[1]*Ui]}function r(){l=Ee(i=hn(m,y,x),o);var t=o(g,v);return s=h-t[0]*d,c=p+t[1]*d,a()}function a(){return u&&(u.valid=!1,u=null),e}var o,i,l,s,c,u,f=rn(function(t,e){return t=o(t,e),[t[0]*d+s,c-t[1]*d]}),d=150,h=480,p=250,g=0,v=0,m=0,y=0,x=0,_=Bl,w=b,k=null,M=null;return e.stream=function(t){return u&&(u.valid=!1),u=un(_(i,f(w(t)))),u.valid=!0,u},e.clipAngle=function(t){return arguments.length?(_=null==t?(k=t,Bl):Ve((k=+t)*Yi),a()):k},e.clipExtent=function(t){return arguments.length?(M=t,w=t?Ye(t[0][0],t[0][1],t[1][0],t[1][1]):b,a()):M},e.scale=function(t){return arguments.length?(d=+t,r()):d},e.translate=function(t){return arguments.length?(h=+t[0],p=+t[1],r()):[h,p]},e.center=function(t){return arguments.length?(g=t[0]%360*Yi,v=t[1]%360*Yi,r()):[g*Ui,v*Ui]},e.rotate=function(t){return arguments.length?(m=t[0]%360*Yi,y=t[1]%360*Yi,x=t.length>2?t[2]%360*Yi:0,r()):[m*Ui,y*Ui,x*Ui]},ui.rebind(e,f,\"precision\"),function(){return o=t.apply(this,arguments),e.invert=o.invert&&n,r()}}function un(t){return ln(t,function(e,n){t.point(e*Yi,n*Yi)})}function fn(t,e){return[t,e]}function dn(t,e){return[t>Bi?t-Hi:-Bi>t?t+Hi:t,e]}function hn(t,e,n){return t?e||n?Ee(gn(t),vn(e,n)):gn(t):e||n?vn(e,n):dn}function pn(t){return function(e,n){return e+=t,[e>Bi?e-Hi:-Bi>e?e+Hi:e,n]}}function gn(t){var e=pn(t);return e.invert=pn(-t),e}function vn(t,e){function n(t,e){var n=Math.cos(e),l=Math.cos(t)*n,s=Math.sin(t)*n,c=Math.sin(e),u=c*r+l*a;return[Math.atan2(s*o-u*i,l*r-c*a),rt(u*o+s*i)]}var r=Math.cos(t),a=Math.sin(t),o=Math.cos(e),i=Math.sin(e);return n.invert=function(t,e){var n=Math.cos(e),l=Math.cos(t)*n,s=Math.sin(t)*n,c=Math.sin(e),u=c*o-s*i;return[Math.atan2(s*o+c*i,l*r+u*a),rt(u*r-l*a)]},n}function mn(t,e){var n=Math.cos(t),r=Math.sin(t);return function(a,o,i,l){var s=i*e;null!=a?(a=yn(n,a),o=yn(n,o),(i>0?o>a:a>o)&&(a+=i*Hi)):(a=t+i*Hi,o=t-.5*s);for(var c,u=a;i>0?u>o:o>u;u-=s)l.point((c=ke([n,-r*Math."
+,
+"cos(u),-r*Math.sin(u)]))[0],c[1])}}function yn(t,e){var n=me(e);n[0]-=t,we(n);var r=nt(-n[1]);return((-n[2]<0?-r:r)+2*Math.PI-qi)%(2*Math.PI)}function xn(t,e,n){var r=ui.range(t,e-qi,n).concat(e);return function(t){return r.map(function(e){return[t,e]})}}function bn(t,e,n){var r=ui.range(t,e-qi,n).concat(e);return function(t){return r.map(function(e){return[e,t]})}}function _n(t){return t.source}function wn(t){return t.target}function kn(t,e,n,r){var a=Math.cos(e),o=Math.sin(e),i=Math.cos(r),l=Math.sin(r),s=a*Math.cos(t),c=a*Math.sin(t),u=i*Math.cos(n),f=i*Math.sin(n),d=2*Math.asin(Math.sqrt(lt(r-e)+a*i*lt(n-t))),h=1/Math.sin(d),p=d?function(t){var e=Math.sin(t*=d)*h,n=Math.sin(d-t)*h,r=n*s+e*u,a=n*c+e*f,i=n*o+e*l;return[Math.atan2(a,r)*Ui,Math.atan2(i,Math.sqrt(r*r+a*a))*Ui]}:function(){return[t*Ui,e*Ui]};return p.distance=d,p}function Mn(){function t(t,a){var o=Math.sin(a*=Yi),i=Math.cos(a),l=wi((t*=Yi)-e),s=Math.cos(l);Jl+=Math.atan2(Math.sqrt((l=i*Math.sin(l))*l+(l=r*o-n*i*s)*l),n*o+r*i*s),e=t,n=o,r=i}var e,n,r;Kl.point=function(a,o){e=a*Yi,n=Math.sin(o*=Yi),r=Math.cos(o),Kl.point=t},Kl.lineEnd=function(){Kl.point=Kl.lineEnd=k}}function An(t,e){function n(e,n){var r=Math.cos(e),a=Math.cos(n),o=t(r*a);return[o*a*Math.sin(e),o*Math.sin(n)]}return n.invert=function(t,n){var r=Math.sqrt(t*t+n*n),a=e(r),o=Math.sin(a),i=Math.cos(a);return[Math.atan2(t*o,r*i),Math.asin(r&&n*o/r)]},n}function Ln(t,e){function n(t,e){i>0?-Zi+qi>e&&(e=-Zi+qi):e>Zi-qi&&(e=Zi-qi);var n=i/Math.pow(a(e),o);return[n*Math.sin(o*t),i-n*Math.cos(o*t)]}var r=Math.cos(t),a=function(t){return Math.tan(Bi/4+t/2)},o=t===e?Math.sin(t):Math.log(r/Math.cos(e))/Math.log(a(e)/a(t)),i=r*Math.pow(a(t),o)/o;return o?(n.invert=function(t,e){var n=i-e,r=tt(o)*Math.sqrt(t*t+n*n);return[Math.atan2(t,n)/o,2*Math.atan(Math.pow(i/r,1/o))-Zi]},n):zn}function Tn(t,e){function n(t,e){var n=o-e;return[n*Math.sin(a*t),o-n*Math.cos(a*t)]}var r=Math.cos(t),a=t===e?Math.sin(t):(r-Math.cos(e))/(e-t),o=r/a+t;return wi(a)<qi?fn:(n.invert=function(t,e){var n=o-e;return[Math.atan2(t,n)/a,o-tt(a)*Math.sqrt(t*t+n*n)]},n)}function zn(t,e){return[t,Math.log(Math.tan(Bi/4+e/2))]}function Sn(t){var e,n=sn(t),r=n.scale,a=n.translate,o=n.clipExtent;return n.scale=function(){var t=r.apply(n,arguments);return t===n?e?n.clipExtent(null):n:t},n.translate=function(){var t=a.apply(n,arguments);return t===n?e?n.clipExtent(null):n:t},n.clipExtent=function(t){var i=o.apply(n,arguments);if(i===n){if(e=null==t){var l=Bi*r(),s=a();o([[s[0]-l,s[1]-l],[s[0]+l,s[1]+l]])}}else e&&(i=null);return i},n.clipExtent(null)}function En(t,e){return[Math.log(Math.tan(Bi/4+e/2)),-t]}function Cn(t){return t[0]}function On(t){return t[1]}function Pn(t){for(var e=t.length,n=[0,1],r=2,a=2;e>a;a++){for(;r>1&&et(t[n[r-2]],t[n[r-1]],t[a])<=0;)--r;n[r++]=a}return n.slice(0,r)}function Nn(t,e){return t[0]-e[0]||t[1]-e[1]}function Dn(t,e,n){return(n[0]-e[0])*(t[1]-e[1])<(n[1]-e[1])*(t[0]-e[0])}function In(t,e,n,r){var a=t[0],o=n[0],i=e[0]-a,l=r[0]-o,s=t[1],c=n[1],u=e[1]-s,f=r[1]-c,d=(l*(s-c)-f*(a-o))/(f*i-l*u);return[a+d*i,s+d*u]}function Rn(t){var e=t[0],n=t[t.length-1];return!(e[0]-n[0]||e[1]-n[1])}function jn(){or(this),this.edge=this.site=this.circle=null}function qn(t){var e=fs.pop()||new jn;return e.site=t,e}function Fn(t){Qn(t),ss.remove(t),fs.push(t),or(t)}function Bn(t){var e=t.circle,n=e.x,r=e.cy,a={x:n,y:r},o=t.P,i=t.N,l=[t];Fn(t);for(var s=o;s.circle&&wi(n-s.circle.x)<qi&&wi(r-s.circle.cy)<qi;)o=s.P,l.unshift(s),Fn(s),s=o;l.unshift(s),Qn(s);for(var c=i;c.circle&&wi(n-c.circle.x)<qi&&wi(r-c.circle.cy)<qi;)i=c.N,l.push(c),Fn(c),c=i;l.push(c),Qn(c);var u,f=l.length;for(u=1;f>u;++u)c=l[u],s=l[u-1],nr(c.edge,s.site,c.site,a);s=l[0],c=l[f-1],c.edge=tr(s.site,c.site,null,a),$n(s),$n(c)}function Hn(t){for(var e,n,r,a,o=t.x,i=t.y,l=ss._;l;)if(r=Vn(l,i)-o,r>qi)l=l.L;else{if(a=o-Zn(l,i),!(a>qi)){r>-qi?(e=l.P,n=l):a>-qi?(e=l,n=l.N):e=n=l;break}if(!l.R){e=l;break}l=l.R}var s=qn(t);if(ss.insert(e,s),e||n){if(e===n)return Qn(e),n=qn(e.site),ss.insert(s,n),s.edge=n.edge=tr(e.site,s.site),$n(e),void $n(n);if(!n)return void(s.edge=tr(e.site,s.site));Qn(e),Qn(n);var c=e.site,u=c.x,f=c.y,d=t.x-u,h=t.y-f,p=n.site,g=p.x-u,v=p.y-f,m=2*(d*v-h*g),y=d*d+h*h,x=g*g+v*v,b={x:(v*y-h*x)/m+u,y:(d*x-g*y)/m+f};nr(n.edge,c,p,b),s.edge=tr(c,t,null,b),n.edge=tr(t,p,null,b),$n(e),$n(n)}}function Vn(t,e){var n=t.site,r=n.x,a=n.y,o=a-e;if(!o)return r;var i=t.P;if(!i)return-(1/0);n=i.site;var l=n.x,s=n.y,c=s-e;if(!c)return l;var u=l-r,f=1/o-1/c,d=u/c;return f?(-d+Math.sqrt(d*d-2*f*(u*u/(-2*c)-s+c/2+a-o/2)))/f+r:(r+l)/2}function Zn(t,e){var n=t.N;if(n)return Vn(n,e);var r=t.site;return r.y===e?r.x:1/0}function Yn(t){this.site=t,this.edges=[]}function Un(t){for(var e,n,r,a,o,i,l,s,c,u,f=t[0][0],d=t[1][0],h=t[0][1],p=t[1][1],g=ls,v=g.length;v--;)if(o=g[v],o&&o.prepare())for(l=o.edges,s=l.length,i=0;s>i;)u=l[i].end(),r=u.x,a=u.y,c=l[++i%s].start(),e=c.x,n=c.y,(wi(r-e)>qi||wi(a-n)>qi)&&(l.splice(i,0,new rr(er(o.site,u,wi(r-f)<qi&&p-a>qi?{x:f,y:wi(e-f)<qi?n:p}:wi(a-p)<qi&&d-r>qi?{x:wi(n-p)<qi?e:d,y:p}:wi(r-d)<qi&&a-h>qi?{x:d,y:wi(e-d)<qi?n:h}:wi(a-h)<qi&&r-f>qi?{x:wi(n-h)<qi?e:f,y:h}:null),o.site,null)),++s)}function Xn(t,e){return e.angle-t.angle}function Gn(){or(this),this.x=this.y=this.arc=this.site=this.cy=null}function $n(t){var e=t.P,n=t.N;if(e&&n){var r=e.site,a=t.site,o=n.site;if(r!==o){var i=a.x,l=a.y,s=r.x-i,c=r.y-l,u=o.x-i,f=o.y-l,d=2*(s*f-c*u);if(!(d>=-Fi)){var h=s*s+c*c,p=u*u+f*f,g=(f*h-c*p)/d,v=(s*p-u*h)/d,f=v+l,m=ds.pop()||new Gn;m.arc=t,m.site=a,m.x=g+i,m.y=f+Math.sqrt(g*g+v*v),m.cy=f,t.circle=m;for(var y=null,x=us._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}us.insert(y,m),y||(cs=m)}}}}function Qn(t){var e=t.circle;e&&(e.P||(cs=e.N),us.remove(e),ds.push(e),or(e),t.circle=null)}function Wn(t){for(var e,n=is,r=Ze(t[0][0],t[0][1],t[1][0],t[1][1]),a=n.length;a--;)e=n[a],(!Jn(e,t)||!r(e)||wi(e.a.x-e.b.x)<qi&&wi(e.a.y-e.b.y)<qi)&&(e.a=e.b=null,n.splice(a,1))}function Jn(t,e){var n=t.b;if(n)return!0;var r,a,o=t.a,i=e[0][0],l=e[1][0],s=e[0][1],c=e[1][1],u=t.l,f=t.r,d=u.x,h=u.y,p=f.x,g=f.y,v=(d+p)/2,m=(h+g)/2;if(g===h){if(i>v||v>=l)return;if(d>p){if(o){if(o.y>=c)return}else o={x:v,y:s};n={x:v,y:c}}else{if(o){if(o.y<s)return}else o={x:v,y:c};n={x:v,y:s}}}else if(r=(d-p)/(g-h),a=m-r*v,-1>r||r>1)if(d>p){if(o){if(o.y>=c)return}else o={x:(s-a)/r,y:s};n={x:(c-a)/r,y:c}}else{if(o){if(o.y<s)return}else o={x:(c-a)/r,y:c};n={x:(s-a)/r,y:s}}else if(g>h){if(o){if(o.x>=l)return}else o={x:i,y:r*i+a};n={x:l,y:r*l+a}}else{if(o){if(o.x<i)return}else o={x:l,y:r*l+a};n={x:i,y:r*i+a}}return t.a=o,t.b=n,!0}function Kn(t,e){this.l=t,this.r=e,this.a=this.b=null}function tr(t,e,n,r){var a=new Kn(t,e);return is.push(a),n&&nr(a,t,e,n),r&&nr(a,e,t,r),ls[t.i].edges.push(new rr(a,t,e)),ls[e.i].edges.push(new rr(a,e,t)),a}function er(t,e,n){var r=new Kn(t,null);return r.a=e,r.b=n,is.push(r),r}function nr(t,e,n,r){t.a||t.b?t.l===n?t.b=r:t.a=r:(t.a=r,t.l=e,t.r=n)}function rr(t,e,n){var r=t.a,a=t.b;this.edge=t,this.site=e,this.angle=n?Math.atan2(n.y-e.y,n.x-e.x):t.l===e?Math.atan2(a.x-r.x,r.y-a.y):Math.atan2(r.x-a.x,a.y-r.y)}function ar(){this._=null}function or(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function ir(t,e){var n=e,r=e.R,a=n.U;a?a.L===n?a.L=r:a.R=r:t._=r,r.U=a,n.U=r,n.R=r.L,n.R&&(n.R.U=n),r.L=n}function lr(t,e){var n=e,r=e.L,a=n.U;a?a.L===n?a.L=r:a.R=r:t._=r,r.U=a,n.U=r,n.L=r.R,n.L&&(n.L.U=n),r.R=n}function sr(t){for(;t.L;)t=t.L;return t}function cr(t,e){var n,r,a,o=t.sort(ur).pop();for(is=[],ls=new Array(t.length),ss=new ar,us=new ar;;)if(a=cs,o&&(!a||o.y<a.y||o.y===a.y&&o.x<a.x))o.x===n&&o.y===r||(ls[o.i]=new Yn(o),Hn(o),n=o.x,r=o.y),o=t.pop();else{if(!a)break;Bn(a.arc)}e&&(Wn(e),Un(e));var i={cells:ls,edges:is};return ss=us=is=ls=null,i}function ur(t,e){return e.y-t.y||e.x-t.x}function fr(t,e,n){return(t.x-n.x)*(e.y-t.y)-(t.x-e.x)*(n.y-t.y)}function dr(t){return t.x}function hr(t){return t.y}function pr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function gr(t,e,n,r,a,o){if(!t(e,n,r,a,o)){var i=.5*(n+a),l=.5*(r+o),s=e.nodes;s[0]&&gr(t,s[0],n,r,i,l),s[1]&&gr(t,s[1],i,r,a,l),s[2]&&gr(t,s[2],n,l,i,o),s[3]&&gr(t,s[3],i,l,a,o)}}function vr(t,e,n,r,a,o,i){var l,s=1/0;return function c(t,u,f,d,h){if(!(u>o||f>i||r>d||a>h)){if(p=t.point){var p,g=e-t.x,v=n-t.y,m=g*g+v*v;if(s>m){var y=Math.sqrt(s=m);r=e-y,a=n-y,o=e+y,i=n+y,l=p}}for(var x=t.nodes,b=.5*(u+d),_=.5*(f+h),w=e>=b,k=n>=_,M=k<<1|w,A=M+4;A>M;++M)if(t=x[3&M])switch(3&M){case 0:c(t,u,f,b,_);break;case 1:c(t,b,f,d,_);break;case 2:c(t,u,_,b,h);break;case 3:c(t,b,_,d,h)}}}(t,r,a,o,i),l}function mr(t,e){t=ui.rgb(t),e=ui.rgb(e);var n=t.r,r=t.g,a=t.b,o=e.r-n,i=e.g-r,l=e.b-a;return function(t){return\"#\"+wt(Math.round(n+o*t))+wt(Math.round(r+i*t))+wt(Math.round(a+l*t))}}function yr(t,e){var n,r={},a={};for(n in t)n in e?r[n]=_r(t[n],e[n]):a[n]=t[n];for(n in e)n in t||(a[n]=e[n]);return function(t){for(n in r)a[n]=r[n](t);return a}}function xr(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}function br(t,e){var n,r,a,o=ps.lastIndex=gs.lastIndex=0,i=-1,l=[],s=[];for(t+=\"\",e+=\"\";(n=ps.exec(t))&&(r=gs.exec(e));)(a=r.index)>o&&(a=e.slice(o,a),l[i]?l[i]+=a:l[++i]=a),(n=n[0])===(r=r[0])?l[i]?l[i]+=r:l[++i]=r:(l[++i]=null,s.push({i:i,x:xr(n,r)})),o=gs.lastIndex;return o<e.length&&(a=e.slice(o),l[i]?l[i]+=a:l[++i]=a),l.length<2?s[0]?(e=s[0].x,function(t){return e(t)+\"\"}):function(){return e}:(e=s.length,function(t){for(var n,r=0;e>r;++r)l[(n=s[r]).i]=n.x(t);return l.join(\"\")})}function _r(t,e){for(var n,r=ui.interpolators.length;--r>=0&&!(n=ui.interpolators[r](t,e)););return n}function wr(t,e){var n,r=[],a=[],o=t.length,i=e.length,l=Math.min(t.length,e.length);for(n=0;l>n;++n)r.push(_r(t[n],e[n]));for(;o>n;++n)a[n]=t[n];for(;i>n;++n)a[n]=e[n];return function(t){for(n=0;l>n;++n)a[n]=r[n](t);return a}}function kr(t){return function(e){return 0>=e?0:e>=1?1:t(e)}}function Mr(t){return function(e){return 1-t(1-e)}}function Ar(t){return function(e){return.5*(.5>e?t(2*e):2-t(2-2*e))}"
+,
+"}function Lr(t){return t*t}function Tr(t){return t*t*t}function zr(t){if(0>=t)return 0;if(t>=1)return 1;var e=t*t,n=e*t;return 4*(.5>t?n:3*(t-e)+n-.75)}function Sr(t){return function(e){return Math.pow(e,t)}}function Er(t){return 1-Math.cos(t*Zi)}function Cr(t){return Math.pow(2,10*(t-1))}function Or(t){return 1-Math.sqrt(1-t*t)}function Pr(t,e){var n;return arguments.length<2&&(e=.45),arguments.length?n=e/Hi*Math.asin(1/t):(t=1,n=e/4),function(r){return 1+t*Math.pow(2,-10*r)*Math.sin((r-n)*Hi/e)}}function Nr(t){return t||(t=1.70158),function(e){return e*e*((t+1)*e-t)}}function Dr(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}function Ir(t,e){t=ui.hcl(t),e=ui.hcl(e);var n=t.h,r=t.c,a=t.l,o=e.h-n,i=e.c-r,l=e.l-a;return isNaN(i)&&(i=0,r=isNaN(r)?e.c:r),isNaN(o)?(o=0,n=isNaN(n)?e.h:n):o>180?o-=360:-180>o&&(o+=360),function(t){return dt(n+o*t,r+i*t,a+l*t)+\"\"}}function Rr(t,e){t=ui.hsl(t),e=ui.hsl(e);var n=t.h,r=t.s,a=t.l,o=e.h-n,i=e.s-r,l=e.l-a;return isNaN(i)&&(i=0,r=isNaN(r)?e.s:r),isNaN(o)?(o=0,n=isNaN(n)?e.h:n):o>180?o-=360:-180>o&&(o+=360),function(t){return ut(n+o*t,r+i*t,a+l*t)+\"\"}}function jr(t,e){t=ui.lab(t),e=ui.lab(e);var n=t.l,r=t.a,a=t.b,o=e.l-n,i=e.a-r,l=e.b-a;return function(t){return pt(n+o*t,r+i*t,a+l*t)+\"\"}}function qr(t,e){return e-=t,function(n){return Math.round(t+e*n)}}function Fr(t){var e=[t.a,t.b],n=[t.c,t.d],r=Hr(e),a=Br(e,n),o=Hr(Vr(n,e,-a))||0;e[0]*n[1]<n[0]*e[1]&&(e[0]*=-1,e[1]*=-1,r*=-1,a*=-1),this.rotate=(r?Math.atan2(e[1],e[0]):Math.atan2(-n[0],n[1]))*Ui,this.translate=[t.e,t.f],this.scale=[r,o],this.skew=o?Math.atan2(a,o)*Ui:0}function Br(t,e){return t[0]*e[0]+t[1]*e[1]}function Hr(t){var e=Math.sqrt(Br(t,t));return e&&(t[0]/=e,t[1]/=e),e}function Vr(t,e,n){return t[0]+=n*e[0],t[1]+=n*e[1],t}function Zr(t){return t.length?t.pop()+\",\":\"\"}function Yr(t,e,n,r){if(t[0]!==e[0]||t[1]!==e[1]){var a=n.push(\"translate(\",null,\",\",null,\")\");r.push({i:a-4,x:xr(t[0],e[0])\n"
+,
+"},{i:a-2,x:xr(t[1],e[1])})}else(e[0]||e[1])&&n.push(\"translate(\"+e+\")\")}function Ur(t,e,n,r){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),r.push({i:n.push(Zr(n)+\"rotate(\",null,\")\")-2,x:xr(t,e)})):e&&n.push(Zr(n)+\"rotate(\"+e+\")\")}function Xr(t,e,n,r){t!==e?r.push({i:n.push(Zr(n)+\"skewX(\",null,\")\")-2,x:xr(t,e)}):e&&n.push(Zr(n)+\"skewX(\"+e+\")\")}function Gr(t,e,n,r){if(t[0]!==e[0]||t[1]!==e[1]){var a=n.push(Zr(n)+\"scale(\",null,\",\",null,\")\");r.push({i:a-4,x:xr(t[0],e[0])},{i:a-2,x:xr(t[1],e[1])})}else 1===e[0]&&1===e[1]||n.push(Zr(n)+\"scale(\"+e+\")\")}function $r(t,e){var n=[],r=[];return t=ui.transform(t),e=ui.transform(e),Yr(t.translate,e.translate,n,r),Ur(t.rotate,e.rotate,n,r),Xr(t.skew,e.skew,n,r),Gr(t.scale,e.scale,n,r),t=e=null,function(t){for(var e,a=-1,o=r.length;++a<o;)n[(e=r[a]).i]=e.x(t);return n.join(\"\")}}function Qr(t,e){return e=(e-=t=+t)||1/e,function(n){return(n-t)/e}}function Wr(t,e){return e=(e-=t=+t)||1/e,function(n){return Math.max(0,Math.min(1,(n-t)/e))}}function Jr(t){for(var e=t.source,n=t.target,r=ta(e,n),a=[e];e!==r;)e=e.parent,a.push(e);for(var o=a.length;n!==r;)a.splice(o,0,n),n=n.parent;return a}function Kr(t){for(var e=[],n=t.parent;null!=n;)e.push(t),t=n,n=n.parent;return e.push(t),e}function ta(t,e){if(t===e)return t;for(var n=Kr(t),r=Kr(e),a=n.pop(),o=r.pop(),i=null;a===o;)i=a,a=n.pop(),o=r.pop();return i}function ea(t){t.fixed|=2}function na(t){t.fixed&=-7}function ra(t){t.fixed|=4,t.px=t.x,t.py=t.y}function aa(t){t.fixed&=-5}function oa(t,e,n){var r=0,a=0;if(t.charge=0,!t.leaf)for(var o,i=t.nodes,l=i.length,s=-1;++s<l;)o=i[s],null!=o&&(oa(o,e,n),t.charge+=o.charge,r+=o.charge*o.cx,a+=o.charge*o.cy);if(t.point){t.leaf||(t.point.x+=Math.random()-.5,t.point.y+=Math.random()-.5);var c=e*n[t.point.index];t.charge+=t.pointCharge=c,r+=c*t.point.x,a+=c*t.point.y}t.cx=r/t.charge,t.cy=a/t.charge}function ia(t,e){return ui.rebind(t,e,\"sort\",\"children\",\"value\"),t.nodes=t,t.links=da,t}function la(t,e){for(var n=[t];null!=(t=n.pop());)if(e(t),(a=t.children)&&(r=a.length))for(var r,a;--r>=0;)n.push(a[r])}function sa(t,e){for(var n=[t],r=[];null!=(t=n.pop());)if(r.push(t),(o=t.children)&&(a=o.length))for(var a,o,i=-1;++i<a;)n.push(o[i]);for(;null!=(t=r.pop());)e(t)}function ca(t){return t.children}function ua(t){return t.value}function fa(t,e){return e.value-t.value}function da(t){return ui.merge(t.map(function(t){return(t.children||[]).map(function(e){return{source:t,target:e}})}))}function ha(t){return t.x}function pa(t){return t.y}function ga(t,e,n){t.y0=e,t.y=n}function va(t){return ui.range(t.length)}function ma(t){for(var e=-1,n=t[0].length,r=[];++e<n;)r[e]=0;return r}function ya(t){for(var e,n=1,r=0,a=t[0][1],o=t.length;o>n;++n)(e=t[n][1])>a&&(r=n,a=e);return r}function xa(t){return t.reduce(ba,0)}function ba(t,e){return t+e[1]}function _a(t,e){return wa(t,Math.ceil(Math.log(e.length)/Math.LN2+1))}function wa(t,e){for(var n=-1,r=+t[0],a=(t[1]-r)/e,o=[];++n<=e;)o[n]=a*n+r;return o}function ka(t){return[ui.min(t),ui.max(t)]}function Ma(t,e){return t.value-e.value}function Aa(t,e){var n=t._pack_next;t._pack_next=e,e._pack_prev=t,e._pack_next=n,n._pack_prev=e}function La(t,e){t._pack_next=e,e._pack_prev=t}function Ta(t,e){var n=e.x-t.x,r=e.y-t.y,a=t.r+e.r;return.999*a*a>n*n+r*r}function za(t){function e(t){u=Math.min(t.x-t.r,u),f=Math.max(t.x+t.r,f),d=Math.min(t.y-t.r,d),h=Math.max(t.y+t.r,h)}if((n=t.children)&&(c=n.length)){var n,r,a,o,i,l,s,c,u=1/0,f=-(1/0),d=1/0,h=-(1/0);if(n.forEach(Sa),r=n[0],r.x=-r.r,r.y=0,e(r),c>1&&(a=n[1],a.x=a.r,a.y=0,e(a),c>2))for(o=n[2],Oa(r,a,o),e(o),Aa(r,o),r._pack_prev=o,Aa(o,a),a=r._pack_next,i=3;c>i;i++){Oa(r,a,o=n[i]);var p=0,g=1,v=1;for(l=a._pack_next;l!==a;l=l._pack_next,g++)if(Ta(l,o)){p=1;break}if(1==p)for(s=r._pack_prev;s!==l._pack_prev&&!Ta(s,o);s=s._pack_prev,v++);p?(v>g||g==v&&a.r<r.r?La(r,a=l):La(r=s,a),i--):(Aa(r,o),a=o,e(o))}var m=(u+f)/2,y=(d+h)/2,x=0;for(i=0;c>i;i++)o=n[i],o.x-=m,o.y-=y,x=Math.max(x,o.r+Math.sqrt(o.x*o.x+o.y*o.y));t.r=x,n.forEach(Ea)}}function Sa(t){t._pack_next=t._pack_prev=t}function Ea(t){delete t._pack_next,delete t._pack_prev}function Ca(t,e,n,r){var a=t.children;if(t.x=e+=r*t.x,t.y=n+=r*t.y,t.r*=r,a)for(var o=-1,i=a.length;++o<i;)Ca(a[o],e,n,r)}function Oa(t,e,n){var r=t.r+n.r,a=e.x-t.x,o=e.y-t.y;if(r&&(a||o)){var i=e.r+n.r,l=a*a+o*o;i*=i,r*=r;var s=.5+(r-i)/(2*l),c=Math.sqrt(Math.max(0,2*i*(r+l)-(r-=l)*r-i*i))/(2*l);n.x=t.x+s*a+c*o,n.y=t.y+s*o-c*a}else n.x=t.x+r,n.y=t.y}function Pa(t,e){return t.parent==e.parent?1:2}function Na(t){var e=t.children;return e.length?e[0]:t.t}function Da(t){var e,n=t.children;return(e=n.length)?n[e-1]:t.t}function Ia(t,e,n){var r=n/(e.i-t.i);e.c-=r,e.s+=n,t.c+=r,e.z+=n,e.m+=n}function Ra(t){for(var e,n=0,r=0,a=t.children,o=a.length;--o>=0;)e=a[o],e.z+=n,e.m+=n,n+=e.s+(r+=e.c)}function ja(t,e,n){return t.a.parent===e.parent?t.a:n}function qa(t){return 1+ui.max(t,function(t){return t.y})}function Fa(t){return t.reduce(function(t,e){return t+e.x},0)/t.length}function Ba(t){var e=t.children;return e&&e.length?Ba(e[0]):t}function Ha(t){var e,n=t.children;return n&&(e=n.length)?Ha(n[e-1]):t}function Va(t){return{x:t.x,y:t.y,dx:t.dx,dy:t.dy}}function Za(t,e){var n=t.x+e[3],r=t.y+e[0],a=t.dx-e[1]-e[3],o=t.dy-e[0]-e[2];return 0>a&&(n+=a/2,a=0),0>o&&(r+=o/2,o=0),{x:n,y:r,dx:a,dy:o}}function Ya(t){var e=t[0],n=t[t.length-1];return n>e?[e,n]:[n,e]}function Ua(t){return t.rangeExtent?t.rangeExtent():Ya(t.range())}function Xa(t,e,n,r){var a=n(t[0],t[1]),o=r(e[0],e[1]);return function(t){return o(a(t))}}function Ga(t,e){var n,r=0,a=t.length-1,o=t[r],i=t[a];return o>i&&(n=r,r=a,a=n,n=o,o=i,i=n),t[r]=e.floor(o),t[a]=e.ceil(i),t}function $a(t){return t?{floor:function(e){return Math.floor(e/t)*t},ceil:function(e){return Math.ceil(e/t)*t}}:Ls}function Qa(t,e,n,r){var a=[],o=[],i=0,l=Math.min(t.length,e.length)-1;for(t[l]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++i<=l;)a.push(n(t[i-1],t[i])),o.push(r(e[i-1],e[i]));return function(e){var n=ui.bisect(t,e,1,l)-1;return o[n](a[n](e))}}function Wa(t,e,n,r){function a(){var a=Math.min(t.length,e.length)>2?Qa:Xa,s=r?Wr:Qr;return i=a(t,e,s,n),l=a(e,t,s,_r),o}function o(t){return i(t)}var i,l;return o.invert=function(t){return l(t)},o.domain=function(e){return arguments.length?(t=e.map(Number),a()):t},o.range=function(t){return arguments.length?(e=t,a()):e},o.rangeRound=function(t){return o.range(t).interpolate(qr)},o.clamp=function(t){return arguments.length?(r=t,a()):r},o.interpolate=function(t){return arguments.length?(n=t,a()):n},o.ticks=function(e){return eo(t,e)},o.tickFormat=function(e,n){return no(t,e,n)},o.nice=function(e){return Ka(t,e),a()},o.copy=function(){return Wa(t,e,n,r)},a()}function Ja(t,e){return ui.rebind(t,e,\"range\",\"rangeRound\",\"interpolate\",\"clamp\")}function Ka(t,e){return Ga(t,$a(to(t,e)[2])),Ga(t,$a(to(t,e)[2])),t}function to(t,e){null==e&&(e=10);var n=Ya(t),r=n[1]-n[0],a=Math.pow(10,Math.floor(Math.log(r/e)/Math.LN10)),o=e/r*a;return.15>=o?a*=10:.35>=o?a*=5:.75>=o&&(a*=2),n[0]=Math.ceil(n[0]/a)*a,n[1]=Math.floor(n[1]/a)*a+.5*a,n[2]=a,n}function eo(t,e){return ui.range.apply(ui,to(t,e))}function no(t,e,n){var r=to(t,e);if(n){var a=pl.exec(n);if(a.shift(),\"s\"===a[8]){var o=ui.formatPrefix(Math.max(wi(r[0]),wi(r[1])));return a[7]||(a[7]=\".\"+ro(o.scale(r[2]))),a[8]=\"f\",n=ui.format(a.join(\"\")),function(t){return n(o.scale(t))+o.symbol}}a[7]||(a[7]=\".\"+ao(a[8],r)),n=a.join(\"\")}else n=\",.\"+ro(r[2])+\"f\";return ui.format(n)}function ro(t){return-Math.floor(Math.log(t)/Math.LN10+.01)}function ao(t,e){var n=ro(e[2]);return t in Ts?Math.abs(n-ro(Math.max(wi(e[0]),wi(e[1]))))+ +(\"e\"!==t):n-2*(\"%\"===t)}function oo(t,e,n,r){function a(t){return(n?Math.log(0>t?0:t):-Math.log(t>0?0:-t))/Math.log(e)}function o(t){return n?Math.pow(e,t):-Math.pow(e,-t)}function i(e){return t(a(e))}return i.invert=function(e){return o(t.invert(e))},i.domain=function(e){return arguments.length?(n=e[0]>=0,t.domain((r=e.map(Number)).map(a)),i):r},i.base=function(n){return arguments.length?(e=+n,t.domain(r.map(a)),i):e},i.nice=function(){var e=Ga(r.map(a),n?Math:Ss);return t.domain(e),r=e.map(o),i},i.ticks=function(){var t=Ya(r),i=[],l=t[0],s=t[1],c=Math.floor(a(l)),u=Math.ceil(a(s)),f=e%1?2:e;if(isFinite(u-c)){if(n){for(;u>c;c++)for(var d=1;f>d;d++)i.push(o(c)*d);i.push(o(c))}else for(i.push(o(c));c++<u;)for(var d=f-1;d>0;d--)i.push(o(c)*d);for(c=0;i[c]<l;c++);for(u=i.length;i[u-1]>s;u--);i=i.slice(c,u)}return i},i.tickFormat=function(t,n){if(!arguments.length)return zs;arguments.length<2?n=zs:\"function\"!=typeof n&&(n=ui.format(n));var r=Math.max(1,e*t/i.ticks().length);return function(t){var i=t/o(Math.round(a(t)));return e-.5>i*e&&(i*=e),r>=i?n(t):\"\"}},i.copy=function(){return oo(t.copy(),e,n,r)},Ja(i,t)}function io(t,e,n){function r(e){return t(a(e))}var a=lo(e),o=lo(1/e);return r.invert=function(e){return o(t.invert(e))},r.domain=function(e){return arguments.length?(t.domain((n=e.map(Number)).map(a)),r):n},r.ticks=function(t){return eo(n,t)},r.tickFormat=function(t,e){return no(n,t,e)},r.nice=function(t){return r.domain(Ka(n,t))},r.exponent=function(i){return arguments.length?(a=lo(e=i),o=lo(1/e),t.domain(n.map(a)),r):e},r.copy=function(){return io(t.copy(),e,n)},Ja(r,t)}function lo(t){return function(e){return 0>e?-Math.pow(-e,t):Math.pow(e,t)}}function so(t,e){function n(n){return o[((a.get(n)||(\"range\"===e.t?a.set(n,t.push(n)):NaN))-1)%o.length]}function r(e,n){return ui.range(t.length).map(function(t){return e+n*t})}var a,o,i;return n.domain=function(r){if(!arguments.length)return t;t=[],a=new f;for(var o,i=-1,l=r.length;++i<l;)a.has(o=r[i])||a.set(o,t.push(o));return n[e.t].apply(n,e.a)},n.range=function(t){return arguments.length?(o=t,i=0,e={t:\"range\",a:arguments},n):o},n.rangePoints=function(a,l){arguments.length<2&&(l=0);var s=a[0],c=a[1],u=t.length<2?(s=(s+c)/2,0):(c-s)/(t.length-1+l);return o=r(s+u*l/2,u),i=0,e={t:\"rangePoints\",a:arguments},n},n.rangeRoundPoints=function(a,l){arguments.length<2&&(l=0);var s=a[0],c=a[1],u=t.length<2?(s=c=Math.round((s+c)/2),0):(c-s)/"
+,
+"(t.length-1+l)|0;return o=r(s+Math.round(u*l/2+(c-s-(t.length-1+l)*u)/2),u),i=0,e={t:\"rangeRoundPoints\",a:arguments},n},n.rangeBands=function(a,l,s){arguments.length<2&&(l=0),arguments.length<3&&(s=l);var c=a[1]<a[0],u=a[c-0],f=a[1-c],d=(f-u)/(t.length-l+2*s);return o=r(u+d*s,d),c&&o.reverse(),i=d*(1-l),e={t:\"rangeBands\",a:arguments},n},n.rangeRoundBands=function(a,l,s){arguments.length<2&&(l=0),arguments.length<3&&(s=l);var c=a[1]<a[0],u=a[c-0],f=a[1-c],d=Math.floor((f-u)/(t.length-l+2*s));return o=r(u+Math.round((f-u-(t.length-l)*d)/2),d),c&&o.reverse(),i=Math.round(d*(1-l)),e={t:\"rangeRoundBands\",a:arguments},n},n.rangeBand=function(){return i},n.rangeExtent=function(){return Ya(e.a[0])},n.copy=function(){return so(t,e)},n.domain(t)}function co(t,e){function n(){var n=0,a=e.length;for(l=[];++n<a;)l[n-1]=ui.quantile(t,n/a);return r}function r(t){return isNaN(t=+t)?void 0:e[ui.bisect(l,t)]}var l;return r.domain=function(e){return arguments.length?(t=e.map(o).filter(i).sort(a),n()):t},r.range=function(t){return arguments.length?(e=t,n()):e},r.quantiles=function(){return l},r.invertExtent=function(n){return n=e.indexOf(n),0>n?[NaN,NaN]:[n>0?l[n-1]:t[0],n<l.length?l[n]:t[t.length-1]]},r.copy=function(){return co(t,e)},n()}function uo(t,e,n){function r(e){return n[Math.max(0,Math.min(i,Math.floor(o*(e-t))))]}function a(){return o=n.length/(e-t),i=n.length-1,r}var o,i;return r.domain=function(n){return arguments.length?(t=+n[0],e=+n[n.length-1],a()):[t,e]},r.range=function(t){return arguments.length?(n=t,a()):n},r.invertExtent=function(e){return e=n.indexOf(e),e=0>e?NaN:e/o+t,[e,e+1/o]},r.copy=function(){return uo(t,e,n)},a()}function fo(t,e){function n(n){return n>=n?e[ui.bisect(t,n)]:void 0}return n.domain=function(e){return arguments.length?(t=e,n):t},n.range=function(t){return arguments.length?(e=t,n):e},n.invertExtent=function(n){return n=e.indexOf(n),[t[n-1],t[n]]},n.copy=function(){return fo(t,e)},n}function ho(t){function e(t){return+t}return e.invert=e,e.domain=e.range=function(n){return arguments.length?(t=n.map(e),e):t},e.ticks=function(e){return eo(t,e)},e.tickFormat=function(e,n){return no(t,e,n)},e.copy=function(){return ho(t)},e}function po(){return 0}function go(t){return t.innerRadius}function vo(t){return t.outerRadius}function mo(t){return t.startAngle}function yo(t){return t.endAngle}function xo(t){return t&&t.padAngle}function bo(t,e,n,r){return(t-n)*e-(e-r)*t>0?0:1}function _o(t,e,n,r,a){var o=t[0]-e[0],i=t[1]-e[1],l=(a?r:-r)/Math.sqrt(o*o+i*i),s=l*i,c=-l*o,u=t[0]+s,f=t[1]+c,d=e[0]+s,h=e[1]+c,p=(u+d)/2,g=(f+h)/2,v=d-u,m=h-f,y=v*v+m*m,x=n-r,b=u*h-d*f,_=(0>m?-1:1)*Math.sqrt(Math.max(0,x*x*y-b*b)),w=(b*m-v*_)/y,k=(-b*v-m*_)/y,M=(b*m+v*_)/y,A=(-b*v+m*_)/y,L=w-p,T=k-g,z=M-p,S=A-g;return L*L+T*T>z*z+S*S&&(w=M,k=A),[[w-s,k-c],[w*n/x,k*n/x]]}function wo(t){function e(e){function i(){c.push(\"M\",o(t(u),l))}for(var s,c=[],u=[],f=-1,d=e.length,h=zt(n),p=zt(r);++f<d;)a.call(this,s=e[f],f)?u.push([+h.call(this,s,f),+p.call(this,s,f)]):u.length&&(i(),u=[]);return u.length&&i(),c.length?c.join(\"\"):null}var n=Cn,r=On,a=Ce,o=ko,i=o.key,l=.7;return e.x=function(t){return arguments.length?(n=t,e):n},e.y=function(t){return arguments.length?(r=t,e):r},e.defined=function(t){return arguments.length?(a=t,e):a},e.interpolate=function(t){return arguments.length?(i=\"function\"==typeof t?o=t:(o=Ds.get(t)||ko).key,e):i},e.tension=function(t){return arguments.length?(l=t,e):l},e}function ko(t){return t.length>1?t.join(\"L\"):t+\"Z\"}function Mo(t){return t.join(\"L\")+\"Z\"}function Ao(t){for(var e=0,n=t.length,r=t[0],a=[r[0],\",\",r[1]];++e<n;)a.push(\"H\",(r[0]+(r=t[e])[0])/2,\"V\",r[1]);return n>1&&a.push(\"H\",r[0]),a.join(\"\")}function Lo(t){for(var e=0,n=t.length,r=t[0],a=[r[0],\",\",r[1]];++e<n;)a.push(\"V\",(r=t[e])[1],\"H\",r[0]);return a.join(\"\")}function To(t){for(var e=0,n=t.length,r=t[0],a=[r[0],\",\",r[1]];++e<n;)a.push(\"H\",(r=t[e])[0],\"V\",r[1]);return a.join(\"\")}function zo(t,e){return t.length<4?ko(t):t[1]+Co(t.slice(1,-1),Oo(t,e))}function So(t,e){return t.length<3?Mo(t):t[0]+Co((t.push(t[0]),t),Oo([t[t.length-2]].concat(t,[t[1]]),e))}function Eo(t,e){return t.length<3?ko(t):t[0]+Co(t,Oo(t,e))}function Co(t,e){if(e.length<1||t.length!=e.length&&t.length!=e.length+2)return ko(t);var n=t.length!=e.length,r=\"\",a=t[0],o=t[1],i=e[0],l=i,s=1;if(n&&(r+=\"Q\"+(o[0]-2*i[0]/3)+\",\"+(o[1]-2*i[1]/3)+\",\"+o[0]+\",\"+o[1],a=t[1],s=2),e.length>1){l=e[1],o=t[s],s++,r+=\"C\"+(a[0]+i[0])+\",\"+(a[1]+i[1])+\",\"+(o[0]-l[0])+\",\"+(o[1]-l[1])+\",\"+o[0]+\",\"+o[1];for(var c=2;c<e.length;c++,s++)o=t[s],l=e[c],r+=\"S\"+(o[0]-l[0])+\",\"+(o[1]-l[1])+\",\"+o[0]+\",\"+o[1]}if(n){var u=t[s];r+=\"Q\"+(o[0]+2*l[0]/3)+\",\"+(o[1]+2*l[1]/3)+\",\"+u[0]+\",\"+u[1]}return r}function Oo(t,e){for(var n,r=[],a=(1-e)/2,o=t[0],i=t[1],l=1,s=t.length;++l<s;)n=o,o=i,i=t[l],r.push([a*(i[0]-n[0]),a*(i[1]-n[1])]);return r}function Po(t){if(t.length<3)return ko(t);var e=1,n=t.length,r=t[0],a=r[0],o=r[1],i=[a,a,a,(r=t[1])[0]],l=[o,o,o,r[1]],s=[a,\",\",o,\"L\",Ro(js,i),\",\",Ro(js,l)];for(t.push(t[n-1]);++e<=n;)r=t[e],i.shift(),i.push(r[0]),l.shift(),l.push(r[1]),jo(s,i,l);return t.pop(),s.push(\"L\",r),s.join(\"\")}function No(t){if(t.length<4)return ko(t);for(var e,n=[],r=-1,a=t.length,o=[0],i=[0];++r<3;)e=t[r],o.push(e[0]),i.push(e[1]);for(n.push(Ro(js,o)+\",\"+Ro(js,i)),--r;++r<a;)e=t[r],o.shift(),o.push(e[0]),i.shift(),i.push(e[1]),jo(n,o,i);return n.join(\"\")}function Do(t){for(var e,n,r=-1,a=t.length,o=a+4,i=[],l=[];++r<4;)n=t[r%a],i.push(n[0]),l.push(n[1]);for(e=[Ro(js,i),\",\",Ro(js,l)],--r;++r<o;)n=t[r%a],i.shift(),i.push(n[0]),l.shift(),l.push(n[1]),jo(e,i,l);return e.join(\"\")}function Io(t,e){var n=t.length-1;if(n)for(var r,a,o=t[0][0],i=t[0][1],l=t[n][0]-o,s=t[n][1]-i,c=-1;++c<=n;)r=t[c],a=c/n,r[0]=e*r[0]+(1-e)*(o+a*l),r[1]=e*r[1]+(1-e)*(i+a*s);return Po(t)}function Ro(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]}function jo(t,e,n){t.push(\"C\",Ro(Is,e),\",\",Ro(Is,n),\",\",Ro(Rs,e),\",\",Ro(Rs,n),\",\",Ro(js,e),\",\",Ro(js,n))}function qo(t,e){return(e[1]-t[1])/(e[0]-t[0])}function Fo(t){for(var e=0,n=t.length-1,r=[],a=t[0],o=t[1],i=r[0]=qo(a,o);++e<n;)r[e]=(i+(i=qo(a=o,o=t[e+1])))/2;return r[e]=i,r}function Bo(t){for(var e,n,r,a,o=[],i=Fo(t),l=-1,s=t.length-1;++l<s;)e=qo(t[l],t[l+1]),wi(e)<qi?i[l]=i[l+1]=0:(n=i[l]/e,r=i[l+1]/e,a=n*n+r*r,a>9&&(a=3*e/Math.sqrt(a),i[l]=a*n,i[l+1]=a*r));for(l=-1;++l<=s;)a=(t[Math.min(s,l+1)][0]-t[Math.max(0,l-1)][0])/(6*(1+i[l]*i[l])),o.push([a||0,i[l]*a||0]);return o}function Ho(t){return t.length<3?ko(t):t[0]+Co(t,Bo(t))}function Vo(t){for(var e,n,r,a=-1,o=t.length;++a<o;)e=t[a],n=e[0],r=e[1]-Zi,e[0]=n*Math.cos(r),e[1]=n*Math.sin(r);return t}function Zo(t){function e(e){function s(){g.push(\"M\",l(t(m),f),u,c(t(v.reverse()),f),\"Z\")}for(var d,h,p,g=[],v=[],m=[],y=-1,x=e.length,b=zt(n),_=zt(a),w=n===r?function(){return h}:zt(r),k=a===o?function(){return p}:zt(o);++y<x;)i.call(this,d=e[y],y)?(v.push([h=+b.call(this,d,y),p=+_.call(this,d,y)]),m.push([+w.call(this,d,y),+k.call(this,d,y)])):v.length&&(s(),v=[],m=[]);return v.length&&s(),g.length?g.join(\"\"):null}var n=Cn,r=Cn,a=0,o=On,i=Ce,l=ko,s=l.key,c=l,u=\"L\",f=.7;return e.x=function(t){return arguments.length?(n=r=t,e):r},e.x0=function(t){return arguments.length?(n=t,e):n},e.x1=function(t){return arguments.length?(r=t,e):r},e.y=function(t){return arguments.length?(a=o=t,e):o},e.y0=function(t){return arguments.length?(a=t,e):a},e.y1=function(t){return arguments.length?(o=t,e):o},e.defined=function(t){return arguments.length?(i=t,e):i},e.interpolate=function(t){return arguments.length?(s=\"function\"==typeof t?l=t:(l=Ds.get(t)||ko).key,c=l.reverse||l,u=l.closed?\"M\":\"L\",e):s},e.tension=function(t){return arguments.length?(f=t,e):f},e}function Yo(t){return t.radius}function Uo(t){return[t.x,t.y]}function Xo(t){return function(){var e=t.apply(this,arguments),n=e[0],r=e[1]-Zi;return[n*Math.cos(r),n*Math.sin(r)]}}function Go(){return 64}function $o(){return\"circle\"}function Qo(t){var e=Math.sqrt(t/Bi);return\"M0,\"+e+\"A\"+e+\",\"+e+\" 0 1,1 0,\"+-e+\"A\"+e+\",\"+e+\" 0 1,1 0,\"+e+\"Z\"}function Wo(t){return function(){var e,n,r;(e=this[t])&&(r=e[n=e.active])&&(r.timer.c=null,r.timer.t=NaN,--e.count?delete e[n]:delete this[t],e.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Jo(t,e,n){return Ti(t,Ys),t.namespace=e,t.id=n,t}function Ko(t,e,n,r){var a=t.id,o=t.namespace;return Y(t,\"function\"==typeof n?function(t,i,l){t[o][a].tween.set(e,r(n.call(t,t.__data__,i,l)))}:(n=r(n),function(t){t[o][a].tween.set(e,n)}))}function ti(t){return null==t&&(t=\"\"),function(){this.textContent=t}}function ei(t){return null==t?\"__transition__\":\"__transition_\"+t+\"__\"}function ni(t,e,n,r,a){function o(t){var e=g.delay;return c.t=e+s,t>=e?i(t-e):void(c.c=i)}function i(n){var a=p.active,o=p[a];o&&(o.timer.c=null,o.timer.t=NaN,--p.count,delete p[a],o.event&&o.event.interrupt.call(t,t.__data__,o.index));for(var i in p)if(r>+i){var f=p[i];f.timer.c=null,f.timer.t=NaN,--p.count,delete p[i]}c.c=l,Pt(function(){return c.c&&l(n||1)&&(c.c=null,c.t=NaN),1},0,s),p.active=r,g.event&&g.event.start.call(t,t.__data__,e),h=[],g.tween.forEach(function(n,r){(r=r.call(t,t.__data__,e))&&h.push(r)}),d=g.ease,u=g.duration}function l(a){for(var o=a/u,i=d(o),l=h.length;l>0;)h[--l].call(t,i);return o>=1?(g.event&&g.event.end.call(t,t.__data__,e),--p.count?delete p[r]:delete t[n],1):void 0}var s,c,u,d,h,p=t[n]||(t[n]={active:0,count:0}),g=p[r];g||(s=a.time,c=Pt(o,0,s),g=p[r]={tween:new f,time:s,timer:c,delay:a.delay,duration:a.duration,ease:a.ease,index:e},a=null,++p.count)}function ri(t,e,n){t.attr(\"transform\",function(t){var r=e(t);return\"translate(\"+(isFinite(r)?r:n(t))+\",0)\"})}function ai(t,e,n){t.attr(\"transform\",function(t){var r=e(t);return\"translate(0,\"+(isFinite(r)?r:n(t))+\")\"})}function oi(t){return t.toISOString()}function ii(t,e,n){function r(e){return t(e)}function a(t,n){var r=t[1]-t[0],a=r/n,o=ui.bisect(tc,a);return o==tc.length?[e.year,to(t.map(function(t){return t/31536e6}),n)[2]]:o?e[a/tc[o-1]<tc[o]/a?o-1:o]:[rc,to(t,n)[2]]}return r.invert=function(e){return li"
+,
+"(t.invert(e))},r.domain=function(e){return arguments.length?(t.domain(e),r):t.domain().map(li)},r.nice=function(t,e){function n(n){return!isNaN(n)&&!t.range(n,li(+n+1),e).length}var o=r.domain(),i=Ya(o),l=null==t?a(i,10):\"number\"==typeof t&&a(i,t);return l&&(t=l[0],e=l[1]),r.domain(Ga(o,e>1?{floor:function(e){for(;n(e=t.floor(e));)e=li(e-1);return e},ceil:function(e){for(;n(e=t.ceil(e));)e=li(+e+1);return e}}:t))},r.ticks=function(t,e){var n=Ya(r.domain()),o=null==t?a(n,10):\"number\"==typeof t?a(n,t):!t.range&&[{range:t},e];return o&&(t=o[0],e=o[1]),t.range(n[0],li(+n[1]+1),1>e?1:e)},r.tickFormat=function(){return n},r.copy=function(){return ii(t.copy(),e,n)},Ja(r,t)}function li(t){return new Date(t)}function si(t){return JSON.parse(t.responseText)}function ci(t){var e=hi.createRange();return e.selectNode(hi.body),e.createContextualFragment(t.responseText)}var ui={version:\"3.5.16\"},fi=[].slice,di=function(t){return fi.call(t)},hi=this.document;if(hi)try{di(hi.documentElement.childNodes)[0].nodeType}catch(pi){di=function(t){for(var e=t.length,n=new Array(e);e--;)n[e]=t[e];return n}}if(Date.now||(Date.now=function(){return+new Date}),hi)try{hi.createElement(\"DIV\").style.setProperty(\"opacity\",0,\"\")}catch(gi){var vi=this.Element.prototype,mi=vi.setAttribute,yi=vi.setAttributeNS,xi=this.CSSStyleDeclaration.prototype,bi=xi.setProperty;vi.setAttribute=function(t,e){mi.call(this,t,e+\"\")},vi.setAttributeNS=function(t,e,n){yi.call(this,t,e,n+\"\")},xi.setProperty=function(t,e,n){bi.call(this,t,e+\"\",n)}}ui.ascending=a,ui.descending=function(t,e){return t>e?-1:e>t?1:e>=t?0:NaN},ui.min=function(t,e){var n,r,a=-1,o=t.length;if(1===arguments.length){for(;++a<o;)if(null!=(r=t[a])&&r>=r){n=r;break}for(;++a<o;)null!=(r=t[a])&&n>r&&(n=r)}else{for(;++a<o;)if(null!=(r=e.call(t,t[a],a))&&r>=r){n=r;break}for(;++a<o;)null!=(r=e.call(t,t[a],a))&&n>r&&(n=r)}return n},ui.max=function(t,e){var n,r,a=-1,o=t.length;if(1===arguments.length){for(;++a<o;)if(null!=(r=t[a])&&r>=r){n=r;break}for(;++a<o;)null!=(r=t[a])&&r>n&&(n=r)}else{for(;++a<o;)if(null!=(r=e.call(t,t[a],a))&&r>=r){n=r;break}for(;++a<o;)null!=(r=e.call(t,t[a],a))&&r>n&&(n=r)}return n},ui.extent=function(t,e){var n,r,a,o=-1,i=t.length;if(1===arguments.length){for(;++o<i;)if(null!=(r=t[o])&&r>=r){n=a=r;break}for(;++o<i;)null!=(r=t[o])&&(n>r&&(n=r),r>a&&(a=r))}else{for(;++o<i;)if(null!=(r=e.call(t,t[o],o))&&r>=r){n=a=r;break}for(;++o<i;)null!=(r=e.call(t,t[o],o))&&(n>r&&(n=r),r>a&&(a=r))}return[n,a]},ui.sum=function(t,e){var n,r=0,a=t.length,o=-1;if(1===arguments.length)for(;++o<a;)i(n=+t[o])&&(r+=n);else for(;++o<a;)i(n=+e.call(t,t[o],o))&&(r+=n);return r},ui.mean=function(t,e){var n,r=0,a=t.length,l=-1,s=a;if(1===arguments.length)for(;++l<a;)i(n=o(t[l]))?r+=n:--s;else for(;++l<a;)i(n=o(e.call(t,t[l],l)))?r+=n:--s;return s?r/s:void 0},ui.quantile=function(t,e){var n=(t.length-1)*e+1,r=Math.floor(n),a=+t[r-1],o=n-r;return o?a+o*(t[r]-a):a},ui.median=function(t,e){var n,r=[],l=t.length,s=-1;if(1===arguments.length)for(;++s<l;)i(n=o(t[s]))&&r.push(n);else for(;++s<l;)i(n=o(e.call(t,t[s],s)))&&r.push(n);return r.length?ui.quantile(r.sort(a),.5):void 0},ui.variance=function(t,e){var n,r,a=t.length,l=0,s=0,c=-1,u=0;if(1===arguments.length)for(;++c<a;)i(n=o(t[c]))&&(r=n-l,l+=r/++u,s+=r*(n-l));else for(;++c<a;)i(n=o(e.call(t,t[c],c)))&&(r=n-l,l+=r/++u,s+=r*(n-l));return u>1?s/(u-1):void 0},ui.deviation=function(){var t=ui.variance.apply(this,arguments);return t?Math.sqrt(t):t};var _i=l(a);ui.bisectLeft=_i.left,ui.bisect=ui.bisectRight=_i.right,ui.bisector=function(t){return l(1===t.length?function(e,n){return a(t(e),n)}:t)},ui.shuffle=function(t,e,n){(o=arguments.length)<3&&(n=t.length,2>o&&(e=0));for(var r,a,o=n-e;o;)a=Math.random()*o--|0,r=t[o+e],t[o+e]=t[a+e],t[a+e]=r;return t},ui.permute=function(t,e){for(var n=e.length,r=new Array(n);n--;)r[n]=t[e[n]];return r},ui.pairs=function(t){for(var e,n=0,r=t.length-1,a=t[0],o=new Array(0>r?0:r);r>n;)o[n]=[e=a,a=t[++n]];return o},ui.transpose=function(t){if(!(a=t.length))return[];for(var e=-1,n=ui.min(t,s),r=new Array(n);++e<n;)for(var a,o=-1,i=r[e]=new Array(a);++o<a;)i[o]=t[o][e];return r},ui.zip=function(){return ui.transpose(arguments)},ui.keys=function(t){var e=[];for(var n in t)e.push(n);return e},ui.values=function(t){var e=[];for(var n in t)e.push(t[n]);return e},ui.entries=function(t){var e=[];for(var n in t)e.push({key:n,value:t[n]});return e},ui.merge=function(t){for(var e,n,r,a=t.length,o=-1,i=0;++o<a;)i+=t[o].length;for(n=new Array(i);--a>=0;)for(r=t[a],e=r.length;--e>=0;)n[--i]=r[e];return n};var wi=Math.abs;ui.range=function(t,e,n){if(arguments.length<3&&(n=1,arguments.length<2&&(e=t,t=0)),(e-t)/n===1/0)throw new Error(\"infinite range\");var r,a=[],o=c(wi(n)),i=-1;if(t*=o,e*=o,n*=o,0>n)for(;(r=t+n*++i)>e;)a.push(r/o);else for(;(r=t+n*++i)<e;)a.push(r/o);return a},ui.map=function(t,e){var n=new f;if(t instanceof f)t.forEach(function(t,e){n.set(t,e)});else if(Array.isArray(t)){var r,a=-1,o=t.length;if(1===arguments.length)for(;++a<o;)n.set(a,t[a]);else for(;++a<o;)n.set(e.call(t,r=t[a],a),r)}else for(var i in t)n.set(i,t[i]);return n};var ki=\"__proto__\",Mi=\"\\x00\";u(f,{has:p,get:function(t){return this._[d(t)]},set:function(t,e){return this._[d(t)]=e},remove:g,keys:v,values:function(){var t=[];for(var e in this._)t.push(this._[e]);return t},entries:function(){var t=[];for(var e in this._)t.push({key:h(e),value:this._[e]});return t},size:m,empty:y,forEach:function(t){for(var e in this._)t.call(this,h(e),this._[e])}}),ui.nest=function(){function t(e,i,l){if(l>=o.length)return r?r.call(a,i):n?i.sort(n):i;for(var s,c,u,d,h=-1,p=i.length,g=o[l++],v=new f;++h<p;)(d=v.get(s=g(c=i[h])))?d.push(c):v.set(s,[c]);return e?(c=e(),u=function(n,r){c.set(n,t(e,r,l))}):(c={},u=function(n,r){c[n]=t(e,r,l)}),v.forEach(u),c}function e(t,n){if(n>=o.length)return t;var r=[],a=i[n++];return t.forEach(function(t,a){r.push({key:t,values:e(a,n)})}),a?r.sort(function(t,e){return a(t.key,e.key)}):r}var n,r,a={},o=[],i=[];return a.map=function(e,n){return t(n,e,0)},a.entries=function(n){return e(t(ui.map,n,0),0)},a.key=function(t){return o.push(t),a},a.sortKeys=function(t){return i[o.length-1]=t,a},a.sortValues=function(t){return n=t,a},a.rollup=function(t){return r=t,a},a},ui.set=function(t){var e=new x;if(t)for(var n=0,r=t.length;r>n;++n)e.add(t[n]);return e},u(x,{has:p,add:function(t){return this._[d(t+=\"\")]=!0,t},remove:g,values:v,size:m,empty:y,forEach:function(t){for(var e in this._)t.call(this,h(e))}}),ui.behavior={},ui.rebind=function(t,e){for(var n,r=1,a=arguments.length;++r<a;)t[n=arguments[r]]=_(t,e,e[n]);return t};var Ai=[\"webkit\",\"ms\",\"moz\",\"Moz\",\"o\",\"O\"];ui.dispatch=function(){for(var t=new M,e=-1,n=arguments.length;++e<n;)t[arguments[e]]=A(t);return t},M.prototype.on=function(t,e){var n=t.indexOf(\".\"),r=\"\";if(n>=0&&(r=t.slice(n+1),t=t.slice(0,n)),t)return arguments.length<2?this[t].on(r):this[t].on(r,e);if(2===arguments.length){if(null==e)for(t in this)this.hasOwnProperty(t)&&this[t].on(r,null);return this}},ui.event=null,ui.requote=function(t){return t.replace(Li,\"\\\\$&\")};var Li=/[\\\\\\^\\$\\*\\+\\?\\|\\[\\]\\(\\)\\.\\{\\}]/g,Ti={}.__proto__?function(t,e){t.__proto__=e}:function(t,e){for(var n in e)t[n]=e[n]},zi=function(t,e){return e.querySelector(t)},Si=function(t,e){return e.querySelectorAll(t)},Ei=function(t,e){var n=t.matches||t[w(t,\"matchesSelector\")];return(Ei=function(t,e){return n.call(t,e)})(t,e)};\"function\"==typeof Sizzle&&(zi=function(t,e){return Sizzle(t,e)[0]||null},Si=Sizzle,Ei=Sizzle.matchesSelector),ui.selection=function(){return ui.select(hi.documentElement)};var Ci=ui.selection.prototype=[];Ci.select=function(t){var e,n,r,a,o=[];t=E(t);for(var i=-1,l=this.length;++i<l;){o.push(e=[]),e.parentNode=(r=this[i]).parentNode;for(var s=-1,c=r.length;++s<c;)(a=r[s])?(e.push(n=t.call(a,a.__data__,s,i)),n&&\"__data__\"in a&&(n.__data__=a.__data__)):e.push(null)}return S(o)},Ci.selectAll=function(t){var e,n,r=[];t=C(t);for(var a=-1,o=this.length;++a<o;)for(var i=this[a],l=-1,s=i.length;++l<s;)(n=i[l])&&(r.push(e=di(t.call(n,n.__data__,l,a))),e.parentNode=n);return S(r)};var Oi=\"http://www.w3.org/1999/xhtml\",Pi={svg:\"http://www.w3.org/2000/svg\",xhtml:Oi,xlink:\"http://www.w3.org/1999/xlink\",xml:\"http://www.w3.org/XML/1998/namespace\",xmlns:\"http://www.w3.org/2000/xmlns/\"};ui.ns={prefix:Pi,qualify:function(t){var e=t.indexOf(\":\"),n=t;return e>=0&&\"xmlns\"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Pi.hasOwnProperty(n)?{space:Pi[n],local:t}:t}},Ci.attr=function(t,e){if(arguments.length<2){if(\"string\"==typeof t){var n=this.node();return t=ui.ns.qualify(t),t.local?n.getAttributeNS(t.space,t.local):n.getAttribute(t)}for(e in t)this.each(O(e,t[e]));return this}return this.each(O(t,e))},Ci.classed=function(t,e){if(arguments.length<2){if(\"string\"==typeof t){var n=this.node(),r=(t=D(t)).length,a=-1;if(e=n.classList){for(;++a<r;)if(!e.contains(t[a]))return!1}else for(e=n.getAttribute(\"class\");++a<r;)if(!N(t[a]).test(e))return!1;return!0}for(e in t)this.each(I(e,t[e]));return this}return this.each(I(t,e))},Ci.style=function(t,e,n){var a=arguments.length;if(3>a){if(\"string\"!=typeof t){2>a&&(e=\"\");for(n in t)this.each(j(n,t[n],e));return this}if(2>a){var o=this.node();return r(o).getComputedStyle(o,null).getPropertyValue(t)}n=\"\"}return this.each(j(t,e,n))},Ci.property=function(t,e){if(arguments.length<2){if(\"string\"==typeof t)return this.node()[t];for(e in t)this.each(q(e,t[e]));return this}return this.each(q(t,e))},Ci.text=function(t){return arguments.length?this.each(\"function\"==typeof t?function(){var e=t.apply(this,arguments);this.textContent=null==e?\"\":e}:null==t?function(){this.textContent=\"\"}:function(){this.textContent=t}):this.node().textContent},Ci.html=function(t){return arguments.length?this.each(\"function\"==typeof t?function(){var e=t.apply(this,arguments);this.innerHTML=null==e?\"\":e}:null==t?function(){this.innerHTML=\"\"}:function(){this.innerHTML=t}):this.node().innerHTML},Ci.append=function(t){return t=F(t),this.select(function(){return this.appendChild(t.apply(t"
+,
+"his,arguments))})},Ci.insert=function(t,e){return t=F(t),e=E(e),this.select(function(){return this.insertBefore(t.apply(this,arguments),e.apply(this,arguments)||null)})},Ci.remove=function(){return this.each(B)},Ci.data=function(t,e){function n(t,n){var r,a,o,i=t.length,u=n.length,d=Math.min(i,u),h=new Array(u),p=new Array(u),g=new Array(i);if(e){var v,m=new f,y=new Array(i);for(r=-1;++r<i;)(a=t[r])&&(m.has(v=e.call(a,a.__data__,r))?g[r]=a:m.set(v,a),y[r]=v);for(r=-1;++r<u;)(a=m.get(v=e.call(n,o=n[r],r)))?a!==!0&&(h[r]=a,a.__data__=o):p[r]=H(o),m.set(v,!0);for(r=-1;++r<i;)r in y&&m.get(y[r])!==!0&&(g[r]=t[r])}else{for(r=-1;++r<d;)a=t[r],o=n[r],a?(a.__data__=o,h[r]=a):p[r]=H(o);for(;u>r;++r)p[r]=H(n[r]);for(;i>r;++r)g[r]=t[r]}p.update=h,p.parentNode=h.parentNode=g.parentNode=t.parentNode,l.push(p),s.push(h),c.push(g)}var r,a,o=-1,i=this.length;if(!arguments.length){for(t=new Array(i=(r=this[0]).length);++o<i;)(a=r[o])&&(t[o]=a.__data__);return t}var l=U([]),s=S([]),c=S([]);if(\"function\"==typeof t)for(;++o<i;)n(r=this[o],t.call(r,r.parentNode.__data__,o));else for(;++o<i;)n(r=this[o],t);return s.enter=function(){return l},s.exit=function(){return c},s},Ci.datum=function(t){return arguments.length?this.property(\"__data__\",t):this.property(\"__data__\")},Ci.filter=function(t){var e,n,r,a=[];\"function\"!=typeof t&&(t=V(t));for(var o=0,i=this.length;i>o;o++){a.push(e=[]),e.parentNode=(n=this[o]).parentNode;for(var l=0,s=n.length;s>l;l++)(r=n[l])&&t.call(r,r.__data__,l,o)&&e.push(r)}return S(a)},Ci.order=function(){for(var t=-1,e=this.length;++t<e;)for(var n,r=this[t],a=r.length-1,o=r[a];--a>=0;)(n=r[a])&&(o&&o!==n.nextSibling&&o.parentNode.insertBefore(n,o),o=n);return this},Ci.sort=function(t){t=Z.apply(this,arguments);for(var e=-1,n=this.length;++e<n;)this[e].sort(t);return this.order()},Ci.each=function(t){return Y(this,function(e,n,r){t.call(e,e.__data__,n,r)})},Ci.call=function(t){var e=di(arguments);return t.apply(e[0]=this,e),this},Ci.empty=function(){return!this.node();\n"
+,
+"},Ci.node=function(){for(var t=0,e=this.length;e>t;t++)for(var n=this[t],r=0,a=n.length;a>r;r++){var o=n[r];if(o)return o}return null},Ci.size=function(){var t=0;return Y(this,function(){++t}),t};var Ni=[];ui.selection.enter=U,ui.selection.enter.prototype=Ni,Ni.append=Ci.append,Ni.empty=Ci.empty,Ni.node=Ci.node,Ni.call=Ci.call,Ni.size=Ci.size,Ni.select=function(t){for(var e,n,r,a,o,i=[],l=-1,s=this.length;++l<s;){r=(a=this[l]).update,i.push(e=[]),e.parentNode=a.parentNode;for(var c=-1,u=a.length;++c<u;)(o=a[c])?(e.push(r[c]=n=t.call(a.parentNode,o.__data__,c,l)),n.__data__=o.__data__):e.push(null)}return S(i)},Ni.insert=function(t,e){return arguments.length<2&&(e=X(this)),Ci.insert.call(this,t,e)},ui.select=function(t){var n;return\"string\"==typeof t?(n=[zi(t,hi)],n.parentNode=hi.documentElement):(n=[t],n.parentNode=e(t)),S([n])},ui.selectAll=function(t){var e;return\"string\"==typeof t?(e=di(Si(t,hi)),e.parentNode=hi.documentElement):(e=di(t),e.parentNode=null),S([e])},Ci.on=function(t,e,n){var r=arguments.length;if(3>r){if(\"string\"!=typeof t){2>r&&(e=!1);for(n in t)this.each(G(n,t[n],e));return this}if(2>r)return(r=this.node()[\"__on\"+t])&&r._;n=!1}return this.each(G(t,e,n))};var Di=ui.map({mouseenter:\"mouseover\",mouseleave:\"mouseout\"});hi&&Di.forEach(function(t){\"on\"+t in hi&&Di.remove(t)});var Ii,Ri=0;ui.mouse=function(t){return J(t,T())};var ji=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ui.touch=function(t,e,n){if(arguments.length<3&&(n=e,e=T().changedTouches),e)for(var r,a=0,o=e.length;o>a;++a)if((r=e[a]).identifier===n)return J(t,r)},ui.behavior.drag=function(){function t(){this.on(\"mousedown.drag\",o).on(\"touchstart.drag\",i)}function e(t,e,r,o,i){return function(){function l(){var t,n,r=e(d,g);r&&(t=r[0]-x[0],n=r[1]-x[1],p|=t|n,x=r,h({type:\"drag\",x:r[0]+c[0],y:r[1]+c[1],dx:t,dy:n}))}function s(){e(d,g)&&(m.on(o+v,null).on(i+v,null),y(p),h({type:\"dragend\"}))}var c,u=this,f=ui.event.target.correspondingElement||ui.event.target,d=u.parentNode,h=n.of(u,arguments),p=0,g=t(),v=\".drag\"+(null==g?\"\":\"-\"+g),m=ui.select(r(f)).on(o+v,l).on(i+v,s),y=W(f),x=e(d,g);a?(c=a.apply(u,arguments),c=[c.x-x[0],c.y-x[1]]):c=[0,0],h({type:\"dragstart\"})}}var n=z(t,\"drag\",\"dragstart\",\"dragend\"),a=null,o=e(k,ui.mouse,r,\"mousemove\",\"mouseup\"),i=e(K,ui.touch,b,\"touchmove\",\"touchend\");return t.origin=function(e){return arguments.length?(a=e,t):a},ui.rebind(t,n,\"on\")},ui.touches=function(t,e){return arguments.length<2&&(e=T().touches),e?di(e).map(function(e){var n=J(t,e);return n.identifier=e.identifier,n}):[]};var qi=1e-6,Fi=qi*qi,Bi=Math.PI,Hi=2*Bi,Vi=Hi-qi,Zi=Bi/2,Yi=Bi/180,Ui=180/Bi,Xi=Math.SQRT2,Gi=2,$i=4;ui.interpolateZoom=function(t,e){var n,r,a=t[0],o=t[1],i=t[2],l=e[0],s=e[1],c=e[2],u=l-a,f=s-o,d=u*u+f*f;if(Fi>d)r=Math.log(c/i)/Xi,n=function(t){return[a+t*u,o+t*f,i*Math.exp(Xi*t*r)]};else{var h=Math.sqrt(d),p=(c*c-i*i+$i*d)/(2*i*Gi*h),g=(c*c-i*i-$i*d)/(2*c*Gi*h),v=Math.log(Math.sqrt(p*p+1)-p),m=Math.log(Math.sqrt(g*g+1)-g);r=(m-v)/Xi,n=function(t){var e=t*r,n=ot(v),l=i/(Gi*h)*(n*it(Xi*e+v)-at(v));return[a+l*u,o+l*f,i*n/ot(Xi*e+v)]}}return n.duration=1e3*r,n},ui.behavior.zoom=function(){function t(t){t.on(C,f).on(Wi+\".zoom\",h).on(\"dblclick.zoom\",p).on(N,d)}function e(t){return[(t[0]-M.x)/M.k,(t[1]-M.y)/M.k]}function n(t){return[t[0]*M.k+M.x,t[1]*M.k+M.y]}function a(t){M.k=Math.max(T[0],Math.min(T[1],t))}function o(t,e){e=n(e),M.x+=t[0]-e[0],M.y+=t[1]-e[1]}function i(e,n,r,i){e.__chart__={x:M.x,y:M.y,k:M.k},a(Math.pow(2,i)),o(v=n,r),e=ui.select(e),S>0&&(e=e.transition().duration(S)),e.call(t.event)}function l(){_&&_.domain(b.range().map(function(t){return(t-M.x)/M.k}).map(b.invert)),k&&k.domain(w.range().map(function(t){return(t-M.y)/M.k}).map(w.invert))}function s(t){E++||t({type:\"zoomstart\"})}function c(t){l(),t({type:\"zoom\",scale:M.k,translate:[M.x,M.y]})}function u(t){--E||(t({type:\"zoomend\"}),v=null)}function f(){function t(){l=1,o(ui.mouse(a),d),c(i)}function n(){f.on(O,null).on(P,null),h(l),u(i)}var a=this,i=D.of(a,arguments),l=0,f=ui.select(r(a)).on(O,t).on(P,n),d=e(ui.mouse(a)),h=W(a);Zs.call(a),s(i)}function d(){function t(){var t=ui.touches(p);return h=M.k,t.forEach(function(t){t.identifier in v&&(v[t.identifier]=e(t))}),t}function n(){var e=ui.event.target;ui.select(e).on(b,r).on(_,l),w.push(e);for(var n=ui.event.changedTouches,a=0,o=n.length;o>a;++a)v[n[a].identifier]=null;var s=t(),c=Date.now();if(1===s.length){if(500>c-x){var u=s[0];i(p,u,v[u.identifier],Math.floor(Math.log(M.k)/Math.LN2)+1),L()}x=c}else if(s.length>1){var u=s[0],f=s[1],d=u[0]-f[0],h=u[1]-f[1];m=d*d+h*h}}function r(){var t,e,n,r,i=ui.touches(p);Zs.call(p);for(var l=0,s=i.length;s>l;++l,r=null)if(n=i[l],r=v[n.identifier]){if(e)break;t=n,e=r}if(r){var u=(u=n[0]-t[0])*u+(u=n[1]-t[1])*u,f=m&&Math.sqrt(u/m);t=[(t[0]+n[0])/2,(t[1]+n[1])/2],e=[(e[0]+r[0])/2,(e[1]+r[1])/2],a(f*h)}x=null,o(t,e),c(g)}function l(){if(ui.event.touches.length){for(var e=ui.event.changedTouches,n=0,r=e.length;r>n;++n)delete v[e[n].identifier];for(var a in v)return void t()}ui.selectAll(w).on(y,null),k.on(C,f).on(N,d),A(),u(g)}var h,p=this,g=D.of(p,arguments),v={},m=0,y=\".zoom-\"+ui.event.changedTouches[0].identifier,b=\"touchmove\"+y,_=\"touchend\"+y,w=[],k=ui.select(p),A=W(p);n(),s(g),k.on(C,null).on(N,n)}function h(){var t=D.of(this,arguments);y?clearTimeout(y):(Zs.call(this),g=e(v=m||ui.mouse(this)),s(t)),y=setTimeout(function(){y=null,u(t)},50),L(),a(Math.pow(2,.002*Qi())*M.k),o(v,g),c(t)}function p(){var t=ui.mouse(this),n=Math.log(M.k)/Math.LN2;i(this,t,e(t),ui.event.shiftKey?Math.ceil(n)-1:Math.floor(n)+1)}var g,v,m,y,x,b,_,w,k,M={x:0,y:0,k:1},A=[960,500],T=Ji,S=250,E=0,C=\"mousedown.zoom\",O=\"mousemove.zoom\",P=\"mouseup.zoom\",N=\"touchstart.zoom\",D=z(t,\"zoomstart\",\"zoom\",\"zoomend\");return Wi||(Wi=\"onwheel\"in hi?(Qi=function(){return-ui.event.deltaY*(ui.event.deltaMode?120:1)},\"wheel\"):\"onmousewheel\"in hi?(Qi=function(){return ui.event.wheelDelta},\"mousewheel\"):(Qi=function(){return-ui.event.detail},\"MozMousePixelScroll\")),t.event=function(t){t.each(function(){var t=D.of(this,arguments),e=M;Hs?ui.select(this).transition().each(\"start.zoom\",function(){M=this.__chart__||{x:0,y:0,k:1},s(t)}).tween(\"zoom:zoom\",function(){var n=A[0],r=A[1],a=v?v[0]:n/2,o=v?v[1]:r/2,i=ui.interpolateZoom([(a-M.x)/M.k,(o-M.y)/M.k,n/M.k],[(a-e.x)/e.k,(o-e.y)/e.k,n/e.k]);return function(e){var r=i(e),l=n/r[2];this.__chart__=M={x:a-r[0]*l,y:o-r[1]*l,k:l},c(t)}}).each(\"interrupt.zoom\",function(){u(t)}).each(\"end.zoom\",function(){u(t)}):(this.__chart__=M,s(t),c(t),u(t))})},t.translate=function(e){return arguments.length?(M={x:+e[0],y:+e[1],k:M.k},l(),t):[M.x,M.y]},t.scale=function(e){return arguments.length?(M={x:M.x,y:M.y,k:null},a(+e),l(),t):M.k},t.scaleExtent=function(e){return arguments.length?(T=null==e?Ji:[+e[0],+e[1]],t):T},t.center=function(e){return arguments.length?(m=e&&[+e[0],+e[1]],t):m},t.size=function(e){return arguments.length?(A=e&&[+e[0],+e[1]],t):A},t.duration=function(e){return arguments.length?(S=+e,t):S},t.x=function(e){return arguments.length?(_=e,b=e.copy(),M={x:0,y:0,k:1},t):_},t.y=function(e){return arguments.length?(k=e,w=e.copy(),M={x:0,y:0,k:1},t):k},ui.rebind(t,D,\"on\")};var Qi,Wi,Ji=[0,1/0];ui.color=st,st.prototype.toString=function(){return this.rgb()+\"\"},ui.hsl=ct;var Ki=ct.prototype=new st;Ki.brighter=function(t){return t=Math.pow(.7,arguments.length?t:1),new ct(this.h,this.s,this.l/t)},Ki.darker=function(t){return t=Math.pow(.7,arguments.length?t:1),new ct(this.h,this.s,t*this.l)},Ki.rgb=function(){return ut(this.h,this.s,this.l)},ui.hcl=ft;var tl=ft.prototype=new st;tl.brighter=function(t){return new ft(this.h,this.c,Math.min(100,this.l+el*(arguments.length?t:1)))},tl.darker=function(t){return new ft(this.h,this.c,Math.max(0,this.l-el*(arguments.length?t:1)))},tl.rgb=function(){return dt(this.h,this.c,this.l).rgb()},ui.lab=ht;var el=18,nl=.95047,rl=1,al=1.08883,ol=ht.prototype=new st;ol.brighter=function(t){return new ht(Math.min(100,this.l+el*(arguments.length?t:1)),this.a,this.b)},ol.darker=function(t){return new ht(Math.max(0,this.l-el*(arguments.length?t:1)),this.a,this.b)},ol.rgb=function(){return pt(this.l,this.a,this.b)},ui.rgb=xt;var il=xt.prototype=new st;il.brighter=function(t){t=Math.pow(.7,arguments.length?t:1);var e=this.r,n=this.g,r=this.b,a=30;return e||n||r?(e&&a>e&&(e=a),n&&a>n&&(n=a),r&&a>r&&(r=a),new xt(Math.min(255,e/t),Math.min(255,n/t),Math.min(255,r/t))):new xt(a,a,a)},il.darker=function(t){return t=Math.pow(.7,arguments.length?t:1),new xt(t*this.r,t*this.g,t*this.b)},il.hsl=function(){return Mt(this.r,this.g,this.b)},il.toString=function(){return\"#\"+wt(this.r)+wt(this.g)+wt(this.b)};var ll=ui.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsa"
+,
+"lmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ll.forEach(function(t,e){ll.set(t,bt(e))}),ui.functor=zt,ui.xhr=St(b),ui.dsv=function(t,e){function n(t,n,o){arguments.length<3&&(o=n,n=null);var i=Et(t,e,null==n?r:a(n),o);return i.row=function(t){return arguments.length?i.response(null==(n=t)?r:a(t)):n},i}function r(t){return n.parse(t.responseText)}function a(t){return function(e){return n.parse(e.responseText,t)}}function o(e){return e.map(i).join(t)}function i(t){return l.test(t)?'\"'+t.replace(/\\\"/g,'\"\"')+'\"':t}var l=new RegExp('[\"'+t+\"\\n]\"),s=t.charCodeAt(0);return n.parse=function(t,e){var r;return n.parseRows(t,function(t,n){if(r)return r(t,n-1);var a=new Function(\"d\",\"return {\"+t.map(function(t,e){return JSON.stringify(t)+\": d[\"+e+\"]\"}).join(\",\")+\"}\");r=e?function(t,n){return e(a(t),n)}:a})},n.parseRows=function(t,e){function n(){if(u>=c)return i;if(a)return a=!1,o;var e=u;if(34===t.charCodeAt(e)){for(var n=e;n++<c;)if(34===t.charCodeAt(n)){if(34!==t.charCodeAt(n+1))break;++n}u=n+2;var r=t.charCodeAt(n+1);return 13===r?(a=!0,10===t.charCodeAt(n+2)&&++u):10===r&&(a=!0),t.slice(e+1,n).replace(/\"\"/g,'\"')}for(;c>u;){var r=t.charCodeAt(u++),l=1;if(10===r)a=!0;else if(13===r)a=!0,10===t.charCodeAt(u)&&(++u,++l);else if(r!==s)continue;return t.slice(e,u-l)}return t.slice(e)}for(var r,a,o={},i={},l=[],c=t.length,u=0,f=0;(r=n())!==i;){for(var d=[];r!==o&&r!==i;)d.push(r),r=n();e&&null==(d=e(d,f++))||l.push(d)}return l},n.format=function(e){if(Array.isArray(e[0]))return n.formatRows(e);var r=new x,a=[];return e.forEach(function(t){for(var e in t)r.has(e)||a.push(r.add(e))}),[a.map(i).join(t)].concat(e.map(function(e){return a.map(function(t){return i(e[t])}).join(t)})).join(\"\\n\")},n.formatRows=function(t){return t.map(o).join(\"\\n\")},n},ui.csv=ui.dsv(\",\",\"text/csv\"),ui.tsv=ui.dsv(\"	\",\"text/tab-separated-values\");var sl,cl,ul,fl,dl=this[w(this,\"requestAnimationFrame\")]||function(t){setTimeout(t,17)};ui.timer=function(){Pt.apply(this,arguments)},ui.timer.flush=function(){Dt(),It()},ui.round=function(t,e){return e?Math.round(t*(e=Math.pow(10,e)))/e:Math.round(t)};var hl=[\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"\\xb5\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"].map(jt);ui.formatPrefix=function(t,e){var n=0;return(t=+t)&&(0>t&&(t*=-1),e&&(t=ui.round(t,Rt(t,e))),n=1+Math.floor(1e-12+Math.log(t)/Math.LN10),n=Math.max(-24,Math.min(24,3*Math.floor((n-1)/3)))),hl[8+n/3]};var pl=/(?:([^{])?([<>=^]))?([+\\- ])?([$#])?(0)?(\\d+)?(,)?(\\.-?\\d+)?([a-z%])?/i,gl=ui.map({b:function(t){return t.toString(2)},c:function(t){return String.fromCharCode(t)},o:function(t){return t.toString(8)},x:function(t){return t.toString(16)},X:function(t){return t.toString(16).toUpperCase()},g:function(t,e){return t.toPrecision(e)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},r:function(t,e){return(t=ui.round(t,Rt(t,e))).toFixed(Math.max(0,Math.min(20,Rt(t*(1+1e-15),e))))}}),vl=ui.time={},ml=Date;Bt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){yl.setUTCDate.apply(this._,arguments)},setDay:function(){yl.setUTCDay.apply(this._,arguments)},setFullYear:function(){yl.setUTCFullYear.apply(this._,arguments)},setHours:function(){yl.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){yl.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){yl.setUTCMinutes.apply(this._,arguments)},setMonth:function(){yl.setUTCMonth.apply(this._,arguments)},setSeconds:function(){yl.setUTCSeconds.apply(this._,arguments)},setTime:function(){yl.setTime.apply(this._,arguments)}};var yl=Date.prototype;vl.year=Ht(function(t){return t=vl.day(t),t.setMonth(0,1),t},function(t,e){t.setFullYear(t.getFullYear()+e)},function(t){return t.getFullYear()}),vl.years=vl.year.range,vl.years.utc=vl.year.utc.range,vl.day=Ht(function(t){var e=new ml(2e3,0);return e.setFullYear(t.getFullYear(),t.getMonth(),t.getDate()),e},function(t,e){t.setDate(t.getDate()+e)},function(t){return t.getDate()-1}),vl.days=vl.day.range,vl.days.utc=vl.day.utc.range,vl.dayOfYear=function(t){var e=vl.year(t);return Math.floor((t-e-6e4*(t.getTimezoneOffset()-e.getTimezoneOffset()))/864e5)},[\"sunday\",\"monday\",\"tuesday\",\"wednesday\",\"thursday\",\"friday\",\"saturday\"].forEach(function(t,e){e=7-e;var n=vl[t]=Ht(function(t){return(t=vl.day(t)).setDate(t.getDate()-(t.getDay()+e)%7),t},function(t,e){t.setDate(t.getDate()+7*Math.floor(e))},function(t){var n=vl.year(t).getDay();return Math.floor((vl.dayOfYear(t)+(n+e)%7)/7)-(n!==e)});vl[t+\"s\"]=n.range,vl[t+\"s\"].utc=n.utc.range,vl[t+\"OfYear\"]=function(t){var n=vl.year(t).getDay();return Math.floor((vl.dayOfYear(t)+(n+e)%7)/7)}}),vl.week=vl.sunday,vl.weeks=vl.sunday.range,vl.weeks.utc=vl.sunday.utc.range,vl.weekOfYear=vl.sundayOfYear;var xl={\"-\":\"\",_:\" \",0:\"0\"},bl=/^\\s*\\d+/,_l=/^%/;ui.locale=function(t){return{numberFormat:qt(t),timeFormat:Zt(t)}};var wl=ui.locale({decimal:\".\",thousands:\",\",grouping:[3],currency:[\"$\",\"\"],dateTime:\"%a %b %e %X %Y\",date:\"%m/%d/%Y\",time:\"%H:%M:%S\",periods:[\"AM\",\"PM\"],days:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],shortDays:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],months:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],shortMonths:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"]});ui.format=wl.numberFormat,ui.geo={},fe.prototype={s:0,t:0,add:function(t){de(t,this.t,kl),de(kl.s,this.s,this),this.s?this.t+=kl.t:this.s=kl.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var kl=new fe;ui.geo.stream=function(t,e){t&&Ml.hasOwnProperty(t.type)?Ml[t.type](t,e):he(t,e)};var Ml={Feature:function(t,e){he(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,a=n.length;++r<a;)he(n[r].geometry,e)}},Al={Sphere:function(t,e){e.sphere()},Point:function(t,e){t=t.coordinates,e.point(t[0],t[1],t[2])},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,a=n.length;++r<a;)t=n[r],e.point(t[0],t[1],t[2])},LineString:function(t,e){pe(t.coordinates,e,0)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,a=n.length;++r<a;)pe(n[r],e,0)},Polygon:function(t,e){ge(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,a=n.length;++r<a;)ge(n[r],e)},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,a=n.length;++r<a;)he(n[r],e)}};ui.geo.area=function(t){return Ll=0,ui.geo.stream(t,zl),Ll};var Ll,Tl=new fe,zl={sphere:function(){Ll+=4*Bi},point:k,lineStart:k,lineEnd:k,polygonStart:function(){Tl.reset(),zl.lineStart=ve},polygonEnd:function(){var t=2*Tl;Ll+=0>t?4*Bi+t:t,zl.lineStart=zl.lineEnd=zl.point=k}};ui.geo.bounds=function(){function t(t,e){x.push(b=[u=t,d=t]),f>e&&(f=e),e>h&&(h=e)}function e(e,n){var r=me([e*Yi,n*Yi]);if(m){var a=xe(m,r),o=[a[1],-a[0],0],i=xe(o,a);we(i),i=ke(i);var s=e-p,c=s>0?1:-1,g=i[0]*Ui*c,v=wi(s)>180;if(v^(g>c*p&&c*e>g)){var y=i[1]*Ui;y>h&&(h=y)}else if(g=(g+360)%360-180,v^(g>c*p&&c*e>g)){var y=-i[1]*Ui;f>y&&(f=y)}else f>n&&(f=n),n>h&&(h=n);v?p>e?l(u,e)>l(u,d)&&(d=e):l(e,d)>l(u,d)&&(u=e):d>=u?(u>e&&(u=e),e>d&&(d=e)):e>p?l(u,e)>l(u,d)&&(d=e):l(e,d)>l(u,d)&&(u=e)}else t(e,n);m=r,p=e}function n(){_.point=e}function r(){b[0]=u,b[1]=d,_.point=t,m=null}function a(t,n){if(m){var r=t-p;y+=wi(r)>180?r+(r>0?360:-360):r}else g=t,v=n;zl.point(t,n),e(t,n)}function o(){zl.lineStart()}function i(){a(g,v),zl.lineEnd(),wi(y)>qi&&(u=-(d=180)),b[0]=u,b[1]=d,m=null}function l(t,e){return(e-=t)<0?e+360:e}function s(t,e){return t[0]-e[0]}function c(t,e){return e[0]<=e[1]?e[0]<=t&&t<=e[1]:t<e[0]||e[1]<t}var u,f,d,h,p,g,v,m,y,x,b,_={point:t,lineStart:n,lineEnd:r,polygonStart:function(){_.point=a,_.lineStart=o,_.lineEnd=i,y=0,zl.polygonStart()},polygonEnd:function(){zl.polygonEnd(),_.point=t,_.lineStart=n,_.lineEnd=r,0>Tl?(u=-(d=180),f=-(h=90)):y>qi?h=90:-qi>y&&(f=-90),b[0]=u,b[1]=d}};return function(t){h=d=-(u=f=1/0),x=[],ui.geo.stream(t,_);var e=x.length;if(e){x.sort(s);for(var n,r=1,a=x[0],o=[a];e>r;++r)n=x[r],c(n[0],a)||c(n[1],a)?(l(a[0],n[1])>l(a[0],a[1])&&(a[1]=n[1]),l(n[0],a[1])>l(a[0],a[1])&&(a[0]=n[0])):o.push(a=n);for(var i,n,p=-(1/0),e=o.length-1,r=0,a=o[e];e>=r;a=n,++r)n=o[r],(i=l(a[1],n[0]))>p&&(p=i,u=n[0],d=a[1])}return x=b=null,u===1/0||f===1/0?[[NaN,NaN],[NaN,NaN]]:"
+,
+"[[u,f],[d,h]]}}(),ui.geo.centroid=function(t){Sl=El=Cl=Ol=Pl=Nl=Dl=Il=Rl=jl=ql=0,ui.geo.stream(t,Fl);var e=Rl,n=jl,r=ql,a=e*e+n*n+r*r;return Fi>a&&(e=Nl,n=Dl,r=Il,qi>El&&(e=Cl,n=Ol,r=Pl),a=e*e+n*n+r*r,Fi>a)?[NaN,NaN]:[Math.atan2(n,e)*Ui,rt(r/Math.sqrt(a))*Ui]};var Sl,El,Cl,Ol,Pl,Nl,Dl,Il,Rl,jl,ql,Fl={sphere:k,point:Ae,lineStart:Te,lineEnd:ze,polygonStart:function(){Fl.lineStart=Se},polygonEnd:function(){Fl.lineStart=Te}},Bl=De(Ce,qe,Be,[-Bi,-Bi/2]),Hl=1e9;ui.geo.clipExtent=function(){var t,e,n,r,a,o,i={stream:function(t){return a&&(a.valid=!1),a=o(t),a.valid=!0,a},extent:function(l){return arguments.length?(o=Ye(t=+l[0][0],e=+l[0][1],n=+l[1][0],r=+l[1][1]),a&&(a.valid=!1,a=null),i):[[t,e],[n,r]]}};return i.extent([[0,0],[960,500]])},(ui.geo.conicEqualArea=function(){return Ue(Xe)}).raw=Xe,ui.geo.albers=function(){return ui.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ui.geo.albersUsa=function(){function t(t){var o=t[0],i=t[1];return e=null,n(o,i),e||(r(o,i),e)||a(o,i),e}var e,n,r,a,o=ui.geo.albers(),i=ui.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),l=ui.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),s={point:function(t,n){e=[t,n]}};return t.invert=function(t){var e=o.scale(),n=o.translate(),r=(t[0]-n[0])/e,a=(t[1]-n[1])/e;return(a>=.12&&.234>a&&r>=-.425&&-.214>r?i:a>=.166&&.234>a&&r>=-.214&&-.115>r?l:o).invert(t)},t.stream=function(t){var e=o.stream(t),n=i.stream(t),r=l.stream(t);return{point:function(t,a){e.point(t,a),n.point(t,a),r.point(t,a)},sphere:function(){e.sphere(),n.sphere(),r.sphere()},lineStart:function(){e.lineStart(),n.lineStart(),r.lineStart()},lineEnd:function(){e.lineEnd(),n.lineEnd(),r.lineEnd()},polygonStart:function(){e.polygonStart(),n.polygonStart(),r.polygonStart()},polygonEnd:function(){e.polygonEnd(),n.polygonEnd(),r.polygonEnd()}}},t.precision=function(e){return arguments.length?(o.precision(e),i.precision(e),l.precision(e),t):o.precision()},t.scale=function(e){return arguments.length?(o.scale(e),i.scale(.35*e),l.scale(e),t.translate(o.translate())):o.scale()},t.translate=function(e){if(!arguments.length)return o.translate();var c=o.scale(),u=+e[0],f=+e[1];return n=o.translate(e).clipExtent([[u-.455*c,f-.238*c],[u+.455*c,f+.238*c]]).stream(s).point,r=i.translate([u-.307*c,f+.201*c]).clipExtent([[u-.425*c+qi,f+.12*c+qi],[u-.214*c-qi,f+.234*c-qi]]).stream(s).point,a=l.translate([u-.205*c,f+.212*c]).clipExtent([[u-.214*c+qi,f+.166*c+qi],[u-.115*c-qi,f+.234*c-qi]]).stream(s).point,t},t.scale(1070)};var Vl,Zl,Yl,Ul,Xl,Gl,$l={point:k,lineStart:k,lineEnd:k,polygonStart:function(){Zl=0,$l.lineStart=Ge},polygonEnd:function(){$l.lineStart=$l.lineEnd=$l.point=k,Vl+=wi(Zl/2)}},Ql={point:$e,lineStart:k,lineEnd:k,polygonStart:k,polygonEnd:k},Wl={point:Je,lineStart:Ke,lineEnd:tn,polygonStart:function(){Wl.lineStart=en},polygonEnd:function(){Wl.point=Je,Wl.lineStart=Ke,Wl.lineEnd=tn}};ui.geo.path=function(){function t(t){return t&&(\"function\"==typeof l&&o.pointRadius(+l.apply(this,arguments)),i&&i.valid||(i=a(o)),ui.geo.stream(t,i)),o.result()}function e(){return i=null,t}var n,r,a,o,i,l=4.5;return t.area=function(t){return Vl=0,ui.geo.stream(t,a($l)),Vl},t.centroid=function(t){return Cl=Ol=Pl=Nl=Dl=Il=Rl=jl=ql=0,ui.geo.stream(t,a(Wl)),ql?[Rl/ql,jl/ql]:Il?[Nl/Il,Dl/Il]:Pl?[Cl/Pl,Ol/Pl]:[NaN,NaN]},t.bounds=function(t){return Xl=Gl=-(Yl=Ul=1/0),ui.geo.stream(t,a(Ql)),[[Yl,Ul],[Xl,Gl]]},t.projection=function(t){return arguments.length?(a=(n=t)?t.stream||an(t):b,e()):n},t.context=function(t){return arguments.length?(o=null==(r=t)?new Qe:new nn(t),\"function\"!=typeof l&&o.pointRadius(l),e()):r},t.pointRadius=function(e){return arguments.length?(l=\"function\"==typeof e?e:(o.pointRadius(+e),+e),t):l},t.projection(ui.geo.albersUsa()).context(null)},ui.geo.transform=function(t){return{stream:function(e){var n=new on(e);for(var r in t)n[r]=t[r];return n}}},on.prototype={point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ui.geo.projection=sn,ui.geo.projectionMutator=cn,(ui.geo.equirectangular=function(){return sn(fn)}).raw=fn.invert=fn,ui.geo.rotation=function(t){function e(e){return e=t(e[0]*Yi,e[1]*Yi),e[0]*=Ui,e[1]*=Ui,e}return t=hn(t[0]%360*Yi,t[1]*Yi,t.length>2?t[2]*Yi:0),e.invert=function(e){return e=t.invert(e[0]*Yi,e[1]*Yi),e[0]*=Ui,e[1]*=Ui,e},e},dn.invert=fn,ui.geo.circle=function(){function t(){var t=\"function\"==typeof r?r.apply(this,arguments):r,e=hn(-t[0]*Yi,-t[1]*Yi,0).invert,a=[];return n(null,null,1,{point:function(t,n){a.push(t=e(t,n)),t[0]*=Ui,t[1]*=Ui}}),{type:\"Polygon\",coordinates:[a]}}var e,n,r=[0,0],a=6;return t.origin=function(e){return arguments.length?(r=e,t):r},t.angle=function(r){return arguments.length?(n=mn((e=+r)*Yi,a*Yi),t):e},t.precision=function(r){return arguments.length?(n=mn(e*Yi,(a=+r)*Yi),t):a},t.angle(90)},ui.geo.distance=function(t,e){var n,r=(e[0]-t[0])*Yi,a=t[1]*Yi,o=e[1]*Yi,i=Math.sin(r),l=Math.cos(r),s=Math.sin(a),c=Math.cos(a),u=Math.sin(o),f=Math.cos(o);return Math.atan2(Math.sqrt((n=f*i)*n+(n=c*u-s*f*l)*n),s*u+c*f*l)},ui.geo.graticule=function(){function t(){return{type:\"MultiLineString\",coordinates:e()}}function e(){return ui.range(Math.ceil(o/v)*v,a,v).map(d).concat(ui.range(Math.ceil(c/m)*m,s,m).map(h)).concat(ui.range(Math.ceil(r/p)*p,n,p).filter(function(t){return wi(t%v)>qi}).map(u)).concat(ui.range(Math.ceil(l/g)*g,i,g).filter(function(t){return wi(t%m)>qi}).map(f))}var n,r,a,o,i,l,s,c,u,f,d,h,p=10,g=p,v=90,m=360,y=2.5;return t.lines=function(){return e().map(function(t){return{type:\"LineString\",coordinates:t}})},t.outline=function(){return{type:\"Polygon\",coordinates:[d(o).concat(h(s).slice(1),d(a).reverse().slice(1),h(c).reverse().slice(1))]}},t.extent=function(e){return arguments.length?t.majorExtent(e).minorExtent(e):t.minorExtent()},t.majorExtent=function(e){return arguments.length?(o=+e[0][0],a=+e[1][0],c=+e[0][1],s=+e[1][1],o>a&&(e=o,o=a,a=e),c>s&&(e=c,c=s,s=e),t.precision(y)):[[o,c],[a,s]]},t.minorExtent=function(e){return arguments.length?(r=+e[0][0],n=+e[1][0],l=+e[0][1],i=+e[1][1],r>n&&(e=r,r=n,n=e),l>i&&(e=l,l=i,i=e),t.precision(y)):[[r,l],[n,i]]},t.step=function(e){return arguments.length?t.majorStep(e).minorStep(e):t.minorStep()},t.majorStep=function(e){return arguments.length?(v=+e[0],m=+e[1],t):[v,m]},t.minorStep=function(e){return arguments.length?(p=+e[0],g=+e[1],t):[p,g]},t.precision=function(e){return arguments.length?(y=+e,u=xn(l,i,90),f=bn(r,n,y),d=xn(c,s,90),h=bn(o,a,y),t):y},t.majorExtent([[-180,-90+qi],[180,90-qi]]).minorExtent([[-180,-80-qi],[180,80+qi]])},ui.geo.greatArc=function(){function t(){return{type:\"LineString\",coordinates:[e||r.apply(this,arguments),n||a.apply(this,arguments)]}}var e,n,r=_n,a=wn;return t.distance=function(){return ui.geo.distance(e||r.apply(this,arguments),n||a.apply(this,arguments))},t.source=function(n){return arguments.length?(r=n,e=\"function\"==typeof n?null:n,t):r},t.target=function(e){return arguments.length?(a=e,n=\"function\"==typeof e?null:e,t):a},t.precision=function(){return arguments.length?t:0},t},ui.geo.interpolate=function(t,e){return kn(t[0]*Yi,t[1]*Yi,e[0]*Yi,e[1]*Yi)},ui.geo.length=function(t){return Jl=0,ui.geo.stream(t,Kl),Jl};var Jl,Kl={sphere:k,point:k,lineStart:Mn,lineEnd:k,polygonStart:k,polygonEnd:k},ts=An(function(t){return Math.sqrt(2/(1+t))},function(t){return 2*Math.asin(t/2)});(ui.geo.azimuthalEqualArea=function(){return sn(ts)}).raw=ts;var es=An(function(t){var e=Math.acos(t);return e&&e/Math.sin(e)},b);(ui.geo.azimuthalEquidistant=function(){return sn(es)}).raw=es,(ui.geo.conicConformal=function(){return Ue(Ln)}).raw=Ln,(ui.geo.conicEquidistant=function(){return Ue(Tn)}).raw=Tn;var ns=An(function(t){return 1/t},Math.atan);(ui.geo.gnomonic=function(){return sn(ns)}).raw=ns,zn.invert=function(t,e){return[t,2*Math.atan(Math.exp(e))-Zi]},(ui.geo.mercator=function(){return Sn(zn)}).raw=zn;var rs=An(function(){return 1},Math.asin);(ui.geo.orthographic=function(){return sn(rs)}).raw=rs;var as=An(function(t){return 1/(1+t)},function(t){return 2*Math.atan(t)});(ui.geo.stereographic=function(){return sn(as)}).raw=as,En.invert=function(t,e){return[-e,2*Math.atan(Math.exp(t))-Zi]},(ui.geo.transverseMercator=function(){var t=Sn(En),e=t.center,n=t.rotate;return t.center=function(t){return t?e([-t[1],t[0]]):(t=e(),[t[1],-t[0]])},t.rotate=function(t){return t?n([t[0],t[1],t.length>2?t[2]+90:90]):(t=n(),[t[0],t[1],t[2]-90])},n([0,0,90])}).raw=En,ui.geom={},ui.geom.hull=function(t){function e(t){if(t.length<3)return[];var e,a=zt(n),o=zt(r),i=t.length,l=[],s=[];for(e=0;i>e;e++)l.push([+a.call(this,t[e],e),+o.call(this,t[e],e),e]);for(l.sort(Nn),e=0;i>e;e++)s.push([l[e][0],-l[e][1]]);var c=Pn(l),u=Pn(s),f=u[0]===c[0],d=u[u.length-1]===c[c.length-1],h=[];for(e=c.length-1;e>=0;--e)h.push(t[l[c[e]][2]]);for(e=+f;e<u.length-d;++e)h.push(t[l[u[e]][2]]);return h}var n=Cn,r=On;return arguments.length?e(t):(e.x=function(t){return arguments.length?(n=t,e):n},e.y=function(t){return arguments.length?(r=t,e):r},e)},ui.geom.polygon=function(t){return Ti(t,os),t};var os=ui.geom.polygon.prototype=[];os.area=function(){for(var t,e=-1,n=this.length,r=this[n-1],a=0;++e<n;)t=r,r=this[e],a+=t[1]*r[0]-t[0]*r[1];return.5*a},os.centroid=function(t){var e,n,r=-1,a=this.length,o=0,i=0,l=this[a-1];for(arguments.length||(t=-1/(6*this.area()));++r<a;)e=l,l=this[r],n=e[0]*l[1]-l[0]*e[1],o+=(e[0]+l[0])*n,i+=(e[1]+l[1])*n;return[o*t,i*t]},os.clip=function(t){for(var e,n,r,a,o,i,l=Rn(t),s=-1,c=this.length-Rn(this),u=this[c-1];++s<c;){for(e=t.slice(),t.length=0,a=this[s],o=e[(r=e.length-l)-1],n=-1;++n<r;)i=e[n],Dn(i,u,a)?(Dn(o,u,a)||t.push(In(o,i,u,a)),t.push(i)):Dn(o,u,a)&&t.push(In(o,i,u,a)),o=i;l&&t.push(t[0]),u=a}return t};var is,ls,ss,cs,us,fs=[],ds=[];Yn.prototype.prepare=function(){for(var t,e=this.edges,n=e"
+,
+".length;n--;)t=e[n].edge,t.b&&t.a||e.splice(n,1);return e.sort(Xn),e.length},rr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},ar.prototype={insert:function(t,e){var n,r,a;if(t){if(e.P=t,e.N=t.N,t.N&&(t.N.P=e),t.N=e,t.R){for(t=t.R;t.L;)t=t.L;t.L=e}else t.R=e;n=t}else this._?(t=sr(this._),e.P=null,e.N=t,t.P=t.L=e,n=t):(e.P=e.N=null,this._=e,n=null);for(e.L=e.R=null,e.U=n,e.C=!0,t=e;n&&n.C;)r=n.U,n===r.L?(a=r.R,a&&a.C?(n.C=a.C=!1,r.C=!0,t=r):(t===n.R&&(ir(this,n),t=n,n=t.U),n.C=!1,r.C=!0,lr(this,r))):(a=r.L,a&&a.C?(n.C=a.C=!1,r.C=!0,t=r):(t===n.L&&(lr(this,n),t=n,n=t.U),n.C=!1,r.C=!0,ir(this,r))),n=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var e,n,r,a=t.U,o=t.L,i=t.R;if(n=o?i?sr(i):o:i,a?a.L===t?a.L=n:a.R=n:this._=n,o&&i?(r=n.C,n.C=t.C,n.L=o,o.U=n,n!==i?(a=n.U,n.U=t.U,t=n.R,a.L=t,n.R=i,i.U=n):(n.U=a,a=n,t=n.R)):(r=t.C,t=n),t&&(t.U=a),!r){if(t&&t.C)return void(t.C=!1);do{if(t===this._)break;if(t===a.L){if(e=a.R,e.C&&(e.C=!1,a.C=!0,ir(this,a),e=a.R),e.L&&e.L.C||e.R&&e.R.C){e.R&&e.R.C||(e.L.C=!1,e.C=!0,lr(this,e),e=a.R),e.C=a.C,a.C=e.R.C=!1,ir(this,a),t=this._;break}}else if(e=a.L,e.C&&(e.C=!1,a.C=!0,lr(this,a),e=a.L),e.L&&e.L.C||e.R&&e.R.C){e.L&&e.L.C||(e.R.C=!1,e.C=!0,ir(this,e),e=a.L),e.C=a.C,a.C=e.L.C=!1,lr(this,a),t=this._;break}e.C=!0,t=a,a=a.U}while(!t.C);t&&(t.C=!1)}}},ui.geom.voronoi=function(t){function e(t){var e=new Array(t.length),r=l[0][0],a=l[0][1],o=l[1][0],i=l[1][1];return cr(n(t),l).cells.forEach(function(n,l){var s=n.edges,c=n.site,u=e[l]=s.length?s.map(function(t){var e=t.start();return[e.x,e.y]}):c.x>=r&&c.x<=o&&c.y>=a&&c.y<=i?[[r,i],[o,i],[o,a],[r,a]]:[];u.point=t[l]}),e}function n(t){return t.map(function(t,e){return{x:Math.round(o(t,e)/qi)*qi,y:Math.round(i(t,e)/qi)*qi,i:e}})}var r=Cn,a=On,o=r,i=a,l=hs;return t?e(t):(e.links=function(t){return cr(n(t)).edges.filter(function(t){return t.l&&t.r}).map(function(e){\n"
+,
+"return{source:t[e.l.i],target:t[e.r.i]}})},e.triangles=function(t){var e=[];return cr(n(t)).cells.forEach(function(n,r){for(var a,o,i=n.site,l=n.edges.sort(Xn),s=-1,c=l.length,u=l[c-1].edge,f=u.l===i?u.r:u.l;++s<c;)a=u,o=f,u=l[s].edge,f=u.l===i?u.r:u.l,r<o.i&&r<f.i&&fr(i,o,f)<0&&e.push([t[r],t[o.i],t[f.i]])}),e},e.x=function(t){return arguments.length?(o=zt(r=t),e):r},e.y=function(t){return arguments.length?(i=zt(a=t),e):a},e.clipExtent=function(t){return arguments.length?(l=null==t?hs:t,e):l===hs?null:l},e.size=function(t){return arguments.length?e.clipExtent(t&&[[0,0],t]):l===hs?null:l&&l[1]},e)};var hs=[[-1e6,-1e6],[1e6,1e6]];ui.geom.delaunay=function(t){return ui.geom.voronoi().triangles(t)},ui.geom.quadtree=function(t,e,n,r,a){function o(t){function o(t,e,n,r,a,o,i,l){if(!isNaN(n)&&!isNaN(r))if(t.leaf){var s=t.x,u=t.y;if(null!=s)if(wi(s-n)+wi(u-r)<.01)c(t,e,n,r,a,o,i,l);else{var f=t.point;t.x=t.y=t.point=null,c(t,f,s,u,a,o,i,l),c(t,e,n,r,a,o,i,l)}else t.x=n,t.y=r,t.point=e}else c(t,e,n,r,a,o,i,l)}function c(t,e,n,r,a,i,l,s){var c=.5*(a+l),u=.5*(i+s),f=n>=c,d=r>=u,h=d<<1|f;t.leaf=!1,t=t.nodes[h]||(t.nodes[h]=pr()),f?a=c:l=c,d?i=u:s=u,o(t,e,n,r,a,i,l,s)}var u,f,d,h,p,g,v,m,y,x=zt(l),b=zt(s);if(null!=e)g=e,v=n,m=r,y=a;else if(m=y=-(g=v=1/0),f=[],d=[],p=t.length,i)for(h=0;p>h;++h)u=t[h],u.x<g&&(g=u.x),u.y<v&&(v=u.y),u.x>m&&(m=u.x),u.y>y&&(y=u.y),f.push(u.x),d.push(u.y);else for(h=0;p>h;++h){var _=+x(u=t[h],h),w=+b(u,h);g>_&&(g=_),v>w&&(v=w),_>m&&(m=_),w>y&&(y=w),f.push(_),d.push(w)}var k=m-g,M=y-v;k>M?y=v+k:m=g+M;var A=pr();if(A.add=function(t){o(A,t,+x(t,++h),+b(t,h),g,v,m,y)},A.visit=function(t){gr(t,A,g,v,m,y)},A.find=function(t){return vr(A,t[0],t[1],g,v,m,y)},h=-1,null==e){for(;++h<p;)o(A,t[h],f[h],d[h],g,v,m,y);--h}else t.forEach(A.add);return f=d=t=u=null,A}var i,l=Cn,s=On;return(i=arguments.length)?(l=dr,s=hr,3===i&&(a=n,r=e,n=e=0),o(t)):(o.x=function(t){return arguments.length?(l=t,o):l},o.y=function(t){return arguments.length?(s=t,o):s},o.extent=function(t){return arguments.length?(null==t?e=n=r=a=null:(e=+t[0][0],n=+t[0][1],r=+t[1][0],a=+t[1][1]),o):null==e?null:[[e,n],[r,a]]},o.size=function(t){return arguments.length?(null==t?e=n=r=a=null:(e=n=0,r=+t[0],a=+t[1]),o):null==e?null:[r-e,a-n]},o)},ui.interpolateRgb=mr,ui.interpolateObject=yr,ui.interpolateNumber=xr,ui.interpolateString=br;var ps=/[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,gs=new RegExp(ps.source,\"g\");ui.interpolate=_r,ui.interpolators=[function(t,e){var n=typeof e;return(\"string\"===n?ll.has(e.toLowerCase())||/^(#|rgb\\(|hsl\\()/i.test(e)?mr:br:e instanceof st?mr:Array.isArray(e)?wr:\"object\"===n&&isNaN(e)?yr:xr)(t,e)}],ui.interpolateArray=wr;var vs=function(){return b},ms=ui.map({linear:vs,poly:Sr,quad:function(){return Lr},cubic:function(){return Tr},sin:function(){return Er},exp:function(){return Cr},circle:function(){return Or},elastic:Pr,back:Nr,bounce:function(){return Dr}}),ys=ui.map({\"in\":b,out:Mr,\"in-out\":Ar,\"out-in\":function(t){return Ar(Mr(t))}});ui.ease=function(t){var e=t.indexOf(\"-\"),n=e>=0?t.slice(0,e):t,r=e>=0?t.slice(e+1):\"in\";return n=ms.get(n)||vs,r=ys.get(r)||b,kr(r(n.apply(null,fi.call(arguments,1))))},ui.interpolateHcl=Ir,ui.interpolateHsl=Rr,ui.interpolateLab=jr,ui.interpolateRound=qr,ui.transform=function(t){var e=hi.createElementNS(ui.ns.prefix.svg,\"g\");return(ui.transform=function(t){if(null!=t){e.setAttribute(\"transform\",t);var n=e.transform.baseVal.consolidate()}return new Fr(n?n.matrix:xs)})(t)},Fr.prototype.toString=function(){return\"translate(\"+this.translate+\")rotate(\"+this.rotate+\")skewX(\"+this.skew+\")scale(\"+this.scale+\")\"};var xs={a:1,b:0,c:0,d:1,e:0,f:0};ui.interpolateTransform=$r,ui.layout={},ui.layout.bundle=function(){return function(t){for(var e=[],n=-1,r=t.length;++n<r;)e.push(Jr(t[n]));return e}},ui.layout.chord=function(){function t(){var t,c,f,d,h,p={},g=[],v=ui.range(o),m=[];for(n=[],r=[],t=0,d=-1;++d<o;){for(c=0,h=-1;++h<o;)c+=a[d][h];g.push(c),m.push(ui.range(o)),t+=c}for(i&&v.sort(function(t,e){return i(g[t],g[e])}),l&&m.forEach(function(t,e){t.sort(function(t,n){return l(a[e][t],a[e][n])})}),t=(Hi-u*o)/t,c=0,d=-1;++d<o;){for(f=c,h=-1;++h<o;){var y=v[d],x=m[y][h],b=a[y][x],_=c,w=c+=b*t;p[y+\"-\"+x]={index:y,subindex:x,startAngle:_,endAngle:w,value:b}}r[y]={index:y,startAngle:f,endAngle:c,value:g[y]},c+=u}for(d=-1;++d<o;)for(h=d-1;++h<o;){var k=p[d+\"-\"+h],M=p[h+\"-\"+d];(k.value||M.value)&&n.push(k.value<M.value?{source:M,target:k}:{source:k,target:M})}s&&e()}function e(){n.sort(function(t,e){return s((t.source.value+t.target.value)/2,(e.source.value+e.target.value)/2)})}var n,r,a,o,i,l,s,c={},u=0;return c.matrix=function(t){return arguments.length?(o=(a=t)&&a.length,n=r=null,c):a},c.padding=function(t){return arguments.length?(u=t,n=r=null,c):u},c.sortGroups=function(t){return arguments.length?(i=t,n=r=null,c):i},c.sortSubgroups=function(t){return arguments.length?(l=t,n=null,c):l},c.sortChords=function(t){return arguments.length?(s=t,n&&e(),c):s},c.chords=function(){return n||t(),n},c.groups=function(){return r||t(),r},c},ui.layout.force=function(){function t(t){return function(e,n,r,a){if(e.point!==t){var o=e.cx-t.x,i=e.cy-t.y,l=a-n,s=o*o+i*i;if(s>l*l/m){if(g>s){var c=e.charge/s;t.px-=o*c,t.py-=i*c}return!0}if(e.point&&s&&g>s){var c=e.pointCharge/s;t.px-=o*c,t.py-=i*c}}return!e.charge}}function e(t){t.px=ui.event.x,t.py=ui.event.y,s.resume()}var n,r,a,o,i,l,s={},c=ui.dispatch(\"start\",\"tick\",\"end\"),u=[1,1],f=.9,d=bs,h=_s,p=-30,g=ws,v=.1,m=.64,y=[],x=[];return s.tick=function(){if((a*=.99)<.005)return n=null,c.end({type:\"end\",alpha:a=0}),!0;var e,r,s,d,h,g,m,b,_,w=y.length,k=x.length;for(r=0;k>r;++r)s=x[r],d=s.source,h=s.target,b=h.x-d.x,_=h.y-d.y,(g=b*b+_*_)&&(g=a*i[r]*((g=Math.sqrt(g))-o[r])/g,b*=g,_*=g,h.x-=b*(m=d.weight+h.weight?d.weight/(d.weight+h.weight):.5),h.y-=_*m,d.x+=b*(m=1-m),d.y+=_*m);if((m=a*v)&&(b=u[0]/2,_=u[1]/2,r=-1,m))for(;++r<w;)s=y[r],s.x+=(b-s.x)*m,s.y+=(_-s.y)*m;if(p)for(oa(e=ui.geom.quadtree(y),a,l),r=-1;++r<w;)(s=y[r]).fixed||e.visit(t(s));for(r=-1;++r<w;)s=y[r],s.fixed?(s.x=s.px,s.y=s.py):(s.x-=(s.px-(s.px=s.x))*f,s.y-=(s.py-(s.py=s.y))*f);c.tick({type:\"tick\",alpha:a})},s.nodes=function(t){return arguments.length?(y=t,s):y},s.links=function(t){return arguments.length?(x=t,s):x},s.size=function(t){return arguments.length?(u=t,s):u},s.linkDistance=function(t){return arguments.length?(d=\"function\"==typeof t?t:+t,s):d},s.distance=s.linkDistance,s.linkStrength=function(t){return arguments.length?(h=\"function\"==typeof t?t:+t,s):h},s.friction=function(t){return arguments.length?(f=+t,s):f},s.charge=function(t){return arguments.length?(p=\"function\"==typeof t?t:+t,s):p},s.chargeDistance=function(t){return arguments.length?(g=t*t,s):Math.sqrt(g)},s.gravity=function(t){return arguments.length?(v=+t,s):v},s.theta=function(t){return arguments.length?(m=t*t,s):Math.sqrt(m)},s.alpha=function(t){return arguments.length?(t=+t,a?t>0?a=t:(n.c=null,n.t=NaN,n=null,c.end({type:\"end\",alpha:a=0})):t>0&&(c.start({type:\"start\",alpha:a=t}),n=Pt(s.tick)),s):a},s.start=function(){function t(t,r){if(!n){for(n=new Array(a),s=0;a>s;++s)n[s]=[];for(s=0;c>s;++s){var o=x[s];n[o.source.index].push(o.target),n[o.target.index].push(o.source)}}for(var i,l=n[e],s=-1,u=l.length;++s<u;)if(!isNaN(i=l[s][t]))return i;return Math.random()*r}var e,n,r,a=y.length,c=x.length,f=u[0],g=u[1];for(e=0;a>e;++e)(r=y[e]).index=e,r.weight=0;for(e=0;c>e;++e)r=x[e],\"number\"==typeof r.source&&(r.source=y[r.source]),\"number\"==typeof r.target&&(r.target=y[r.target]),++r.source.weight,++r.target.weight;for(e=0;a>e;++e)r=y[e],isNaN(r.x)&&(r.x=t(\"x\",f)),isNaN(r.y)&&(r.y=t(\"y\",g)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(o=[],\"function\"==typeof d)for(e=0;c>e;++e)o[e]=+d.call(this,x[e],e);else for(e=0;c>e;++e)o[e]=d;if(i=[],\"function\"==typeof h)for(e=0;c>e;++e)i[e]=+h.call(this,x[e],e);else for(e=0;c>e;++e)i[e]=h;if(l=[],\"function\"==typeof p)for(e=0;a>e;++e)l[e]=+p.call(this,y[e],e);else for(e=0;a>e;++e)l[e]=p;return s.resume()},s.resume=function(){return s.alpha(.1)},s.stop=function(){return s.alpha(0)},s.drag=function(){return r||(r=ui.behavior.drag().origin(b).on(\"dragstart.force\",ea).on(\"drag.force\",e).on(\"dragend.force\",na)),arguments.length?void this.on(\"mouseover.force\",ra).on(\"mouseout.force\",aa).call(r):r},ui.rebind(s,c,\"on\")};var bs=20,_s=1,ws=1/0;ui.layout.hierarchy=function(){function t(a){var o,i=[a],l=[];for(a.depth=0;null!=(o=i.pop());)if(l.push(o),(c=n.call(t,o,o.depth))&&(s=c.length)){for(var s,c,u;--s>=0;)i.push(u=c[s]),u.parent=o,u.depth=o.depth+1;r&&(o.value=0),o.children=c}else r&&(o.value=+r.call(t,o,o.depth)||0),delete o.children;return sa(a,function(t){var n,a;e&&(n=t.children)&&n.sort(e),r&&(a=t.parent)&&(a.value+=t.value)}),l}var e=fa,n=ca,r=ua;return t.sort=function(n){return arguments.length?(e=n,t):e},t.children=function(e){return arguments.length?(n=e,t):n},t.value=function(e){return arguments.length?(r=e,t):r},t.revalue=function(e){return r&&(la(e,function(t){t.children&&(t.value=0)}),sa(e,function(e){var n;e.children||(e.value=+r.call(t,e,e.depth)||0),(n=e.parent)&&(n.value+=e.value)})),e},t},ui.layout.partition=function(){function t(e,n,r,a){var o=e.children;if(e.x=n,e.y=e.depth*a,e.dx=r,e.dy=a,o&&(i=o.length)){var i,l,s,c=-1;for(r=e.value?r/e.value:0;++c<i;)t(l=o[c],n,s=l.value*r,a),n+=s}}function e(t){var n=t.children,r=0;if(n&&(a=n.length))for(var a,o=-1;++o<a;)r=Math.max(r,e(n[o]));return 1+r}function n(n,o){var i=r.call(this,n,o);return t(i[0],0,a[0],a[1]/e(i[0])),i}var r=ui.layout.hierarchy(),a=[1,1];return n.size=function(t){return arguments.length?(a=t,n):a},ia(n,r)},ui.layout.pie=function(){function t(i){var l,s=i.length,c=i.map(function(n,r){return+e.call(t,n,r)}),u=+(\"function\"==typeof r?r.apply(this,arguments):r),f=(\"function\"==typeof a?a.apply(this,arguments):a)-u,d=Math.min(Math.abs(f)/s,+(\"function\"==typeof o?o.apply(this,arguments):o)),h=d*(0>f?-1:1),p=ui.sum(c),g=p?(f-s*h)/p:0,v=ui.range(s),m=[];return null!=n&&v.sort(n===ks?function(t,e){return c[e]-c[t]}:function(t,e){ret"
+,
+"urn n(i[t],i[e])}),v.forEach(function(t){m[t]={data:i[t],value:l=c[t],startAngle:u,endAngle:u+=l*g+h,padAngle:d}}),m}var e=Number,n=ks,r=0,a=Hi,o=0;return t.value=function(n){return arguments.length?(e=n,t):e},t.sort=function(e){return arguments.length?(n=e,t):n},t.startAngle=function(e){return arguments.length?(r=e,t):r},t.endAngle=function(e){return arguments.length?(a=e,t):a},t.padAngle=function(e){return arguments.length?(o=e,t):o},t};var ks={};ui.layout.stack=function(){function t(l,s){if(!(d=l.length))return l;var c=l.map(function(n,r){return e.call(t,n,r)}),u=c.map(function(e){return e.map(function(e,n){return[o.call(t,e,n),i.call(t,e,n)]})}),f=n.call(t,u,s);c=ui.permute(c,f),u=ui.permute(u,f);var d,h,p,g,v=r.call(t,u,s),m=c[0].length;for(p=0;m>p;++p)for(a.call(t,c[0][p],g=v[p],u[0][p][1]),h=1;d>h;++h)a.call(t,c[h][p],g+=u[h-1][p][1],u[h][p][1]);return l}var e=b,n=va,r=ma,a=ga,o=ha,i=pa;return t.values=function(n){return arguments.length?(e=n,t):e},t.order=function(e){return arguments.length?(n=\"function\"==typeof e?e:Ms.get(e)||va,t):n},t.offset=function(e){return arguments.length?(r=\"function\"==typeof e?e:As.get(e)||ma,t):r},t.x=function(e){return arguments.length?(o=e,t):o},t.y=function(e){return arguments.length?(i=e,t):i},t.out=function(e){return arguments.length?(a=e,t):a},t};var Ms=ui.map({\"inside-out\":function(t){var e,n,r=t.length,a=t.map(ya),o=t.map(xa),i=ui.range(r).sort(function(t,e){return a[t]-a[e]}),l=0,s=0,c=[],u=[];for(e=0;r>e;++e)n=i[e],s>l?(l+=o[n],c.push(n)):(s+=o[n],u.push(n));return u.reverse().concat(c)},reverse:function(t){return ui.range(t.length).reverse()},\"default\":va}),As=ui.map({silhouette:function(t){var e,n,r,a=t.length,o=t[0].length,i=[],l=0,s=[];for(n=0;o>n;++n){for(e=0,r=0;a>e;e++)r+=t[e][n][1];r>l&&(l=r),i.push(r)}for(n=0;o>n;++n)s[n]=(l-i[n])/2;return s},wiggle:function(t){var e,n,r,a,o,i,l,s,c,u=t.length,f=t[0],d=f.length,h=[];for(h[0]=s=c=0,n=1;d>n;++n){for(e=0,a=0;u>e;++e)a+=t[e][n][1];for(e=0,o=0,l=f[n][0]-f[n-1][0];u>e;++e){for(r=0,i=(t[e][n][1]-t[e][n-1][1])/(2*l);e>r;++r)i+=(t[r][n][1]-t[r][n-1][1])/l;o+=i*t[e][n][1]}h[n]=s-=a?o/a*l:0,c>s&&(c=s)}for(n=0;d>n;++n)h[n]-=c;return h},expand:function(t){var e,n,r,a=t.length,o=t[0].length,i=1/a,l=[];for(n=0;o>n;++n){for(e=0,r=0;a>e;e++)r+=t[e][n][1];if(r)for(e=0;a>e;e++)t[e][n][1]/=r;else for(e=0;a>e;e++)t[e][n][1]=i}for(n=0;o>n;++n)l[n]=0;return l},zero:ma});ui.layout.histogram=function(){function t(t,o){for(var i,l,s=[],c=t.map(n,this),u=r.call(this,c,o),f=a.call(this,u,c,o),o=-1,d=c.length,h=f.length-1,p=e?1:1/d;++o<h;)i=s[o]=[],i.dx=f[o+1]-(i.x=f[o]),i.y=0;if(h>0)for(o=-1;++o<d;)l=c[o],l>=u[0]&&l<=u[1]&&(i=s[ui.bisect(f,l,1,h)-1],i.y+=p,i.push(t[o]));return s}var e=!0,n=Number,r=ka,a=_a;return t.value=function(e){return arguments.length?(n=e,t):n},t.range=function(e){return arguments.length?(r=zt(e),t):r},t.bins=function(e){return arguments.length?(a=\"number\"==typeof e?function(t){return wa(t,e)}:zt(e),t):a},t.frequency=function(n){return arguments.length?(e=!!n,t):e},t},ui.layout.pack=function(){function t(t,o){var i=n.call(this,t,o),l=i[0],s=a[0],c=a[1],u=null==e?Math.sqrt:\"function\"==typeof e?e:function(){return e};if(l.x=l.y=0,sa(l,function(t){t.r=+u(t.value)}),sa(l,za),r){var f=r*(e?1:Math.max(2*l.r/s,2*l.r/c))/2;sa(l,function(t){t.r+=f}),sa(l,za),sa(l,function(t){t.r-=f})}return Ca(l,s/2,c/2,e?1:1/Math.max(2*l.r/s,2*l.r/c)),i}var e,n=ui.layout.hierarchy().sort(Ma),r=0,a=[1,1];return t.size=function(e){return arguments.length?(a=e,t):a},t.radius=function(n){return arguments.length?(e=null==n||\"function\"==typeof n?n:+n,t):e},t.padding=function(e){return arguments.length?(r=+e,t):r},ia(t,n)},ui.layout.tree=function(){function t(t,a){var u=i.call(this,t,a),f=u[0],d=e(f);if(sa(d,n),d.parent.m=-d.z,la(d,r),c)la(f,o);else{var h=f,p=f,g=f;la(f,function(t){t.x<h.x&&(h=t),t.x>p.x&&(p=t),t.depth>g.depth&&(g=t)});var v=l(h,p)/2-h.x,m=s[0]/(p.x+l(p,h)/2+v),y=s[1]/(g.depth||1);la(f,function(t){t.x=(t.x+v)*m,t.y=t.depth*y})}return u}function e(t){for(var e,n={A:null,children:[t]},r=[n];null!=(e=r.pop());)for(var a,o=e.children,i=0,l=o.length;l>i;++i)r.push((o[i]=a={_:o[i],parent:e,children:(a=o[i].children)&&a.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:i}).a=a);return n.children[0]}function n(t){var e=t.children,n=t.parent.children,r=t.i?n[t.i-1]:null;if(e.length){Ra(t);var o=(e[0].z+e[e.length-1].z)/2;r?(t.z=r.z+l(t._,r._),t.m=t.z-o):t.z=o}else r&&(t.z=r.z+l(t._,r._));t.parent.A=a(t,r,t.parent.A||n[0])}function r(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function a(t,e,n){if(e){for(var r,a=t,o=t,i=e,s=a.parent.children[0],c=a.m,u=o.m,f=i.m,d=s.m;i=Da(i),a=Na(a),i&&a;)s=Na(s),o=Da(o),o.a=t,r=i.z+f-a.z-c+l(i._,a._),r>0&&(Ia(ja(i,t,n),t,r),c+=r,u+=r),f+=i.m,c+=a.m,d+=s.m,u+=o.m;i&&!Da(o)&&(o.t=i,o.m+=f-u),a&&!Na(s)&&(s.t=a,s.m+=c-d,n=t)}return n}function o(t){t.x*=s[0],t.y=t.depth*s[1]}var i=ui.layout.hierarchy().sort(null).value(null),l=Pa,s=[1,1],c=null;return t.separation=function(e){return arguments.length?(l=e,t):l},t.size=function(e){return arguments.length?(c=null==(s=e)?o:null,t):c?null:s},t.nodeSize=function(e){return arguments.length?(c=null==(s=e)?null:o,t):c?s:null},ia(t,i)},ui.layout.cluster=function(){function t(t,o){var i,l=e.call(this,t,o),s=l[0],c=0;sa(s,function(t){var e=t.children;e&&e.length?(t.x=Fa(e),t.y=qa(e)):(t.x=i?c+=n(t,i):0,t.y=0,i=t)});var u=Ba(s),f=Ha(s),d=u.x-n(u,f)/2,h=f.x+n(f,u)/2;return sa(s,a?function(t){t.x=(t.x-s.x)*r[0],t.y=(s.y-t.y)*r[1]}:function(t){t.x=(t.x-d)/(h-d)*r[0],t.y=(1-(s.y?t.y/s.y:1))*r[1]}),l}var e=ui.layout.hierarchy().sort(null).value(null),n=Pa,r=[1,1],a=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(e){return arguments.length?(a=null==(r=e),t):a?null:r},t.nodeSize=function(e){return arguments.length?(a=null!=(r=e),t):a?r:null},ia(t,e)},ui.layout.treemap=function(){function t(t,e){for(var n,r,a=-1,o=t.length;++a<o;)r=(n=t[a]).value*(0>e?0:e),n.area=isNaN(r)||0>=r?0:r}function e(n){var o=n.children;if(o&&o.length){var i,l,s,c=f(n),u=[],d=o.slice(),p=1/0,g=\"slice\"===h?c.dx:\"dice\"===h?c.dy:\"slice-dice\"===h?1&n.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(t(d,c.dx*c.dy/n.value),u.area=0;(s=d.length)>0;)u.push(i=d[s-1]),u.area+=i.area,\"squarify\"!==h||(l=r(u,g))<=p?(d.pop(),p=l):(u.area-=u.pop().area,a(u,g,c,!1),g=Math.min(c.dx,c.dy),u.length=u.area=0,p=1/0);u.length&&(a(u,g,c,!0),u.length=u.area=0),o.forEach(e)}}function n(e){var r=e.children;if(r&&r.length){var o,i=f(e),l=r.slice(),s=[];for(t(l,i.dx*i.dy/e.value),s.area=0;o=l.pop();)s.push(o),s.area+=o.area,null!=o.z&&(a(s,o.z?i.dx:i.dy,i,!l.length),s.length=s.area=0);r.forEach(n)}}function r(t,e){for(var n,r=t.area,a=0,o=1/0,i=-1,l=t.length;++i<l;)(n=t[i].area)&&(o>n&&(o=n),n>a&&(a=n));return r*=r,e*=e,r?Math.max(e*a*p/r,r/(e*o*p)):1/0}function a(t,e,n,r){var a,o=-1,i=t.length,l=n.x,c=n.y,u=e?s(t.area/e):0;if(e==n.dx){for((r||u>n.dy)&&(u=n.dy);++o<i;)a=t[o],a.x=l,a.y=c,a.dy=u,l+=a.dx=Math.min(n.x+n.dx-l,u?s(a.area/u):0);a.z=!0,a.dx+=n.x+n.dx-l,n.y+=u,n.dy-=u}else{for((r||u>n.dx)&&(u=n.dx);++o<i;)a=t[o],a.x=l,a.y=c,a.dx=u,c+=a.dy=Math.min(n.y+n.dy-c,u?s(a.area/u):0);a.z=!1,a.dy+=n.y+n.dy-c,n.x+=u,n.dx-=u}}function o(r){var a=i||l(r),o=a[0];return o.x=o.y=0,o.value?(o.dx=c[0],o.dy=c[1]):o.dx=o.dy=0,i&&l.revalue(o),t([o],o.dx*o.dy/o.value),(i?n:e)(o),d&&(i=a),a}var i,l=ui.layout.hierarchy(),s=Math.round,c=[1,1],u=null,f=Va,d=!1,h=\"squarify\",p=.5*(1+Math.sqrt(5));return o.size=function(t){return arguments.length?(c=t,o):c},o.padding=function(t){function e(e){var n=t.call(o,e,e.depth);return null==n?Va(e):Za(e,\"number\"==typeof n?[n,n,n,n]:n)}function n(e){return Za(e,t)}if(!arguments.length)return u;var r;return f=null==(u=t)?Va:\"function\"==(r=typeof t)?e:\"number\"===r?(t=[t,t,t,t],n):n,o},o.round=function(t){return arguments.length?(s=t?Math.round:Number,o):s!=Number},o.sticky=function(t){return arguments.length?(d=t,i=null,o):d},o.ratio=function(t){return arguments.length?(p=t,o):p},o.mode=function(t){return arguments.length?(h=t+\"\",o):h},ia(o,l)},ui.random={normal:function(t,e){var n=arguments.length;return 2>n&&(e=1),1>n&&(t=0),function(){var n,r,a;do n=2*Math.random()-1,r=2*Math.random()-1,a=n*n+r*r;while(!a||a>1);return t+e*n*Math.sqrt(-2*Math.log(a)/a)}},logNormal:function(){var t=ui.random.normal.apply(ui,arguments);return function(){return Math.exp(t())}},bates:function(t){var e=ui.random.irwinHall(t);return function(){return e()/t}},irwinHall:function(t){return function(){for(var e=0,n=0;t>n;n++)e+=Math.random();return e}}},ui.scale={};var Ls={floor:b,ceil:b};ui.scale.linear=function(){return Wa([0,1],[0,1],_r,!1)};var Ts={s:1,g:1,p:1,r:1,e:1};ui.scale.log=function(){return oo(ui.scale.linear().domain([0,1]),10,!0,[1,10])};var zs=ui.format(\".0e\"),Ss={floor:function(t){return-Math.ceil(-t)},ceil:function(t){return-Math.floor(-t)}};ui.scale.pow=function(){return io(ui.scale.linear(),1,[0,1])},ui.scale.sqrt=function(){return ui.scale.pow().exponent(.5)},ui.scale.ordinal=function(){return so([],{t:\"range\",a:[[]]})},ui.scale.category10=function(){return ui.scale.ordinal().range(Es)},ui.scale.category20=function(){return ui.scale.ordinal().range(Cs)},ui.scale.category20b=function(){return ui.scale.ordinal().range(Os)},ui.scale.category20c=function(){return ui.scale.ordinal().range(Ps)};var Es=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(_t),Cs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(_t),Os=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(_t),Ps=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(_t);ui.scale.quantile=function(){return co([],[])},ui.scal"
+,
+"e.quantize=function(){return uo(0,1,[0,1])},ui.scale.threshold=function(){return fo([.5],[0,1])},ui.scale.identity=function(){return ho([0,1])},ui.svg={},ui.svg.arc=function(){function t(){var t=Math.max(0,+n.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),u=i.apply(this,arguments)-Zi,f=l.apply(this,arguments)-Zi,d=Math.abs(f-u),h=u>f?0:1;if(t>c&&(p=c,c=t,t=p),d>=Vi)return e(c,h)+(t?e(t,1-h):\"\")+\"Z\";var p,g,v,m,y,x,b,_,w,k,M,A,L=0,T=0,z=[];if((m=(+s.apply(this,arguments)||0)/2)&&(v=o===Ns?Math.sqrt(t*t+c*c):+o.apply(this,arguments),h||(T*=-1),c&&(T=rt(v/c*Math.sin(m))),t&&(L=rt(v/t*Math.sin(m)))),c){y=c*Math.cos(u+T),x=c*Math.sin(u+T),b=c*Math.cos(f-T),_=c*Math.sin(f-T);var S=Math.abs(f-u-2*T)<=Bi?0:1;if(T&&bo(y,x,b,_)===h^S){var E=(u+f)/2;y=c*Math.cos(E),x=c*Math.sin(E),b=_=null}}else y=x=0;if(t){w=t*Math.cos(f-L),k=t*Math.sin(f-L),M=t*Math.cos(u+L),A=t*Math.sin(u+L);var C=Math.abs(u-f+2*L)<=Bi?0:1;if(L&&bo(w,k,M,A)===1-h^C){var O=(u+f)/2;w=t*Math.cos(O),k=t*Math.sin(O),M=A=null}}else w=k=0;if(d>qi&&(p=Math.min(Math.abs(c-t)/2,+a.apply(this,arguments)))>.001){g=c>t^h?0:1;var P=p,N=p;if(Bi>d){var D=null==M?[w,k]:null==b?[y,x]:In([y,x],[M,A],[b,_],[w,k]),I=y-D[0],R=x-D[1],j=b-D[0],q=_-D[1],F=1/Math.sin(Math.acos((I*j+R*q)/(Math.sqrt(I*I+R*R)*Math.sqrt(j*j+q*q)))/2),B=Math.sqrt(D[0]*D[0]+D[1]*D[1]);N=Math.min(p,(t-B)/(F-1)),P=Math.min(p,(c-B)/(F+1))}if(null!=b){var H=_o(null==M?[w,k]:[M,A],[y,x],c,P,h),V=_o([b,_],[w,k],c,P,h);p===P?z.push(\"M\",H[0],\"A\",P,\",\",P,\" 0 0,\",g,\" \",H[1],\"A\",c,\",\",c,\" 0 \",1-h^bo(H[1][0],H[1][1],V[1][0],V[1][1]),\",\",h,\" \",V[1],\"A\",P,\",\",P,\" 0 0,\",g,\" \",V[0]):z.push(\"M\",H[0],\"A\",P,\",\",P,\" 0 1,\",g,\" \",V[0])}else z.push(\"M\",y,\",\",x);if(null!=M){var Z=_o([y,x],[M,A],t,-N,h),Y=_o([w,k],null==b?[y,x]:[b,_],t,-N,h);p===N?z.push(\"L\",Y[0],\"A\",N,\",\",N,\" 0 0,\",g,\" \",Y[1],\"A\",t,\",\",t,\" 0 \",h^bo(Y[1][0],Y[1][1],Z[1][0],Z[1][1]),\",\",1-h,\" \",Z[1],\"A\",N,\",\",N,\" 0 0,\",g,\" \",Z[0]):z.push(\"L\",Y[0],\"A\",N,\",\",N,\" 0 0,\",g,\" \",Z[0])}else z.push(\"L\",w,\",\",k)}else z.push(\"M\",y,\",\",x),null!=b&&z.push(\"A\",c,\",\",c,\" 0 \",S,\",\",h,\" \",b,\",\",_),z.push(\"L\",w,\",\",k),null!=M&&z.push(\"A\",t,\",\",t,\" 0 \",C,\",\",1-h,\" \",M,\",\",A);return z.push(\"Z\"),z.join(\"\")}function e(t,e){return\"M0,\"+t+\"A\"+t+\",\"+t+\" 0 1,\"+e+\" 0,\"+-t+\"A\"+t+\",\"+t+\" 0 1,\"+e+\" 0,\"+t}var n=go,r=vo,a=po,o=Ns,i=mo,l=yo,s=xo;return t.innerRadius=function(e){return arguments.length?(n=zt(e),t):n},t.outerRadius=function(e){return arguments.length?(r=zt(e),t):r},t.cornerRadius=function(e){return arguments.length?(a=zt(e),t):a},t.padRadius=function(e){return arguments.length?(o=e==Ns?Ns:zt(e),t):o},t.startAngle=function(e){return arguments.length?(i=zt(e),t):i},t.endAngle=function(e){return arguments.length?(l=zt(e),t):l},t.padAngle=function(e){return arguments.length?(s=zt(e),t):s},t.centroid=function(){var t=(+n.apply(this,arguments)+ +r.apply(this,arguments))/2,e=(+i.apply(this,arguments)+ +l.apply(this,arguments))/2-Zi;return[Math.cos(e)*t,Math.sin(e)*t]},t};var Ns=\"auto\";ui.svg.line=function(){return wo(b)};var Ds=ui.map({linear:ko,\"linear-closed\":Mo,step:Ao,\"step-before\":Lo,\"step-after\":To,basis:Po,\"basis-open\":No,\"basis-closed\":Do,bundle:Io,cardinal:Eo,\"cardinal-open\":zo,\"cardinal-closed\":So,monotone:Ho});Ds.forEach(function(t,e){e.key=t,e.closed=/-closed$/.test(t)});var Is=[0,2/3,1/3,0],Rs=[0,1/3,2/3,0],js=[0,1/6,2/3,1/6];ui.svg.line.radial=function(){var t=wo(Vo);return t.radius=t.x,delete t.x,t.angle=t.y,delete t.y,t},Lo.reverse=To,To.reverse=Lo,ui.svg.area=function(){return Zo(b)},ui.svg.area.radial=function(){var t=Zo(Vo);return t.radius=t.x,delete t.x,t.innerRadius=t.x0,delete t.x0,t.outerRadius=t.x1,delete t.x1,t.angle=t.y,delete t.y,t.startAngle=t.y0,delete t.y0,t.endAngle=t.y1,delete t.y1,t},ui.svg.chord=function(){function t(t,l){var s=e(this,o,t,l),c=e(this,i,t,l);return\"M\"+s.p0+r(s.r,s.p1,s.a1-s.a0)+(n(s,c)?a(s.r,s.p1,s.r,s.p0):a(s.r,s.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+a(c.r,c.p1,s.r,s.p0))+\"Z\"}function e(t,e,n,r){var a=e.call(t,n,r),o=l.call(t,a,r),i=s.call(t,a,r)-Zi,u=c.call(t,a,r)-Zi;return{r:o,a0:i,a1:u,p0:[o*Math.cos(i),o*Math.sin(i)],p1:[o*Math.cos(u),o*Math.sin(u)]}}function n(t,e){return t.a0==e.a0&&t.a1==e.a1}function r(t,e,n){return\"A\"+t+\",\"+t+\" 0 \"+ +(n>Bi)+\",1 \"+e}function a(t,e,n,r){return\"Q 0,0 \"+r}var o=_n,i=wn,l=Yo,s=mo,c=yo;return t.radius=function(e){return arguments.length?(l=zt(e),t):l},t.source=function(e){return arguments.length?(o=zt(e),t):o},t.target=function(e){return arguments.length?(i=zt(e),t):i},t.startAngle=function(e){return arguments.length?(s=zt(e),t):s},t.endAngle=function(e){return arguments.length?(c=zt(e),t):c},t},ui.svg.diagonal=function(){function t(t,a){var o=e.call(this,t,a),i=n.call(this,t,a),l=(o.y+i.y)/2,s=[o,{x:o.x,y:l},{x:i.x,y:l},i];return s=s.map(r),\"M\"+s[0]+\"C\"+s[1]+\" \"+s[2]+\" \"+s[3]}var e=_n,n=wn,r=Uo;return t.source=function(n){return arguments.length?(e=zt(n),t):e},t.target=function(e){return arguments.length?(n=zt(e),t):n},t.projection=function(e){return arguments.length?(r=e,t):r},t},ui.svg.diagonal.radial=function(){var t=ui.svg.diagonal(),e=Uo,n=t.projection;return t.projection=function(t){return arguments.length?n(Xo(e=t)):e},t},ui.svg.symbol=function(){function t(t,r){return(qs.get(e.call(this,t,r))||Qo)(n.call(this,t,r))}var e=$o,n=Go;return t.type=function(n){return arguments.length?(e=zt(n),t):e},t.size=function(e){return arguments.length?(n=zt(e),t):n},t};var qs=ui.map({circle:Qo,cross:function(t){var e=Math.sqrt(t/5)/2;return\"M\"+-3*e+\",\"+-e+\"H\"+-e+\"V\"+-3*e+\"H\"+e+\"V\"+-e+\"H\"+3*e+\"V\"+e+\"H\"+e+\"V\"+3*e+\"H\"+-e+\"V\"+e+\"H\"+-3*e+\"Z\"},diamond:function(t){var e=Math.sqrt(t/(2*Bs)),n=e*Bs;return\"M0,\"+-e+\"L\"+n+\",0 0,\"+e+\" \"+-n+\",0Z\"},square:function(t){var e=Math.sqrt(t)/2;return\"M\"+-e+\",\"+-e+\"L\"+e+\",\"+-e+\" \"+e+\",\"+e+\" \"+-e+\",\"+e+\"Z\"},\"triangle-down\":function(t){var e=Math.sqrt(t/Fs),n=e*Fs/2;return\"M0,\"+n+\"L\"+e+\",\"+-n+\" \"+-e+\",\"+-n+\"Z\"},\"triangle-up\":function(t){var e=Math.sqrt(t/Fs),n=e*Fs/2;return\"M0,\"+-n+\"L\"+e+\",\"+n+\" \"+-e+\",\"+n+\"Z\"}});ui.svg.symbolTypes=qs.keys();var Fs=Math.sqrt(3),Bs=Math.tan(30*Yi);Ci.transition=function(t){for(var e,n,r=Hs||++Us,a=ei(t),o=[],i=Vs||{time:Date.now(),ease:zr,delay:0,duration:250},l=-1,s=this.length;++l<s;){o.push(e=[]);for(var c=this[l],u=-1,f=c.length;++u<f;)(n=c[u])&&ni(n,u,a,r,i),e.push(n)}return Jo(o,a,r)},Ci.interrupt=function(t){return this.each(null==t?Zs:Wo(ei(t)))};var Hs,Vs,Zs=Wo(ei()),Ys=[],Us=0;Ys.call=Ci.call,Ys.empty=Ci.empty,Ys.node=Ci.node,Ys.size=Ci.size,ui.transition=function(t,e){return t&&t.transition?Hs?t.transition(e):t:ui.selection().transition(t)},ui.transition.prototype=Ys,Ys.select=function(t){var e,n,r,a=this.id,o=this.namespace,i=[];t=E(t);for(var l=-1,s=this.length;++l<s;){i.push(e=[]);for(var c=this[l],u=-1,f=c.length;++u<f;)(r=c[u])&&(n=t.call(r,r.__data__,u,l))?(\"__data__\"in r&&(n.__data__=r.__data__),ni(n,u,o,a,r[o][a]),e.push(n)):e.push(null)}return Jo(i,o,a)},Ys.selectAll=function(t){var e,n,r,a,o,i=this.id,l=this.namespace,s=[];t=C(t);for(var c=-1,u=this.length;++c<u;)for(var f=this[c],d=-1,h=f.length;++d<h;)if(r=f[d]){o=r[l][i],n=t.call(r,r.__data__,d,c),s.push(e=[]);for(var p=-1,g=n.length;++p<g;)(a=n[p])&&ni(a,p,l,i,o),e.push(a)}return Jo(s,l,i)},Ys.filter=function(t){var e,n,r,a=[];\"function\"!=typeof t&&(t=V(t));for(var o=0,i=this.length;i>o;o++){a.push(e=[]);for(var n=this[o],l=0,s=n.length;s>l;l++)(r=n[l])&&t.call(r,r.__data__,l,o)&&e.push(r)}return Jo(a,this.namespace,this.id)},Ys.tween=function(t,e){var n=this.id,r=this.namespace;return arguments.length<2?this.node()[r][n].tween.get(t):Y(this,null==e?function(e){e[r][n].tween.remove(t)}:function(a){a[r][n].tween.set(t,e)})},Ys.attr=function(t,e){function n(){this.removeAttribute(l)}function r(){this.removeAttributeNS(l.space,l.local)}function a(t){return null==t?n:(t+=\"\",function(){var e,n=this.getAttribute(l);return n!==t&&(e=i(n,t),function(t){this.setAttribute(l,e(t))})})}function o(t){return null==t?r:(t+=\"\",function(){var e,n=this.getAttributeNS(l.space,l.local);return n!==t&&(e=i(n,t),function(t){this.setAttributeNS(l.space,l.local,e(t))})})}if(arguments.length<2){for(e in t)this.attr(e,t[e]);return this}var i=\"transform\"==t?$r:_r,l=ui.ns.qualify(t);return Ko(this,\"attr.\"+t,e,l.local?o:a)},Ys.attrTween=function(t,e){function n(t,n){var r=e.call(this,t,n,this.getAttribute(a));return r&&function(t){this.setAttribute(a,r(t))}}function r(t,n){var r=e.call(this,t,n,this.getAttributeNS(a.space,a.local));return r&&function(t){this.setAttributeNS(a.space,a.local,r(t))}}var a=ui.ns.qualify(t);return this.tween(\"attr.\"+t,a.local?r:n)},Ys.style=function(t,e,n){function a(){this.style.removeProperty(t)}function o(e){return null==e?a:(e+=\"\",function(){var a,o=r(this).getComputedStyle(this,null).getPropertyValue(t);return o!==e&&(a=_r(o,e),function(e){this.style.setProperty(t,a(e),n)})})}var i=arguments.length;if(3>i){if(\"string\"!=typeof t){2>i&&(e=\"\");for(n in t)this.style(n,t[n],e);return this}n=\"\"}return Ko(this,\"style.\"+t,e,o)},Ys.styleTween=function(t,e,n){function a(a,o){var i=e.call(this,a,o,r(this).getComputedStyle(this,null).getPropertyValue(t));return i&&function(e){this.style.setProperty(t,i(e),n)}}return arguments.length<3&&(n=\"\"),this.tween(\"style.\"+t,a)},Ys.text=function(t){return Ko(this,\"text\",t,ti)},Ys.remove=function(){var t=this.namespace;return this.each(\"end.transition\",function(){var e;this[t].count<2&&(e=this.parentNode)&&e.removeChild(this)})},Ys.ease=function(t){var e=this.id,n=this.namespace;return arguments.length<1?this.node()[n][e].ease:(\"function\"!=typeof t&&(t=ui.ease.apply(ui,arguments)),Y(this,function(r){r[n][e].ease=t}))},Ys.delay=function(t){var e=this.id,n=this.namespace;return arguments.length<1?this.node()[n][e].delay:Y(this,\"function\"==typeof t?function(r,a,o){r[n][e].delay=+t.call(r,r.__data__,a,o)}:(t=+t,function(r){r[n][e].delay=t}))},Ys.duration=function(t){var e=this.id,n=this.namespace;return arguments.length<1?this.node()[n][e].duration:Y(this,\"function\"==typeof t?function(r,a,o){r[n][e].duration=Math.max(1,t.call(r,r.__"
+,
+"data__,a,o))}:(t=Math.max(1,t),function(r){r[n][e].duration=t}))},Ys.each=function(t,e){var n=this.id,r=this.namespace;if(arguments.length<2){var a=Vs,o=Hs;try{Hs=n,Y(this,function(e,a,o){Vs=e[r][n],t.call(e,e.__data__,a,o)})}finally{Vs=a,Hs=o}}else Y(this,function(a){var o=a[r][n];(o.event||(o.event=ui.dispatch(\"start\",\"end\",\"interrupt\"))).on(t,e)});return this},Ys.transition=function(){for(var t,e,n,r,a=this.id,o=++Us,i=this.namespace,l=[],s=0,c=this.length;c>s;s++){l.push(t=[]);for(var e=this[s],u=0,f=e.length;f>u;u++)(n=e[u])&&(r=n[i][a],ni(n,u,i,o,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),t.push(n)}return Jo(l,i,o)},ui.svg.axis=function(){function t(t){t.each(function(){var t,c=ui.select(this),u=this.__chart__||n,f=this.__chart__=n.copy(),d=null==s?f.ticks?f.ticks.apply(f,l):f.domain():s,h=null==e?f.tickFormat?f.tickFormat.apply(f,l):b:e,p=c.selectAll(\".tick\").data(d,f),g=p.enter().insert(\"g\",\".domain\").attr(\"class\",\"tick\").style(\"opacity\",qi),v=ui.transition(p.exit()).style(\"opacity\",qi).remove(),m=ui.transition(p.order()).style(\"opacity\",1),y=Math.max(a,0)+i,x=Ua(f),_=c.selectAll(\".domain\").data([0]),w=(_.enter().append(\"path\").attr(\"class\",\"domain\"),ui.transition(_));g.append(\"line\"),g.append(\"text\");var k,M,A,L,T=g.select(\"line\"),z=m.select(\"line\"),S=p.select(\"text\").text(h),E=g.select(\"text\"),C=m.select(\"text\"),O=\"top\"===r||\"left\"===r?-1:1;if(\"bottom\"===r||\"top\"===r?(t=ri,k=\"x\",A=\"y\",M=\"x2\",L=\"y2\",S.attr(\"dy\",0>O?\"0em\":\".71em\").style(\"text-anchor\",\"middle\"),w.attr(\"d\",\"M\"+x[0]+\",\"+O*o+\"V0H\"+x[1]+\"V\"+O*o)):(t=ai,k=\"y\",A=\"x\",M=\"y2\",L=\"x2\",S.attr(\"dy\",\".32em\").style(\"text-anchor\",0>O?\"end\":\"start\"),w.attr(\"d\",\"M\"+O*o+\",\"+x[0]+\"H0V\"+x[1]+\"H\"+O*o)),T.attr(L,O*a),E.attr(A,O*y),z.attr(M,0).attr(L,O*a),C.attr(k,0).attr(A,O*y),f.rangeBand){var P=f,N=P.rangeBand()/2;u=f=function(t){return P(t)+N}}else u.rangeBand?u=f:v.call(t,f,u);g.call(t,u,f),m.call(t,f,f)})}var e,n=ui.scale.linear(),r=Xs,a=6,o=6,i=3,l=[10],s=null;return t.scale=function(e){\n"
+,
+"return arguments.length?(n=e,t):n},t.orient=function(e){return arguments.length?(r=e in Gs?e+\"\":Xs,t):r},t.ticks=function(){return arguments.length?(l=di(arguments),t):l},t.tickValues=function(e){return arguments.length?(s=e,t):s},t.tickFormat=function(n){return arguments.length?(e=n,t):e},t.tickSize=function(e){var n=arguments.length;return n?(a=+e,o=+arguments[n-1],t):a},t.innerTickSize=function(e){return arguments.length?(a=+e,t):a},t.outerTickSize=function(e){return arguments.length?(o=+e,t):o},t.tickPadding=function(e){return arguments.length?(i=+e,t):i},t.tickSubdivide=function(){return arguments.length&&t},t};var Xs=\"bottom\",Gs={top:1,right:1,bottom:1,left:1};ui.svg.brush=function(){function t(r){r.each(function(){var r=ui.select(this).style(\"pointer-events\",\"all\").style(\"-webkit-tap-highlight-color\",\"rgba(0,0,0,0)\").on(\"mousedown.brush\",o).on(\"touchstart.brush\",o),i=r.selectAll(\".background\").data([0]);i.enter().append(\"rect\").attr(\"class\",\"background\").style(\"visibility\",\"hidden\").style(\"cursor\",\"crosshair\"),r.selectAll(\".extent\").data([0]).enter().append(\"rect\").attr(\"class\",\"extent\").style(\"cursor\",\"move\");var l=r.selectAll(\".resize\").data(g,b);l.exit().remove(),l.enter().append(\"g\").attr(\"class\",function(t){return\"resize \"+t}).style(\"cursor\",function(t){return $s[t]}).append(\"rect\").attr(\"x\",function(t){return/[ew]$/.test(t)?-3:null}).attr(\"y\",function(t){return/^[ns]/.test(t)?-3:null}).attr(\"width\",6).attr(\"height\",6).style(\"visibility\",\"hidden\"),l.style(\"display\",t.empty()?\"none\":null);var s,f=ui.transition(r),d=ui.transition(i);c&&(s=Ua(c),d.attr(\"x\",s[0]).attr(\"width\",s[1]-s[0]),n(f)),u&&(s=Ua(u),d.attr(\"y\",s[0]).attr(\"height\",s[1]-s[0]),a(f)),e(f)})}function e(t){t.selectAll(\".resize\").attr(\"transform\",function(t){return\"translate(\"+f[+/e$/.test(t)]+\",\"+d[+/^s/.test(t)]+\")\"})}function n(t){t.select(\".extent\").attr(\"x\",f[0]),t.selectAll(\".extent,.n>rect,.s>rect\").attr(\"width\",f[1]-f[0])}function a(t){t.select(\".extent\").attr(\"y\",d[0]),t.selectAll(\".extent,.e>rect,.w>rect\").attr(\"height\",d[1]-d[0])}function o(){function o(){32==ui.event.keyCode&&(S||(x=null,C[0]-=f[1],C[1]-=d[1],S=2),L())}function g(){32==ui.event.keyCode&&2==S&&(C[0]+=f[1],C[1]+=d[1],S=0,L())}function v(){var t=ui.mouse(_),r=!1;b&&(t[0]+=b[0],t[1]+=b[1]),S||(ui.event.altKey?(x||(x=[(f[0]+f[1])/2,(d[0]+d[1])/2]),C[0]=f[+(t[0]<x[0])],C[1]=d[+(t[1]<x[1])]):x=null),T&&m(t,c,0)&&(n(M),r=!0),z&&m(t,u,1)&&(a(M),r=!0),r&&(e(M),k({type:\"brush\",mode:S?\"move\":\"resize\"}))}function m(t,e,n){var r,a,o=Ua(e),s=o[0],c=o[1],u=C[n],g=n?d:f,v=g[1]-g[0];return S&&(s-=u,c-=v+u),r=(n?p:h)?Math.max(s,Math.min(c,t[n])):t[n],S?a=(r+=u)+v:(x&&(u=Math.max(s,Math.min(c,2*x[n]-r))),r>u?(a=r,r=u):a=u),g[0]!=r||g[1]!=a?(n?l=null:i=null,g[0]=r,g[1]=a,!0):void 0}function y(){v(),M.style(\"pointer-events\",\"all\").selectAll(\".resize\").style(\"display\",t.empty()?\"none\":null),ui.select(\"body\").style(\"cursor\",null),O.on(\"mousemove.brush\",null).on(\"mouseup.brush\",null).on(\"touchmove.brush\",null).on(\"touchend.brush\",null).on(\"keydown.brush\",null).on(\"keyup.brush\",null),E(),k({type:\"brushend\"})}var x,b,_=this,w=ui.select(ui.event.target),k=s.of(_,arguments),M=ui.select(_),A=w.datum(),T=!/^(n|s)$/.test(A)&&c,z=!/^(e|w)$/.test(A)&&u,S=w.classed(\"extent\"),E=W(_),C=ui.mouse(_),O=ui.select(r(_)).on(\"keydown.brush\",o).on(\"keyup.brush\",g);if(ui.event.changedTouches?O.on(\"touchmove.brush\",v).on(\"touchend.brush\",y):O.on(\"mousemove.brush\",v).on(\"mouseup.brush\",y),M.interrupt().selectAll(\"*\").interrupt(),S)C[0]=f[0]-C[0],C[1]=d[0]-C[1];else if(A){var P=+/w$/.test(A),N=+/^n/.test(A);b=[f[1-P]-C[0],d[1-N]-C[1]],C[0]=f[P],C[1]=d[N]}else ui.event.altKey&&(x=C.slice());M.style(\"pointer-events\",\"none\").selectAll(\".resize\").style(\"display\",null),ui.select(\"body\").style(\"cursor\",w.style(\"cursor\")),k({type:\"brushstart\"}),v()}var i,l,s=z(t,\"brushstart\",\"brush\",\"brushend\"),c=null,u=null,f=[0,0],d=[0,0],h=!0,p=!0,g=Qs[0];return t.event=function(t){t.each(function(){var t=s.of(this,arguments),e={x:f,y:d,i:i,j:l},n=this.__chart__||e;this.__chart__=e,Hs?ui.select(this).transition().each(\"start.brush\",function(){i=n.i,l=n.j,f=n.x,d=n.y,t({type:\"brushstart\"})}).tween(\"brush:brush\",function(){var n=wr(f,e.x),r=wr(d,e.y);return i=l=null,function(a){f=e.x=n(a),d=e.y=r(a),t({type:\"brush\",mode:\"resize\"})}}).each(\"end.brush\",function(){i=e.i,l=e.j,t({type:\"brush\",mode:\"resize\"}),t({type:\"brushend\"})}):(t({type:\"brushstart\"}),t({type:\"brush\",mode:\"resize\"}),t({type:\"brushend\"}))})},t.x=function(e){return arguments.length?(c=e,g=Qs[!c<<1|!u],t):c},t.y=function(e){return arguments.length?(u=e,g=Qs[!c<<1|!u],t):u},t.clamp=function(e){return arguments.length?(c&&u?(h=!!e[0],p=!!e[1]):c?h=!!e:u&&(p=!!e),t):c&&u?[h,p]:c?h:u?p:null},t.extent=function(e){var n,r,a,o,s;return arguments.length?(c&&(n=e[0],r=e[1],u&&(n=n[0],r=r[0]),i=[n,r],c.invert&&(n=c(n),r=c(r)),n>r&&(s=n,n=r,r=s),n==f[0]&&r==f[1]||(f=[n,r])),u&&(a=e[0],o=e[1],c&&(a=a[1],o=o[1]),l=[a,o],u.invert&&(a=u(a),o=u(o)),a>o&&(s=a,a=o,o=s),a==d[0]&&o==d[1]||(d=[a,o])),t):(c&&(i?(n=i[0],r=i[1]):(n=f[0],r=f[1],c.invert&&(n=c.invert(n),r=c.invert(r)),n>r&&(s=n,n=r,r=s))),u&&(l?(a=l[0],o=l[1]):(a=d[0],o=d[1],u.invert&&(a=u.invert(a),o=u.invert(o)),a>o&&(s=a,a=o,o=s))),c&&u?[[n,a],[r,o]]:c?[n,r]:u&&[a,o])},t.clear=function(){return t.empty()||(f=[0,0],d=[0,0],i=l=null),t},t.empty=function(){return!!c&&f[0]==f[1]||!!u&&d[0]==d[1]},ui.rebind(t,s,\"on\")};var $s={n:\"ns-resize\",e:\"ew-resize\",s:\"ns-resize\",w:\"ew-resize\",nw:\"nwse-resize\",ne:\"nesw-resize\",se:\"nwse-resize\",sw:\"nesw-resize\"},Qs=[[\"n\",\"e\",\"s\",\"w\",\"nw\",\"ne\",\"se\",\"sw\"],[\"e\",\"w\"],[\"n\",\"s\"],[]],Ws=vl.format=wl.timeFormat,Js=Ws.utc,Ks=Js(\"%Y-%m-%dT%H:%M:%S.%LZ\");Ws.iso=Date.prototype.toISOString&&+new Date(\"2000-01-01T00:00:00.000Z\")?oi:Ks,oi.parse=function(t){var e=new Date(t);return isNaN(e)?null:e},oi.toString=Ks.toString,vl.second=Ht(function(t){return new ml(1e3*Math.floor(t/1e3))},function(t,e){t.setTime(t.getTime()+1e3*Math.floor(e))},function(t){return t.getSeconds()}),vl.seconds=vl.second.range,vl.seconds.utc=vl.second.utc.range,vl.minute=Ht(function(t){return new ml(6e4*Math.floor(t/6e4))},function(t,e){t.setTime(t.getTime()+6e4*Math.floor(e))},function(t){return t.getMinutes()}),vl.minutes=vl.minute.range,vl.minutes.utc=vl.minute.utc.range,vl.hour=Ht(function(t){var e=t.getTimezoneOffset()/60;return new ml(36e5*(Math.floor(t/36e5-e)+e))},function(t,e){t.setTime(t.getTime()+36e5*Math.floor(e))},function(t){return t.getHours()}),vl.hours=vl.hour.range,vl.hours.utc=vl.hour.utc.range,vl.month=Ht(function(t){return t=vl.day(t),t.setDate(1),t},function(t,e){t.setMonth(t.getMonth()+e)},function(t){return t.getMonth()}),vl.months=vl.month.range,vl.months.utc=vl.month.utc.range;var tc=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],ec=[[vl.second,1],[vl.second,5],[vl.second,15],[vl.second,30],[vl.minute,1],[vl.minute,5],[vl.minute,15],[vl.minute,30],[vl.hour,1],[vl.hour,3],[vl.hour,6],[vl.hour,12],[vl.day,1],[vl.day,2],[vl.week,1],[vl.month,1],[vl.month,3],[vl.year,1]],nc=Ws.multi([[\".%L\",function(t){return t.getMilliseconds()}],[\":%S\",function(t){return t.getSeconds()}],[\"%I:%M\",function(t){return t.getMinutes()}],[\"%I %p\",function(t){return t.getHours()}],[\"%a %d\",function(t){return t.getDay()&&1!=t.getDate()}],[\"%b %d\",function(t){return 1!=t.getDate()}],[\"%B\",function(t){return t.getMonth()}],[\"%Y\",Ce]]),rc={range:function(t,e,n){return ui.range(Math.ceil(t/n)*n,+e,n).map(li)},floor:b,ceil:b};ec.year=vl.year,vl.scale=function(){return ii(ui.scale.linear(),ec,nc)};var ac=ec.map(function(t){return[t[0].utc,t[1]]}),oc=Js.multi([[\".%L\",function(t){return t.getUTCMilliseconds()}],[\":%S\",function(t){return t.getUTCSeconds()}],[\"%I:%M\",function(t){return t.getUTCMinutes()}],[\"%I %p\",function(t){return t.getUTCHours()}],[\"%a %d\",function(t){return t.getUTCDay()&&1!=t.getUTCDate()}],[\"%b %d\",function(t){return 1!=t.getUTCDate()}],[\"%B\",function(t){return t.getUTCMonth()}],[\"%Y\",Ce]]);ac.year=vl.year.utc,vl.scale.utc=function(){return ii(ui.scale.linear(),ac,oc)},ui.text=St(function(t){return t.responseText}),ui.json=function(t,e){return Et(t,\"application/json\",si,e)},ui.html=function(t,e){return Et(t,\"text/html\",ci,e)},ui.xml=St(function(t){return t.responseXML}),\"function\"==typeof t&&t.amd?(this.d3=ui,t(ui)):\"object\"==typeof n&&n.exports?n.exports=ui:this.d3=ui}()},{}],10:[function(e,n,r){(function(r,a){(function(){\"use strict\";function o(t){return\"function\"==typeof t||\"object\"==typeof t&&null!==t}function i(t){return\"function\"==typeof t}function l(t){U=t}function s(t){Q=t}function c(){return function(){r.nextTick(p)}}function u(){return function(){Y(p)}}function f(){var t=0,e=new K(p),n=document.createTextNode(\"\");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function d(){var t=new MessageChannel;return t.port1.onmessage=p,function(){t.port2.postMessage(0)}}function h(){return function(){setTimeout(p,1)}}function p(){for(var t=0;$>t;t+=2){var e=nt[t],n=nt[t+1];e(n),nt[t]=void 0,nt[t+1]=void 0}$=0}function g(){try{var t=e,n=t(\"vertx\");return Y=n.runOnLoop||n.runOnContext,u()}catch(r){return h()}}function v(t,e){var n=this,r=n._state;if(r===it&&!t||r===lt&&!e)return this;var a=new this.constructor(y),o=n._result;if(r){var i=arguments[r-1];Q(function(){N(r,a,i,o)})}else E(n,a,t,e);return a}function m(t){var e=this;if(t&&\"object\"==typeof t&&t.constructor===e)return t;var n=new e(y);return L(n,t),n}function y(){}function x(){return new TypeError(\"You cannot resolve a promise with itself\")}function b(){return new TypeError(\"A promises callback cannot return that same promise.\")}function _(t){try{return t.then}catch(e){return st.error=e,st}}function w(t,e,n,r){try{t.call(e,n,r)}catch(a){return a}}function k(t,e,n){Q(function(t){var r=!1,a=w(n,e,function(n){r||(r=!0,e!==n?L(t,n):z(t,n))},function(e){r||(r=!0,S(t,e))},\"Settle: \"+(t._label||\" unknown promise\"));!r&&a&&(r=!0,S(t,a))},t)}function M(t,e){e._state===it?z(t,e._result):e._state===lt?S(t,"
+,
+"e._result):E(e,void 0,function(e){L(t,e)},function(e){S(t,e)})}function A(t,e,n){e.constructor===t.constructor&&n===rt&&constructor.resolve===at?M(t,e):n===st?S(t,st.error):void 0===n?z(t,e):i(n)?k(t,e,n):z(t,e)}function L(t,e){t===e?S(t,x()):o(e)?A(t,e,_(e)):z(t,e)}function T(t){t._onerror&&t._onerror(t._result),C(t)}function z(t,e){t._state===ot&&(t._result=e,t._state=it,0!==t._subscribers.length&&Q(C,t))}function S(t,e){t._state===ot&&(t._state=lt,t._result=e,Q(T,t))}function E(t,e,n,r){var a=t._subscribers,o=a.length;t._onerror=null,a[o]=e,a[o+it]=n,a[o+lt]=r,0===o&&t._state&&Q(C,t)}function C(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r,a,o=t._result,i=0;i<e.length;i+=3)r=e[i],a=e[i+n],r?N(n,r,a,o):a(o);t._subscribers.length=0}}function O(){this.error=null}function P(t,e){try{return t(e)}catch(n){return ct.error=n,ct}}function N(t,e,n,r){var a,o,l,s,c=i(n);if(c){if(a=P(n,r),a===ct?(s=!0,o=a.error,a=null):l=!0,e===a)return void S(e,b())}else a=r,l=!0;e._state!==ot||(c&&l?L(e,a):s?S(e,o):t===it?z(e,a):t===lt&&S(e,a))}function D(t,e){try{e(function(e){L(t,e)},function(e){S(t,e)})}catch(n){S(t,n)}}function I(t){return new gt(this,t).promise}function R(t){function e(t){L(a,t)}function n(t){S(a,t)}var r=this,a=new r(y);if(!G(t))return S(a,new TypeError(\"You must pass an array to race.\")),a;for(var o=t.length,i=0;a._state===ot&&o>i;i++)E(r.resolve(t[i]),void 0,e,n);return a}function j(t){var e=this,n=new e(y);return S(n,t),n}function q(){throw new TypeError(\"You must pass a resolver function as the first argument to the promise constructor\")}function F(){throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\")}function B(t){this._id=ht++,this._state=void 0,this._result=void 0,this._subscribers=[],y!==t&&(\"function\"!=typeof t&&q(),this instanceof B?D(this,t):F())}function H(t,e){this._instanceConstructor=t,this.promise=new t(y),Array.isArray(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?z(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&z(this.promise,this._result))):S(this.promise,this._validationError())}function V(){var t;if(\"undefined\"!=typeof a)t=a;else if(\"undefined\"!=typeof self)t=self;else try{t=Function(\"return this\")()}catch(e){throw new Error(\"polyfill failed because global object is unavailable in this environment\")}var n=t.Promise;n&&\"[object Promise]\"===Object.prototype.toString.call(n.resolve())&&!n.cast||(t.Promise=pt)}var Z;Z=Array.isArray?Array.isArray:function(t){return\"[object Array]\"===Object.prototype.toString.call(t)};var Y,U,X,G=Z,$=0,Q=function(t,e){nt[$]=t,nt[$+1]=e,$+=2,2===$&&(U?U(p):X())},W=\"undefined\"!=typeof window?window:void 0,J=W||{},K=J.MutationObserver||J.WebKitMutationObserver,tt=\"undefined\"!=typeof r&&\"[object process]\"==={}.toString.call(r),et=\"undefined\"!=typeof Uint8ClampedArray&&\"undefined\"!=typeof importScripts&&\"undefined\"!=typeof MessageChannel,nt=new Array(1e3);X=tt?c():K?f():et?d():void 0===W&&\"function\"==typeof e?g():h();var rt=v,at=m,ot=void 0,it=1,lt=2,st=new O,ct=new O,ut=I,ft=R,dt=j,ht=0,pt=B;B.all=ut,B.race=ft,B.resolve=at,B.reject=dt,B._setScheduler=l,B._setAsap=s,B._asap=Q,B.prototype={constructor:B,then:rt,\"catch\":function(t){return this.then(null,t)}};var gt=H;H.prototype._validationError=function(){return new Error(\"Array Methods must be provided an Array\")},H.prototype._enumerate=function(){for(var t=this.length,e=this._input,n=0;this._state===ot&&t>n;n++)this._eachEntry(e[n],n)},H.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===at){var a=_(t);if(a===rt&&t._state!==ot)this._settledAt(t._state,e,t._result);else if(\"function\"!=typeof a)this._remaining--,this._result[e]=t;else if(n===pt){var o=new n(y);A(o,t,a),this._willSettleAt(o,e)}else this._willSettleAt(new n(function(e){e(t)}),e)}else this._willSettleAt(r(t),e)},H.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===ot&&(this._remaining--,t===lt?S(r,n):this._result[e]=n),0===this._remaining&&z(r,this._result)},H.prototype._willSettleAt=function(t,e){var n=this;E(t,void 0,function(t){n._settledAt(it,e,t)},function(t){n._settledAt(lt,e,t)})};var vt=V,mt={Promise:pt,polyfill:vt};\"function\"==typeof t&&t.amd?t(function(){return mt}):\"undefined\"!=typeof n&&n.exports?n.exports=mt:\"undefined\"!=typeof this&&(this.ES6Promise=mt),vt()}).call(this)}).call(this,e(\"_process\"),\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:{})},{_process:8}],11:[function(t,e,n){\"use strict\";function r(t){for(var e,n=t.length,r=0;n>r;r++)if(e=t.charCodeAt(r),(9>e||e>13)&&32!==e&&133!==e&&160!==e&&5760!==e&&6158!==e&&(8192>e||e>8205)&&8232!==e&&8233!==e&&8239!==e&&8287!==e&&8288!==e&&12288!==e&&65279!==e)return!1;return!0}e.exports=function(t){var e=typeof t;if(\"string\"===e){var n=t;if(t=+t,0===t&&r(n))return!1}else if(\"number\"!==e)return!1;return 1>t-t}},{}],12:[function(t,e,n){function r(t,e){var n=e[0],r=e[1],a=e[2],o=e[3],i=n+n,l=r+r,s=a+a,c=n*i,u=r*i,f=r*l,d=a*i,h=a*l,p=a*s,g=o*i,v=o*l,m=o*s;return t[0]=1-f-p,t[1]=u+m,t[2]=d-v,t[3]=0,t[4]=u-m,t[5]=1-c-p,t[6]=h+g,t[7]=0,t[8]=d+v,t[9]=h-g,t[10]=1-c-f,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}e.exports=r},{}],13:[function(e,n,r){!function(){function e(t,n){if(t=t?t:\"\",n=n||{},t instanceof e)return t;if(!(this instanceof e))return new e(t,n);var a=r(t);this._originalInput=t,this._r=a.r,this._g=a.g,this._b=a.b,this._a=a.a,this._roundA=B(100*this._a)/100,this._format=n.format||a.format,this._gradientType=n.gradientType,this._r<1&&(this._r=B(this._r)),this._g<1&&(this._g=B(this._g)),this._b<1&&(this._b=B(this._b)),this._ok=a.ok,this._tc_id=q++}function r(t){var e={r:0,g:0,b:0},n=1,r=!1,o=!1;return\"string\"==typeof t&&(t=D(t)),\"object\"==typeof t&&(t.hasOwnProperty(\"r\")&&t.hasOwnProperty(\"g\")&&t.hasOwnProperty(\"b\")?(e=a(t.r,t.g,t.b),r=!0,o=\"%\"===String(t.r).substr(-1)?\"prgb\":\"rgb\"):t.hasOwnProperty(\"h\")&&t.hasOwnProperty(\"s\")&&t.hasOwnProperty(\"v\")?(t.s=O(t.s),t.v=O(t.v),e=s(t.h,t.s,t.v),r=!0,o=\"hsv\"):t.hasOwnProperty(\"h\")&&t.hasOwnProperty(\"s\")&&t.hasOwnProperty(\"l\")&&(t.s=O(t.s),t.l=O(t.l),e=i(t.h,t.s,t.l),r=!0,o=\"hsl\"),t.hasOwnProperty(\"a\")&&(n=t.a)),n=A(n),{ok:r,format:t.format||o,r:H(255,V(e.r,0)),g:H(255,V(e.g,0)),b:H(255,V(e.b,0)),a:n}}function a(t,e,n){return{r:255*L(t,255),g:255*L(e,255),b:255*L(n,255)}}function o(t,e,n){t=L(t,255),e=L(e,255),n=L(n,255);var r,a,o=V(t,e,n),i=H(t,e,n),l=(o+i)/2;if(o==i)r=a=0;else{var s=o-i;switch(a=l>.5?s/(2-o-i):s/(o+i),o){case t:r=(e-n)/s+(n>e?6:0);break;case e:r=(n-t)/s+2;break;case n:r=(t-e)/s+4}r/=6}return{h:r,s:a,l:l}}function i(t,e,n){function r(t,e,n){return 0>n&&(n+=1),n>1&&(n-=1),1/6>n?t+6*(e-t)*n:.5>n?e:2/3>n?t+(e-t)*(2/3-n)*6:t}var a,o,i;if(t=L(t,360),e=L(e,100),n=L(n,100),0===e)a=o=i=n;else{var l=.5>n?n*(1+e):n+e-n*e,s=2*n-l;a=r(s,l,t+1/3),o=r(s,l,t),i=r(s,l,t-1/3)}return{r:255*a,g:255*o,b:255*i}}function l(t,e,n){t=L(t,255),e=L(e,255),n=L(n,255);var r,a,o=V(t,e,n),i=H(t,e,n),l=o,s=o-i;if(a=0===o?0:s/o,o==i)r=0;else{switch(o){case t:r=(e-n)/s+(n>e?6:0);break;case e:r=(n-t)/s+2;break;case n:r=(t-e)/s+4}r/=6}return{h:r,s:a,v:l}}function s(t,e,n){t=6*L(t,360),e=L(e,100),n=L(n,100);var r=F.floor(t),a=t-r,o=n*(1-e),i=n*(1-a*e),l=n*(1-(1-a)*e),s=r%6,c=[n,i,o,o,l,n][s],u=[l,n,n,i,o,o][s],f=[o,o,l,n,n,i][s];return{r:255*c,g:255*u,b:255*f}}function c(t,e,n,r){var a=[C(B(t).toString(16)),C(B(e).toString(16)),C(B(n).toString(16))];return r&&a[0].charAt(0)==a[0].charAt(1)&&a[1].charAt(0)==a[1].charAt(1)&&a[2].charAt(0)==a[2].charAt(1)?a[0].charAt(0)+a[1].charAt(0)+a[2].charAt(0):a.join(\"\")}function u(t,e,n,r){var a=[C(P(r)),C(B(t).toString(16)),C(B(e).toString(16)),C(B(n).toString(16))];return a.join(\"\")}function f(t,n){n=0===n?0:n||10;var r=e(t).toHsl();return r.s-=n/100,r.s=T(r.s),e(r)}function d(t,n){n=0===n?0:n||10;var r=e(t).toHsl();return r.s+=n/100,r.s=T(r.s),e(r)}function h(t){return e(t).desaturate(100)}function p(t,n){n=0===n?0:n||10;var r=e(t).toHsl();return r.l+=n/100,r.l=T(r.l),e(r)}function g(t,n){n=0===n?0:n||10;var r=e(t).toRgb();return r.r=V(0,H(255,r.r-B(255*-(n/100)))),r.g=V(0,H(255,r.g-B(255*-(n/100)))),r.b=V(0,H(255,r.b-B(255*-(n/100)))),e(r)}function v(t,n){n=0===n?0:n||10;var r=e(t).toHsl();return r.l-=n/100,r.l=T(r.l),e(r)}function m(t,n){var r=e(t).toHsl(),a=(B(r.h)+n)%360;return r.h=0>a?360+a:a,e(r)}function y(t){var n=e(t).toHsl();return n.h=(n.h+180)%360,e(n)}function x(t){var n=e(t).toHsl(),r=n.h;return[e(t),e({h:(r+120)%360,s:n.s,l:n.l}),e({h:(r+240)%360,s:n.s,l:n.l})]}function b(t){var n=e(t).toHsl(),r=n.h;return[e(t),e({h:(r+90)%360,s:n.s,l:n.l}),e({h:(r+180)%360,s:n.s,l:n.l}),e({h:(r+270)%360,s:n.s,l:n.l})]}function _(t){var n=e(t).toHsl(),r=n.h;return[e(t),e({h:(r+72)%360,s:n.s,l:n.l}),e({h:(r+216)%360,s:n.s,l:n.l})]}function w(t,n,r){n=n||6,r=r||30;var a=e(t).toHsl(),o=360/r,i=[e(t)];for(a.h=(a.h-(o*n>>1)+720)%360;--n;)a.h=(a.h+o)%360,i.push(e(a));return i}function k(t,n){n=n||6;for(var r=e(t).toHsv(),a=r.h,o=r.s,i=r.v,l=[],s=1/n;n--;)l.push(e({h:a,s:o,v:i})),i=(i+s)%1;return l}function M(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[t[n]]=n);return e}function A(t){return t=parseFloat(t),(isNaN(t)||0>t||t>1)&&(t=1),t}function L(t,e){S(t)&&(t=\"100%\");var n=E(t);return t=H(e,V(0,parseFloat(t))),n&&(t=parseInt(t*e,10)/100),F.abs(t-e)<1e-6?1:t%e/parseFloat(e)}function T(t){return H(1,V(0,t))}function z(t){return parseInt(t,16)}function S(t){return\"string\"==typeof t&&-1!=t.indexOf(\".\")&&1===parseFloat(t)}function E(t){return\"string\"==typeof t&&-1!=t.indexOf(\"%\")}function C(t){return 1==t.length?\"0\"+t:\"\"+t}function O(t){return 1>=t&&(t=100*t+\"%\"),t}function P(t){return Math.round(255*parseFloat(t)).toString(16)}function N(t){return z(t)/255}function D(t){t=t.replace(R,\"\").replace(j,\"\").toLowerCase();var e=!1;if(Y[t])t=Y[t],e=!0;else if(\"transparent\"==t)return{r:0,g:0,b:0,a:0,format:\"name\"};var n;return"
+,
+"(n=X.rgb.exec(t))?{r:n[1],g:n[2],b:n[3]}:(n=X.rgba.exec(t))?{r:n[1],g:n[2],b:n[3],a:n[4]}:(n=X.hsl.exec(t))?{h:n[1],s:n[2],l:n[3]}:(n=X.hsla.exec(t))?{h:n[1],s:n[2],l:n[3],a:n[4]}:(n=X.hsv.exec(t))?{h:n[1],s:n[2],v:n[3]}:(n=X.hsva.exec(t))?{h:n[1],s:n[2],v:n[3],a:n[4]}:(n=X.hex8.exec(t))?{a:N(n[1]),r:z(n[2]),g:z(n[3]),b:z(n[4]),format:e?\"name\":\"hex8\"}:(n=X.hex6.exec(t))?{r:z(n[1]),g:z(n[2]),b:z(n[3]),format:e?\"name\":\"hex\"}:(n=X.hex3.exec(t))?{r:z(n[1]+\"\"+n[1]),g:z(n[2]+\"\"+n[2]),b:z(n[3]+\"\"+n[3]),format:e?\"name\":\"hex\"}:!1}function I(t){var e,n;return t=t||{level:\"AA\",size:\"small\"},e=(t.level||\"AA\").toUpperCase(),n=(t.size||\"small\").toLowerCase(),\"AA\"!==e&&\"AAA\"!==e&&(e=\"AA\"),\"small\"!==n&&\"large\"!==n&&(n=\"small\"),{level:e,size:n}}var R=/^\\s+/,j=/\\s+$/,q=0,F=Math,B=F.round,H=F.min,V=F.max,Z=F.random;e.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},getLuminance:function(){var t,e,n,r,a,o,i=this.toRgb();return t=i.r/255,e=i.g/255,n=i.b/255,r=.03928>=t?t/12.92:Math.pow((t+.055)/1.055,2.4),a=.03928>=e?e/12.92:Math.pow((e+.055)/1.055,2.4),o=.03928>=n?n/12.92:Math.pow((n+.055)/1.055,2.4),.2126*r+.7152*a+.0722*o},setAlpha:function(t){return this._a=A(t),this._roundA=B(100*this._a)/100,this},toHsv:function(){var t=l(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=l(this._r,this._g,this._b),e=B(360*t.h),n=B(100*t.s),r=B(100*t.v);return 1==this._a?\"hsv(\"+e+\", \"+n+\"%, \"+r+\"%)\":\"hsva(\"+e+\", \"+n+\"%, \"+r+\"%, \"+this._roundA+\")\"},toHsl:function(){var t=o(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=o(this._r,this._g,this._b),e=B(360*t.h),n=B(100*t.s),r=B(100*t.l);return 1==this._a?\"hsl(\"+e+\", \"+n+\"%, \"+r+\"%)\":\"hsla(\"+e+\", \"+n+\"%, \"+r+\"%, \"+this._roundA+\")\"},toHex:function(t){return c(this._r,this._g,this._b,t)},toHexString:function(t){return\"#\"+this.toHex(t)},toHex8:function(){return u(this._r,this._g,this._b,this._a)},toHex8String:function(){return\"#\"+this.toHex8()},toRgb:function(){return{r:B(this._r),g:B(this._g),b:B(this._b),a:this._a}},toRgbString:function(){return 1==this._a?\"rgb(\"+B(this._r)+\", \"+B(this._g)+\", \"+B(this._b)+\")\":\"rgba(\"+B(this._r)+\", \"+B(this._g)+\", \"+B(this._b)+\", \"+this._roundA+\")\"},toPercentageRgb:function(){return{r:B(100*L(this._r,255))+\"%\",g:B(100*L(this._g,255))+\"%\",b:B(100*L(this._b,255))+\"%\",a:this._a}},toPercentageRgbString:function(){return 1==this._a?\"rgb(\"+B(100*L(this._r,255))+\"%, \"+B(100*L(this._g,255))+\"%, \"+B(100*L(this._b,255))+\"%)\":\"rgba(\"+B(100*L(this._r,255))+\"%, \"+B(100*L(this._g,255))+\"%, \"+B(100*L(this._b,255))+\"%, \"+this._roundA+\")\"},toName:function(){return 0===this._a?\"transparent\":this._a<1?!1:U[c(this._r,this._g,this._b,!0)]||!1},toFilter:function(t){var n=\"#\"+u(this._r,this._g,this._b,this._a),r=n,a=this._gradientType?\"GradientType = 1, \":\"\";if(t){var o=e(t);r=o.toHex8String()}return\"progid:DXImageTransform.Microsoft.gradient(\"+a+\"startColorstr=\"+n+\",endColorstr=\"+r+\")\"},toString:function(t){var e=!!t;t=t||this._format;var n=!1,r=this._a<1&&this._a>=0,a=!e&&r&&(\"hex\"===t||\"hex6\"===t||\"hex3\"===t||\"name\"===t);return a?\"name\"===t&&0===this._a?this.toName():this.toRgbString():(\"rgb\"===t&&(n=this.toRgbString()),\"prgb\"===t&&(n=this.toPercentageRgbString()),\"hex\"!==t&&\"hex6\"!==t||(n=this.toHexString()),\"hex3\"===t&&(n=this.toHexString(!0)),\"hex8\"===t&&(n=this.toHex8String()),\"name\"===t&&(n=this.toName()),\"hsl\"===t&&(n=this.toHslString()),\"hsv\"===t&&(n=this.toHsvString()),n||this.toHexString())},clone:function(){return e(this.toString())},_applyModification:function(t,e){var n=t.apply(null,[this].concat([].slice.call(e)));return this._r=n._r,this._g=n._g,this._b=n._b,this.setAlpha(n._a),this},lighten:function(){return this._applyModification(p,arguments)},brighten:function(){return this._applyModification(g,arguments)},darken:function(){return this._applyModification(v,arguments)},desaturate:function(){return this._applyModification(f,arguments)},saturate:function(){return this._applyModification(d,arguments)},greyscale:function(){return this._applyModification(h,arguments)},spin:function(){return this._applyModification(m,arguments)},_applyCombination:function(t,e){return t.apply(null,[this].concat([].slice.call(e)))},analogous:function(){return this._applyCombination(w,arguments)},complement:function(){return this._applyCombination(y,arguments)},monochromatic:function(){return this._applyCombination(k,arguments)},splitcomplement:function(){return this._applyCombination(_,arguments)},triad:function(){return this._applyCombination(x,arguments)},tetrad:function(){return this._applyCombination(b,arguments)}},e.fromRatio=function(t,n){if(\"object\"==typeof t){var r={};for(var a in t)t.hasOwnProperty(a)&&(\"a\"===a?r[a]=t[a]:r[a]=O(t[a]));t=r}return e(t,n)},e.equals=function(t,n){return t&&n?e(t).toRgbString()==e(n).toRgbString():!1},e.random=function(){return e.fromRatio({r:Z(),g:Z(),b:Z()})},e.mix=function(t,n,r){r=0===r?0:r||50;var a,o=e(t).toRgb(),i=e(n).toRgb(),l=r/100,s=2*l-1,c=i.a-o.a;a=s*c==-1?s:(s+c)/(1+s*c),a=(a+1)/2;var u=1-a,f={r:i.r*a+o.r*u,g:i.g*a+o.g*u,b:i.b*a+o.b*u,a:i.a*l+o.a*(1-l)};return e(f)},e.readability=function(t,n){var r=e(t),a=e(n);return(Math.max(r.getLuminance(),a.getLuminance())+.05)/(Math.min(r.getLuminance(),a.getLuminance())+.05)},e.isReadable=function(t,n,r){var a,o,i=e.readability(t,n);switch(o=!1,a=I(r),a.level+a.size){case\"AAsmall\":case\"AAAlarge\":o=i>=4.5;break;case\"AAlarge\":o=i>=3;break;case\"AAAsmall\":o=i>=7}return o},e.mostReadable=function(t,n,r){var a,o,i,l,s=null,c=0;r=r||{},o=r.includeFallbackColors,i=r.level,l=r.size;for(var u=0;u<n.length;u++)a=e.readability(t,n[u]),a>c&&(c=a,s=e(n[u]));return e.isReadable(t,s,{level:i,size:l})||!o?s:(r.includeFallbackColors=!1,e.mostReadable(t,[\"#fff\",\"#000\"],r))};var Y=e.names={aliceblue:\"f0f8ff\",antiquewhite:\"faebd7\",aqua:\"0ff\",aquamarine:\"7fffd4\",azure:\"f0ffff\",beige:\"f5f5dc\",bisque:\"ffe4c4\",black:\"000\",blanchedalmond:\"ffebcd\",blue:\"00f\",blueviolet:\"8a2be2\",brown:\"a52a2a\",burlywood:\"deb887\",burntsienna:\"ea7e5d\",cadetblue:\"5f9ea0\",chartreuse:\"7fff00\",chocolate:\"d2691e\",coral:\"ff7f50\",cornflowerblue:\"6495ed\",cornsilk:\"fff8dc\",crimson:\"dc143c\",cyan:\"0ff\",darkblue:\"00008b\",darkcyan:\"008b8b\",darkgoldenrod:\"b8860b\",darkgray:\"a9a9a9\",darkgreen:\"006400\",darkgrey:\"a9a9a9\",darkkhaki:\"bdb76b\",darkmagenta:\"8b008b\",darkolivegreen:\"556b2f\",darkorange:\"ff8c00\",darkorchid:\"9932cc\",darkred:\"8b0000\",darksalmon:\"e9967a\",darkseagreen:\"8fbc8f\",darkslateblue:\"483d8b\",darkslategray:\"2f4f4f\",darkslategrey:\"2f4f4f\",darkturquoise:\"00ced1\",darkviolet:\"9400d3\",deeppink:\"ff1493\",deepskyblue:\"00bfff\",dimgray:\"696969\",dimgrey:\"696969\",dodgerblue:\"1e90ff\",firebrick:\"b22222\",floralwhite:\"fffaf0\",forestgreen:\"228b22\",fuchsia:\"f0f\",gainsboro:\"dcdcdc\",ghostwhite:\"f8f8ff\",gold:\"ffd700\",goldenrod:\"daa520\",gray:\"808080\",green:\"008000\",greenyellow:\"adff2f\",grey:\"808080\",honeydew:\"f0fff0\",hotpink:\"ff69b4\",indianred:\"cd5c5c\",indigo:\"4b0082\",ivory:\"fffff0\",khaki:\"f0e68c\",lavender:\"e6e6fa\",lavenderblush:\"fff0f5\",lawngreen:\"7cfc00\",lemonchiffon:\"fffacd\",lightblue:\"add8e6\",lightcoral:\"f08080\",lightcyan:\"e0ffff\",lightgoldenrodyellow:\"fafad2\",lightgray:\"d3d3d3\",lightgreen:\"90ee90\",lightgrey:\"d3d3d3\",lightpink:\"ffb6c1\",lightsalmon:\"ffa07a\",lightseagreen:\"20b2aa\",lightskyblue:\"87cefa\",lightslategray:\"789\",lightslategrey:\"789\",lightsteelblue:\"b0c4de\",lightyellow:\"ffffe0\",lime:\"0f0\",limegreen:\"32cd32\",linen:\"faf0e6\",magenta:\"f0f\",maroon:\"800000\",mediumaquamarine:\"66cdaa\",mediumblue:\"0000cd\",mediumorchid:\"ba55d3\",mediumpurple:\"9370db\",mediumseagreen:\"3cb371\",mediumslateblue:\"7b68ee\",mediumspringgreen:\"00fa9a\",mediumturquoise:\"48d1cc\",mediumvioletred:\"c71585\",midnightblue:\"191970\",mintcream:\"f5fffa\",mistyrose:\"ffe4e1\",moccasin:\"ffe4b5\",navajowhite:\"ffdead\",navy:\"000080\",oldlace:\"fdf5e6\",olive:\"808000\",olivedrab:\"6b8e23\",orange:\"ffa500\",orangered:\"ff4500\",orchid:\"da70d6\",palegoldenrod:\"eee8aa\",palegreen:\"98fb98\",paleturquoise:\"afeeee\",palevioletred:\"db7093\",papayawhip:\"ffefd5\",peachpuff:\"ffdab9\",peru:\"cd853f\",pink:\"ffc0cb\",plum:\"dda0dd\",powderblue:\"b0e0e6\",purple:\"800080\",rebeccapurple:\"663399\",red:\"f00\",rosybrown:\"bc8f8f\",royalblue:\"4169e1\",saddlebrown:\"8b4513\",salmon:\"fa8072\",sandybrown:\"f4a460\",seagreen:\"2e8b57\",seashell:\"fff5ee\",sienna:\"a0522d\",silver:\"c0c0c0\",skyblue:\"87ceeb\",slateblue:\"6a5acd\",slategray:\"708090\",slategrey:\"708090\",snow:\"fffafa\",springgreen:\"00ff7f\",steelblue:\"4682b4\",tan:\"d2b48c\",teal:\"008080\",thistle:\"d8bfd8\",tomato:\"ff6347\",turquoise:\"40e0d0\",violet:\"ee82ee\",wheat:\"f5deb3\",white:\"fff\",whitesmoke:\"f5f5f5\",yellow:\"ff0\",yellowgreen:\"9acd32\"},U=e.hexNames=M(Y),X=function(){var t=\"[-\\\\+]?\\\\d+%?\",e=\"[-\\\\+]?\\\\d*\\\\.\\\\d+%?\",n=\"(?:\"+e+\")|(?:\"+t+\")\",r=\"[\\\\s|\\\\(]+(\"+n+\")[,|\\\\s]+(\"+n+\")[,|\\\\s]+(\"+n+\")\\\\s*\\\\)?\",a=\"[\\\\s|\\\\(]+(\"+n+\")[,|\\\\s]+(\"+n+\")[,|\\\\s]+(\"+n+\")[,|\\\\s]+(\"+n+\")\\\\s*\\\\)?\";return{rgb:new RegExp(\"rgb\"+r),rgba:new RegExp(\"rgba\"+a),hsl:new RegExp(\"hsl\"+r),hsla:new RegExp(\"hsla\"+a),hsv:new RegExp(\"hsv\"+r),hsva:new RegExp(\"hsva\"+a),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}}();\"undefined\"!=typeof n&&n.exports?n.exports=e:\"function\"==typeof t&&t.amd?t(function(){return e}):window.tinycolor=e}()},{}],14:[function(t,e,n){\"use strict\";e.exports=[\"\",{path:\"M-2.4,-3V3L0.6,0Z\",backoff:.6},{path:\"M-3.7,-2.5V2.5L1.3,0Z\",backoff:1.3},{path:\"M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z\",backoff:1.55},{path:\"M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z\",backoff:1.6},{path:\"M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z\",backoff:2},{path:\"M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z\",backoff:0},{path:\"M2,2V-2H-2V2Z"
+,
+"\",backoff:0}]},{}],15:[function(t,e,n){\"use strict\";var r=t(\"./arrow_paths\"),a=t(\"../../plots/font_attributes\"),o=t(\"../../plots/cartesian/constants\"),i=t(\"../../lib/extend\").extendFlat;e.exports={_isLinkedToArray:!0,text:{valType:\"string\"},textangle:{valType:\"angle\",dflt:0},font:i({},a,{}),opacity:{valType:\"number\",min:0,max:1,dflt:1},align:{valType:\"enumerated\",values:[\"left\",\"center\",\"right\"],dflt:\"center\"},bgcolor:{valType:\"color\",dflt:\"rgba(0,0,0,0)\"},bordercolor:{valType:\"color\",dflt:\"rgba(0,0,0,0)\"},borderpad:{valType:\"number\",min:0,dflt:1},borderwidth:{valType:\"number\",min:0,dflt:1},showarrow:{valType:\"boolean\",dflt:!0},arrowcolor:{valType:\"color\"},arrowhead:{valType:\"integer\",min:0,max:r.length,dflt:1},arrowsize:{valType:\"number\",min:.3,dflt:1},arrowwidth:{valType:\"number\",min:.1},ax:{valType:\"number\",dflt:-10},ay:{valType:\"number\",dflt:-30},axref:{valType:\"enumerated\",dflt:\"pixel\",values:[\"pixel\",o.idRegex.x.toString()]},ayref:{valType:\"enumerated\",dflt:\"pixel\",values:[\"pixel\",o.idRegex.y.toString()]},xref:{valType:\"enumerated\",values:[\"paper\",o.idRegex.x.toString()]},x:{valType:\"number\"},xanchor:{valType:\"enumerated\",values:[\"auto\",\"left\",\"center\",\"right\"],dflt:\"auto\"},yref:{valType:\"enumerated\",values:[\"paper\",o.idRegex.y.toString()]},y:{valType:\"number\"},yanchor:{valType:\"enumerated\",values:[\"auto\",\"top\",\"middle\",\"bottom\"],dflt:\"auto\"},_deprecated:{ref:{valType:\"string\"}}}},{\"../../lib/extend\":88,\"../../plots/cartesian/constants\":115,\"../../plots/font_attributes\":128,\"./arrow_paths\":14}],16:[function(t,e,n){\"use strict\";function r(t,e){function n(e,n){return c.coerce(t,r,v.layoutAttributes,e,n)}var r={};n(\"opacity\"),n(\"align\"),n(\"bgcolor\");var a=n(\"bordercolor\"),o=f.opacity(a);n(\"borderpad\");var i=n(\"borderwidth\"),l=n(\"showarrow\");l&&(n(\"arrowcolor\",o?r.bordercolor:f.defaultLine),n(\"arrowhead\"),n(\"arrowsize\"),n(\"arrowwidth\",2*(o&&i||1)),n(\"ax\"),n(\"ay\"),n(\"axref\"),n(\"ayref\"),c.noneOrAll(t,r,[\"ax\",\"ay\"])),n(\"text\",l?\"&nbsp;\":\"new text\"),n(\"textangle\"),c.coerceFont(n,\"font\",e.font);\n"
+,
+"for(var s=[\"x\",\"y\"],d=0;2>d;d++){var h=s[d],p={_fullLayout:e},g=u.coerceRef(t,r,p,h),m=u.coerceARef(t,r,p,h),y=.5;if(\"paper\"!==g){var x=u.getFromId(p,g);if(y=x.range[0]+y*(x.range[1]-x.range[0]),-1!==[\"date\",\"category\"].indexOf(x.type)&&\"string\"==typeof t[h]){var b;if(\"date\"===x.type){if(b=c.dateTime2ms(t[h]),b!==!1&&(t[h]=b),m===g){var _=c.dateTime2ms(t[\"a\"+h]);_!==!1&&(t[\"a\"+h]=_)}}else(x._categories||[]).length&&(b=x._categories.indexOf(t[h]),-1!==b&&(t[h]=b))}}n(h,y),l||n(h+\"anchor\")}return c.noneOrAll(t,r,[\"x\",\"y\"]),r}function a(t){var e=t._fullLayout;e.annotations.forEach(function(e){var n=u.getFromId(t,e.xref),r=u.getFromId(t,e.yref);if(n||r){var a=(e._xsize||0)/2,o=e._xshift||0,i=(e._ysize||0)/2,l=e._yshift||0,s=a-o,c=a+o,f=i-l,d=i+l;if(e.showarrow){var h=3*e.arrowsize*e.arrowwidth;s=Math.max(s,h),c=Math.max(c,h),f=Math.max(f,h),d=Math.max(d,h)}n&&n.autorange&&u.expand(n,[n.l2c(e.x)],{ppadplus:c,ppadminus:s}),r&&r.autorange&&u.expand(r,[r.l2c(e.y)],{ppadplus:d,ppadminus:f})}})}function o(t,e,n,r,a,o,i,l){var s=n-t,c=a-t,u=i-a,f=r-e,d=o-e,h=l-o,p=s*h-u*f;if(0===p)return null;var g=(c*h-u*d)/p,v=(c*f-s*d)/p;return 0>v||v>1||0>g||g>1?null:{x:t+s*g,y:e+f*g}}var i=t(\"d3\"),l=t(\"fast-isnumeric\"),s=t(\"../../plotly\"),c=t(\"../../lib\"),u=t(\"../../plots/cartesian/axes\"),f=t(\"../color\"),d=t(\"../drawing\"),h=t(\"../../lib/svg_text_utils\"),p=t(\"../../lib/setcursor\"),g=t(\"../dragelement\"),v=e.exports={};v.ARROWPATHS=t(\"./arrow_paths\"),v.layoutAttributes=t(\"./attributes\"),v.supplyLayoutDefaults=function(t,e){for(var n=t.annotations||[],a=e.annotations=[],o=0;o<n.length;o++)a.push(r(n[o]||{},e))},v.drawAll=function(t){var e=t._fullLayout;e._infolayer.selectAll(\".annotation\").remove();for(var n=0;n<e.annotations.length;n++)v.draw(t,n);return s.Plots.previousPromises(t)},v.add=function(t){var e=t._fullLayout.annotations.length;s.relayout(t,\"annotations[\"+e+\"]\",\"add\")},v.draw=function(t,e,n,a){function m(t){return t.call(d.font,K).attr({\"text-anchor\":{left:\"start\",right:\"end\"}[B.align]||\"middle\"}),h.convertToTspans(t,y),t}function y(){function n(t,e){return\"auto\"===e&&(e=1/3>t?\"left\":t>2/3?\"right\":\"center\"),{center:0,middle:0,left:.5,bottom:-.5,right:-.5,top:.5}[e]}tt.selectAll(\"tspan.line\").attr({y:0,x:0});var r=G.select(\".annotation-math-group\"),a=!r.empty(),l=d.bBox((a?r:tt).node()),h=l.width,m=l.height,y=Math.round(h+2*W),x=Math.round(m+2*W);B._w=h,B._h=m;var b=!1;if([\"x\",\"y\"].forEach(function(e){var r,a=B[e+\"ref\"]||e,o=u.getFromId(t,a),i=(Y+(\"x\"===e?0:90))*Math.PI/180,l=y*Math.abs(Math.cos(i))+x*Math.abs(Math.sin(i)),s=B[e+\"anchor\"];if(o){if(!o.autorange&&(B[e]-o.range[0])*(B[e]-o.range[1])>0&&(B[\"a\"+e+\"ref\"]===a?(B[\"a\"+e]-o.range[0])*(B[\"a\"+e]-o.range[1])>0&&(b=!0):b=!0,b))return;Z[e]=o._offset+o.l2p(B[e]),r=.5}else r=B[e],\"y\"===e&&(r=1-r),Z[e]=\"x\"===e?S.l+S.w*r:S.t+S.h*r;var c=0;B[\"a\"+e+\"ref\"]===a?Z[\"aa\"+e]=o._offset+o.l2p(B[\"a\"+e]):(c=B.showarrow?B[\"a\"+e]:l*n(r,s),Z[e]+=c),B[\"_\"+e+\"type\"]=o&&o.type,B[\"_\"+e+\"size\"]=l,B[\"_\"+e+\"shift\"]=c}),b)return void G.remove();var w,k;B.showarrow&&(w=B.axref===B.xref?Z.x:c.constrain(Z.x-B.ax,1,_.width-1),k=B.ayref===B.yref?Z.y:c.constrain(Z.y-B.ay,1,_.height-1)),Z.x=c.constrain(Z.x,1,_.width-1),Z.y=c.constrain(Z.y,1,_.height-1);var M=W-l.top,A=W-l.left;a?r.select(\"svg\").attr({x:W-1,y:W}):(tt.attr({x:A,y:M}),tt.selectAll(\"tspan.line\").attr({y:M,x:A})),J.call(d.setRect,$/2,$/2,y-$,x-$);var L=0,T=0;L=B.axref===B.xref?Math.round(Z.aax-y/2):Math.round(Z.x-y/2),T=B.ayref===B.yref?Math.round(Z.aay-x/2):Math.round(Z.y-x/2),G.call(c.setTranslate,L,T);var z=\"annotations[\"+e+\"]\",E=function(n,r){i.select(t).selectAll('.annotation-arrow-g[data-index=\"'+e+'\"]').remove();var a,l;a=B.axref===B.xref?Z.aax+n:Z.x+n,l=B.ayref===B.yref?Z.aay+r:Z.y+r;var u=c.rotationXYMatrix(Y,a,l),d=c.apply2DTransform(u),h=c.apply2DTransform2(u),p=J.attr(\"width\")/2,m=J.attr(\"height\")/2,y=[[a-p,l-m,a-p,l+m],[a-p,l+m,a+p,l+m],[a+p,l+m,a+p,l-m],[a+p,l-m,a-p,l-m]].map(h);if(!y.reduce(function(t,e){return t^!!o(w,k,w+1e6,k+1e6,e[0],e[1],e[2],e[3])},!1)){y.forEach(function(t){var e=o(a,l,w,k,t[0],t[1],t[2],t[3]);e&&(a=e.x,l=e.y)});var x=B.arrowwidth,b=B.arrowcolor,_=U.append(\"g\").style({opacity:f.opacity(b)}).classed(\"annotation-arrow-g\",!0).attr(\"data-index\",String(e)),M=_.append(\"path\").attr(\"d\",\"M\"+a+\",\"+l+\"L\"+w+\",\"+k).style(\"stroke-width\",x+\"px\").call(f.stroke,f.rgb(b));v.arrowhead(M,B.arrowhead,\"end\",B.arrowsize);var A=_.append(\"path\").classed(\"annotation\",!0).classed(\"anndrag\",!0).attr({\"data-index\":String(e),d:\"M3,3H-3V-3H3ZM0,0L\"+(a-w)+\",\"+(l-k),transform:\"translate(\"+w+\",\"+k+\")\"}).style(\"stroke-width\",x+6+\"px\").call(f.stroke,\"rgba(0,0,0,0)\").call(f.fill,\"rgba(0,0,0,0)\");if(t._context.editable){var L,T,E;g.init({element:A.node(),prepFn:function(){var t=c.getTranslate(G);T=t.x,E=t.y,L={},H&&H.autorange&&(L[H._name+\".autorange\"]=!0),V&&V.autorange&&(L[V._name+\".autorange\"]=!0)},moveFn:function(t,e){_.attr(\"transform\",\"translate(\"+t+\",\"+e+\")\");var n=d(T,E),r=n[0]+t,a=n[1]+e;G.call(c.setTranslate,r,a),L[z+\".x\"]=H?B.x+t/H._m:(w+t-S.l)/S.w,L[z+\".y\"]=V?B.y+e/V._m:1-(k+e-S.t)/S.h,B.axref===B.xref&&(L[z+\".ax\"]=H?B.ax+t/H._m:(w+t-S.l)/S.w),B.ayref===B.yref&&(L[z+\".ay\"]=V?B.ay+e/V._m:1-(k+e-S.t)/S.h),X.attr({transform:\"rotate(\"+Y+\",\"+r+\",\"+a+\")\"})},doneFn:function(e){if(e){s.relayout(t,L);var n=document.querySelector(\".js-notes-box-panel\");n&&n.redraw(n.selectedObj)}}})}}};B.showarrow&&E(0,0);var C=c.rotationXYMatrix(Y,Z.x,Z.y),O=c.apply2DTransform(C);if(t._context.editable){var P,N,D;g.init({element:G.node(),prepFn:function(){var t=c.getTranslate(G);P=t.x,N=t.y,D={}},moveFn:function(t,e){G.call(c.setTranslate,P+t,N+e);var n=\"pointer\";if(B.showarrow)B.axref===B.xref?D[z+\".ax\"]=H.p2l(H.l2p(B.ax)+t):D[z+\".ax\"]=B.ax+t,B.ayref===B.yref?D[z+\".ay\"]=V.p2l(V.l2p(B.ay)+e):D[z+\".ay\"]=B.ay+e,E(t,e);else{if(H)D[z+\".x\"]=B.x+t/H._m;else{var r=B._xsize/S.w,a=B.x+B._xshift/S.w-r/2;D[z+\".x\"]=g.align(a+t/S.w,r,0,1,B.xanchor)}if(V)D[z+\".y\"]=B.y+e/V._m;else{var o=B._ysize/S.h,i=B.y-B._yshift/S.h-o/2;D[z+\".y\"]=g.align(i-e/S.h,o,0,1,B.yanchor)}H&&V||(n=g.getCursor(H?.5:D[z+\".x\"],V?.5:D[z+\".y\"],B.xanchor,B.yanchor))}var l=O(P,N),s=l[0]+t,u=l[1]+e;G.call(c.setTranslate,P+t,N+e),X.attr({transform:\"rotate(\"+Y+\",\"+s+\",\"+u+\")\"}),p(G,n)},doneFn:function(e){if(p(G),e){s.relayout(t,D);var n=document.querySelector(\".js-notes-box-panel\");n&&n.redraw(n.selectedObj)}}})}}var x,b=t.layout,_=t._fullLayout;if(!l(e)||-1===e){if(!e&&Array.isArray(a))return b.annotations=a,v.supplyLayoutDefaults(b,_),void v.drawAll(t);if(\"remove\"===a)return delete b.annotations,_.annotations=[],void v.drawAll(t);if(n&&\"add\"!==a){for(x=0;x<_.annotations.length;x++)v.draw(t,x,n,a);return}e=_.annotations.length,_.annotations.push({})}if(!n&&a){if(\"remove\"===a){for(_._infolayer.selectAll('.annotation[data-index=\"'+e+'\"]').remove(),_.annotations.splice(e,1),b.annotations.splice(e,1),x=e;x<_.annotations.length;x++)_._infolayer.selectAll('.annotation[data-index=\"'+(x+1)+'\"]').attr(\"data-index\",String(x)),v.draw(t,x);return}if(\"add\"===a||c.isPlainObject(a)){_.annotations.splice(e,0,{});var w=c.isPlainObject(a)?c.extendFlat({},a):{text:\"New text\"};for(b.annotations?b.annotations.splice(e,0,w):b.annotations=[w],x=_.annotations.length-1;x>e;x--)_._infolayer.selectAll('.annotation[data-index=\"'+(x-1)+'\"]').attr(\"data-index\",String(x)),v.draw(t,x)}}_._infolayer.selectAll('.annotation[data-index=\"'+e+'\"]').remove();var k=b.annotations[e],M=_.annotations[e];if(k){var A={xref:k.xref,yref:k.yref},L={};\"string\"==typeof n&&n?L[n]=a:c.isPlainObject(n)&&(L=n);var T=Object.keys(L);for(x=0;x<T.length;x++){var z=T[x];c.nestedProperty(k,z).set(L[z])}var S=_._size,E=[\"x\",\"y\"];for(x=0;2>x;x++){var C=E[x];if(void 0===L[C]&&void 0!==k[C]){var O=u.getFromId(t,u.coerceRef(A,{},t,C)),P=u.getFromId(t,u.coerceRef(k,{},t,C)),N=k[C],D=M[\"_\"+C+\"type\"];if(void 0!==L[C+\"ref\"]){var I=\"auto\"===k[C+\"anchor\"],R=\"x\"===C?S.w:S.h,j=(M[\"_\"+C+\"size\"]||0)/(2*R);if(O&&P)N=(N-O.range[0])/(O.range[1]-O.range[0]),N=P.range[0]+N*(P.range[1]-P.range[0]);else if(O){if(N=(N-O.range[0])/(O.range[1]-O.range[0]),N=O.domain[0]+N*(O.domain[1]-O.domain[0]),I){var q=N+j,F=N-j;2/3>N+F?N=F:N+q>4/3&&(N=q)}}else P&&(I&&(1/3>N?N+=j:N>2/3&&(N-=j)),N=(N-P.domain[0])/(P.domain[1]-P.domain[0]),N=P.range[0]+N*(P.range[1]-P.range[0]))}P&&P===O&&D&&(\"log\"===D&&\"log\"!==P.type?N=Math.pow(10,N):\"log\"!==D&&\"log\"===P.type&&(N=N>0?Math.log(N)/Math.LN10:void 0)),k[C]=N}}var B=r(k,_);_.annotations[e]=B;var H=u.getFromId(t,B.xref),V=u.getFromId(t,B.yref),Z={x:0,y:0},Y=+B.textangle||0,U=_._infolayer.append(\"g\").classed(\"annotation\",!0).attr(\"data-index\",String(e)).style(\"opacity\",B.opacity).on(\"click\",function(){t._dragging=!1,t.emit(\"plotly_clickannotation\",{index:e,annotation:k,fullAnnotation:B})}),X=U.append(\"g\").classed(\"annotation-text-g\",!0).attr(\"data-index\",String(e)),G=X.append(\"g\"),$=B.borderwidth,Q=B.borderpad,W=$+Q,J=G.append(\"rect\").attr(\"class\",\"bg\").style(\"stroke-width\",$+\"px\").call(f.stroke,B.bordercolor).call(f.fill,B.bgcolor),K=B.font,tt=G.append(\"text\").classed(\"annotation\",!0).attr(\"data-unformatted\",B.text).text(B.text);t._context.editable?tt.call(h.makeEditable,G).call(m).on(\"edit\",function(n){B.text=n,this.attr({\"data-unformatted\":B.text}),this.call(m);var r={};r[\"annotations[\"+e+\"].text\"]=B.text,H&&H.autorange&&(r[H._name+\".autorange\"]=!0),V&&V.autorange&&(r[V._name+\".autorange\"]=!0),s.relayout(t,r)}):tt.call(m),X.attr({transform:\"rotate(\"+Y+\",\"+Z.x+\",\"+Z.y+\")\"}).call(d.setPosition,Z.x,Z.y)}},v.arrowhead=function(t,e,n,r){l(r)||(r=1);var a=t.node(),o=v.ARROWPATHS[e||0];if(o){\"string\"==typeof n&&n||(n=\"end\");var s,c,u,h,p=(d.getPx(t,\"stroke-width\")||1)*r,g=t.style(\"stroke\")||f.defaultLine,m=t.style(\"stroke-opacity\")||1,y=n.indexOf(\"start\")>=0,x=n.indexOf(\"end\")>=0,b=o.backoff*p;if(\"line\"===a.nodeName){if(s={x:+t.attr(\"x1\"),y:+t.attr(\"y1\")},c={x:+t.attr(\"x2\"),y:+t.attr(\"y2\")},u=Math.atan2(s.y-c.y,s.x-c.x),h=u+Math.PI,b){var _=b*Math.cos(u),w=b*Math.sin(u);y&&(s.x-=_,s.y-=w,t.attr({x1:s.x,y1:s.y})),x&&(c.x+=_,c.y+=w,t.attr({x2:c.x,y2:c.y}))}}else i"
+,
+"f(\"path\"===a.nodeName){var k=a.getTotalLength(),M=\"\";if(y){var A=a.getPointAtLength(0),L=a.getPointAtLength(.1);u=Math.atan2(A.y-L.y,A.x-L.x),s=a.getPointAtLength(Math.min(b,k)),b&&(M=\"0px,\"+b+\"px,\")}if(x){var T=a.getPointAtLength(k),z=a.getPointAtLength(k-.1);if(h=Math.atan2(T.y-z.y,T.x-z.x),c=a.getPointAtLength(Math.max(0,k-b)),b){var S=M?2*b:b;M+=k-S+\"px,\"+k+\"px\"}}else M&&(M+=k+\"px\");M&&t.style(\"stroke-dasharray\",M)}var E=function(n,r){e>5&&(r=0),i.select(a.parentElement).append(\"path\").attr({\"class\":t.attr(\"class\"),d:o.path,transform:\"translate(\"+n.x+\",\"+n.y+\")rotate(\"+180*r/Math.PI+\")scale(\"+p+\")\"}).style({fill:g,opacity:m,\"stroke-width\":0})};y&&E(s,u),x&&E(c,h)}},v.calcAutorange=function(t){var e=t._fullLayout,n=e.annotations;if(n.length&&t._fullData.length){var r={};n.forEach(function(t){r[t.xref]=!0,r[t.yref]=!0});var o=u.list(t).filter(function(t){return t.autorange&&r[t._id]});if(o.length)return c.syncOrAsync([v.drawAll,a],t)}}},{\"../../lib\":89,\"../../lib/setcursor\":98,\"../../lib/svg_text_utils\":100,\"../../plotly\":107,\"../../plots/cartesian/axes\":110,\"../color\":18,\"../dragelement\":39,\"../drawing\":41,\"./arrow_paths\":14,\"./attributes\":15,d3:9,\"fast-isnumeric\":11}],17:[function(t,e,n){\"use strict\";n.defaults=[\"#1f77b4\",\"#ff7f0e\",\"#2ca02c\",\"#d62728\",\"#9467bd\",\"#8c564b\",\"#e377c2\",\"#7f7f7f\",\"#bcbd22\",\"#17becf\"],n.defaultLine=\"#444\",n.lightLine=\"#eee\",n.background=\"#fff\",n.lightFraction=1e3/11},{}],18:[function(t,e,n){\"use strict\";function r(t){if(o(t)||\"string\"!=typeof t)return t;var e=t.trim();if(\"rgb\"!==e.substr(0,3))return t;var n=e.match(/^rgba?\\s*\\(([^()]*)\\)$/);if(!n)return t;var r=n[1].trim().split(/\\s*[\\s,]\\s*/),a=\"a\"===e.charAt(3)&&4===r.length;if(!a&&3!==r.length)return t;for(var i=0;i<r.length;i++){if(!r[i].length)return t;if(r[i]=Number(r[i]),!(r[i]>=0))return t;if(3===i)r[i]>1&&(r[i]=1);else if(r[i]>=1)return t}var l=Math.round(255*r[0])+\", \"+Math.round(255*r[1])+\", \"+Math.round(255*r[2]);return a?\"rgba(\"+l+\", \"+r[3]+\")\":\"rgb(\"+l+\")\"}var a=t(\"tinycolor2\"),o=t(\"fast-isnumeric\"),i=e.exports={},l=t(\"./attributes\");i.defaults=l.defaults,i.defaultLine=l.defaultLine,i.lightLine=l.lightLine,i.background=l.background,i.tinyRGB=function(t){var e=t.toRgb();return\"rgb(\"+Math.round(e.r)+\", \"+Math.round(e.g)+\", \"+Math.round(e.b)+\")\"},i.rgb=function(t){return i.tinyRGB(a(t))},i.opacity=function(t){return t?a(t).getAlpha():0},i.addOpacity=function(t,e){var n=a(t).toRgb();return\"rgba(\"+Math.round(n.r)+\", \"+Math.round(n.g)+\", \"+Math.round(n.b)+\", \"+e+\")\"},i.combine=function(t,e){var n=a(t).toRgb();if(1===n.a)return a(t).toRgbString();var r=a(e||i.background).toRgb(),o=1===r.a?r:{r:255*(1-r.a)+r.r*r.a,g:255*(1-r.a)+r.g*r.a,b:255*(1-r.a)+r.b*r.a},l={r:o.r*(1-n.a)+n.r*n.a,g:o.g*(1-n.a)+n.g*n.a,b:o.b*(1-n.a)+n.b*n.a};return a(l).toRgbString()},i.stroke=function(t,e){var n=a(e);t.style({stroke:i.tinyRGB(n),\"stroke-opacity\":n.getAlpha()})},i.fill=function(t,e){var n=a(e);t.style({fill:i.tinyRGB(n),\"fill-opacity\":n.getAlpha()})},i.clean=function(t){if(t&&\"object\"==typeof t){var e,n,a,o,l=Object.keys(t);for(e=0;e<l.length;e++)if(a=l[e],o=t[a],\"color\"===a.substr(a.length-5))if(Array.isArray(o))for(n=0;n<o.length;n++)o[n]=r(o[n]);else t[a]=r(o);else if(\"colorscale\"===a.substr(a.length-10)&&Array.isArray(o))for(n=0;n<o.length;n++)Array.isArray(o[n])&&(o[n][1]=r(o[n][1]));else if(Array.isArray(o)){var s=o[0];if(!Array.isArray(s)&&s&&\"object\"==typeof s)for(n=0;n<o.length;n++)i.clean(o[n])}else o&&\"object\"==typeof o&&i.clean(o)}}},{\"./attributes\":17,\"fast-isnumeric\":11,tinycolor2:13}],19:[function(t,e,n){\"use strict\";var r=t(\"../../plots/cartesian/layout_attributes\"),a=t(\"../../plots/font_attributes\"),o=t(\"../../lib/extend\").extendFlat;e.exports={thicknessmode:{valType:\"enumerated\",values:[\"fraction\",\"pixels\"],dflt:\"pixels\"},thickness:{valType:\"number\",min:0,dflt:30},lenmode:{valType:\"enumerated\",values:[\"fraction\",\"pixels\"],dflt:\"fraction\"},len:{valType:\"number\",min:0,dflt:1},x:{valType:\"number\",dflt:1.02,min:-2,max:3},xanchor:{valType:\"enumerated\",values:[\"left\",\"center\",\"right\"],dflt:\"left\"},xpad:{valType:\"number\",min:0,dflt:10},y:{valType:\"number\",dflt:.5,min:-2,max:3},yanchor:{valType:\"enumerated\",values:[\"top\",\"middle\",\"bottom\"],dflt:\"middle\"},ypad:{valType:\"number\",min:0,dflt:10},outlinecolor:r.linecolor,outlinewidth:r.linewidth,bordercolor:r.linecolor,borderwidth:{valType:\"number\",min:0,dflt:0},bgcolor:{valType:\"color\",dflt:\"rgba(0,0,0,0)\"},tickmode:r.tickmode,nticks:r.nticks,tick0:r.tick0,dtick:r.dtick,tickvals:r.tickvals,ticktext:r.ticktext,ticks:o({},r.ticks,{dflt:\"\"}),ticklen:r.ticklen,tickwidth:r.tickwidth,tickcolor:r.tickcolor,showticklabels:r.showticklabels,tickfont:r.tickfont,tickangle:r.tickangle,tickformat:r.tickformat,tickprefix:r.tickprefix,showtickprefix:r.showtickprefix,ticksuffix:r.ticksuffix,showticksuffix:r.showticksuffix,exponentformat:r.exponentformat,showexponent:r.showexponent,title:{valType:\"string\",dflt:\"Click to enter colorscale title\"},titlefont:o({},a,{}),titleside:{valType:\"enumerated\",values:[\"right\",\"top\",\"bottom\"],dflt:\"top\"}}},{\"../../lib/extend\":88,\"../../plots/cartesian/layout_attributes\":119,\"../../plots/font_attributes\":128}],20:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"../../plots/cartesian/tick_value_defaults\"),o=t(\"../../plots/cartesian/tick_mark_defaults\"),i=t(\"../../plots/cartesian/tick_label_defaults\"),l=t(\"./attributes\");e.exports=function(t,e,n){function s(t,e){return r.coerce(u,c,l,t,e)}var c=e.colorbar={},u=t.colorbar||{},f=s(\"thicknessmode\");s(\"thickness\",\"fraction\"===f?30/(n.width-n.margin.l-n.margin.r):30);var d=s(\"lenmode\");s(\"len\",\"fraction\"===d?1:n.height-n.margin.t-n.margin.b),s(\"x\"),s(\"xanchor\"),s(\"xpad\"),s(\"y\"),s(\"yanchor\"),s(\"ypad\"),r.noneOrAll(u,c,[\"x\",\"y\"]),s(\"outlinecolor\"),s(\"outlinewidth\"),s(\"bordercolor\"),s(\"borderwidth\"),s(\"bgcolor\"),a(u,c,s,\"linear\"),i(u,c,s,\"linear\",{outerTicks:!1,font:n.font,noHover:!0}),o(u,c,s,\"linear\",{outerTicks:!1,font:n.font,noHover:!0}),s(\"title\"),r.coerceFont(s,\"titlefont\",n.font),s(\"titleside\")}},{\"../../lib\":89,\"../../plots/cartesian/tick_label_defaults\":125,\"../../plots/cartesian/tick_mark_defaults\":126,\"../../plots/cartesian/tick_value_defaults\":127,\"./attributes\":19}],21:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"tinycolor2\"),o=t(\"../../plotly\"),i=t(\"../../plots/plots\"),l=t(\"../../plots/cartesian/axes\"),s=t(\"../dragelement\"),c=t(\"../../lib\"),u=t(\"../../lib/extend\").extendFlat,f=t(\"../../lib/setcursor\"),d=t(\"../drawing\"),h=t(\"../color\"),p=t(\"../titles\"),g=t(\"../../plots/cartesian/axis_defaults\"),v=t(\"../../plots/cartesian/position_defaults\"),m=t(\"../../plots/cartesian/layout_attributes\"),y=t(\"./attributes\");e.exports=function(t,e){function n(){function y(t,e){return c.coerce(W,J,m,t,e)}function _(){if(-1!==[\"top\",\"bottom\"].indexOf(b.titleside)){var e=at.select(\".cbtitle\"),n=e.select(\"text\"),o=[-b.outlinewidth/2,b.outlinewidth/2],i=e.select(\".h\"+J._id+\"title-math-group\").node(),s=15.6;if(n.node()&&(s=1.3*parseInt(n.style(\"font-size\"),10)),i?(it=d.bBox(i).height,it>s&&(o[1]-=(it-s)/2)):n.node()&&!n.classed(\"js-placeholder\")&&(it=d.bBox(e.node()).height),it){if(it+=5,\"top\"===b.titleside)J.domain[1]-=it/A.h,o[1]*=-1;else{J.domain[0]+=it/A.h;var u=Math.max(1,n.selectAll(\"tspan.line\").size());o[1]+=(1-u)*s}e.attr(\"transform\",\"translate(\"+o+\")\"),J.setScale()}}at.selectAll(\".cbfills,.cblines,.cbaxis\").attr(\"transform\",\"translate(0,\"+Math.round(A.h*(1-J.domain[1]))+\")\");var f=at.select(\".cbfills\").selectAll(\"rect.cbfill\").data(S);f.enter().append(\"rect\").classed(\"cbfill\",!0).style(\"stroke\",\"none\"),f.exit().remove(),f.each(function(t,e){var n=[0===e?T[0]:(S[e]+S[e-1])/2,e===S.length-1?T[1]:(S[e]+S[e+1])/2].map(J.c2p).map(Math.round);e!==S.length-1&&(n[1]+=n[1]>n[0]?1:-1);var o=C(t).replace(\"e-\",\"\"),i=a(o).toHexString();r.select(this).attr({x:U,width:Math.max(q,2),y:r.min(n),height:Math.max(r.max(n)-r.min(n),2),fill:i})});var h=at.select(\".cblines\").selectAll(\"path.cbline\").data(b.line.color&&b.line.width?z:[]);return h.enter().append(\"path\").classed(\"cbline\",!0),h.exit().remove(),h.each(function(t){r.select(this).attr(\"d\",\"M\"+U+\",\"+(Math.round(J.c2p(t))+b.line.width/2%1)+\"h\"+q).call(d.lineGroupStyle,b.line.width,E(t),b.line.dash)}),J._axislayer.selectAll(\"g.\"+J._id+\"tick,path\").remove(),J._pos=U+q+(b.outlinewidth||0)/2-(\"outside\"===b.ticks?1:0),J.side=\"right\",c.syncOrAsync([function(){return l.doTicks(t,J,!0)},function(){if(-1===[\"top\",\"bottom\"].indexOf(b.titleside)){var e=J.titlefont.size,n=J._offset+J._length/2,a=A.l+(J.position||0)*A.w+(\"right\"===J.side?10+e*(J.showticklabels?1:.5):-10-e*(J.showticklabels?.5:0));w(\"h\"+J._id+\"title\",{avoid:{selection:r.select(t).selectAll(\"g.\"+J._id+\"tick\"),side:b.titleside,offsetLeft:A.l,offsetTop:A.t,maxShift:M.width},attributes:{x:a,y:n,\"text-anchor\":\"middle\"},transform:{rotate:\"-90\",offset:0}})}}])}function w(e,n){var r,a=x();r=i.traceIs(a,\"markerColorscale\")?\"marker.colorbar.title\":\"colorbar.title\";var o={propContainer:J,propName:r,traceIndex:a.index,dfltName:\"colorscale\",containerGroup:at.select(\".cbtitle\")},l=\"h\"===e.charAt(0)?e.substr(1):\"h\"+e;at.selectAll(\".\"+l+\",.\"+l+\"-math-group\").remove(),p.draw(t,e,u(o,n||{}))}function k(){var n=q+b.outlinewidth/2+d.bBox(J._axislayer.node()).width;if(I=ot.select(\"text\"),I.node()&&!I.classed(\"js-placeholder\")){var r,a=ot.select(\".h\"+J._id+\"title-math-group\").node();r=a&&-1!==[\"top\",\"bottom\"].indexOf(b.titleside)?d.bBox(a).width:d.bBox(ot.node()).right-U-A.l,n=Math.max(n,r)}var o=2*b.xpad+n+b.borderwidth+b.outlinewidth/2,l=$-Q;at.select(\".cbbg\").attr({x:U-b.xpad-(b.borderwidth+b.outlinewidth)/2,y:Q-Z,width:Math.max(o,2),height:Math.max(l+2*Z,2)}).call(h.fill,b.bgcolor).call(h.stroke,b.bordercolor).style({\"stroke-width\":b.borderwidth}),at.selectAll(\".cboutline\").attr({x:U,y:Q+b.ypad+(\"top\"===b.titleside?it:0),width:Math.max(q,2),height:Math.max(l-2*b.ypad-it,2)}).call(h.stroke,b.outlinecolor).style({fill:\"None\",\"stroke-width\":b.outlinewidth});var s=({center:.5,right:1}[b.xanchor]||0)*o;at.attr(\"transform\",\"translate(\"+(A.l-s)+\",\"+A.t+"
+,
+"\")\"),i.autoMargin(t,e,{x:b.x,y:b.y,l:o*({right:1,center:.5}[b.xanchor]||0),r:o*({left:1,center:.5}[b.xanchor]||0),t:l*({bottom:1,middle:.5}[b.yanchor]||0),b:l*({top:1,middle:.5}[b.yanchor]||0)})}var M=t._fullLayout,A=M._size;if(\"function\"!=typeof b.fillcolor&&\"function\"!=typeof b.line.color)return void M._infolayer.selectAll(\"g.\"+e).remove();var L,T=r.extent((\"function\"==typeof b.fillcolor?b.fillcolor:b.line.color).domain()),z=[],S=[],E=\"function\"==typeof b.line.color?b.line.color:function(){return b.line.color},C=\"function\"==typeof b.fillcolor?b.fillcolor:function(){return b.fillcolor},O=b.levels.end+b.levels.size/100,P=b.levels.size,N=1.001*T[0]-.001*T[1],D=1.001*T[1]-.001*T[0];for(L=b.levels.start;0>(L-O)*P;L+=P)L>N&&D>L&&z.push(L);if(\"function\"==typeof b.fillcolor)if(b.filllevels)for(O=b.filllevels.end+b.filllevels.size/100,P=b.filllevels.size,L=b.filllevels.start;0>(L-O)*P;L+=P)L>T[0]&&L<T[1]&&S.push(L);else S=z.map(function(t){return t-b.levels.size/2}),S.push(S[S.length-1]+b.levels.size);else b.fillcolor&&\"string\"==typeof b.fillcolor&&(S=[0]);b.levels.size<0&&(z.reverse(),S.reverse());var I,R=M.height-M.margin.t-M.margin.b,j=M.width-M.margin.l-M.margin.r,q=Math.round(b.thickness*(\"fraction\"===b.thicknessmode?j:1)),F=q/A.w,B=Math.round(b.len*(\"fraction\"===b.lenmode?R:1)),H=B/A.h,V=b.xpad/A.w,Z=(b.borderwidth+b.outlinewidth)/2,Y=b.ypad/A.h,U=Math.round(b.x*A.w+b.xpad),X=b.x-F*({middle:.5,right:1}[b.xanchor]||0),G=b.y+H*(({top:-.5,bottom:.5}[b.yanchor]||0)-.5),$=Math.round(A.h*(1-G)),Q=$-B,W={type:\"linear\",range:T,tickmode:b.tickmode,nticks:b.nticks,tick0:b.tick0,dtick:b.dtick,tickvals:b.tickvals,ticktext:b.ticktext,ticks:b.ticks,ticklen:b.ticklen,tickwidth:b.tickwidth,tickcolor:b.tickcolor,showticklabels:b.showticklabels,tickfont:b.tickfont,tickangle:b.tickangle,tickformat:b.tickformat,exponentformat:b.exponentformat,showexponent:b.showexponent,showtickprefix:b.showtickprefix,tickprefix:b.tickprefix,showticksuffix:b.showticksuffix,ticksuffix:b.ticksuffix,title:b.title,titlefont:b.titlefont,anchor:\"free\",position:1},J={},K={letter:\"y\",font:M.font,noHover:!0};if(g(W,J,y,K),v(W,J,y,K),J._id=\"y\"+e,J._gd=t,J.position=b.x+V+F,n.axis=J,-1!==[\"top\",\"bottom\"].indexOf(b.titleside)&&(J.titleside=b.titleside,J.titlex=b.x+V,J.titley=G+(\"top\"===b.titleside?H-Y:Y)),b.line.color&&\"auto\"===b.tickmode){J.tickmode=\"linear\",J.tick0=b.levels.start;var tt=b.levels.size,et=c.constrain(($-Q)/50,4,15)+1,nt=(T[1]-T[0])/((b.nticks||et)*tt);if(nt>1){var rt=Math.pow(10,Math.floor(Math.log(nt)/Math.LN10));tt*=rt*c.roundUp(nt/rt,[2,5,10]),(Math.abs(b.levels.start)/b.levels.size+1e-6)%1<2e-6&&(J.tick0=0)}J.dtick=tt}J.domain=[G+Y,G+H-Y],J.setScale();var at=M._infolayer.selectAll(\"g.\"+e).data([0]);at.enter().append(\"g\").classed(e,!0).each(function(){var t=r.select(this);t.append(\"rect\").classed(\"cbbg\",!0),t.append(\"g\").classed(\"cbfills\",!0),t.append(\"g\").classed(\"cblines\",!0),t.append(\"g\").classed(\"cbaxis\",!0).classed(\"crisp\",!0),t.append(\"g\").classed(\"cbtitleunshift\",!0).append(\"g\").classed(\"cbtitle\",!0),t.append(\"rect\").classed(\"cboutline\",!0),t.select(\".cbtitle\").datum(0)}),at.attr(\"transform\",\"translate(\"+Math.round(A.l)+\",\"+Math.round(A.t)+\")\");var ot=at.select(\".cbtitleunshift\").attr(\"transform\",\"translate(-\"+Math.round(A.l)+\",-\"+Math.round(A.t)+\")\");J._axislayer=at.select(\".cbaxis\");var it=0;if(-1!==[\"top\",\"bottom\"].indexOf(b.titleside)){var lt,st=A.l+(b.x+V)*A.w,ct=J.titlefont.size;lt=\"top\"===b.titleside?(1-(G+H-Y))*A.h+A.t+3+.75*ct:(1-(G+Y))*A.h+A.t-3-.25*ct,w(J._id+\"title\",{attributes:{x:st,y:lt,\"text-anchor\":\"start\"}})}var ut=c.syncOrAsync([i.previousPromises,_,i.previousPromises,k],t);if(ut&&ut.then&&(t._promises||[]).push(ut),t._context.editable){var ft,dt,ht;s.init({element:at.node(),prepFn:function(){ft=at.attr(\"transform\"),f(at)},moveFn:function(t,e){at.attr(\"transform\",ft+\" translate(\"+t+\",\"+e+\")\"),dt=s.align(X+t/A.w,F,0,1,b.xanchor),ht=s.align(G-e/A.h,H,0,1,b.yanchor);var n=s.getCursor(dt,ht,b.xanchor,b.yanchor);f(at,n)},doneFn:function(e){f(at),e&&void 0!==dt&&void 0!==ht&&o.restyle(t,{\"colorbar.x\":dt,\"colorbar.y\":ht},x().index)}})}return ut}function x(){var n,r,a=e.substr(2);for(n=0;n<t._fullData.length;n++)if(r=t._fullData[n],r.uid===a)return r}var b={};return Object.keys(y).forEach(function(t){b[t]=null}),b.fillcolor=null,b.line={color:null,width:null,dash:null},b.levels={start:null,end:null,size:null},b.filllevels=null,Object.keys(b).forEach(function(t){n[t]=function(e){return arguments.length?(b[t]=c.isPlainObject(b[t])?c.extendFlat(b[t],e):e,n):b[t]}}),n.options=function(t){return Object.keys(t).forEach(function(e){\"function\"==typeof n[e]&&n[e](t[e])}),n},n._opts=b,n}},{\"../../lib\":89,\"../../lib/extend\":88,\"../../lib/setcursor\":98,\"../../plotly\":107,\"../../plots/cartesian/axes\":110,\"../../plots/cartesian/axis_defaults\":111,\"../../plots/cartesian/layout_attributes\":119,\"../../plots/cartesian/position_defaults\":122,\"../../plots/plots\":130,\"../color\":18,\"../dragelement\":39,\"../drawing\":41,\"../titles\":81,\"./attributes\":19,d3:9,tinycolor2:13}],22:[function(t,e,n){\"use strict\";e.exports=function(t){return\"object\"==typeof t.colorbar&&null!==t.colorbar}},{}],23:[function(t,e,n){\"use strict\";n.attributes=t(\"./attributes\"),n.supplyDefaults=t(\"./defaults\"),n.draw=t(\"./draw\"),n.hasColorbar=t(\"./has_colorbar\")},{\"./attributes\":19,\"./defaults\":20,\"./draw\":21,\"./has_colorbar\":22}],24:[function(t,e,n){\"use strict\";e.exports={zauto:{valType:\"boolean\",dflt:!0},zmin:{valType:\"number\",dflt:null},zmax:{valType:\"number\",dflt:null},colorscale:{valType:\"colorscale\"},autocolorscale:{valType:\"boolean\",dflt:!0},reversescale:{valType:\"boolean\",dflt:!1},showscale:{valType:\"boolean\",dflt:!0}}},{}],25:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./scales\"),o=t(\"./flip_scale\");e.exports=function(t,e,n,i){var l,s;n?(l=r.nestedProperty(t,n).get(),s=r.nestedProperty(t._input,n).get()):(l=t,s=t._input);var c=l[i+\"auto\"],u=l[i+\"min\"],f=l[i+\"max\"],d=l.colorscale;c===!1&&void 0!==u||(u=r.aggNums(Math.min,null,e)),c===!1&&void 0!==f||(f=r.aggNums(Math.max,null,e)),u===f&&(u-=.5,f+=.5),l[i+\"min\"]=u,l[i+\"max\"]=f,s[i+\"min\"]=u,s[i+\"max\"]=f,l.autocolorscale&&(d=0>u*f?a.RdBu:u>=0?a.Reds:a.Blues,s.colorscale=d,l.reversescale&&(d=o(d)),l.colorscale=d)}},{\"../../lib\":89,\"./flip_scale\":29,\"./scales\":36}],26:[function(t,e,n){\"use strict\";var r=t(\"./attributes\"),a=t(\"../../lib/extend\").extendDeep;t(\"./scales.js\");e.exports=function(t){return{color:{valType:\"color\",arrayOk:!0},colorscale:a({},r.colorscale,{}),cauto:a({},r.zauto,{}),cmax:a({},r.zmax,{}),cmin:a({},r.zmin,{}),autocolorscale:a({},r.autocolorscale,{}),reversescale:a({},r.reversescale,{})}}},{\"../../lib/extend\":88,\"./attributes\":24,\"./scales.js\":36}],27:[function(t,e,n){\"use strict\";var r=t(\"./scales\");e.exports=r.RdBu},{\"./scales\":36}],28:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../lib\"),o=t(\"../colorbar/has_colorbar\"),i=t(\"../colorbar/defaults\"),l=t(\"./is_valid_scale\"),s=t(\"./flip_scale\");e.exports=function(t,e,n,c,u){var f=u.prefix,d=u.cLetter,h=f.slice(0,f.length-1),p=f?a.nestedProperty(t,h).get()||{}:t,g=f?a.nestedProperty(e,h).get()||{}:e,v=p[d+\"min\"],m=p[d+\"max\"],y=p.colorscale,x=r(v)&&r(m)&&m>v;c(f+d+\"auto\",!x),c(f+d+\"min\"),c(f+d+\"max\");var b;void 0!==y&&(b=!l(y)),c(f+\"autocolorscale\",b);var _=c(f+\"colorscale\"),w=c(f+\"reversescale\");if(w&&(g.colorscale=s(_)),\"marker.line.\"!==f){var k;f&&(k=o(p));var M=c(f+\"showscale\",k);M&&i(p,g,n)}}},{\"../../lib\":89,\"../colorbar/defaults\":20,\"../colorbar/has_colorbar\":22,\"./flip_scale\":29,\"./is_valid_scale\":33,\"fast-isnumeric\":11}],29:[function(t,e,n){\"use strict\";e.exports=function(t){for(var e,n=t.length,r=new Array(n),a=n-1,o=0;a>=0;a--,o++)e=t[a],r[o]=[1-e[0],e[1]];return r}},{}],30:[function(t,e,n){\"use strict\";var r=t(\"./scales\"),a=t(\"./default_scale\"),o=t(\"./is_valid_scale_array\");e.exports=function(t,e){function n(){try{t=r[t]||JSON.parse(t)}catch(n){t=e}}return e||(e=a),t?(\"string\"==typeof t&&(n(),\"string\"==typeof t&&n()),o(t)?t:e):e}},{\"./default_scale\":27,\"./is_valid_scale_array\":34,\"./scales\":36}],31:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../lib\"),o=t(\"./is_valid_scale\");e.exports=function(t,e){var n=e?a.nestedProperty(t,e).get()||{}:t,i=n.color,l=!1;if(Array.isArray(i))for(var s=0;s<i.length;s++)if(r(i[s])){l=!0;break}return\"object\"==typeof n&&null!==n&&(l||n.showscale===!0||r(n.cmin)&&r(n.cmax)||o(n.colorscale)||\"object\"==typeof n.colorbar&&null!==n.colorbar)}},{\"../../lib\":89,\"./is_valid_scale\":33,\"fast-isnumeric\":11}],32:[function(t,e,n){\"use strict\";n.scales=t(\"./scales\"),n.defaultScale=t(\"./default_scale\"),n.attributes=t(\"./attributes\"),n.handleDefaults=t(\"./defaults\"),n.calc=t(\"./calc\"),n.hasColorscale=t(\"./has_colorscale\"),n.isValidScale=t(\"./is_valid_scale\"),n.getScale=t(\"./get_scale\"),n.flipScale=t(\"./flip_scale\"),n.makeScaleFunction=t(\"./make_scale_function\")},{\"./attributes\":24,\"./calc\":25,\"./default_scale\":27,\"./defaults\":28,\"./flip_scale\":29,\"./get_scale\":30,\"./has_colorscale\":31,\"./is_valid_scale\":33,\"./make_scale_function\":35,\"./scales\":36}],33:[function(t,e,n){\"use strict\";var r=t(\"./scales\"),a=t(\"./is_valid_scale_array\");e.exports=function(t){return void 0!==r[t]?!0:a(t)}},{\"./is_valid_scale_array\":34,\"./scales\":36}],34:[function(t,e,n){\"use strict\";var r=t(\"tinycolor2\");e.exports=function(t){var e=0;if(!Array.isArray(t)||t.length<2)return!1;if(!t[0]||!t[t.length-1])return!1;if(0!==+t[0][0]||1!==+t[t.length-1][0])return!1;for(var n=0;n<t.length;n++){var a=t[n];if(2!==a.length||+a[0]<e||!r(a[1]).isValid())return!1;e=+a[0]}return!0}},{tinycolor2:13}],35:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"tinycolor2\"),o=t(\"fast-isnumeric\"),i=t(\"../../lib\"),l=t(\"../color\");e.exports=function(t,e,n){for(var s,c=t.length,u=new Array(c),f=new Array(c),d=0;c>d;d++)s=t[d],u[d]=e+s[0]*(n-e),f[d]=a(s[1]).toRgb();var h=r.scale.linear().domain(u).interpolate(r.interpolateObject).range(f);return function(t){if(o(t)){var r=i.constrain(t,e,n),s=h(r);return a(s).toRgbString()}return a(t).isVali"
+,
+"d()?t:l.defaultLine}}},{\"../../lib\":89,\"../color\":18,d3:9,\"fast-isnumeric\":11,tinycolor2:13}],36:[function(t,e,n){\"use strict\";e.exports={Greys:[[0,\"rgb(0,0,0)\"],[1,\"rgb(255,255,255)\"]],YlGnBu:[[0,\"rgb(8,29,88)\"],[.125,\"rgb(37,52,148)\"],[.25,\"rgb(34,94,168)\"],[.375,\"rgb(29,145,192)\"],[.5,\"rgb(65,182,196)\"],[.625,\"rgb(127,205,187)\"],[.75,\"rgb(199,233,180)\"],[.875,\"rgb(237,248,217)\"],[1,\"rgb(255,255,217)\"]],Greens:[[0,\"rgb(0,68,27)\"],[.125,\"rgb(0,109,44)\"],[.25,\"rgb(35,139,69)\"],[.375,\"rgb(65,171,93)\"],[.5,\"rgb(116,196,118)\"],[.625,\"rgb(161,217,155)\"],[.75,\"rgb(199,233,192)\"],[.875,\"rgb(229,245,224)\"],[1,\"rgb(247,252,245)\"]],YlOrRd:[[0,\"rgb(128,0,38)\"],[.125,\"rgb(189,0,38)\"],[.25,\"rgb(227,26,28)\"],[.375,\"rgb(252,78,42)\"],[.5,\"rgb(253,141,60)\"],[.625,\"rgb(254,178,76)\"],[.75,\"rgb(254,217,118)\"],[.875,\"rgb(255,237,160)\"],[1,\"rgb(255,255,204)\"]],Bluered:[[0,\"rgb(0,0,255)\"],[1,\"rgb(255,0,0)\"]],RdBu:[[0,\"rgb(5,10,172)\"],[.35,\"rgb(106,137,247)\"],[.5,\"rgb(190,190,190)\"],[.6,\"rgb(220,170,132)\"],[.7,\"rgb(230,145,90)\"],[1,\"rgb(178,10,28)\"]],Reds:[[0,\"rgb(220,220,220)\"],[.2,\"rgb(245,195,157)\"],[.4,\"rgb(245,160,105)\"],[1,\"rgb(178,10,28)\"]],Blues:[[0,\"rgb(5,10,172)\"],[.35,\"rgb(40,60,190)\"],[.5,\"rgb(70,100,245)\"],[.6,\"rgb(90,120,245)\"],[.7,\"rgb(106,137,247)\"],[1,\"rgb(220,220,220)\"]],Picnic:[[0,\"rgb(0,0,255)\"],[.1,\"rgb(51,153,255)\"],[.2,\"rgb(102,204,255)\"],[.3,\"rgb(153,204,255)\"],[.4,\"rgb(204,204,255)\"],[.5,\"rgb(255,255,255)\"],[.6,\"rgb(255,204,255)\"],[.7,\"rgb(255,153,255)\"],[.8,\"rgb(255,102,204)\"],[.9,\"rgb(255,102,102)\"],[1,\"rgb(255,0,0)\"]],Rainbow:[[0,\"rgb(150,0,90)\"],[.125,\"rgb(0,0,200)\"],[.25,\"rgb(0,25,255)\"],[.375,\"rgb(0,152,255)\"],[.5,\"rgb(44,255,150)\"],[.625,\"rgb(151,255,0)\"],[.75,\"rgb(255,234,0)\"],[.875,\"rgb(255,111,0)\"],[1,\"rgb(255,0,0)\"]],Portland:[[0,\"rgb(12,51,131)\"],[.25,\"rgb(10,136,186)\"],[.5,\"rgb(242,211,56)\"],[.75,\"rgb(242,143,56)\"],[1,\"rgb(217,30,30)\"]],Jet:[[0,\"rgb(0,0,131)\"],[.125,\"rgb(0,60,170)\"],[.375,\"rgb(5,255,255)\"],[.625,\"rgb(255,255,0)\"],[.875,\"rgb(250,0,0)\"],[1,\"rgb(128,0,0)\"]],\n"
+,
+"Hot:[[0,\"rgb(0,0,0)\"],[.3,\"rgb(230,0,0)\"],[.6,\"rgb(255,210,0)\"],[1,\"rgb(255,255,255)\"]],Blackbody:[[0,\"rgb(0,0,0)\"],[.2,\"rgb(230,0,0)\"],[.4,\"rgb(230,210,0)\"],[.7,\"rgb(255,255,255)\"],[1,\"rgb(160,200,255)\"]],Earth:[[0,\"rgb(0,0,130)\"],[.1,\"rgb(0,180,180)\"],[.2,\"rgb(40,210,40)\"],[.4,\"rgb(230,230,50)\"],[.6,\"rgb(120,70,20)\"],[1,\"rgb(255,255,255)\"]],Electric:[[0,\"rgb(0,0,0)\"],[.15,\"rgb(30,0,100)\"],[.4,\"rgb(120,0,100)\"],[.6,\"rgb(160,90,0)\"],[.8,\"rgb(230,200,0)\"],[1,\"rgb(255,250,220)\"]],Viridis:[[0,\"#440154\"],[.06274509803921569,\"#48186a\"],[.12549019607843137,\"#472d7b\"],[.18823529411764706,\"#424086\"],[.25098039215686274,\"#3b528b\"],[.3137254901960784,\"#33638d\"],[.3764705882352941,\"#2c728e\"],[.4392156862745098,\"#26828e\"],[.5019607843137255,\"#21918c\"],[.5647058823529412,\"#1fa088\"],[.6274509803921569,\"#28ae80\"],[.6901960784313725,\"#3fbc73\"],[.7529411764705882,\"#5ec962\"],[.8156862745098039,\"#84d44b\"],[.8784313725490196,\"#addc30\"],[.9411764705882353,\"#d8e219\"],[1,\"#fde725\"]]}},{}],37:[function(t,e,n){\"use strict\";e.exports=function(t,e,n,r,a){var o=(t-n)/(r-n),i=o+e/(r-n),l=(o+i)/2;return\"left\"===a||\"bottom\"===a?o:\"center\"===a||\"middle\"===a?l:\"right\"===a||\"top\"===a?i:2/3-l>o?o:i>4/3-l?i:l}},{}],38:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=[[\"sw-resize\",\"s-resize\",\"se-resize\"],[\"w-resize\",\"move\",\"e-resize\"],[\"nw-resize\",\"n-resize\",\"ne-resize\"]];e.exports=function(t,e,n,o){return t=\"left\"===n?0:\"center\"===n?1:\"right\"===n?2:r.constrain(Math.floor(3*t),0,2),e=\"bottom\"===o?0:\"middle\"===o?1:\"top\"===o?2:r.constrain(Math.floor(3*e),0,2),a[e][t]}},{\"../../lib\":89}],39:[function(t,e,n){\"use strict\";function r(){var t=document.createElement(\"div\");t.className=\"dragcover\";var e=t.style;return e.position=\"fixed\",e.left=0,e.right=0,e.top=0,e.bottom=0,e.zIndex=999999999,e.background=\"none\",document.body.appendChild(t),t}function a(t){t._dragging=!1,t._replotPending&&o.plot(t)}var o=t(\"../../plotly\"),i=t(\"../../lib\"),l=t(\"../../plots/cartesian/constants\"),s=e.exports={};s.align=t(\"./align\"),s.getCursor=t(\"./cursor\");var c=t(\"./unhover\");s.unhover=c.wrapped,s.unhoverRaw=c.raw,s.init=function(t){function e(e){return t.element.onmousemove=p,g._dragged=!1,g._dragging=!0,c=e.clientX,u=e.clientY,h=e.target,f=(new Date).getTime(),f-g._mouseDownTime<m?v+=1:(v=1,g._mouseDownTime=f),t.prepFn&&t.prepFn(e,c,u),d=r(),d.onmousemove=n,d.onmouseup=o,d.onmouseout=o,d.style.cursor=window.getComputedStyle(t.element).cursor,i.pauseEvent(e)}function n(e){var n=e.clientX-c,r=e.clientY-u,a=t.minDrag||l.MINDRAG;return Math.abs(n)<a&&(n=0),Math.abs(r)<a&&(r=0),(n||r)&&(g._dragged=!0,s.unhover(g)),t.moveFn&&t.moveFn(n,r,g._dragged),i.pauseEvent(e)}function o(e){if(p=t.element.onmousemove,t.setCursor&&(t.element.onmousemove=t.setCursor),d.onmousemove=null,d.onmouseup=null,d.onmouseout=null,i.removeElement(d),!g._dragging)return void(g._dragged=!1);if(g._dragging=!1,(new Date).getTime()-g._mouseDownTime>m&&(v=Math.max(v-1,1)),t.doneFn&&t.doneFn(g._dragged,v),!g._dragged){var n=document.createEvent(\"MouseEvents\");n.initEvent(\"click\",!0,!0),h.dispatchEvent(n)}return a(g),g._dragged=!1,i.pauseEvent(e)}var c,u,f,d,h,p,g=i.getPlotDiv(t.element)||{},v=1,m=l.DBLCLICKDELAY;g._mouseDownTime||(g._mouseDownTime=0),p=t.element.onmousemove,t.setCursor&&(t.element.onmousemove=t.setCursor),t.element.onmousedown=e,t.element.style.pointerEvents=\"all\"}},{\"../../lib\":89,\"../../plotly\":107,\"../../plots/cartesian/constants\":115,\"./align\":37,\"./cursor\":38,\"./unhover\":40}],40:[function(t,e,n){\"use strict\";var r=t(\"../../lib/events\"),a=e.exports={};a.wrapped=function(t,e,n){\"string\"==typeof t&&(t=document.getElementById(t)),t._hoverTimer&&(clearTimeout(t._hoverTimer),t._hoverTimer=void 0),a.raw(t,e,n)},a.raw=function(t,e){var n=t._fullLayout;e||(e={}),e.target&&r.triggerHandler(t,\"plotly_beforehover\",e)===!1||(n._hoverlayer.selectAll(\"g\").remove(),e.target&&t._hoverdata&&t.emit(\"plotly_unhover\",{points:t._hoverdata}),t._hoverdata=void 0)}},{\"../../lib/events\":87}],41:[function(t,e,n){\"use strict\";function r(t,e,n,r){var o=t[0]-e[0],i=t[1]-e[1],l=n[0]-e[0],s=n[1]-e[1],c=Math.pow(o*o+i*i,b/2),u=Math.pow(l*l+s*s,b/2),f=(u*u*o-c*c*l)*r,d=(u*u*i-c*c*s)*r,h=3*u*(c+u),p=3*c*(c+u);return[[a.round(e[0]+(h&&f/h),2),a.round(e[1]+(h&&d/h),2)],[a.round(e[0]-(p&&f/p),2),a.round(e[1]-(p&&d/p),2)]]}var a=t(\"d3\"),o=t(\"fast-isnumeric\"),i=t(\"../../plots/plots\"),l=t(\"../color\"),s=t(\"../colorscale\"),c=t(\"../../lib\"),u=t(\"../../lib/svg_text_utils\"),f=t(\"../../constants/xmlns_namespaces\"),d=t(\"../../traces/scatter/subtypes\"),h=t(\"../../traces/scatter/make_bubble_size_func\"),p=e.exports={};p.font=function(t,e,n,r){e&&e.family&&(r=e.color,n=e.size,e=e.family),e&&t.style(\"font-family\",e),n+1&&t.style(\"font-size\",n+\"px\"),r&&t.call(l.fill,r)},p.setPosition=function(t,e,n){t.attr(\"x\",e).attr(\"y\",n)},p.setSize=function(t,e,n){t.attr(\"width\",e).attr(\"height\",n)},p.setRect=function(t,e,n,r,a){t.call(p.setPosition,e,n).call(p.setSize,r,a)},p.translatePoints=function(t,e,n){t.each(function(t){var r=t.xp||e.c2p(t.x),i=t.yp||n.c2p(t.y),l=a.select(this);o(r)&&o(i)?\"text\"===this.nodeName?l.attr(\"x\",r).attr(\"y\",i):l.attr(\"transform\",\"translate(\"+r+\",\"+i+\")\"):l.remove()})},p.getPx=function(t,e){return Number(t.style(e).replace(/px$/,\"\"))},p.crispRound=function(t,e,n){return e&&o(e)?t._context.staticPlot?e:1>e?1:Math.round(e):n||0},p.lineGroupStyle=function(t,e,n,r){t.style(\"fill\",\"none\").each(function(t){var o=(((t||[])[0]||{}).trace||{}).line||{},i=e||o.width||0,s=r||o.dash||\"\";a.select(this).call(l.stroke,n||o.color).call(p.dashLine,s,i)})},p.dashLine=function(t,e,n){var r=Math.max(n,3);\"solid\"===e?e=\"\":\"dot\"===e?e=r+\"px,\"+r+\"px\":\"dash\"===e?e=3*r+\"px,\"+3*r+\"px\":\"longdash\"===e?e=5*r+\"px,\"+5*r+\"px\":\"dashdot\"===e?e=3*r+\"px,\"+r+\"px,\"+r+\"px,\"+r+\"px\":\"longdashdot\"===e&&(e=5*r+\"px,\"+2*r+\"px,\"+r+\"px,\"+2*r+\"px\"),t.style({\"stroke-dasharray\":e,\"stroke-width\":n+\"px\"})},p.fillGroupStyle=function(t){t.style(\"stroke-width\",0).each(function(e){var n=a.select(this);try{n.call(l.fill,e[0].trace.fillcolor)}catch(r){c.error(r,t),n.remove()}})};var g=t(\"./symbol_defs\");p.symbolNames=[],p.symbolFuncs=[],p.symbolNeedLines={},p.symbolNoDot={},p.symbolList=[],Object.keys(g).forEach(function(t){var e=g[t];p.symbolList=p.symbolList.concat([e.n,t,e.n+100,t+\"-open\"]),p.symbolNames[e.n]=t,p.symbolFuncs[e.n]=e.f,e.needLine&&(p.symbolNeedLines[e.n]=!0),e.noDot?p.symbolNoDot[e.n]=!0:p.symbolList=p.symbolList.concat([e.n+200,t+\"-dot\",e.n+300,t+\"-open-dot\"])});var v=p.symbolNames.length,m=\"M0,0.5L0.5,0L0,-0.5L-0.5,0Z\";p.symbolNumber=function(t){if(\"string\"==typeof t){var e=0;t.indexOf(\"-open\")>0&&(e=100,t=t.replace(\"-open\",\"\")),t.indexOf(\"-dot\")>0&&(e+=200,t=t.replace(\"-dot\",\"\")),t=p.symbolNames.indexOf(t),t>=0&&(t+=e)}return t%100>=v||t>=400?0:Math.floor(Math.max(t,0))},p.pointStyle=function(t,e){if(t.size()){var n=e.marker,r=n.line;if(i.traceIs(e,\"symbols\")){var o=h(e);t.attr(\"d\",function(t){var r;r=\"various\"===t.ms||\"various\"===n.size?3:d.isBubble(e)?o(t.ms):(n.size||6)/2,t.mrc=r;var a=p.symbolNumber(t.mx||n.symbol)||0,i=a%100;return t.om=a%200>=100,p.symbolFuncs[i](r)+(a>=200?m:\"\")}).style(\"opacity\",function(t){return(t.mo+1||n.opacity+1)-1})}var s=(e._input||{}).marker||{},c=p.tryColorscale(n,s,\"\"),u=p.tryColorscale(n,s,\"line.\");t.each(function(t){var e,o,i;t.so?(i=r.outlierwidth,o=r.outliercolor,e=n.outliercolor):(i=(t.mlw+1||r.width+1||(t.trace?t.trace.marker.line.width:0)+1)-1,o=\"mlc\"in t?t.mlcc=u(t.mlc):Array.isArray(r.color)?l.defaultLine:r.color,e=\"mc\"in t?t.mcc=c(t.mc):Array.isArray(n.color)?l.defaultLine:n.color||\"rgba(0,0,0,0)\");var s=a.select(this);t.om?s.call(l.stroke,e).style({\"stroke-width\":(i||1)+\"px\",fill:\"none\"}):(s.style(\"stroke-width\",i+\"px\").call(l.fill,e),i&&s.call(l.stroke,o))})}},p.tryColorscale=function(t,e,n){var r=c.nestedProperty(t,n+\"color\").get(),a=c.nestedProperty(t,n+\"colorscale\").get(),i=c.nestedProperty(t,n+\"cauto\").get(),l=c.nestedProperty(t,n+\"cmin\"),u=c.nestedProperty(t,n+\"cmax\"),f=l.get(),d=u.get();return a&&Array.isArray(r)?(!i&&o(f)&&o(d)||(f=1/0,d=-(1/0),r.forEach(function(t){o(t)&&(f>t&&(f=+t),t>d&&(d=+t))}),f>d&&(f=0,d=1),l.set(f),u.set(d),c.nestedProperty(e,n+\"cmin\").set(f),c.nestedProperty(e,n+\"cmax\").set(d)),s.makeScaleFunction(a,f,d)):c.identity};var y={start:1,end:-1,middle:0,bottom:1,top:-1},x=1.3;p.textPointStyle=function(t,e){t.each(function(t){var n=a.select(this),r=t.tx||e.text;if(!r||Array.isArray(r))return void n.remove();var i=t.tp||e.textposition,l=-1!==i.indexOf(\"top\")?\"top\":-1!==i.indexOf(\"bottom\")?\"bottom\":\"middle\",s=-1!==i.indexOf(\"left\")?\"end\":-1!==i.indexOf(\"right\")?\"start\":\"middle\",c=t.ts||e.textfont.size,f=t.mrc?t.mrc/.8+1:0;c=o(c)&&c>0?c:0,n.call(p.font,t.tf||e.textfont.family,c,t.tc||e.textfont.color).attr(\"text-anchor\",s).text(r).call(u.convertToTspans);var d=a.select(this.parentNode),h=n.selectAll(\"tspan.line\"),g=((h[0].length||1)-1)*x+1,v=y[s]*f,m=.75*c+y[l]*f+(y[l]-1)*g*c/2;d.attr(\"transform\",\"translate(\"+v+\",\"+m+\")\"),g>1&&h.attr({x:n.attr(\"x\"),y:n.attr(\"y\")})})};var b=.5;p.smoothopen=function(t,e){if(t.length<3)return\"M\"+t.join(\"L\");var n,a=\"M\"+t[0],o=[];for(n=1;n<t.length-1;n++)o.push(r(t[n-1],t[n],t[n+1],e));for(a+=\"Q\"+o[0][0]+\" \"+t[1],n=2;n<t.length-1;n++)a+=\"C\"+o[n-2][1]+\" \"+o[n-1][0]+\" \"+t[n];return a+=\"Q\"+o[t.length-3][1]+\" \"+t[t.length-1]},p.smoothclosed=function(t,e){if(t.length<3)return\"M\"+t.join(\"L\")+\"Z\";var n,a=\"M\"+t[0],o=t.length-1,i=[r(t[o],t[0],t[1],e)];for(n=1;o>n;n++)i.push(r(t[n-1],t[n],t[n+1],e));for(i.push(r(t[o-1],t[o],t[0],e)),n=1;o>=n;n++)a+=\"C\"+i[n-1][1]+\" \"+i[n][0]+\" \"+t[n];return a+=\"C\"+i[o][1]+\" \"+i[0][0]+\" \"+t[0]+\"Z\"};var _={hv:function(t,e){return\"H\"+a.round(e[0],2)+\"V\"+a.round(e[1],2)},vh:function(t,e){return\"V\"+a.round(e[1],2)+\"H\"+a.round(e[0],2)},hvh:function(t,e){return\"H\"+a.round((t[0]+e[0])/2,2)+\"V\"+a.round(e[1],2)+\"H\"+a.round(e[0],2)},vhv:function(t,e){return\"V\"+a.round((t[1]+e[1])/2,2)+\"H\"+a.round(e[0],2)+\"V\"+a.round(e[1],2)}},w=function(t,e){return\"L\"+a.round(e[0],2)+\",\"+a.round(e[1],2)};p.steps=function(t){var e=_[t]||w;return function(t){f"
+,
+"or(var n=\"M\"+a.round(t[0][0],2)+\",\"+a.round(t[0][1],2),r=1;r<t.length;r++)n+=e(t[r-1],t[r]);return n}},p.makeTester=function(t){var e=a.select(\"body\").selectAll(\"#js-plotly-tester\").data([0]);e.enter().append(\"svg\").attr(\"id\",\"js-plotly-tester\").attr(f.svgAttrs).style({position:\"absolute\",left:\"-10000px\",top:\"-10000px\",width:\"9000px\",height:\"9000px\",\"z-index\":\"1\"});var n=e.selectAll(\".js-reference-point\").data([0]);n.enter().append(\"path\").classed(\"js-reference-point\",!0).attr(\"d\",\"M0,0H1V1H0Z\").style({\"stroke-width\":0,fill:\"black\"}),e.node()._cache||(e.node()._cache={}),t._tester=e,t._testref=n};var k=[],M=1e4;p.bBox=function(t){var e=t.attributes[\"data-bb\"];if(e&&e.value)return c.extendFlat({},k[e.value]);var n=a.select(\"#js-plotly-tester\"),r=n.node(),o=t.cloneNode(!0);r.appendChild(o),a.select(o).attr({x:0,y:0,transform:\"\"});var i=o.getBoundingClientRect(),l=n.select(\".js-reference-point\").node().getBoundingClientRect();r.removeChild(o);var s={height:i.height,width:i.width,left:i.left-l.left,top:i.top-l.top,right:i.right-l.left,bottom:i.bottom-l.top};return k.length>=M&&(a.selectAll(\"[data-bb]\").attr(\"data-bb\",null),k=[]),t.setAttribute(\"data-bb\",k.length),k.push(s),c.extendFlat({},s)},p.setClipUrl=function(t,e){if(!e)return void t.attr(\"clip-path\",null);var n=\"#\"+e,r=a.select(\"base\");r.size()&&r.attr(\"href\")&&(n=window.location.href+n),t.attr(\"clip-path\",\"url(\"+n+\")\")}},{\"../../constants/xmlns_namespaces\":82,\"../../lib\":89,\"../../lib/svg_text_utils\":100,\"../../plots/plots\":130,\"../../traces/scatter/make_bubble_size_func\":181,\"../../traces/scatter/subtypes\":186,\"../color\":18,\"../colorscale\":32,\"./symbol_defs\":42,d3:9,\"fast-isnumeric\":11}],42:[function(t,e,n){\"use strict\";var r=t(\"d3\");e.exports={circle:{n:0,f:function(t){var e=r.round(t,2);return\"M\"+e+\",0A\"+e+\",\"+e+\" 0 1,1 0,-\"+e+\"A\"+e+\",\"+e+\" 0 0,1 \"+e+\",0Z\"}},square:{n:1,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"H-\"+e+\"V-\"+e+\"H\"+e+\"Z\"}},diamond:{n:2,f:function(t){var e=r.round(1.3*t,2);return\"M\"+e+\",0L0,\"+e+\"L-\"+e+\",0L0,-\"+e+\"Z\"}},cross:{n:3,f:function(t){var e=r.round(.4*t,2),n=r.round(1.2*t,2);return\"M\"+n+\",\"+e+\"H\"+e+\"V\"+n+\"H-\"+e+\"V\"+e+\"H-\"+n+\"V-\"+e+\"H-\"+e+\"V-\"+n+\"H\"+e+\"V-\"+e+\"H\"+n+\"Z\"}},x:{n:4,f:function(t){var e=r.round(.8*t/Math.sqrt(2),2),n=\"l\"+e+\",\"+e,a=\"l\"+e+\",-\"+e,o=\"l-\"+e+\",-\"+e,i=\"l-\"+e+\",\"+e;return\"M0,\"+e+n+a+o+a+o+i+o+i+n+i+n+\"Z\"}},\"triangle-up\":{n:5,f:function(t){var e=r.round(2*t/Math.sqrt(3),2),n=r.round(t/2,2),a=r.round(t,2);return\"M-\"+e+\",\"+n+\"H\"+e+\"L0,-\"+a+\"Z\"}},\"triangle-down\":{n:6,f:function(t){var e=r.round(2*t/Math.sqrt(3),2),n=r.round(t/2,2),a=r.round(t,2);return\"M-\"+e+\",-\"+n+\"H\"+e+\"L0,\"+a+\"Z\"}},\"triangle-left\":{n:7,f:function(t){var e=r.round(2*t/Math.sqrt(3),2),n=r.round(t/2,2),a=r.round(t,2);return\"M\"+n+\",-\"+e+\"V\"+e+\"L-\"+a+\",0Z\"}},\"triangle-right\":{n:8,f:function(t){var e=r.round(2*t/Math.sqrt(3),2),n=r.round(t/2,2),a=r.round(t,2);return\"M-\"+n+\",-\"+e+\"V\"+e+\"L\"+a+\",0Z\"}},\"triangle-ne\":{n:9,f:function(t){var e=r.round(.6*t,2),n=r.round(1.2*t,2);return\"M-\"+n+\",-\"+e+\"H\"+e+\"V\"+n+\"Z\"}},\"triangle-se\":{n:10,f:function(t){var e=r.round(.6*t,2),n=r.round(1.2*t,2);return\"M\"+e+\",-\"+n+\"V\"+e+\"H-\"+n+\"Z\"}},\"triangle-sw\":{n:11,f:function(t){var e=r.round(.6*t,2),n=r.round(1.2*t,2);return\"M\"+n+\",\"+e+\"H-\"+e+\"V-\"+n+\"Z\"}},\"triangle-nw\":{n:12,f:function(t){var e=r.round(.6*t,2),n=r.round(1.2*t,2);return\"M-\"+e+\",\"+n+\"V-\"+e+\"H\"+n+\"Z\"}},pentagon:{n:13,f:function(t){var e=r.round(.951*t,2),n=r.round(.588*t,2),a=r.round(-t,2),o=r.round(t*-.309,2),i=r.round(.809*t,2);return\"M\"+e+\",\"+o+\"L\"+n+\",\"+i+\"H-\"+n+\"L-\"+e+\",\"+o+\"L0,\"+a+\"Z\"}},hexagon:{n:14,f:function(t){var e=r.round(t,2),n=r.round(t/2,2),a=r.round(t*Math.sqrt(3)/2,2);return\"M\"+a+\",-\"+n+\"V\"+n+\"L0,\"+e+\"L-\"+a+\",\"+n+\"V-\"+n+\"L0,-\"+e+\"Z\"}},hexagon2:{n:15,f:function(t){var e=r.round(t,2),n=r.round(t/2,2),a=r.round(t*Math.sqrt(3)/2,2);return\"M-\"+n+\",\"+a+\"H\"+n+\"L\"+e+\",0L\"+n+\",-\"+a+\"H-\"+n+\"L-\"+e+\",0Z\"}},octagon:{n:16,f:function(t){var e=r.round(.924*t,2),n=r.round(.383*t,2);return\"M-\"+n+\",-\"+e+\"H\"+n+\"L\"+e+\",-\"+n+\"V\"+n+\"L\"+n+\",\"+e+\"H-\"+n+\"L-\"+e+\",\"+n+\"V-\"+n+\"Z\"}},star:{n:17,f:function(t){var e=1.4*t,n=r.round(.225*e,2),a=r.round(.951*e,2),o=r.round(.363*e,2),i=r.round(.588*e,2),l=r.round(-e,2),s=r.round(e*-.309,2),c=r.round(.118*e,2),u=r.round(.809*e,2),f=r.round(.382*e,2);return\"M\"+n+\",\"+s+\"H\"+a+\"L\"+o+\",\"+c+\"L\"+i+\",\"+u+\"L0,\"+f+\"L-\"+i+\",\"+u+\"L-\"+o+\",\"+c+\"L-\"+a+\",\"+s+\"H-\"+n+\"L0,\"+l+\"Z\"}},hexagram:{n:18,f:function(t){var e=r.round(.66*t,2),n=r.round(.38*t,2),a=r.round(.76*t,2);return\"M-\"+a+\",0l-\"+n+\",-\"+e+\"h\"+a+\"l\"+n+\",-\"+e+\"l\"+n+\",\"+e+\"h\"+a+\"l-\"+n+\",\"+e+\"l\"+n+\",\"+e+\"h-\"+a+\"l-\"+n+\",\"+e+\"l-\"+n+\",-\"+e+\"h-\"+a+\"Z\"}},\"star-triangle-up\":{n:19,f:function(t){var e=r.round(t*Math.sqrt(3)*.8,2),n=r.round(.8*t,2),a=r.round(1.6*t,2),o=r.round(4*t,2),i=\"A \"+o+\",\"+o+\" 0 0 1 \";return\"M-\"+e+\",\"+n+i+e+\",\"+n+i+\"0,-\"+a+i+\"-\"+e+\",\"+n+\"Z\"}},\"star-triangle-down\":{n:20,f:function(t){var e=r.round(t*Math.sqrt(3)*.8,2),n=r.round(.8*t,2),a=r.round(1.6*t,2),o=r.round(4*t,2),i=\"A \"+o+\",\"+o+\" 0 0 1 \";return\"M\"+e+\",-\"+n+i+\"-\"+e+\",-\"+n+i+\"0,\"+a+i+e+\",-\"+n+\"Z\"}},\"star-square\":{n:21,f:function(t){var e=r.round(1.1*t,2),n=r.round(2*t,2),a=\"A \"+n+\",\"+n+\" 0 0 1 \";return\"M-\"+e+\",-\"+e+a+\"-\"+e+\",\"+e+a+e+\",\"+e+a+e+\",-\"+e+a+\"-\"+e+\",-\"+e+\"Z\"}},\"star-diamond\":{n:22,f:function(t){var e=r.round(1.4*t,2),n=r.round(1.9*t,2),a=\"A \"+n+\",\"+n+\" 0 0 1 \";return\"M-\"+e+\",0\"+a+\"0,\"+e+a+e+\",0\"+a+\"0,-\"+e+a+\"-\"+e+\",0Z\"}},\"diamond-tall\":{n:23,f:function(t){var e=r.round(.7*t,2),n=r.round(1.4*t,2);return\"M0,\"+n+\"L\"+e+\",0L0,-\"+n+\"L-\"+e+\",0Z\"}},\"diamond-wide\":{n:24,f:function(t){var e=r.round(1.4*t,2),n=r.round(.7*t,2);return\"M0,\"+n+\"L\"+e+\",0L0,-\"+n+\"L-\"+e+\",0Z\"}},hourglass:{n:25,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"H-\"+e+\"L\"+e+\",-\"+e+\"H-\"+e+\"Z\"},noDot:!0},bowtie:{n:26,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"V-\"+e+\"L-\"+e+\",\"+e+\"V-\"+e+\"Z\"},noDot:!0},\"circle-cross\":{n:27,f:function(t){var e=r.round(t,2);return\"M0,\"+e+\"V-\"+e+\"M\"+e+\",0H-\"+e+\"M\"+e+\",0A\"+e+\",\"+e+\" 0 1,1 0,-\"+e+\"A\"+e+\",\"+e+\" 0 0,1 \"+e+\",0Z\"},needLine:!0,noDot:!0},\"circle-x\":{n:28,f:function(t){var e=r.round(t,2),n=r.round(t/Math.sqrt(2),2);return\"M\"+n+\",\"+n+\"L-\"+n+\",-\"+n+\"M\"+n+\",-\"+n+\"L-\"+n+\",\"+n+\"M\"+e+\",0A\"+e+\",\"+e+\" 0 1,1 0,-\"+e+\"A\"+e+\",\"+e+\" 0 0,1 \"+e+\",0Z\"},needLine:!0,noDot:!0},\"square-cross\":{n:29,f:function(t){var e=r.round(t,2);return\"M0,\"+e+\"V-\"+e+\"M\"+e+\",0H-\"+e+\"M\"+e+\",\"+e+\"H-\"+e+\"V-\"+e+\"H\"+e+\"Z\"},needLine:!0,noDot:!0},\"square-x\":{n:30,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"L-\"+e+\",-\"+e+\"M\"+e+\",-\"+e+\"L-\"+e+\",\"+e+\"M\"+e+\",\"+e+\"H-\"+e+\"V-\"+e+\"H\"+e+\"Z\"},needLine:!0,noDot:!0},\"diamond-cross\":{n:31,f:function(t){var e=r.round(1.3*t,2);return\"M\"+e+\",0L0,\"+e+\"L-\"+e+\",0L0,-\"+e+\"ZM0,-\"+e+\"V\"+e+\"M-\"+e+\",0H\"+e},needLine:!0,noDot:!0},\"diamond-x\":{n:32,f:function(t){var e=r.round(1.3*t,2),n=r.round(.65*t,2);return\"M\"+e+\",0L0,\"+e+\"L-\"+e+\",0L0,-\"+e+\"ZM-\"+n+\",-\"+n+\"L\"+n+\",\"+n+\"M-\"+n+\",\"+n+\"L\"+n+\",-\"+n},needLine:!0,noDot:!0},\"cross-thin\":{n:33,f:function(t){var e=r.round(1.4*t,2);return\"M0,\"+e+\"V-\"+e+\"M\"+e+\",0H-\"+e},needLine:!0,noDot:!0},\"x-thin\":{n:34,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"L-\"+e+\",-\"+e+\"M\"+e+\",-\"+e+\"L-\"+e+\",\"+e},needLine:!0,noDot:!0},asterisk:{n:35,f:function(t){var e=r.round(1.2*t,2),n=r.round(.85*t,2);return\"M0,\"+e+\"V-\"+e+\"M\"+e+\",0H-\"+e+\"M\"+n+\",\"+n+\"L-\"+n+\",-\"+n+\"M\"+n+\",-\"+n+\"L-\"+n+\",\"+n},needLine:!0,noDot:!0},hash:{n:36,f:function(t){var e=r.round(t/2,2),n=r.round(t,2);return\"M\"+e+\",\"+n+\"V-\"+n+\"m-\"+n+\",0V\"+n+\"M\"+n+\",\"+e+\"H-\"+n+\"m0,-\"+n+\"H\"+n},needLine:!0},\"y-up\":{n:37,f:function(t){var e=r.round(1.2*t,2),n=r.round(1.6*t,2),a=r.round(.8*t,2);return\"M-\"+e+\",\"+a+\"L0,0M\"+e+\",\"+a+\"L0,0M0,-\"+n+\"L0,0\"},needLine:!0,noDot:!0},\"y-down\":{n:38,f:function(t){var e=r.round(1.2*t,2),n=r.round(1.6*t,2),a=r.round(.8*t,2);return\"M-\"+e+\",-\"+a+\"L0,0M\"+e+\",-\"+a+\"L0,0M0,\"+n+\"L0,0\"},needLine:!0,noDot:!0},\"y-left\":{n:39,f:function(t){var e=r.round(1.2*t,2),n=r.round(1.6*t,2),a=r.round(.8*t,2);return\"M\"+a+\",\"+e+\"L0,0M\"+a+\",-\"+e+\"L0,0M-\"+n+\",0L0,0\"},needLine:!0,noDot:!0},\"y-right\":{n:40,f:function(t){var e=r.round(1.2*t,2),n=r.round(1.6*t,2),a=r.round(.8*t,2);return\"M-\"+a+\",\"+e+\"L0,0M-\"+a+\",-\"+e+\"L0,0M\"+n+\",0L0,0\"},needLine:!0,noDot:!0},\"line-ew\":{n:41,f:function(t){var e=r.round(1.4*t,2);return\"M\"+e+\",0H-\"+e},needLine:!0,noDot:!0},\"line-ns\":{n:42,f:function(t){var e=r.round(1.4*t,2);return\"M0,\"+e+\"V-\"+e},needLine:!0,noDot:!0},\"line-ne\":{n:43,f:function(t){var e=r.round(t,2);return\"M\"+e+\",-\"+e+\"L-\"+e+\",\"+e},needLine:!0,noDot:!0},\"line-nw\":{n:44,f:function(t){var e=r.round(t,2);return\"M\"+e+\",\"+e+\"L-\"+e+\",-\"+e},needLine:!0,noDot:!0}}},{d3:9}],43:[function(t,e,n){\"use strict\";e.exports={visible:{valType:\"boolean\"},type:{valType:\"enumerated\",values:[\"percent\",\"constant\",\"sqrt\",\"data\"]},symmetric:{valType:\"boolean\"},array:{valType:\"data_array\"},arrayminus:{valType:\"data_array\"},value:{valType:\"number\",min:0,dflt:10},valueminus:{valType:\"number\",min:0,dflt:10},traceref:{valType:\"integer\",min:0,dflt:0},tracerefminus:{valType:\"integer\",min:0,dflt:0},copy_ystyle:{valType:\"boolean\"},copy_zstyle:{valType:\"boolean\"},color:{valType:\"color\"},thickness:{valType:\"number\",min:0,dflt:2},width:{valType:\"number\",min:0},_deprecated:{opacity:{valType:\"number\"}}}},{}],44:[function(t,e,n){\"use strict\";function r(t,e,n,r){var o=e[\"error_\"+r]||{},s=o.visible&&-1!==[\"linear\",\"log\"].indexOf(n.type),c=[];if(s){for(var u=l(o),f=0;f<t.length;f++){var d=t[f],h=d[r];if(a(n.c2l(h))){var p=u(h,f);if(a(p[0])&&a(p[1])){var g=d[r+\"s\"]=h-p[0],v=d[r+\"h\"]=h+p[1];c.push(g,v)}}}i.expand(n,c,{padded:!0})}}var a=t(\"fast-isnumeric\"),o=t(\"../../plots/plots\"),i=t(\"../../plots/cartesian/axes\"),l=t(\"./compute_error\");e.exports=function(t){for(var e=t.calcdata,n=0;n<e.length;n++){var a=e[n],l=a[0].trace;if(o.traceIs(l,\"errorBarsOK\")){var s=i.getFromId(t,l.xaxis),c=i.getFromId(t,l.yaxis);r(a,l,s,\"x\"),r(a,l,c,\"y\")}}}},{\"../../plots/cartesian/axes\":110,\"../../plots/plots\":130,\"./compute_error\":45,\"fast-isnumeric\":11}],45:[function(t,e,n){\"use strict\";function r(t,e){return\"percent\"===t?function(t){return Math.abs(t*e/10"
+,
+"0)}:\"constant\"===t?function(){return Math.abs(e)}:\"sqrt\"===t?function(t){return Math.sqrt(Math.abs(t))}:void 0}e.exports=function(t){var e=t.type,n=t.symmetric;if(\"data\"===e){var a=t.array,o=t.arrayminus;return n||void 0===o?function(t,e){var n=+a[e];return[n,n]}:function(t,e){return[+o[e],+a[e]]}}var i=r(e,t.value),l=r(e,t.valueminus);return n||void 0===t.valueminus?function(t){var e=i(t);return[e,e]}:function(t){return[l(t),i(t)]}}},{}],46:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../plots/plots\"),o=t(\"../../lib\"),i=t(\"./attributes\");e.exports=function(t,e,n,l){function s(t,e){return o.coerce(f,u,i,t,e)}var c=\"error_\"+l.axis,u=e[c]={},f=t[c]||{},d=void 0!==f.array||void 0!==f.value||\"sqrt\"===f.type,h=s(\"visible\",d);if(h!==!1){var p=s(\"type\",\"array\"in f?\"data\":\"percent\"),g=!0;if(\"sqrt\"!==p&&(g=s(\"symmetric\",!((\"data\"===p?\"arrayminus\":\"valueminus\")in f))),\"data\"===p){var v=s(\"array\");if(v||(u.array=[]),s(\"traceref\"),!g){var m=s(\"arrayminus\");m||(u.arrayminus=[]),s(\"tracerefminus\")}}else\"percent\"!==p&&\"constant\"!==p||(s(\"value\"),g||s(\"valueminus\"));var y=\"copy_\"+l.inherit+\"style\";if(l.inherit){var x=e[\"error_\"+l.inherit];(x||{}).visible&&s(y,!(f.color||r(f.thickness)||r(f.width)))}l.inherit&&u[y]||(s(\"color\",n),s(\"thickness\"),s(\"width\",a.traceIs(e,\"gl3d\")?0:4))}}},{\"../../lib\":89,\"../../plots/plots\":130,\"./attributes\":43,\"fast-isnumeric\":11}],47:[function(t,e,n){\"use strict\";var r=e.exports={};r.attributes=t(\"./attributes\"),r.supplyDefaults=t(\"./defaults\"),r.calc=t(\"./calc\"),r.calcFromTrace=function(t,e){for(var n=t.x||[],a=t.y,o=n.length||a.length,i=new Array(o),l=0;o>l;l++)i[l]={x:n[l],y:a[l]};return i[0].trace=t,r.calc({calcdata:[i],_fullLayout:e}),i},r.plot=t(\"./plot\"),r.style=t(\"./style\"),r.hoverInfo=function(t,e,n){(e.error_y||{}).visible&&(n.yerr=t.yh-t.y,e.error_y.symmetric||(n.yerrneg=t.y-t.ys)),(e.error_x||{}).visible&&(n.xerr=t.xh-t.x,e.error_x.symmetric||(n.xerrneg=t.x-t.xs))}},{\"./attributes\":43,\"./calc\":44,\"./defaults\":46,\"./plot\":48,\"./style\":49}],48:[function(t,e,n){\"use strict\";function r(t,e,n){var r={x:e.c2p(t.x),y:n.c2p(t.y)};return void 0!==t.yh&&(r.yh=n.c2p(t.yh),r.ys=n.c2p(t.ys),o(r.ys)||(r.noYS=!0,r.ys=n.c2p(t.ys,!0))),void 0!==t.xh&&(r.xh=e.c2p(t.xh),r.xs=e.c2p(t.xs),o(r.xs)||(r.noXS=!0,r.xs=e.c2p(t.xs,!0))),r}var a=t(\"d3\"),o=t(\"fast-isnumeric\"),i=t(\"../../lib\"),l=t(\"../../traces/scatter/subtypes\");e.exports=function(t,e){var n=e.x(),s=e.y();t.each(function(t){var e=t[0].trace,c=e.error_x||{},u=e.error_y||{},f=l.hasMarkers(e)&&e.marker.maxdisplayed>0;if(u.visible||c.visible){var d=a.select(this).selectAll(\"g.errorbar\").data(i.identity);d.enter().append(\"g\").classed(\"errorbar\",!0),d.each(function(t){var e=a.select(this),i=r(t,n,s);if(!f||t.vis){var l;if(u.visible&&o(i.x)&&o(i.yh)&&o(i.ys)){var d=u.width;l=\"M\"+(i.x-d)+\",\"+i.yh+\"h\"+2*d+\"m-\"+d+\",0V\"+i.ys,i.noYS||(l+=\"m-\"+d+\",0h\"+2*d),e.append(\"path\").classed(\"yerror\",!0).attr(\"d\",l)}if(c.visible&&o(i.y)&&o(i.xh)&&o(i.xs)){var h=(c.copy_ystyle?u:c).width;l=\"M\"+i.xh+\",\"+(i.y-h)+\"v\"+2*h+\"m0,-\"+h+\"H\"+i.xs,i.noXS||(l+=\"m0,-\"+h+\"v\"+2*h),e.append(\"path\").classed(\"xerror\",!0).attr(\"d\",l)}}})}})}},{\"../../lib\":89,\"../../traces/scatter/subtypes\":186,d3:9,\"fast-isnumeric\":11}],49:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"../color\");e.exports=function(t){t.each(function(t){var e=t[0].trace,n=e.error_y||{},o=e.error_x||{},i=r.select(this);i.selectAll(\"path.yerror\").style(\"stroke-width\",n.thickness+\"px\").call(a.stroke,n.color),o.copy_ystyle&&(o=n),i.selectAll(\"path.xerror\").style(\"stroke-width\",o.thickness+\"px\").call(a.stroke,o.color)})}},{\"../color\":18,d3:9}],50:[function(t,e,n){\"use strict\";var r=t(\"../../plots/cartesian/constants\");e.exports={_isLinkedToArray:!0,source:{valType:\"string\"},layer:{valType:\"enumerated\",values:[\"below\",\"above\"],dflt:\"above\"},sizex:{valType:\"number\",dflt:0},sizey:{valType:\"number\",dflt:0},sizing:{valType:\"enumerated\",values:[\"fill\",\"contain\",\"stretch\"],dflt:\"contain\"},opacity:{valType:\"number\",min:0,max:1,dflt:1},x:{valType:\"number\",dflt:0},y:{valType:\"number\",dflt:0},xanchor:{valType:\"enumerated\",values:[\"left\",\"center\",\"right\"],dflt:\"left\"},yanchor:{valType:\"enumerated\",values:[\"top\",\"middle\",\"bottom\"],dflt:\"top\"},xref:{valType:\"enumerated\",values:[\"paper\",r.idRegex.x.toString()],dflt:\"paper\"},yref:{valType:\"enumerated\",values:[\"paper\",r.idRegex.y.toString()],dflt:\"paper\"}}},{\"../../plots/cartesian/constants\":115}],51:[function(t,e,n){\"use strict\";function r(t,e,n){function r(n,r){return o.coerce(t,e,i,n,r)}e=e||{},r(\"source\"),r(\"layer\"),r(\"x\"),r(\"y\"),r(\"xanchor\"),r(\"yanchor\"),r(\"sizex\"),r(\"sizey\"),r(\"sizing\"),r(\"opacity\");for(var l=0;2>l;l++){var s={_fullLayout:n},c=[\"x\",\"y\"][l];a.coerceRef(t,e,s,c,\"paper\")}return e}var a=t(\"../../plots/cartesian/axes\"),o=t(\"../../lib\"),i=t(\"./attributes\");e.exports=function(t,e){if(t.images&&Array.isArray(t.images))for(var n=t.images,a=e.images=[],o=0;o<n.length;o++){var i=n[o];if(i.source){var l=r(n[o]||{},a[o]||{},e);a.push(l)}}}},{\"../../lib\":89,\"../../plots/cartesian/axes\":110,\"./attributes\":50}],52:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"../drawing\"),o=t(\"../../plots/cartesian/axes\");e.exports=function(t){function e(e){var n=r.select(this),a=new Promise(function(t){function r(){n.remove(),t()}var a=new Image;a.setAttribute(\"crossOrigin\",\"anonymous\"),a.onerror=r,a.onload=function(){var t=document.createElement(\"canvas\");t.width=this.width,t.height=this.height;var e=t.getContext(\"2d\");e.drawImage(this,0,0);var r=t.toDataURL(\"image/png\");n.attr(\"xlink:href\",r)},n.on(\"error\",r),n.on(\"load\",t),a.src=e.source});t._promises.push(a)}function n(e){var n=r.select(this),i=o.getFromId(t,e.xref),s=o.getFromId(t,e.yref),c=l._size,u=i?Math.abs(i.l2p(e.sizex)-i.l2p(0)):e.sizex*c.w,f=s?Math.abs(s.l2p(e.sizey)-s.l2p(0)):e.sizey*c.h,d=u*h.x[e.xanchor].offset+c.l,p=f*h.y[e.yanchor].offset+c.t,g=h.x[e.xanchor].sizing+h.y[e.yanchor].sizing,v=(i?i.l2p(e.x):e.x*c.w)+d,m=(s?s.l2p(e.y):c.h-e.y*c.h)+p;switch(e.sizing){case\"fill\":g+=\" slice\";break;case\"stretch\":g=\"none\"}n.attr({x:v,y:m,width:u,height:f,preserveAspectRatio:g,opacity:e.opacity});var y=i?i._id:\"\",x=s?s._id:\"\",b=y+x;n.call(a.setClipUrl,\"clip\"+l._uid+b)}function i(t,e){return t.source+e}var l=t._fullLayout,s=[],c=[],u=[];if(l.images){for(var f=0;f<l.images.length;f++){var d=l.images[f];\"below\"===d.layer&&\"paper\"!==d.xref&&\"paper\"!==d.yref?c.push(d):\"above\"===d.layer?s.push(d):u.push(d)}var h={x:{left:{sizing:\"xMin\",offset:0},center:{sizing:\"xMid\",offset:-0.5},right:{sizing:\"xMax\",offset:-1}},y:{top:{sizing:\"YMin\",offset:0},middle:{sizing:\"YMid\",offset:-0.5},bottom:{sizing:\"YMax\",offset:-1}}},p=l._imageLowerLayer.selectAll(\"image\").data(u,i),g=l._imageSubplotLayer.selectAll(\"image\").data(c,i),v=l._imageUpperLayer.selectAll(\"image\").data(s,i);p.enter().append(\"image\").each(e),g.enter().append(\"image\").each(e),v.enter().append(\"image\").each(e),p.exit().remove(),g.exit().remove(),v.exit().remove(),p.each(n),g.each(n),v.each(n)}}},{\"../../plots/cartesian/axes\":110,\"../drawing\":41,d3:9}],53:[function(t,e,n){\"use strict\";var r=t(\"./draw\"),a=t(\"./defaults\"),o=t(\"./attributes\");e.exports={draw:r,layoutAttributes:o,supplyLayoutDefaults:a}},{\"./attributes\":50,\"./defaults\":51,\"./draw\":52}],54:[function(t,e,n){\"use strict\";n.isRightAnchor=function(t){return\"right\"===t.xanchor||\"auto\"===t.xanchor&&t.x>=2/3},n.isCenterAnchor=function(t){return\"center\"===t.xanchor||\"auto\"===t.xanchor&&t.x>1/3&&t.x<2/3},n.isBottomAnchor=function(t){return\"bottom\"===t.yanchor||\"auto\"===t.yanchor&&t.y<=1/3},n.isMiddleAnchor=function(t){return\"middle\"===t.yanchor||\"auto\"===t.yanchor&&t.y>1/3&&t.y<2/3}},{}],55:[function(t,e,n){\"use strict\";var r=t(\"../../plots/font_attributes\"),a=t(\"../color/attributes\"),o=t(\"../../lib/extend\").extendFlat;e.exports={bgcolor:{valType:\"color\"},bordercolor:{valType:\"color\",dflt:a.defaultLine},borderwidth:{valType:\"number\",min:0,dflt:0},font:o({},r,{}),orientation:{valType:\"enumerated\",values:[\"v\",\"h\"],dflt:\"v\"},traceorder:{valType:\"flaglist\",flags:[\"reversed\",\"grouped\"],extras:[\"normal\"]},tracegroupgap:{valType:\"number\",min:0,dflt:10},x:{valType:\"number\",min:-2,max:3,dflt:1.02},xanchor:{valType:\"enumerated\",values:[\"auto\",\"left\",\"center\",\"right\"],dflt:\"left\"},y:{valType:\"number\",min:-2,max:3,dflt:1},yanchor:{valType:\"enumerated\",values:[\"auto\",\"top\",\"middle\",\"bottom\"],dflt:\"auto\"}}},{\"../../lib/extend\":88,\"../../plots/font_attributes\":128,\"../color/attributes\":17}],56:[function(t,e,n){\"use strict\";e.exports={scrollBarWidth:4,scrollBarHeight:20,scrollBarColor:\"#808BA4\",scrollBarMargin:4}},{}],57:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"../../plots/plots\"),o=t(\"./attributes\"),i=t(\"./helpers\");e.exports=function(t,e,n){function l(t,e){return r.coerce(d,h,o,t,e)}for(var s,c,u,f,d=t.legend||{},h=e.legend={},p=0,g=\"normal\",v=0;v<n.length;v++){var m=n[v];i.legendGetsTrace(m)&&(p++,a.traceIs(m,\"pie\")&&p++),(a.traceIs(m,\"bar\")&&\"stack\"===e.barmode||-1!==[\"tonextx\",\"tonexty\"].indexOf(m.fill))&&(g=i.isGrouped({traceorder:g})?\"grouped+reversed\":\"reversed\"),void 0!==m.legendgroup&&\"\"!==m.legendgroup&&(g=i.isReversed({traceorder:g})?\"reversed+grouped\":\"grouped\")}var y=r.coerce(t,e,a.layoutAttributes,\"showlegend\",p>1);if(y!==!1){if(l(\"bgcolor\",e.paper_bgcolor),l(\"bordercolor\"),l(\"borderwidth\"),r.coerceFont(l,\"font\",e.font),l(\"orientation\"),\"h\"===h.orientation){var x=t.xaxis;x&&x.rangeslider&&x.rangeslider.visible?(s=0,u=\"left\",c=1.1,f=\"bottom\"):(s=0,u=\"left\",c=-.1,f=\"top\")}l(\"traceorder\",g),i.isGrouped(e.legend)&&l(\"tracegroupgap\"),l(\"x\",s),l(\"xanchor\",u),l(\"y\",c),l(\"yanchor\",f),r.noneOrAll(d,h,[\"x\",\"y\"])}}},{\"../../lib\":89,\"../../plots/plots\":130,\"./attributes\":55,\"./helpers\":60}],58:[function(t,e,n){\"use strict\";function r(t,e){function n(n){u.util.convertToTspans(n,function(){n.selectAll(\"tspan.line\").attr({x:n.attr(\"x\")}),t.call(o,e)})}var r=t.data()[0][0],a=e._fullLayout,i=r.trace,l=d.traceIs(i,\"pie\"),s=i.index,c=l?r.label:i.name,f=t.selectAll(\"text.legendtext\").data([0]);f.enter().append(\"text\").classed(\"legen"
+,
+"dtext\",!0),f.attr({x:40,y:0,\"data-unformatted\":c}).style(\"text-anchor\",\"start\").classed(\"user-select-none\",!0).call(p.font,a.legend.font).text(c),e._context.editable&&!l?f.call(u.util.makeEditable).call(n).on(\"edit\",function(t){this.attr({\"data-unformatted\":t}),this.text(t).call(n),this.text()||(t=\"    \"),u.restyle(e,\"name\",t,s)}):f.call(n)}function a(t,e){var n=e._fullLayout.hiddenlabels?e._fullLayout.hiddenlabels.slice():[],r=t.selectAll(\"rect\").data([0]);r.enter().append(\"rect\").classed(\"legendtoggle\",!0).style(\"cursor\",\"pointer\").attr(\"pointer-events\",\"all\").call(g.fill,\"rgba(0,0,0,0)\"),r.on(\"click\",function(){if(!e._dragged){var r,a,o=t.data()[0][0],i=e._fullData,l=o.trace,s=l.legendgroup,c=[];if(d.traceIs(l,\"pie\")){var f=o.label,h=n.indexOf(f);-1===h?n.push(f):n.splice(h,1),u.relayout(e,\"hiddenlabels\",n)}else{if(\"\"===s)c=[l.index];else for(var p=0;p<i.length;p++)r=i[p],r.legendgroup===s&&c.push(r.index);a=l.visible===!0?\"legendonly\":!0,u.restyle(e,\"visible\",a,c)}}})}function o(t,e){var n,r,a=t.data()[0][0],o=t.selectAll(\".legendtoggle\"),i=t.select(\"g[class*=math-group]\"),l=e._fullLayout.legend,s=1.3*l.font.size;if(!a.trace.showlegend)return void t.remove();if(i.node()){var c=p.bBox(i.node());n=c.height,r=c.width,f.setTranslate(i,0,n/4)}else{var u=t.selectAll(\".legendtext\"),d=t.selectAll(\".legendtext>tspan\"),h=d[0].length||1;n=s*h,r=u.node()&&p.bBox(u.node()).width;var g=s*(.3+(1-h)/2);u.attr(\"y\",g),d.attr(\"y\",g)}n=Math.max(n,16)+3,o.attr({x:0,y:-n/2,height:n}),a.height=n,a.width=r}function i(t,e,n){var r=t._fullLayout,a=r.legend,o=a.borderwidth,i=x.isGrouped(a);if(x.isVertical(a))i&&e.each(function(t,e){f.setTranslate(this,0,e*a.tracegroupgap)}),a.width=0,a.height=0,n.each(function(t){var e=t[0],n=e.height,r=e.width;f.setTranslate(this,o,5+o+a.height+n/2),a.height+=n,a.width=Math.max(a.width,r)}),a.width+=45+2*o,a.height+=10+2*o,i&&(a.height+=(a._lgroupsLength-1)*a.tracegroupgap),n.selectAll(\".legendtoggle\").attr(\"width\",(t._context.editable?0:a.width)+40),a.width=Math.ceil(a.width),\n"
+,
+"a.height=Math.ceil(a.height);else if(i){a.width=0,a.height=0;for(var l=[a.width],s=e.data(),u=0,d=s.length;d>u;u++){var h=s[u].map(function(t){return t[0].width}),p=40+Math.max.apply(null,h);a.width+=a.tracegroupgap+p,l.push(a.width)}e.each(function(t,e){f.setTranslate(this,l[e],0)}),e.each(function(){var t=c.select(this),e=t.selectAll(\"g.traces\"),n=0;e.each(function(t){var e=t[0],r=e.height;f.setTranslate(this,0,5+o+n+r/2),n+=r}),a.height=Math.max(a.height,n)}),a.height+=10+2*o,a.width+=2*o,a.width=Math.ceil(a.width),a.height=Math.ceil(a.height),n.selectAll(\".legendtoggle\").attr(\"width\",t._context.editable?0:a.width)}else a.width=0,a.height=0,n.each(function(t){var e=t[0],n=40+e.width,r=a.tracegroupgap||5;f.setTranslate(this,o+a.width,5+o+e.height/2),a.width+=r+n,a.height=Math.max(a.height,e.height)}),a.width+=2*o,a.height+=10+2*o,a.width=Math.ceil(a.width),a.height=Math.ceil(a.height),n.selectAll(\".legendtoggle\").attr(\"width\",t._context.editable?0:a.width)}function l(t){var e=t._fullLayout,n=e.legend,r=\"left\";b.isRightAnchor(n)?r=\"right\":b.isCenterAnchor(n)&&(r=\"center\");var a=\"top\";b.isBottomAnchor(n)?a=\"bottom\":b.isMiddleAnchor(n)&&(a=\"middle\"),d.autoMargin(t,\"legend\",{x:n.x,y:n.y,l:n.width*({right:1,center:.5}[r]||0),r:n.width*({left:1,center:.5}[r]||0),b:n.height*({top:1,middle:.5}[a]||0),t:n.height*({bottom:1,middle:.5}[a]||0)})}function s(t){var e=t._fullLayout,n=e.legend,r=\"left\";b.isRightAnchor(n)?r=\"right\":b.isCenterAnchor(n)&&(r=\"center\"),d.autoMargin(t,\"legend\",{x:n.x,y:.5,l:n.width*({right:1,center:.5}[r]||0),r:n.width*({left:1,center:.5}[r]||0),b:0,t:0})}var c=t(\"d3\"),u=t(\"../../plotly\"),f=t(\"../../lib\"),d=t(\"../../plots/plots\"),h=t(\"../dragelement\"),p=t(\"../drawing\"),g=t(\"../color\"),v=t(\"./constants\"),m=t(\"./get_legend_data\"),y=t(\"./style\"),x=t(\"./helpers\"),b=t(\"./anchor_utils\");e.exports=function(t){function e(t,e){L.attr(\"data-scroll\",e).call(f.setTranslate,0,e),T.call(p.setRect,j,t,v.scrollBarWidth,v.scrollBarHeight),M.select(\"rect\").attr({y:x.borderwidth-e})}var n=t._fullLayout,o=\"legend\"+n._uid;if(n._infolayer&&t.calcdata){var x=n.legend,_=n.showlegend&&m(t.calcdata,x),w=n.hiddenlabels||[];if(!n.showlegend||!_.length)return n._infolayer.selectAll(\".legend\").remove(),n._topdefs.select(\"#\"+o).remove(),void d.autoMargin(t,\"legend\");var k=n._infolayer.selectAll(\"g.legend\").data([0]);k.enter().append(\"g\").attr({\"class\":\"legend\",\"pointer-events\":\"all\"});var M=n._topdefs.selectAll(\"#\"+o).data([0]);M.enter().append(\"clipPath\").attr(\"id\",o).append(\"rect\");var A=k.selectAll(\"rect.bg\").data([0]);A.enter().append(\"rect\").attr({\"class\":\"bg\",\"shape-rendering\":\"crispEdges\"}),A.call(g.stroke,x.bordercolor),A.call(g.fill,x.bgcolor),A.style(\"stroke-width\",x.borderwidth+\"px\");var L=k.selectAll(\"g.scrollbox\").data([0]);L.enter().append(\"g\").attr(\"class\",\"scrollbox\");var T=k.selectAll(\"rect.scrollbar\").data([0]);T.enter().append(\"rect\").attr({\"class\":\"scrollbar\",rx:20,ry:2,width:0,height:0}).call(g.fill,\"#808BA4\");var z=L.selectAll(\"g.groups\").data(_);z.enter().append(\"g\").attr(\"class\",\"groups\"),z.exit().remove();var S=z.selectAll(\"g.traces\").data(f.identity);S.enter().append(\"g\").attr(\"class\",\"traces\"),S.exit().remove(),S.call(y).style(\"opacity\",function(t){var e=t[0].trace;return d.traceIs(e,\"pie\")?-1!==w.indexOf(t[0].label)?.5:1:\"legendonly\"===e.visible?.5:1}).each(function(){c.select(this).call(r,t).call(a,t)});var E=0!==k.enter().size();E&&(i(t,z,S),l(t));var C=0,O=n.width,P=0,N=n.height;i(t,z,S),x.height>N?s(t):l(t);var D=n._size,I=D.l+D.w*x.x,R=D.t+D.h*(1-x.y);b.isRightAnchor(x)?I-=x.width:b.isCenterAnchor(x)&&(I-=x.width/2),b.isBottomAnchor(x)?R-=x.height:b.isMiddleAnchor(x)&&(R-=x.height/2);var j=x.width,q=D.w;j>q?(I=D.l,j=q):(I+j>O&&(I=O-j),C>I&&(I=C),j=Math.min(O-I,x.width));var F=x.height,B=D.h;F>B?(R=D.t,F=B):(R+F>N&&(R=N-F),P>R&&(R=P),F=Math.min(N-R,x.height)),f.setTranslate(k,I,R);var H,V,Z=F-v.scrollBarHeight-2*v.scrollBarMargin,Y=x.height-F;if(x.height<=F||t._context.staticPlot)A.attr({width:j-x.borderwidth,height:F-x.borderwidth,x:x.borderwidth/2,y:x.borderwidth/2}),f.setTranslate(L,0,0),M.select(\"rect\").attr({width:j-2*x.borderwidth,height:F-2*x.borderwidth,x:x.borderwidth,y:x.borderwidth}),L.call(p.setClipUrl,o);else{H=v.scrollBarMargin,V=L.attr(\"data-scroll\")||0,A.attr({width:j-2*x.borderwidth+v.scrollBarWidth+v.scrollBarMargin,height:F-x.borderwidth,x:x.borderwidth/2,y:x.borderwidth/2}),M.select(\"rect\").attr({width:j-2*x.borderwidth+v.scrollBarWidth+v.scrollBarMargin,height:F-2*x.borderwidth,x:x.borderwidth,y:x.borderwidth-V}),L.call(p.setClipUrl,o),E&&e(H,V),k.on(\"wheel\",null),k.on(\"wheel\",function(){V=f.constrain(L.attr(\"data-scroll\")-c.event.deltaY/Z*Y,-Y,0),H=v.scrollBarMargin-V/Y*Z,e(H,V),c.event.preventDefault()}),T.on(\".drag\",null),L.on(\".drag\",null);var U=c.behavior.drag().on(\"drag\",function(){H=f.constrain(c.event.y-v.scrollBarHeight/2,v.scrollBarMargin,v.scrollBarMargin+Z),V=-(H-v.scrollBarMargin)/Z*Y,e(H,V)});T.call(U),L.call(U)}if(t._context.editable){var X,G,$,Q;k.classed(\"cursor-move\",!0),h.init({element:k.node(),prepFn:function(){var t=f.getTranslate(k);$=t.x,Q=t.y},moveFn:function(t,e){var n=$+t,r=Q+e;f.setTranslate(k,n,r),X=h.align(n,0,D.l,D.l+D.w,x.xanchor),G=h.align(r,0,D.t+D.h,D.t,x.yanchor)},doneFn:function(e){e&&void 0!==X&&void 0!==G&&u.relayout(t,{\"legend.x\":X,\"legend.y\":G})}})}}}},{\"../../lib\":89,\"../../plotly\":107,\"../../plots/plots\":130,\"../color\":18,\"../dragelement\":39,\"../drawing\":41,\"./anchor_utils\":54,\"./constants\":56,\"./get_legend_data\":59,\"./helpers\":60,\"./style\":62,d3:9}],59:[function(t,e,n){\"use strict\";var r=t(\"../../plots/plots\"),a=t(\"./helpers\");e.exports=function(t,e){function n(t,n){if(\"\"!==t&&a.isGrouped(e))-1===s.indexOf(t)?(s.push(t),c=!0,l[t]=[[n]]):l[t].push([n]);else{var r=\"~~i\"+f;s.push(r),l[r]=[[n]],f++}}var o,i,l={},s=[],c=!1,u={},f=0;for(o=0;o<t.length;o++){var d=t[o],h=d[0],p=h.trace,g=p.legendgroup;if(a.legendGetsTrace(p)&&p.showlegend)if(r.traceIs(p,\"pie\"))for(u[g]||(u[g]={}),i=0;i<d.length;i++){var v=d[i].label;u[g][v]||(n(g,{label:v,color:d[i].color,i:d[i].i,trace:p}),u[g][v]=!0)}else n(g,h)}if(!s.length)return[];var m,y,x=s.length;if(c&&a.isGrouped(e))for(y=new Array(x),o=0;x>o;o++)m=l[s[o]],y[o]=a.isReversed(e)?m.reverse():m;else{for(y=[new Array(x)],o=0;x>o;o++)m=l[s[o]][0],y[0][a.isReversed(e)?x-o-1:o]=m;x=1}return e._lgroupsLength=x,y}},{\"../../plots/plots\":130,\"./helpers\":60}],60:[function(t,e,n){\"use strict\";var r=t(\"../../plots/plots\");n.legendGetsTrace=function(t){return t.visible&&r.traceIs(t,\"showLegend\")},n.isGrouped=function(t){return-1!==(t.traceorder||\"\").indexOf(\"grouped\")},n.isVertical=function(t){return\"h\"!==t.orientation},n.isReversed=function(t){return-1!==(t.traceorder||\"\").indexOf(\"reversed\")}},{\"../../plots/plots\":130}],61:[function(t,e,n){\"use strict\";var r=e.exports={};r.layoutAttributes=t(\"./attributes\"),r.supplyLayoutDefaults=t(\"./defaults\"),r.draw=t(\"./draw\"),r.style=t(\"./style\")},{\"./attributes\":55,\"./defaults\":57,\"./draw\":58,\"./style\":62}],62:[function(t,e,n){\"use strict\";function r(t){var e=t[0].trace,n=e.visible&&e.fill&&\"none\"!==e.fill,r=h.hasLines(e),a=s.select(this).select(\".legendfill\").selectAll(\"path\").data(n?[t]:[]);a.enter().append(\"path\").classed(\"js-fill\",!0),a.exit().remove(),a.attr(\"d\",\"M5,0h30v6h-30z\").call(f.fillGroupStyle);var o=s.select(this).select(\".legendlines\").selectAll(\"path\").data(r?[t]:[]);o.enter().append(\"path\").classed(\"js-line\",!0).attr(\"d\",\"M5,0h30\"),o.exit().remove(),o.call(f.lineGroupStyle)}function a(t){function e(t,e,n){var r=c.nestedProperty(i,t).get(),a=Array.isArray(r)&&e?e(r):r;if(n){if(a<n[0])return n[0];if(a>n[1])return n[1]}return a}function n(t){return t[0]}var r,a,o=t[0],i=o.trace,l=h.hasMarkers(i),u=h.hasText(i),d=h.hasLines(i);if(l||u||d){var p={},g={};l&&(p.mc=e(\"marker.color\",n),p.mo=e(\"marker.opacity\",c.mean,[.2,1]),p.ms=e(\"marker.size\",c.mean,[2,16]),p.mlc=e(\"marker.line.color\",n),p.mlw=e(\"marker.line.width\",c.mean,[0,5]),g.marker={sizeref:1,sizemin:1,sizemode:\"diameter\"}),d&&(g.line={width:e(\"line.width\",n,[0,10])}),u&&(p.tx=\"Aa\",p.tp=e(\"textposition\",n),p.ts=10,p.tc=e(\"textfont.color\",n),p.tf=e(\"textfont.family\",n)),r=[c.minExtend(o,p)],a=c.minExtend(i,g)}var v=s.select(this).select(\"g.legendpoints\"),m=v.selectAll(\"path.scatterpts\").data(l?r:[]);m.enter().append(\"path\").classed(\"scatterpts\",!0).attr(\"transform\",\"translate(20,0)\"),m.exit().remove(),m.call(f.pointStyle,a),l&&(r[0].mrc=3);var y=v.selectAll(\"g.pointtext\").data(u?r:[]);y.enter().append(\"g\").classed(\"pointtext\",!0).append(\"text\").attr(\"transform\",\"translate(20,0)\"),y.exit().remove(),y.selectAll(\"text\").call(f.textPointStyle,a)}function o(t){var e=t[0].trace,n=e.marker||{},r=n.line||{},a=s.select(this).select(\"g.legendpoints\").selectAll(\"path.legendbar\").data(u.traceIs(e,\"bar\")?[t]:[]);a.enter().append(\"path\").classed(\"legendbar\",!0).attr(\"d\",\"M6,6H-6V-6H6Z\").attr(\"transform\",\"translate(20,0)\"),a.exit().remove(),a.each(function(t){var e=(t.mlw+1||r.width+1)-1,a=s.select(this);a.style(\"stroke-width\",e+\"px\").call(d.fill,t.mc||n.color),e&&a.call(d.stroke,t.mlc||r.color)})}function i(t){var e=t[0].trace,n=s.select(this).select(\"g.legendpoints\").selectAll(\"path.legendbox\").data(u.traceIs(e,\"box\")&&e.visible?[t]:[]);n.enter().append(\"path\").classed(\"legendbox\",!0).attr(\"d\",\"M6,6H-6V-6H6Z\").attr(\"transform\",\"translate(20,0)\"),n.exit().remove(),n.each(function(t){var n=(t.lw+1||e.line.width+1)-1,r=s.select(this);r.style(\"stroke-width\",n+\"px\").call(d.fill,t.fc||e.fillcolor),n&&r.call(d.stroke,t.lc||e.line.color)})}function l(t){var e=t[0].trace,n=s.select(this).select(\"g.legendpoints\").selectAll(\"path.legendpie\").data(u.traceIs(e,\"pie\")&&e.visible?[t]:[]);n.enter().append(\"path\").classed(\"legendpie\",!0).attr(\"d\",\"M6,6H-6V-6H6Z\").attr(\"transform\",\"translate(20,0)\"),n.exit().remove(),n.size()&&n.call(p,t[0],e)}var s=t(\"d3\"),c=t(\"../../lib\"),u=t(\"../../plots/plots\"),f=t(\"../drawing\"),d=t(\"../color\"),h=t(\"../../traces/scatter/subtypes\"),p=t(\"../../traces/pie/style_one\");e.exports=function(t){t.each(functio"
+,
+"n(t){var e=s.select(this),n=e.selectAll(\"g.legendfill\").data([t]);n.enter().append(\"g\").classed(\"legendfill\",!0);var r=e.selectAll(\"g.legendlines\").data([t]);r.enter().append(\"g\").classed(\"legendlines\",!0);var a=e.selectAll(\"g.legendsymbols\").data([t]);a.enter().append(\"g\").classed(\"legendsymbols\",!0),a.style(\"opacity\",t[0].trace.opacity),a.selectAll(\"g.legendpoints\").data([t]).enter().append(\"g\").classed(\"legendpoints\",!0)}).each(o).each(i).each(l).each(r).each(a)}},{\"../../lib\":89,\"../../plots/plots\":130,\"../../traces/pie/style_one\":165,\"../../traces/scatter/subtypes\":186,\"../color\":18,\"../drawing\":41,d3:9}],63:[function(t,e,n){\"use strict\";function r(t,e){var n=e.currentTarget,r=n.getAttribute(\"data-attr\"),a=n.getAttribute(\"data-val\")||!0,o=t._fullLayout,i={};if(\"zoom\"===r){for(var l,s,u=\"in\"===a?.5:2,f=(1+u)/2,d=(1-u)/2,h=c.Axes.list(t,null,!0),p=0;p<h.length;p++)if(l=h[p],!l.fixedrange)if(s=l._name,\"auto\"===a)i[s+\".autorange\"]=!0;else if(\"reset\"===a)if(void 0===l._rangeInitial)i[s+\".autorange\"]=!0;else{var g=l._rangeInitial.slice();i[s+\".range[0]\"]=g[0],i[s+\".range[1]\"]=g[1]}else{var v=l.range;i[s+\".range[0]\"]=f*v[0]+d*v[1],i[s+\".range[1]\"]=f*v[1]+d*v[0]}}else\"hovermode\"!==r||\"x\"!==a&&\"y\"!==a||(a=o._isHoriz?\"y\":\"x\",n.setAttribute(\"data-val\",a)),i[r]=a;c.relayout(t,i)}function a(t,e){for(var n=e.currentTarget,r=n.getAttribute(\"data-attr\"),a=n.getAttribute(\"data-val\")||!0,o=t._fullLayout,i=c.Plots.getSubplotIds(o,\"gl3d\"),l={},s=r.split(\".\"),u=0;u<i.length;u++)l[i[u]+\".\"+s[1]]=a;c.relayout(t,l)}function o(t,e){for(var n=e.currentTarget,r=n.getAttribute(\"data-attr\"),a=t._fullLayout,o=c.Plots.getSubplotIds(a,\"gl3d\"),i=0;i<o.length;i++){var l=o[i],s=a[l],u=s._scene;\"resetDefault\"===r?u.setCameraToDefault():\"resetLastSave\"===r&&u.setCamera(s.camera)}}function i(t,e){var n=e.currentTarget,r=n._previousVal||!1,a=t.layout,o=t._fullLayout,i=c.Plots.getSubplotIds(o,\"gl3d\"),l=[\"xaxis\",\"yaxis\",\"zaxis\"],s=[\"showspikes\",\"spikesides\",\"spikethickness\",\"spikecolor\"],f={},d={},h={};if(r)h=u.extendDeep(a,r),n._previousVal=null;else{h={\"allaxes.showspikes\":!1};for(var p=0;p<i.length;p++){var g=i[p],v=o[g],m=f[g]={};m.hovermode=v.hovermode,h[g+\".hovermode\"]=!1;for(var y=0;3>y;y++){var x=l[y];d=m[x]={};for(var b=0;b<s.length;b++){var _=s[b];d[_]=v[x][_]}}}n._previousVal=u.extendDeep({},f)}c.relayout(t,h)}function l(t,e){for(var n=e.currentTarget,r=n.getAttribute(\"data-attr\"),a=n.getAttribute(\"data-val\")||!0,o=t._fullLayout,i=c.Plots.getSubplotIds(o,\"geo\"),l=0;l<i.length;l++){var s=o[i[l]]._geo;if(\"zoom\"===r){var u=s.projection.scale(),f=\"in\"===a?2*u:.5*u;s.projection.scale(f),s.zoom.scale(f),s.render()}else\"reset\"===r&&s.zoomReset()}}function s(t){var e,n=t._fullLayout;e=n._has(\"cartesian\")?n._isHoriz?\"y\":\"x\":\"closest\";var r=t._fullLayout.hovermode?!1:e;c.relayout(t,\"hovermode\",r)}var c=t(\"../../plotly\"),u=t(\"../../lib\"),f=t(\"../../snapshot/download\"),d=t(\"../../../build/ploticon\"),h=e.exports={};h.toImage={name:\"toImage\",title:\"Download plot as a png\",icon:d.camera,click:function(t){var e=\"png\";u.notifier(\"Taking snapshot - this may take a few seconds\",\"long\"),u.isIE()&&(u.notifier(\"IE only supports svg.  Changing format to svg.\",\"long\"),e=\"svg\"),f(t,{format:e}).then(function(t){u.notifier(\"Snapshot succeeded - \"+t,\"long\")}).catch(function(){u.notifier(\"Sorry there was a problem downloading your snapshot!\",\"long\")})}},h.sendDataToCloud={name:\"sendDataToCloud\",title:\"Save and edit plot in cloud\",icon:d.disk,click:function(t){c.Plots.sendDataToCloud(t)}},h.zoom2d={name:\"zoom2d\",title:\"Zoom\",attr:\"dragmode\",val:\"zoom\",icon:d.zoombox,click:r},h.pan2d={name:\"pan2d\",title:\"Pan\",attr:\"dragmode\",val:\"pan\",icon:d.pan,click:r},h.select2d={name:\"select2d\",title:\"Box Select\",attr:\"dragmode\",val:\"select\",icon:d.selectbox,click:r},h.lasso2d={name:\"lasso2d\",title:\"Lasso Select\",attr:\"dragmode\",val:\"lasso\",icon:d.lasso,click:r},h.zoomIn2d={name:\"zoomIn2d\",title:\"Zoom in\",attr:\"zoom\",val:\"in\",icon:d.zoom_plus,click:r},h.zoomOut2d={name:\"zoomOut2d\",title:\"Zoom out\",attr:\"zoom\",val:\"out\",icon:d.zoom_minus,click:r},h.autoScale2d={name:\"autoScale2d\",title:\"Autoscale\",attr:\"zoom\",val:\"auto\",icon:d.autoscale,click:r},h.resetScale2d={name:\"resetScale2d\",title:\"Reset axes\",attr:\"zoom\",val:\"reset\",icon:d.home,click:r},h.hoverClosestCartesian={name:\"hoverClosestCartesian\",title:\"Show closest data on hover\",attr:\"hovermode\",val:\"closest\",icon:d.tooltip_basic,gravity:\"ne\",click:r},h.hoverCompareCartesian={name:\"hoverCompareCartesian\",title:\"Compare data on hover\",attr:\"hovermode\",val:function(t){return t._fullLayout._isHoriz?\"y\":\"x\"},icon:d.tooltip_compare,gravity:\"ne\",click:r},h.zoom3d={name:\"zoom3d\",title:\"Zoom\",attr:\"scene.dragmode\",val:\"zoom\",icon:d.zoombox,click:a},h.pan3d={name:\"pan3d\",title:\"Pan\",attr:\"scene.dragmode\",val:\"pan\",icon:d.pan,click:a},h.orbitRotation={name:\"orbitRotation\",title:\"orbital rotation\",attr:\"scene.dragmode\",val:\"orbit\",icon:d[\"3d_rotate\"],click:a},h.tableRotation={name:\"tableRotation\",title:\"turntable rotation\",attr:\"scene.dragmode\",val:\"turntable\",icon:d[\"z-axis\"],click:a},h.resetCameraDefault3d={name:\"resetCameraDefault3d\",title:\"Reset camera to default\",attr:\"resetDefault\",icon:d.home,click:o},h.resetCameraLastSave3d={name:\"resetCameraLastSave3d\",title:\"Reset camera to last save\",attr:\"resetLastSave\",icon:d.movie,click:o},h.hoverClosest3d={name:\"hoverClosest3d\",title:\"Toggle show closest data on hover\",attr:\"hovermode\",val:null,toggle:!0,icon:d.tooltip_basic,gravity:\"ne\",click:i},h.zoomInGeo={name:\"zoomInGeo\",title:\"Zoom in\",attr:\"zoom\",val:\"in\",icon:d.zoom_plus,click:l},h.zoomOutGeo={name:\"zoomOutGeo\",title:\"Zoom out\",attr:\"zoom\",val:\"out\",icon:d.zoom_minus,click:l},h.resetGeo={name:\"resetGeo\",title:\"Reset\",attr:\"reset\",val:null,icon:d.autoscale,click:l},h.hoverClosestGeo={name:\"hoverClosestGeo\",title:\"Toggle show closest data on hover\",attr:\"hovermode\",val:null,toggle:!0,icon:d.tooltip_basic,gravity:\"ne\",click:s},h.hoverClosestGl2d={name:\"hoverClosestGl2d\",title:\"Toggle show closest data on hover\",attr:\"hovermode\",val:null,toggle:!0,icon:d.tooltip_basic,gravity:\"ne\",click:s},h.hoverClosestPie={name:\"hoverClosestPie\",title:\"Toggle show closest data on hover\",attr:\"hovermode\",val:\"closest\",icon:d.tooltip_basic,gravity:\"ne\",click:s},h.toggleHover={name:\"toggleHover\",title:\"Toggle show closest data on hover\",attr:\"hovermode\",val:null,toggle:!0,icon:d.tooltip_basic,gravity:\"ne\",click:function(t,e){s(t),i(t,e)}},h.resetViews={name:\"resetViews\",title:\"Reset views\",icon:d.home,click:function(t,e){var n=e.currentTarget;n.setAttribute(\"data-attr\",\"zoom\"),n.setAttribute(\"data-val\",\"reset\"),r(t,e),n.setAttribute(\"data-attr\",\"resetLastSave\"),o(t,e)}}},{\"../../../build/ploticon\":2,\"../../lib\":89,\"../../plotly\":107,\"../../snapshot/download\":137}],64:[function(t,e,n){\"use strict\";function r(t){this.container=t.container,this.element=document.createElement(\"div\"),this.update(t.graphInfo,t.buttons),this.container.appendChild(this.element)}function a(t,e){var n=t._fullLayout,a=new r({graphInfo:t,container:n._paperdiv.node(),buttons:e});return n._privateplot&&o.select(a.element).append(\"span\").classed(\"badge-private float--left\",!0).text(\"PRIVATE\"),a}var o=t(\"d3\"),i=t(\"../../lib\"),l=t(\"../../../build/ploticon\"),s=r.prototype;s.update=function(t,e){this.graphInfo=t;var n=this.graphInfo._context;\"hover\"===n.displayModeBar?this.element.className=\"modebar modebar--hover\":this.element.className=\"modebar\";var r=!this.hasButtons(e),a=this.hasLogo!==n.displaylogo;(r||a)&&(this.removeAllButtons(),this.updateButtons(e),n.displaylogo&&(this.element.appendChild(this.getLogo()),this.hasLogo=!0)),this.updateActiveButton()},s.updateButtons=function(t){var e=this;this.buttons=t,this.buttonElements=[],this.buttonsNames=[],this.buttons.forEach(function(t){var n=e.createGroup();t.forEach(function(t){var r=t.name;if(!r)throw new Error(\"must provide button 'name' in button config\");if(-1!==e.buttonsNames.indexOf(r))throw new Error(\"button name '\"+r+\"' is taken\");e.buttonsNames.push(r);var a=e.createButton(t);e.buttonElements.push(a),n.appendChild(a)}),e.element.appendChild(n)})},s.createGroup=function(){var t=document.createElement(\"div\");return t.className=\"modebar-group\",t},s.createButton=function(t){var e=this,n=document.createElement(\"a\");n.setAttribute(\"rel\",\"tooltip\"),n.className=\"modebar-btn\";var r=t.title;void 0===r&&(r=t.name),(r||0===r)&&n.setAttribute(\"data-title\",r),void 0!==t.attr&&n.setAttribute(\"data-attr\",t.attr);var a=t.val;void 0!==a&&(\"function\"==typeof a&&(a=a(this.graphInfo)),n.setAttribute(\"data-val\",a));var o=t.click;if(\"function\"!=typeof o)throw new Error(\"must provide button 'click' function in button config\");return n.addEventListener(\"click\",function(n){t.click(e.graphInfo,n),e.updateActiveButton(n.currentTarget)}),n.setAttribute(\"data-toggle\",t.toggle||!1),t.toggle&&n.classList.add(\"active\"),n.appendChild(this.createIcon(t.icon||l.question)),n.setAttribute(\"data-gravity\",t.gravity||\"n\"),n},s.createIcon=function(t){var e=t.ascent-t.descent,n=\"http://www.w3.org/2000/svg\",r=document.createElementNS(n,\"svg\"),a=document.createElementNS(n,\"path\");return r.setAttribute(\"height\",\"1em\"),r.setAttribute(\"width\",t.width/e+\"em\"),r.setAttribute(\"viewBox\",[0,0,t.width,e].join(\" \")),a.setAttribute(\"d\",t.path),a.setAttribute(\"transform\",\"matrix(1 0 0 -1 0 \"+t.ascent+\")\"),r.appendChild(a),r},s.updateActiveButton=function(t){var e=this.graphInfo._fullLayout,n=void 0!==t?t.getAttribute(\"data-attr\"):null;this.buttonElements.forEach(function(t){var r=t.getAttribute(\"data-val\")||!0,a=t.getAttribute(\"data-attr\"),l=\"true\"===t.getAttribute(\"data-toggle\"),s=o.select(t);if(l)a===n&&s.classed(\"active\",!s.classed(\"active\"));else{var c=null===a?a:i.nestedProperty(e,a).get();s.classed(\"active\",c===r)}})},s.hasButtons=function(t){var e=this.buttons;if(!e)return!1;if(t.length!==e.length)return!1;for(var n=0;n<t.length;++n){if(t[n].length!==e[n].length)return!1;for(var r=0;r<t[n].length;r++)if(t[n][r].name!==e[n][r].name)return!1}return!0},s.getLogo=function(){var t="
+,
+"this.createGroup(),e=document.createElement(\"a\");return e.href=\"https://plot.ly/\",e.target=\"_blank\",e.setAttribute(\"data-title\",\"Produced with Plotly\"),e.className=\"modebar-btn plotlyjsicon modebar-btn--logo\",e.appendChild(this.createIcon(l.plotlylogo)),t.appendChild(e),t},s.removeAllButtons=function(){for(;this.element.firstChild;)this.element.removeChild(this.element.firstChild);this.hasLogo=!1},s.destroy=function(){i.removeElement(this.container.querySelector(\".modebar\"))},e.exports=a},{\"../../../build/ploticon\":2,\"../../lib\":89,d3:9}],65:[function(t,e,n){\"use strict\";function r(t,e,n){function r(t){for(var n=[],r=0;r<t.length;r++){var a=t[r];-1===e.indexOf(a)&&n.push(f[a])}v.push(n)}var l=t._fullLayout,s=t._fullData,c=l._has(\"cartesian\"),u=l._has(\"gl3d\"),d=l._has(\"geo\"),h=l._has(\"pie\"),p=l._has(\"gl2d\"),g=l._has(\"ternary\"),v=[];if(r([\"toImage\",\"sendDataToCloud\"]),(c||p||h||g)+d+u>1)return r([\"resetViews\",\"toggleHover\"]),i(v,n);u&&(r([\"zoom3d\",\"pan3d\",\"orbitRotation\",\"tableRotation\"]),r([\"resetCameraDefault3d\",\"resetCameraLastSave3d\"]),r([\"hoverClosest3d\"])),d&&(r([\"zoomInGeo\",\"zoomOutGeo\",\"resetGeo\"]),r([\"hoverClosestGeo\"]));var m=a(l),y=[];return((c||p)&&!m||g)&&(y=[\"zoom2d\",\"pan2d\"]),(c||g)&&o(s)&&(y.push(\"select2d\"),y.push(\"lasso2d\")),y.length&&r(y),!c&&!p||m||g||r([\"zoomIn2d\",\"zoomOut2d\",\"autoScale2d\",\"resetScale2d\"]),c&&h?r([\"toggleHover\"]):p?r([\"hoverClosestGl2d\"]):c?r([\"hoverClosestCartesian\",\"hoverCompareCartesian\"]):h&&r([\"hoverClosestPie\"]),i(v,n)}function a(t){for(var e=s.Axes.list({_fullLayout:t},null,!0),n=!0,r=0;r<e.length;r++)if(!e[r].fixedrange){n=!1;break}return n}function o(t){for(var e=!1,n=0;n<t.length&&!e;n++){var r=t[n];r._module&&r._module.selectPoints&&(\"scatter\"===r.type||\"scatterternary\"===r.type?(c.hasMarkers(r)||c.hasText(r))&&(e=!0):e=!0)}return e}function i(t,e){if(e.length)if(Array.isArray(e[0]))for(var n=0;n<e.length;n++)t.push(e[n]);else t.push(e);return t}function l(t){for(var e=0;e<t.length;e++)for(var n=t[e],r=0;r<n.length;r++){var a=n[r];if(\"string\"==typeof a){if(void 0===f[a])throw new Error([\"*modeBarButtons* configuration options\",\"invalid button name\"].join(\" \"));t[e][r]=f[a]}}return t}var s=t(\"../../plotly\"),c=t(\"../../traces/scatter/subtypes\"),u=t(\"./\"),f=t(\"./buttons\");e.exports=function(t){var e=t._fullLayout,n=t._context,a=e._modeBar;if(!n.displayModeBar)return void(a&&(a.destroy(),delete e._modeBar));if(!Array.isArray(n.modeBarButtonsToRemove))throw new Error([\"*modeBarButtonsToRemove* configuration options\",\"must be an array.\"].join(\" \"));if(!Array.isArray(n.modeBarButtonsToAdd))throw new Error([\"*modeBarButtonsToAdd* configuration options\",\"must be an array.\"].join(\" \"));var o,i=n.modeBarButtons;o=Array.isArray(i)&&i.length?l(i):r(t,n.modeBarButtonsToRemove,n.modeBarButtonsToAdd),a?a.update(t,o):e._modeBar=u(t,o)}},{\"../../plotly\":107,\"../../traces/scatter/subtypes\":186,\"./\":64,\"./buttons\":63}],66:[function(t,e,n){\"use strict\";var r=t(\"../../plots/font_attributes\"),a=t(\"../color/attributes\"),o=t(\"../../lib/extend\").extendFlat,i=t(\"./button_attributes\");i=o(i,{_isLinkedToArray:!0}),e.exports={visible:{valType:\"boolean\"},buttons:i,x:{valType:\"number\",min:-2,max:3},xanchor:{valType:\"enumerated\",values:[\"auto\",\"left\",\"center\",\"right\"],dflt:\"left\"},y:{valType:\"number\",min:-2,max:3},yanchor:{valType:\"enumerated\",values:[\"auto\",\"top\",\"middle\",\"bottom\"],dflt:\"bottom\"},font:o({},r,{}),bgcolor:{valType:\"color\",dflt:a.lightLine},bordercolor:{valType:\"color\",dflt:a.defaultLine},borderwidth:{valType:\"number\",min:0,dflt:0}}},{\"../../lib/extend\":88,\"../../plots/font_attributes\":128,\"../color/attributes\":17,\"./button_attributes\":67}],67:[function(t,e,n){\"use strict\";e.exports={step:{valType:\"enumerated\",values:[\"month\",\"year\",\"day\",\"hour\",\"minute\",\"second\",\"all\"],dflt:\"month\"},stepmode:{valType:\"enumerated\",values:[\"backward\",\"todate\"],dflt:\"backward\"},count:{valType:\"number\",min:0,dflt:1},label:{valType:\"string\"}}},{}],68:[function(t,e,n){\"use strict\";e.exports={yPad:.02,minButtonWidth:30,rx:3,ry:3,activeColor:\"#d3d3d3\"}},{}],69:[function(t,e,n){\"use strict\";function r(t,e){function n(t,e){return o.coerce(r,a,l,t,e)}for(var r,a,i=t.buttons||[],s=e.buttons=[],c=0;c<i.length;c++){r=i[c],a={};var u=n(\"step\");\"all\"!==u&&(n(\"stepmode\"),n(\"count\")),n(\"label\"),s.push(a)}return s}function a(t,e,n){for(var r=n.filter(function(n){return e[n].anchor===t._id}),a=0,o=0;o<r.length;o++)a=Math.max(e[r[o]].domain[1],a);return[t.domain[0],a+s.yPad]}var o=t(\"../../lib\"),i=t(\"./attributes\"),l=t(\"./button_attributes\"),s=t(\"./constants\");e.exports=function(t,e,n,l){function s(t,e){return o.coerce(c,u,i,t,e)}var c=t.rangeselector||{},u=e.rangeselector={},f=r(c,u),d=s(\"visible\",f.length>0);if(d){var h=a(e,n,l);s(\"x\",h[0]),s(\"y\",h[1]),o.noneOrAll(t,e,[\"x\",\"y\"]),s(\"xanchor\"),s(\"yanchor\"),o.coerceFont(s,\"font\",n.font),s(\"bgcolor\"),s(\"bordercolor\"),s(\"borderwidth\")}}},{\"../../lib\":89,\"./attributes\":66,\"./button_attributes\":67,\"./constants\":68}],70:[function(t,e,n){\"use strict\";function r(t){for(var e=m.list(t,\"x\",!0),n=[],r=0;r<e.length;r++){var a=e[r];a.rangeselector&&a.rangeselector.visible&&n.push(a)}return n}function a(t){return t._id}function o(t,e,n){if(\"all\"===e.step)return t.autorange===!0;var r=Object.keys(n);return t.range[0]===n[r[0]]&&t.range[1]===n[r[1]]}function i(t,e,n){var r=t.selectAll(\"rect\").data([0]);r.enter().append(\"rect\").classed(\"selector-rect\",!0),r.attr(\"shape-rendering\",\"crispEdges\"),r.attr({rx:x.rx,ry:x.ry}),r.call(p.stroke,e.bordercolor).call(p.fill,l(e,n)).style(\"stroke-width\",e.borderwidth+\"px\")}function l(t,e){return e.isActive||e.isHovered?x.activeColor:t.bgcolor}function s(t,e,n){function r(t){v.convertToTspans(t)}var a=t.selectAll(\"text\").data([0]);a.enter().append(\"text\").classed(\"selector-text\",!0).classed(\"user-select-none\",!0),a.attr(\"text-anchor\",\"middle\"),a.call(g.font,e.font).text(c(n)).call(r)}function c(t){return t.label?t.label:\"all\"===t.step?\"all\":t.count+t.step.charAt(0)}function u(t,e,n,r){n.width=0,n.height=0;var a=n.borderwidth;e.each(function(){var t=f.select(this),e=t.select(\".selector-text\"),r=e.selectAll(\"tspan\"),a=1.3*n.font.size,o=r[0].length||1,i=Math.max(a*o,16)+3;n.height=Math.max(n.height,i)}),e.each(function(){var t=f.select(this),e=t.select(\".selector-rect\"),r=t.select(\".selector-text\"),o=r.selectAll(\"tspan\"),i=r.node()&&g.bBox(r.node()).width,l=1.3*n.font.size,s=o[0].length||1,c=Math.max(i+10,x.minButtonWidth);t.attr(\"transform\",\"translate(\"+(a+n.width)+\",\"+a+\")\"),e.attr({x:0,y:0,width:c,height:n.height});var u={x:c/2,y:n.height/2-(s-1)*l/2+3};r.attr(u),o.attr(u),n.width+=c+5}),e.selectAll(\"rect\").attr(\"height\",n.height);var o=t._fullLayout._size;n.lx=o.l+o.w*n.x,n.ly=o.t+o.h*(1-n.y);var i=\"left\";y.isRightAnchor(n)&&(n.lx-=n.width,i=\"right\"),y.isCenterAnchor(n)&&(n.lx-=n.width/2,i=\"center\");var l=\"top\";y.isBottomAnchor(n)&&(n.ly-=n.height,l=\"bottom\"),y.isMiddleAnchor(n)&&(n.ly-=n.height/2,l=\"middle\"),n.width=Math.ceil(n.width),n.height=Math.ceil(n.height),n.lx=Math.round(n.lx),n.ly=Math.round(n.ly),h.autoMargin(t,r+\"-range-selector\",{x:n.x,y:n.y,l:n.width*({right:1,center:.5}[i]||0),r:n.width*({left:1,center:.5}[i]||0),b:n.height*({top:1,middle:.5}[l]||0),t:n.height*({bottom:1,middle:.5}[l]||0)})}var f=t(\"d3\"),d=t(\"../../plotly\"),h=t(\"../../plots/plots\"),p=t(\"../color\"),g=t(\"../drawing\"),v=t(\"../../lib/svg_text_utils\"),m=t(\"../../plots/cartesian/axis_ids\"),y=t(\"../legend/anchor_utils\"),x=t(\"./constants\"),b=t(\"./get_update_object\");e.exports=function(t){var e=t._fullLayout,n=e._infolayer.selectAll(\".rangeselector\").data(r(t),a);n.enter().append(\"g\").classed(\"rangeselector\",!0),n.exit().remove(),n.style({cursor:\"pointer\",\"pointer-events\":\"all\"}),n.each(function(e){var n=f.select(this),r=e,a=r.rangeselector,l=n.selectAll(\"g.button\").data(a.buttons);l.enter().append(\"g\").classed(\"button\",!0),l.exit().remove(),l.each(function(e){var n=f.select(this),l=b(r,e);e.isActive=o(r,e,l),n.call(i,a,e),n.call(s,a,e),n.on(\"click\",function(){t._dragged||d.relayout(t,l)}),n.on(\"mouseover\",function(){e.isHovered=!0,n.call(i,a,e)}),n.on(\"mouseout\",function(){e.isHovered=!1,n.call(i,a,e)})}),u(t,l,a,r._name),n.attr(\"transform\",\"translate(\"+a.lx+\",\"+a.ly+\")\")})}},{\"../../lib/svg_text_utils\":100,\"../../plotly\":107,\"../../plots/cartesian/axis_ids\":112,\"../../plots/plots\":130,\"../color\":18,\"../drawing\":41,\"../legend/anchor_utils\":54,\"./constants\":68,\"./get_update_object\":71,d3:9}],71:[function(t,e,n){\"use strict\";function r(t,e){var n,r=t.range,o=new Date(r[1]),i=e.step,l=e.count;switch(e.stepmode){case\"backward\":n=a.time[i].offset(o,-l).getTime();break;case\"todate\":var s=a.time[i].offset(o,-(l-1));n=a.time[i].floor(s).getTime()}var c=r[1];return[n,c]}var a=t(\"d3\");e.exports=function(t,e){var n=t._name,a={};if(\"all\"===e.step)a[n+\".autorange\"]=!0;else{var o=r(t,e);a[n+\".range[0]\"]=o[0],a[n+\".range[1]\"]=o[1]}return a}},{d3:9}],72:[function(t,e,n){\"use strict\";n.attributes=t(\"./attributes\"),n.supplyLayoutDefaults=t(\"./defaults\"),n.draw=t(\"./draw\")},{\"./attributes\":66,\"./defaults\":69,\"./draw\":70}],73:[function(t,e,n){\"use strict\";var r=t(\"../color/attributes\");e.exports={bgcolor:{valType:\"color\",dflt:r.background},bordercolor:{valType:\"color\",dflt:r.defaultLine},borderwidth:{valType:\"integer\",dflt:0,min:0},range:{valType:\"info_array\",items:[{valType:\"number\"},{valType:\"number\"}]},thickness:{valType:\"number\",dflt:.15,min:0,max:1},visible:{valType:\"boolean\",dflt:!0}}},{\"../color/attributes\":17}],74:[function(t,e,n){\"use strict\";var r=t(\"../../plotly\"),a=t(\"../../plots/cartesian/axes\"),o=t(\"../../lib\"),i=t(\"../../constants/xmlns_namespaces\").svg,l=t(\"./helpers\"),s=t(\"./range_plot\");e.exports=function(t){function e(t){var e=h.range[0],n=h.range[1],r=n-e,a=t/p*r+e;return a=o.constrain(a,e,n)}function n(t,e){t=t||-(1/0),e=e||1/0;var n=h.range[0],r=h.range[1],a=r-n,o=(t-n)/a*p,i=(e-n)/a*p;u(o,i)}function c(e,n){window.requestAnimationFrame?window.requestAnimationFrame(function(){r.relayout(t,\"xaxis.range\",[e,n])}):setTimeout(function(){r.relayout(t,\"xaxis.range\",[e,n])},16)}"
+,
+"function u(t,e){if(t=o.constrain(t,0,p),e=o.constrain(e,0,p),t>e){var n=e;e=t,t=n}l.setAttributes(w,{\"data-min\":t,\"data-max\":e}),l.setAttributes(P,{x:t,width:e-t}),l.setAttributes(A,{width:t}),l.setAttributes(L,{x:e,width:p-e}),l.setAttributes(T,{transform:\"translate(\"+(t-v-1)+\")\"}),l.setAttributes(E,{transform:\"translate(\"+e+\")\"})}var f=t._fullLayout,d=f._infolayer.selectAll(\"g.range-slider\"),h=f.xaxis.rangeslider,p=f._size.w,g=(f.height-f.margin.b-f.margin.t)*h.thickness,v=2,m=Math.floor(h.borderwidth/2),y=f.margin.l,x=f.height-g-f.margin.b,b=0,_=p,w=document.createElementNS(i,\"g\");l.setAttributes(w,{\"class\":\"range-slider\",\"data-min\":b,\"data-max\":_,\"pointer-events\":\"all\",transform:\"translate(\"+y+\",\"+x+\")\"});var k=document.createElementNS(i,\"rect\"),M=h.borderwidth%2===0?h.borderwidth:h.borderwidth-1;l.setAttributes(k,{fill:h.bgcolor,stroke:h.bordercolor,\"stroke-width\":h.borderwidth,height:g+M,width:p+M,transform:\"translate(-\"+m+\", -\"+m+\")\",\"shape-rendering\":\"crispEdges\"});var A=document.createElementNS(i,\"rect\");l.setAttributes(A,{x:0,width:b,height:g,fill:\"rgba(0,0,0,0.4)\"});var L=document.createElementNS(i,\"rect\");l.setAttributes(L,{x:_,width:p-_,height:g,fill:\"rgba(0,0,0,0.4)\"});var T=document.createElementNS(i,\"g\"),z=document.createElementNS(i,\"rect\"),S=document.createElementNS(i,\"rect\");l.setAttributes(T,{transform:\"translate(\"+(b-v-1)+\")\"}),l.setAttributes(z,{width:10,height:g,x:-6,fill:\"transparent\",cursor:\"col-resize\"}),l.setAttributes(S,{width:v,height:g/2,y:g/4,rx:1,fill:\"white\",stroke:\"#666\",\"shape-rendering\":\"crispEdges\"}),l.appendChildren(T,[S,z]);var E=document.createElementNS(i,\"g\"),C=document.createElementNS(i,\"rect\"),O=document.createElementNS(i,\"rect\");l.setAttributes(E,{transform:\"translate(\"+_+\")\"}),l.setAttributes(C,{width:10,height:g,x:-2,fill:\"transparent\",cursor:\"col-resize\"}),l.setAttributes(O,{width:v,height:g/2,y:g/4,rx:1,fill:\"white\",stroke:\"#666\",\"shape-rendering\":\"crispEdges\"}),l.appendChildren(E,[O,C]);var P=document.createElementNS(i,\"rect\");\n"
+,
+"l.setAttributes(P,{x:b,width:_-b,height:g,cursor:\"ew-resize\",fill:\"transparent\"}),w.addEventListener(\"mousedown\",function(t){function n(t){var n,r,f=+t.clientX-o;switch(a){case P:w.style.cursor=\"ew-resize\",n=+l+f,r=+s+f,u(n,r),c(e(n),e(r));break;case z:w.style.cursor=\"col-resize\",n=+l+f,r=+s,u(n,r),c(e(n),e(r));break;case C:w.style.cursor=\"col-resize\",n=+l,r=+s+f,u(n,r),c(e(n),e(r));break;default:w.style.cursor=\"ew-resize\",n=i,r=i+f,u(n,r),c(e(n),e(r))}}function r(){window.removeEventListener(\"mousemove\",n),window.removeEventListener(\"mouseup\",r),w.style.cursor=\"auto\"}var a=t.target,o=t.clientX,i=o-w.getBoundingClientRect().left,l=w.getAttribute(\"data-min\"),s=w.getAttribute(\"data-max\");window.addEventListener(\"mousemove\",n),window.addEventListener(\"mouseup\",r)}),h.range||(h.range=a.getAutoRange(f.xaxis));var N=s(t,p,g);l.appendChildren(w,[k,N,A,L,P,T,E]),n(f.xaxis.range[0],f.xaxis.range[1]),d.data([0]).enter().append(function(){return h.setRange=n,w})}},{\"../../constants/xmlns_namespaces\":82,\"../../lib\":89,\"../../plotly\":107,\"../../plots/cartesian/axes\":110,\"./helpers\":76,\"./range_plot\":78}],75:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./attributes\");e.exports=function(t,e,n,o){function i(t,e){return r.coerce(l,s,a,t,e)}if(t[n].rangeslider){var l=\"object\"==typeof t[n].rangeslider?t[n].rangeslider:{},s=e[n].rangeslider={};if(i(\"bgcolor\"),i(\"bordercolor\"),i(\"borderwidth\"),i(\"thickness\"),i(\"visible\"),i(\"range\"),s.range&&!e[n].autorange){var c=s.range,u=e[n].range;c[0]=Math.min(c[0],u[0]),c[1]=Math.max(c[1],u[1])}else e[n]._needsExpand=!0;s.visible&&o.forEach(function(t){var n=e[t]||{};n.fixedrange=!0,e[t]=n})}}},{\"../../lib\":89,\"./attributes\":73}],76:[function(t,e,n){\"use strict\";n.setAttributes=function(t,e){for(var n in e)t.setAttribute(n,e[n])},n.appendChildren=function(t,e){for(var n=0;n<e.length;n++)e[n]&&t.appendChild(e[n])}},{}],77:[function(t,e,n){\"use strict\";function r(t){if(t._fullLayout.xaxis){var e=t._fullLayout,n=e._infolayer.selectAll(\"g.range-slider\"),r=e.xaxis.rangeslider;if(!r||!r.visible)return n.data([]).exit().remove(),void a.autoMargin(t,\"range-slider\");var i=(e.height-e.margin.b-e.margin.t)*r.thickness,l=Math.floor(r.borderwidth/2);0!==n[0].length||e._has(\"gl2d\")||o(t);var s=e.xaxis._boundingBox?e.xaxis._boundingBox.height:0;a.autoMargin(t,\"range-slider\",{x:0,y:0,l:0,r:0,t:0,b:i+e.margin.b+s,pad:15+2*l})}}var a=t(\"../../plots/plots\"),o=t(\"./create_slider\"),i=t(\"./defaults\");e.exports={draw:r,supplyLayoutDefaults:i}},{\"../../plots/plots\":130,\"./create_slider\":74,\"./defaults\":75}],78:[function(t,e,n){\"use strict\";function r(t,e){for(var n=e.makeCalcdata(t||[],e._id[0]),r=0;r<n.length;r++)n[r]=e.c2l(n[r]);return n}function a(t,e,n,r){var a,o,f;if(t.line){a=document.createElementNS(u,\"path\");var d=s.smoothopen(e,t.line.smoothing||0);c.setAttributes(a,{d:d,fill:\"none\",stroke:t.line?t.line.color:\"transparent\",\"stroke-width\":t.line.width/2||1,opacity:1})}if(t.marker){o=document.createElementNS(u,\"g\");var h=e.map(function(e,n){var r,a=document.createElementNS(u,\"g\"),o=document.createElementNS(u,\"path\");return r=Array.isArray(t.marker.size)?\"number\"==typeof t.marker.size[n]?Math.max(t.marker.size[n]/(t.marker.sizeref||1)/15,0):0:Math.max(t.marker.size/15,2),c.setAttributes(o,{d:l[t.marker.symbol].f(r),fill:t.marker.color,stroke:t.marker.line.color,\"stroke-width\":t.marker.line.width,opacity:t.marker.opacity}),c.setAttributes(a,{transform:\"translate(\"+e[0]+\",\"+e[1]+\")\"}),a.appendChild(o),a});c.appendChildren(o,h)}if(\"none\"!==t.fill){switch(f=document.createElementNS(u,\"path\"),t.fill){case\"tozeroy\":e.unshift([e[0][0],r]),e.push([e[e.length-1][0],r]);break;case\"tozerox\":e.unshift([0,e[e.length-1][1]]);break;default:i.warn(\"Fill type \"+t.fill+\" not supported for range slider! (yet...)\")}var p=s.smoothopen(e,t.line.smoothing||0);c.setAttributes(f,{d:p,fill:t.fillcolor||\"transparent\"})}return[a,o,f]}var o=t(\"d3\"),i=t(\"../../lib\"),l=t(\"../drawing/symbol_defs\"),s=t(\"../drawing\"),c=t(\"./helpers\"),u=t(\"../../constants/xmlns_namespaces\").svg;e.exports=function f(t,e,n){var l=t._fullLayout,d=t._fullData,h=l.xaxis,p=l.yaxis,g=h.rangeslider.range[0],v=h.rangeslider.range[1],m=p.range[0],y=p.range[1],x=document.createElementNS(u,\"path\");x.setAttribute(\"d\",[\"M0,0\",e+\",0\",e+\",\"+n,\"0,\"+n,\"Z\"].join(\" \"));var b=document.createElementNS(u,\"clipPath\");b.setAttribute(\"id\",\"range-clip-path\"),b.appendChild(x);var _=document.createElementNS(u,\"defs\");_.appendChild(b);var f=document.createElementNS(u,\"g\");o.select(f).call(s.setClipUrl,\"range-clip-path\"),f.appendChild(_);for(var w=[\"scatter\"],k=0;k<d.length;k++){var M=d[k],A=[];if(w.indexOf(M.type)<0)i.warn(\"Trace type \"+M.type+\" not supported for range slider!\");else{for(var L=r(M,h),T=r(M,p),z=0;z<L.length;z++){var S=e*(L[z]-g)/(v-g),E=n*(1-(T[z]-m)/(y-m));isNaN(S)||isNaN(E)||A.push([S,E])}c.appendChildren(f,a(M,A,e,n))}}return f}},{\"../../constants/xmlns_namespaces\":82,\"../../lib\":89,\"../drawing\":41,\"../drawing/symbol_defs\":42,\"./helpers\":76,d3:9}],79:[function(t,e,n){\"use strict\";var r=t(\"../annotations/attributes\"),a=t(\"../../traces/scatter/attributes\"),o=t(\"../../lib/extend\").extendFlat,i=a.line;e.exports={_isLinkedToArray:!0,type:{valType:\"enumerated\",values:[\"circle\",\"rect\",\"path\",\"line\"]},layer:{valType:\"enumerated\",values:[\"below\",\"above\"],dflt:\"above\"},xref:o({},r.xref,{}),x0:{valType:\"any\"},x1:{valType:\"any\"},yref:o({},r.yref,{}),y0:{valType:\"any\"},y1:{valType:\"any\"},path:{valType:\"string\"},opacity:{valType:\"number\",min:0,max:1,dflt:1},line:{color:i.color,width:i.width,dash:i.dash},fillcolor:{valType:\"color\",dflt:\"rgba(0,0,0,0)\"}}},{\"../../lib/extend\":88,\"../../traces/scatter/attributes\":167,\"../annotations/attributes\":15}],80:[function(t,e,n){\"use strict\";function r(t,e){function n(e,n){return M.coerce(t,r,E.layoutAttributes,e,n)}var r={};n(\"layer\"),n(\"opacity\"),n(\"fillcolor\"),n(\"line.color\"),n(\"line.width\"),n(\"line.dash\");for(var a=t.path?\"path\":\"rect\",i=n(\"type\",a),l=[\"x\",\"y\"],s=0;2>s;s++){var c=l[s],u={_fullLayout:e},f=A.coerceRef(t,r,u,c);if(\"path\"!==i){var d=.25,h=.75;if(\"paper\"!==f){var p=A.getFromId(u,f),g=o(p);d=g(p.range[0]+d*(p.range[1]-p.range[0])),h=g(p.range[0]+h*(p.range[1]-p.range[0]))}n(c+\"0\",d),n(c+\"1\",h)}}return\"path\"===i?n(\"path\"):M.noneOrAll(t,r,[\"x0\",\"x1\",\"y0\",\"y1\"]),r}function a(t){return\"category\"===t.type?t.c2l:t.d2l}function o(t){return\"category\"===t.type?t.l2c:t.l2d}function i(t,e){t.layout.shapes=e,E.supplyLayoutDefaults(t.layout,t._fullLayout),E.drawAll(t)}function l(t){delete t.layout.shapes,t._fullLayout.shapes=[],E.drawAll(t)}function s(t,e,n){for(var r=0;r<t._fullLayout.shapes.length;r++)E.draw(t,r,e,n)}function c(t,e){h(t,e).selectAll('[data-index=\"'+e+'\"]').remove(),t._fullLayout.shapes.splice(e,1),t.layout.shapes.splice(e,1);for(var n=e;n<t._fullLayout.shapes.length;n++)h(t,n).selectAll('[data-index=\"'+(n+1)+'\"]').attr(\"data-index\",n),E.draw(t,n)}function u(t,e,n){t._fullLayout.shapes.splice(e,0,{});var r=M.isPlainObject(n)?M.extendFlat({},n):{text:\"New text\"};t.layout.shapes?t.layout.shapes.splice(e,0,r):t.layout.shapes=[r];for(var a=t._fullLayout.shapes.length-1;a>e;a--)h(t,a).selectAll('[data-index=\"'+(a-1)+'\"]').attr(\"data-index\",a),E.draw(t,a)}function f(t,e,n,i){function l(n){var r={\"data-index\":e,\"fill-rule\":\"evenodd\",d:x(t,E)},a=E.line.width?E.line.color:\"rgba(0,0,0,0)\",o=n.append(\"path\").attr(r).style(\"opacity\",E.opacity).call(L.stroke,a).call(L.fill,E.fillcolor).call(T.dashLine,E.line.dash,E.line.width);C&&o.call(T.setClipUrl,\"clip\"+t._fullLayout._uid+C),t._context.editable&&d(t,o,E,e)}var s,c;h(t,e).selectAll('[data-index=\"'+e+'\"]').remove();var u=t.layout.shapes[e];if(u){var f={xref:u.xref,yref:u.yref},g={};\"string\"==typeof n&&n?g[n]=i:M.isPlainObject(n)&&(g=n);var v=Object.keys(g);for(s=0;s<v.length;s++){var m=v[s];M.nestedProperty(u,m).set(g[m])}var y=[\"x0\",\"x1\",\"y0\",\"y1\"];for(s=0;4>s;s++){var b=y[s];if(void 0===g[b]&&void 0!==u[b]){var _,w=b.charAt(0),k=A.getFromId(t,A.coerceRef(f,{},t,w)),z=A.getFromId(t,A.coerceRef(u,{},t,w)),S=u[b];void 0!==g[w+\"ref\"]&&(k?(_=a(k)(S),S=(_-k.range[0])/(k.range[1]-k.range[0])):S=(S-z.domain[0])/(z.domain[1]-z.domain[0]),z?(_=z.range[0]+S*(z.range[1]-z.range[0]),S=o(z)(_)):S=k.domain[0]+S*(k.domain[1]-k.domain[0])),u[b]=S}}var E=r(u,t._fullLayout);t._fullLayout.shapes[e]=E;var C;if(\"below\"!==E.layer)C=(E.xref+E.yref).replace(/paper/g,\"\"),l(t._fullLayout._shapeUpperLayer);else if(\"paper\"===E.xref&&\"paper\"===E.yref)C=\"\",l(t._fullLayout._shapeLowerLayer);else{var O,P=t._fullLayout._plots||{},N=Object.keys(P);for(s=0,c=N.length;c>s;s++)O=P[N[s]],C=N[s],p(t,E,O)&&l(O.shapelayer)}}}function d(t,e,n,r){function a(t){var n=W.right-W.left,r=W.bottom-W.top,a=t.clientX-W.left,o=t.clientY-W.top,i=n>G&&r>$&&!t.shiftKey?z.getCursor(a/n,1-o/r):\"move\";S(e,i),X=i.split(\"-\")[0]}function o(e){B=A.getFromId(t,n.xref),H=A.getFromId(t,n.yref),V=m(t,B),Z=m(t,H,!0),Y=y(t,B),U=y(t,H,!0);var o=\"shapes[\"+r+\"]\";\"path\"===n.type?(q=n.path,F=o+\".path\"):(u=V(n.x0),f=Z(n.y0),d=V(n.x1),h=Z(n.y1),p=o+\".x0\",g=o+\".y0\",_=o+\".x1\",w=o+\".y1\"),d>u?(T=u,P=o+\".x0\",R=\"x0\",E=d,N=o+\".x1\",j=\"x1\"):(T=d,P=o+\".x1\",R=\"x1\",E=u,N=o+\".x0\",j=\"x0\"),h>f?(M=f,C=o+\".y0\",D=\"y0\",L=h,O=o+\".y1\",I=\"y1\"):(M=h,C=o+\".y1\",D=\"y1\",L=f,O=o+\".y0\",I=\"y0\"),c={},a(e),Q.moveFn=\"move\"===X?l:s}function i(n){S(e),n&&k.relayout(t,c)}function l(r,a){if(\"path\"===n.type){var o=function(t){return Y(V(t)+r)};B&&\"date\"===B.type&&(o=v(o));var i=function(t){return U(Z(t)+a)};H&&\"date\"===H.type&&(i=v(i)),n.path=b(q,o,i),c[F]=n.path}else c[p]=n.x0=Y(u+r),c[g]=n.y0=U(f+a),c[_]=n.x1=Y(d+r),c[w]=n.y1=U(h+a);e.attr(\"d\",x(t,n))}function s(r,a){if(\"path\"===n.type){var o=function(t){return Y(V(t)+r)};B&&\"date\"===B.type&&(o=v(o));var i=function(t){return U(Z(t)+a)};H&&\"date\"===H.type&&(i=v(i)),n.path=b(q,o,i),c[F]=n.path}else{var l=~X.indexOf(\"n\")?M+a:M,s=~X.indexOf(\"s\")?L+a:L,u=~X.indexOf(\"w\")?T+r:T,f=~X.indexOf(\"e\")?E+r:E;s-l>$&&(c[C]=n[D]=U(l),c[O]=n[I]=U(s)),f-u>G&&(c[P]=n[R]=Y(u),c[N]=n[j]=Y(f))}e.attr(\"d\",x(t,n))}var c,u,f,d,h,p,g,_,w,M,L,T,E,C,O,P,N,D,I,R,j,q"
+,
+",F,B,H,V,Z,Y,U,X,G=10,$=10,Q={setCursor:a,element:e.node(),prepFn:o,doneFn:i},W=Q.element.getBoundingClientRect();z.init(Q)}function h(t,e){var n=t._fullLayout.shapes[e],r=t._fullLayout._shapeUpperLayer;return n?\"below\"===n.layer&&(r=\"paper\"===n.xref&&\"paper\"===n.yref?t._fullLayout._shapeLowerLayer:t._fullLayout._shapeSubplotLayer):M.log(\"getShapeLayer: undefined shape: index\",e),r}function p(t,e,n){var r=k.Axes.getFromId(t,n.id,\"x\")._id,a=k.Axes.getFromId(t,n.id,\"y\")._id,o=\"below\"===e.layer,i=r===e.xref||a===e.yref,l=!!n.shapelayer;return o&&i&&l}function g(t){return function(e){return e.replace&&(e=e.replace(\"_\",\" \")),t(e)}}function v(t){return function(e){return t(e).replace(\" \",\"_\")}}function m(t,e,n){var r,o=t._fullLayout._size;if(e){var i=a(e);r=function(t){return e._offset+e.l2p(i(t,!0))},\"date\"===e.type&&(r=g(r))}else r=n?function(t){return o.t+o.h*(1-t)}:function(t){return o.l+o.w*t};return r}function y(t,e,n){var r,a=t._fullLayout._size;if(e){var i=o(e);r=function(t){return i(e.p2l(t-e._offset))}}else r=n?function(t){return 1-(t-a.t)/a.h}:function(t){return(t-a.l)/a.w};return r}function x(t,e){var n,r,o,i,l=e.type,s=A.getFromId(t,e.xref),c=A.getFromId(t,e.yref),u=t._fullLayout._size;if(s?(n=a(s),r=function(t){return s._offset+s.l2p(n(t,!0))}):r=function(t){return u.l+u.w*t},c?(o=a(c),i=function(t){return c._offset+c.l2p(o(t,!0))}):i=function(t){return u.t+u.h*(1-t)},\"path\"===l)return s&&\"date\"===s.type&&(r=g(r)),c&&\"date\"===c.type&&(i=g(i)),E.convertPath(e.path,r,i);var f=r(e.x0),d=r(e.x1),h=i(e.y0),p=i(e.y1);if(\"line\"===l)return\"M\"+f+\",\"+h+\"L\"+d+\",\"+p;if(\"rect\"===l)return\"M\"+f+\",\"+h+\"H\"+d+\"V\"+p+\"H\"+f+\"Z\";var v=(f+d)/2,m=(h+p)/2,y=Math.abs(v-f),x=Math.abs(m-h),b=\"A\"+y+\",\"+x,_=v+y+\",\"+m,w=v+\",\"+(m-x);return\"M\"+_+b+\" 0 1,1 \"+w+b+\" 0 0,1 \"+_+\"Z\"}function b(t,e,n){return t.replace(C,function(t){var r=0,a=t.charAt(0),o=P[a],i=N[a],l=D[a],s=t.substr(1).replace(O,function(t){return r>=l?t:(o[r]?t=e(t):i[r]&&(t=n(t)),r++,t)});return a+s})}function _(t,e,n,r,a){var o=\"category\"===t.type?Number:t.d2c;if(void 0!==e)return[o(e),o(n)];if(r){var i,l,s,c,u,f=1/0,d=-(1/0),h=r.match(C);for(\"date\"===t.type&&(o=g(o)),i=0;i<h.length;i++)l=h[i],s=a[l.charAt(0)].drawn,void 0!==s&&(c=h[i].substr(1).match(O),!c||c.length<s||(u=o(c[s]),f>u&&(f=u),u>d&&(d=u)));return d>=f?[f,d]:void 0}}var w=t(\"fast-isnumeric\"),k=t(\"../../plotly\"),M=t(\"../../lib\"),A=t(\"../../plots/cartesian/axes\"),L=t(\"../color\"),T=t(\"../drawing\"),z=t(\"../dragelement\"),S=t(\"../../lib/setcursor\"),E=e.exports={};E.layoutAttributes=t(\"./attributes\"),E.supplyLayoutDefaults=function(t,e){for(var n=t.shapes||[],a=e.shapes=[],o=0;o<n.length;o++)a.push(r(n[o]||{},e))},E.drawAll=function(t){var e=t._fullLayout;e._shapeUpperLayer.selectAll(\"path\").remove(),e._shapeLowerLayer.selectAll(\"path\").remove(),e._shapeSubplotLayer.selectAll(\"path\").remove();for(var n=0;n<e.shapes.length;n++)E.draw(t,n)},E.add=function(t){var e=t._fullLayout.shapes.length;k.relayout(t,\"shapes[\"+e+\"]\",\"add\")},E.draw=function(t,e,n,r){if(!w(e)||-1===e){if(!e&&Array.isArray(r))return void i(t,r);if(\"remove\"===r)return void l(t);if(n&&\"add\"!==r)return void s(t,n,r);e=t._fullLayout.shapes.length,t._fullLayout.shapes.push({})}if(!n&&r){if(\"remove\"===r)return void c(t,e);(\"add\"===r||M.isPlainObject(r))&&u(t,e,r)}f(t,e,n,r)};var C=/[MLHVQCTSZ][^MLHVQCTSZ]*/g,O=/[^\\s,]+/g,P={M:{0:!0,drawn:0},L:{0:!0,drawn:0},H:{0:!0,drawn:0},V:{},Q:{0:!0,2:!0,drawn:2},C:{0:!0,2:!0,4:!0,drawn:4},T:{0:!0,drawn:0},S:{0:!0,2:!0,drawn:2},Z:{}},N={M:{1:!0,drawn:1},L:{1:!0,drawn:1},H:{},V:{0:!0,drawn:0},Q:{1:!0,3:!0,drawn:3},C:{1:!0,3:!0,5:!0,drawn:5},T:{1:!0,drawn:1},S:{1:!0,3:!0,drawn:5},Z:{}},D={M:2,L:2,H:1,V:1,Q:4,C:6,T:2,S:4,Z:0};E.convertPath=function(t,e,n){return t.replace(C,function(t){var r=0,a=t.charAt(0),o=P[a],i=N[a],l=D[a],s=t.substr(1).replace(O,function(t){return o[r]?t=e(t):i[r]&&(t=n(t)),r++,r>l&&(t=\"X\"),t});return r>l&&(s=s.replace(/[\\s,]*X.*/,\"\"),M.log(\"Ignoring extra params in segment \"+t)),a+s})},E.calcAutorange=function(t){var e,n,r,a,o,i=t._fullLayout,l=i.shapes;if(l.length&&t._fullData.length)for(e=0;e<l.length;e++)n=l[e],r=n.line.width/2,\"paper\"!==n.xref&&(a=A.getFromId(t,n.xref),o=_(a,n.x0,n.x1,n.path,P),o&&A.expand(a,o,{ppad:r})),\"paper\"!==n.yref&&(a=A.getFromId(t,n.yref),o=_(a,n.y0,n.y1,n.path,N),o&&A.expand(a,o,{ppad:r}))}},{\"../../lib\":89,\"../../lib/setcursor\":98,\"../../plotly\":107,\"../../plots/cartesian/axes\":110,\"../color\":18,\"../dragelement\":39,\"../drawing\":41,\"./attributes\":79,\"fast-isnumeric\":11}],81:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"fast-isnumeric\"),o=t(\"../../plotly\"),i=t(\"../../plots/plots\"),l=t(\"../../lib\"),s=t(\"../drawing\"),c=t(\"../color\"),u=t(\"../../lib/svg_text_utils\"),f=e.exports={};f.draw=function(t,e,n){function f(t){l.syncOrAsync([d,h],t)}function d(e){return e.attr(\"transform\",_?\"rotate(\"+[_.rotate,b.x,b.y]+\") translate(0, \"+_.offset+\")\":null),e.style({\"font-family\":M,\"font-size\":r.round(A,2)+\"px\",fill:c.rgb(L),opacity:T*c.opacity(L),\"font-weight\":i.fontWeight}).attr(b).call(u.convertToTspans).attr(b),e.selectAll(\"tspan.line\").attr(b),i.previousPromises(t)}function h(t){var e=r.select(t.node().parentNode);if(x&&x.selection&&x.side&&S){e.attr(\"transform\",null);var n=0,o={left:\"right\",right:\"left\",top:\"bottom\",bottom:\"top\"}[x.side],i=-1!==[\"left\",\"top\"].indexOf(x.side)?-1:1,c=a(x.pad)?x.pad:2,u=s.bBox(e.node()),f={left:0,top:0,right:k.width,bottom:k.height},d=x.maxShift||(f[x.side]-u[x.side])*(\"left\"===x.side||\"top\"===x.side?-1:1);if(0>d?n=d:(u.left-=x.offsetLeft,u.right-=x.offsetLeft,u.top-=x.offsetTop,u.bottom-=x.offsetTop,x.selection.each(function(){var t=s.bBox(this);l.bBoxIntersect(u,t,c)&&(n=Math.max(n,i*(t[x.side]-u[o])+c))}),n=Math.min(d,n)),n>0||0>d){var h={left:[-n,0],right:[n,0],top:[0,-n],bottom:[0,n]}[x.side];e.attr(\"transform\",\"translate(\"+h+\")\")}}}function p(){T=0,z=!0,S=C,k._infolayer.select(\".\"+e).attr({\"data-unformatted\":S}).text(S).on(\"mouseover.opacity\",function(){r.select(this).transition().duration(100).style(\"opacity\",1)}).on(\"mouseout.opacity\",function(){r.select(this).transition().duration(1e3).style(\"opacity\",0)})}var g=n.propContainer,v=n.propName,m=n.traceIndex,y=n.dfltName,x=n.avoid||{},b=n.attributes,_=n.transform,w=n.containerGroup,k=t._fullLayout,M=g.titlefont.family,A=g.titlefont.size,L=g.titlefont.color,T=1,z=!1,S=g.title.trim();\"\"===S&&(T=0),S.match(/Click to enter .+ title/)&&(T=.2,z=!0),w||(w=k._infolayer.selectAll(\".g-\"+e).data([0]),w.enter().append(\"g\").classed(\"g-\"+e,!0));var E=w.selectAll(\"text\").data([0]);E.enter().append(\"text\"),E.text(S).attr(\"class\",e),E.attr({\"data-unformatted\":S}).call(f);var C=\"Click to enter \"+y+\" title\";t._context.editable?(S||p(),E.call(u.makeEditable).on(\"edit\",function(e){void 0!==m?o.restyle(t,v,e,m):o.relayout(t,v,e)}).on(\"cancel\",function(){this.text(this.attr(\"data-unformatted\")).call(f)}).on(\"input\",function(t){this.text(t||\" \").attr(b).selectAll(\"tspan.line\").attr(b)})):S&&!S.match(/Click to enter .+ title/)||E.remove(),E.classed(\"js-placeholder\",z)}},{\"../../lib\":89,\"../../lib/svg_text_utils\":100,\"../../plotly\":107,\"../../plots/plots\":130,\"../color\":18,\"../drawing\":41,d3:9,\"fast-isnumeric\":11}],82:[function(t,e,n){\"use strict\";n.xmlns=\"http://www.w3.org/2000/xmlns/\",n.svg=\"http://www.w3.org/2000/svg\",n.xlink=\"http://www.w3.org/1999/xlink\",n.svgAttrs={xmlns:n.svg,\"xmlns:xlink\":n.xlink}},{}],83:[function(t,e,n){\"use strict\";var r=t(\"./plotly\");n.version=\"1.15.0\",n.plot=r.plot,n.newPlot=r.newPlot,n.restyle=r.restyle,n.relayout=r.relayout,n.redraw=r.redraw,n.extendTraces=r.extendTraces,n.prependTraces=r.prependTraces,n.addTraces=r.addTraces,n.deleteTraces=r.deleteTraces,n.moveTraces=r.moveTraces,n.purge=r.purge,n.setPlotConfig=t(\"./plot_api/set_plot_config\"),n.register=r.register,n.toImage=t(\"./plot_api/to_image\"),n.downloadImage=t(\"./snapshot/download\"),n.validate=t(\"./plot_api/validate\"),n.Icons=t(\"../build/ploticon\"),n.Plots=r.Plots,n.Fx=r.Fx,n.Snapshot=r.Snapshot,n.PlotSchema=r.PlotSchema,n.Queue=r.Queue,n.d3=t(\"d3\")},{\"../build/ploticon\":2,\"./plot_api/set_plot_config\":104,\"./plot_api/to_image\":105,\"./plot_api/validate\":106,\"./plotly\":107,\"./snapshot/download\":137,d3:9}],84:[function(t,e,n){\"use strict\";\"undefined\"!=typeof MathJax?(n.MathJax=!0,MathJax.Hub.Config({messageStyle:\"none\",skipStartupTypeset:!0,displayAlign:\"left\",tex2jax:{inlineMath:[[\"$\",\"$\"],[\"\\\\(\",\"\\\\)\"]]}}),MathJax.Hub.Configured()):n.MathJax=!1},{}],85:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"tinycolor2\"),o=t(\"./nested_property\"),i=t(\"../components/colorscale/get_scale\"),l=(Object.keys(t(\"../components/colorscale/scales\")),/^([2-9]|[1-9][0-9]+)$/);n.valObjects={data_array:{coerceFunction:function(t,e,n){Array.isArray(t)?e.set(t):void 0!==n&&e.set(n)}},enumerated:{coerceFunction:function(t,e,n,r){r.coerceNumber&&(t=+t),-1===r.values.indexOf(t)?e.set(n):e.set(t)}},\"boolean\":{coerceFunction:function(t,e,n){t===!0||t===!1?e.set(t):e.set(n)}},number:{coerceFunction:function(t,e,n,a){!r(t)||void 0!==a.min&&t<a.min||void 0!==a.max&&t>a.max?e.set(n):e.set(+t)}},integer:{coerceFunction:function(t,e,n,a){t%1||!r(t)||void 0!==a.min&&t<a.min||void 0!==a.max&&t>a.max?e.set(n):e.set(+t)}},string:{coerceFunction:function(t,e,n,r){if(\"string\"!=typeof t){var a=\"number\"==typeof t;r.strict!==!0&&a?e.set(String(t)):e.set(n)}else r.noBlank&&!t?e.set(n):e.set(t)}},color:{coerceFunction:function(t,e,n){a(t).isValid()?e.set(t):e.set(n)}},colorscale:{coerceFunction:function(t,e,n){e.set(i(t,n))}},angle:{coerceFunction:function(t,e,n){\"auto\"===t?e.set(\"auto\"):r(t)?(Math.abs(t)>180&&(t-=360*Math.round(t/360)),e.set(+t)):e.set(n)}},subplotid:{coerceFunction:function(t,e,n){var r=n.length;return\"string\"==typeof t&&t.substr(0,r)===n&&l.test(t.substr(r))?void e.set(t):void e.set(n)},validateFunction:function(t,e){var n=e.dflt,r=n.length;return t===n?!0:\"string\"!=typeof t?!1:!(t.substr(0,r)!==n||!l.test(t.substr(r)))}},flaglist:{coerceFunction:function(t,e,n,r){if(\"string\"!=typeof t)return void e.set(n);if(-1!==(r.extras||[]).indexOf(t)"
+,
+")return void e.set(t);for(var a=t.split(\"+\"),o=0;o<a.length;){var i=a[o];-1===r.flags.indexOf(i)||a.indexOf(i)<o?a.splice(o,1):o++}a.length?e.set(a.join(\"+\")):e.set(n)}},any:{coerceFunction:function(t,e,n){void 0===t?e.set(n):e.set(t)}},info_array:{coerceFunction:function(t,e,r,a){if(!Array.isArray(t))return void e.set(r);var o=a.items,i=[];r=Array.isArray(r)?r:[];for(var l=0;l<o.length;l++)n.coerce(t,i,o,\"[\"+l+\"]\",r[l]);e.set(i)},validateFunction:function(t,e){if(!Array.isArray(t))return!1;var r=e.items;if(t.length!==r.length)return!1;for(var a=0;a<r.length;a++){var o=n.validate(t[a],e.items[a]);if(!o)return!1}return!0}}},n.coerce=function(t,e,r,a,i){var l=o(r,a).get(),s=o(t,a),c=o(e,a),u=s.get();return void 0===i&&(i=l.dflt),l.arrayOk&&Array.isArray(u)?(c.set(u),u):(n.valObjects[l.valType].coerceFunction(u,c,i,l),c.get())},n.coerce2=function(t,e,r,a,i){var l=o(t,a),s=n.coerce(t,e,r,a,i);return l.get()?s:!1},n.coerceFont=function(t,e,n){var r={};return n=n||{},r.family=t(e+\".family\",n.family),r.size=t(e+\".size\",n.size),r.color=t(e+\".color\",n.color),r},n.validate=function(t,e){var r=n.valObjects[e.valType];if(e.arrayOk&&Array.isArray(t))return!0;if(r.validateFunction)return r.validateFunction(t,e);var a={},o=a,i={set:function(t){o=t}};return r.coerceFunction(t,i,a,e),o!==a}},{\"../components/colorscale/get_scale\":30,\"../components/colorscale/scales\":36,\"./nested_property\":93,\"fast-isnumeric\":11,tinycolor2:13}],86:[function(t,e,n){\"use strict\";function r(t,e){return String(t+Math.pow(10,e)).substr(1)}function a(t){var e;return e=x.test(t)?\"Y\":\"y\",e+=b.test(t)?\"b\":\"\"}function o(t){var e;return e=w.test(t)?_.test(t)?\"I\":\"H\":\"D\"}var i=t(\"d3\"),l=t(\"fast-isnumeric\"),s=t(\"../lib\");n.dateTime2ms=function(t){try{if(t.getTime)return+t}catch(e){return!1}var n,r,a,o,i=String(t).split(\" \");if(i.length>2)return!1;var s=i[0].split(\"-\");if(s.length>3||3!==s.length&&i[1])return!1;if(4===s[0].length)n=Number(s[0]);else{if(2!==s[0].length)return!1;var c=(new Date).getFullYear();n=((Number(s[0])-c+70)%100+200)%100+c-70}return l(n)?1===s.length?new Date(n,0,1).getTime():(r=Number(s[1])-1,s[1].length>2||!(r>=0&&11>=r)?!1:2===s.length?new Date(n,r,1).getTime():(a=Number(s[2]),s[2].length>2||!(a>=1&&31>=a)?!1:(a=new Date(n,r,a).getTime(),i[1]?(s=i[1].split(\":\"),s.length>3?!1:(o=Number(s[0]),s[0].length>2||!(o>=0&&23>=o)?!1:(a+=36e5*o,1===s.length?a:(r=Number(s[1]),s[1].length>2||!(r>=0&&59>=r)?!1:(a+=6e4*r,2===s.length?a:(t=Number(s[2]),t>=0&&60>t?a+1e3*t:!1)))))):a))):!1},n.isDateTime=function(t){return n.dateTime2ms(t)!==!1},n.ms2DateTime=function(t,e){if(\"undefined\"==typeof i)return void s.error(\"d3 is not defined.\");e||(e=0);var n=new Date(t),a=i.time.format(\"%Y-%m-%d\")(n);return 7776e6>e?(a+=\" \"+r(n.getHours(),2),432e6>e&&(a+=\":\"+r(n.getMinutes(),2),108e5>e&&(a+=\":\"+r(n.getSeconds(),2),3e5>e&&(a+=\".\"+r(n.getMilliseconds(),3)))),a.replace(/([:\\s]00)*\\.?[0]*$/,\"\")):a};var c={H:[\"%H:%M:%S~%L\",\"%H:%M:%S\",\"%H:%M\"],I:[\"%I:%M:%S~%L%p\",\"%I:%M:%S%p\",\"%I:%M%p\"],D:[\"%H\",\"%I%p\",\"%Hh\"]},u={Y:[\"%Y~%m~%d\",\"%Y%m%d\",\"%y%m%d\",\"%m~%d~%Y\",\"%d~%m~%Y\"],Yb:[\"%b~%d~%Y\",\"%d~%b~%Y\",\"%Y~%d~%b\",\"%Y~%b~%d\"],y:[\"%m~%d~%y\",\"%d~%m~%y\",\"%y~%m~%d\"],yb:[\"%b~%d~%y\",\"%d~%b~%y\",\"%y~%d~%b\",\"%y~%b~%d\"]},f=i.time.format.utc,d={Y:{H:[\"%Y~%m~%dT%H:%M:%S\",\"%Y~%m~%dT%H:%M:%S~%L\"].map(f),I:[],D:[\"%Y%m%d%H%M%S\",\"%Y~%m\",\"%m~%Y\"].map(f)},Yb:{H:[],I:[],D:[\"%Y~%b\",\"%b~%Y\"].map(f)},y:{H:[],I:[],D:[]},yb:{H:[],I:[],D:[]}};[\"Y\",\"Yb\",\"y\",\"yb\"].forEach(function(t){u[t].forEach(function(e){d[t].D.push(f(e)),[\"H\",\"I\",\"D\"].forEach(function(n){c[n].forEach(function(r){var a=d[t][n];a.push(f(e+\"~\"+r)),a.push(f(r+\"~\"+e))})})})});var h=/[a-z]*/g,p=function(t){return t.substr(0,3)},g=/(mon|tue|wed|thu|fri|sat|sun|the|of|st|nd|rd|th)/g,v=/[\\s,\\/\\-\\.\\(\\)]+/g,m=/~?([ap])~?m(~|$)/,y=function(t,e){return e+\"m \"},x=/\\d\\d\\d\\d/,b=/(^|~)[a-z]{3}/,_=/[ap]m/,w=/:/,k=/q([1-4])/,M=[\"31~mar\",\"30~jun\",\"30~sep\",\"31~dec\"],A=function(t,e){return M[e-1]},L=/ ?([+\\-]\\d\\d:?\\d\\d|Z)$/;n.parseDate=function(t){if(t.getTime)return t;if(\"string\"!=typeof t)return!1;t=t.toLowerCase().replace(h,p).replace(g,\"\").replace(v,\"~\").replace(m,y).replace(k,A).trim().replace(L,\"\");var e,n,r=null,i=a(t),l=o(t);e=d[i][l],n=e.length;for(var s=0;n>s&&!(r=e[s].parse(t));s++);if(!(r instanceof Date))return!1;var c=r.getTimezoneOffset();return r.setTime(r.getTime()+60*c*1e3),r}},{\"../lib\":89,d3:9,\"fast-isnumeric\":11}],87:[function(t,e,n){\"use strict\";var r=t(\"events\").EventEmitter,a={init:function(t){if(t._ev instanceof r)return t;var e=new r;return t._ev=e,t.on=e.on.bind(e),t.once=e.once.bind(e),t.removeListener=e.removeListener.bind(e),t.removeAllListeners=e.removeAllListeners.bind(e),t.emit=function(n,r){\"undefined\"!=typeof jQuery&&jQuery(t).trigger(n,r),e.emit(n,r)},t},triggerHandler:function(t,e,n){var r,a;\"undefined\"!=typeof jQuery&&(r=jQuery(t).triggerHandler(e,n));var o=t._ev;if(!o)return r;var i=o._events[e];if(!i)return r;\"function\"==typeof i&&(i=[i]);for(var l=i.pop(),s=0;s<i.length;s++)i[s](n);return a=l(n),void 0!==r?r:a},purge:function(t){return delete t._ev,delete t.on,delete t.once,delete t.removeListener,delete t.removeAllListeners,delete t.emit,t}};e.exports=a},{events:7}],88:[function(t,e,n){\"use strict\";function r(t,e){var n,r;for(n=0;n<t.length;n++){if(r=t[n],null!==r&&\"object\"==typeof r)return!1;void 0!==r&&(e[n]=r)}return!0}function a(t,e,n){var l,s,c,u,f,d,h,p=t[0],g=t.length;if(2===g&&i(p)&&i(t[1])&&0===p.length){if(h=r(t[1],p))return p;p.splice(0,p.length)}for(var v=1;g>v;v++){l=t[v];for(s in l)c=p[s],u=l[s],e&&u&&(o(u)||(f=i(u)))?(f?(f=!1,d=c&&i(c)?c:[]):d=c&&o(c)?c:{},p[s]=a([d,u],e,n)):(\"undefined\"!=typeof u||n)&&(p[s]=u)}return p}var o=t(\"./is_plain_object.js\"),i=Array.isArray;n.extendFlat=function(){return a(arguments,!1,!1)},n.extendDeep=function(){return a(arguments,!0,!1)},n.extendDeepAll=function(){return a(arguments,!0,!0)}},{\"./is_plain_object.js\":90}],89:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=e.exports={};a.nestedProperty=t(\"./nested_property\"),a.isPlainObject=t(\"./is_plain_object\");var o=t(\"./coerce\");a.valObjects=o.valObjects,a.coerce=o.coerce,a.coerce2=o.coerce2,a.coerceFont=o.coerceFont,a.validate=o.validate;var i=t(\"./dates\");a.dateTime2ms=i.dateTime2ms,a.isDateTime=i.isDateTime,a.ms2DateTime=i.ms2DateTime,a.parseDate=i.parseDate;var l=t(\"./search\");a.findBin=l.findBin,a.sorterAsc=l.sorterAsc,a.sorterDes=l.sorterDes,a.distinctVals=l.distinctVals,a.roundUp=l.roundUp;var s=t(\"./stats\");a.aggNums=s.aggNums,a.len=s.len,a.mean=s.mean,a.variance=s.variance,a.stdev=s.stdev,a.interp=s.interp;var c=t(\"./matrix\");a.init2dArray=c.init2dArray,a.transposeRagged=c.transposeRagged,a.dot=c.dot,a.translationMatrix=c.translationMatrix,a.rotationMatrix=c.rotationMatrix,a.rotationXYMatrix=c.rotationXYMatrix,a.apply2DTransform=c.apply2DTransform,a.apply2DTransform2=c.apply2DTransform2;var u=t(\"./extend\");a.extendFlat=u.extendFlat,a.extendDeep=u.extendDeep,a.extendDeepAll=u.extendDeepAll;var f=t(\"./loggers\");a.log=f.log,a.warn=f.warn,a.error=f.error,a.notifier=t(\"./notifier\"),a.swapAttrs=function(t,e,n,r){n||(n=\"x\"),r||(r=\"y\");for(var o=0;o<e.length;o++){var i=e[o],l=a.nestedProperty(t,i.replace(\"?\",n)),s=a.nestedProperty(t,i.replace(\"?\",r)),c=l.get();l.set(s.get()),s.set(c)}},a.pauseEvent=function(t){return t.stopPropagation&&t.stopPropagation(),t.preventDefault&&t.preventDefault(),t.cancelBubble=!0,!1},a.constrain=function(t,e,n){return e>n?Math.max(n,Math.min(e,t)):Math.max(e,Math.min(n,t))},a.bBoxIntersect=function(t,e,n){return n=n||0,t.left<=e.right+n&&e.left<=t.right+n&&t.top<=e.bottom+n&&e.top<=t.bottom+n},a.identity=function(t){return t},a.randstr=function d(t,e,n){if(n||(n=16),void 0===e&&(e=24),0>=e)return\"0\";var r,a,o,i=Math.log(Math.pow(2,e))/Math.log(n),l=\"\";for(r=2;i===1/0;r*=2)i=Math.log(Math.pow(2,e/r))/Math.log(n)*r;var s=i-Math.floor(i);for(r=0;r<Math.floor(i);r++)o=Math.floor(Math.random()*n).toString(n),l=o+l;s&&(a=Math.pow(n,s),o=Math.floor(Math.random()*a).toString(n),l=o+l);var c=parseInt(l,n);return t&&t.indexOf(l)>-1||c!==1/0&&c>=Math.pow(2,e)?d(t,e,n):l},a.OptionControl=function(t,e){t||(t={}),e||(e=\"opt\");var n={};return n.optionList=[],n._newoption=function(r){r[e]=t,n[r.name]=r,n.optionList.push(r)},n[\"_\"+e]=t,n},a.smooth=function(t,e){if(e=Math.round(e)||0,2>e)return t;var n,r,a,o,i=t.length,l=2*i,s=2*e-1,c=new Array(s),u=new Array(i);for(n=0;s>n;n++)c[n]=(1-Math.cos(Math.PI*(n+1)/e))/(2*e);for(n=0;i>n;n++){for(o=0,r=0;s>r;r++)a=n+r+1-e,-i>a?a-=l*Math.round(a/l):a>=l&&(a-=l*Math.floor(a/l)),0>a?a=-1-a:a>=i&&(a=l-1-a),o+=t[a]*c[r];u[n]=o}return u},a.syncOrAsync=function(t,e,n){function r(){return a.syncOrAsync(t,e,n)}for(var o,i;t.length;)if(i=t.splice(0,1)[0],o=i(e),o&&o.then)return o.then(r).then(void 0,a.promiseError);return n&&n(e)},a.stripTrailingSlash=function(t){return\"/\"===t.substr(-1)?t.substr(0,t.length-1):t},a.noneOrAll=function(t,e,n){if(t){var r,a,o=!1,i=!0;for(r=0;r<n.length;r++)a=t[n[r]],void 0!==a&&null!==a?o=!0:i=!1;if(o&&!i)for(r=0;r<n.length;r++)t[n[r]]=e[n[r]]}},a.pushUnique=function(t,e){return e&&-1===t.indexOf(e)&&t.push(e),t},a.mergeArray=function(t,e,n){if(Array.isArray(t))for(var r=Math.min(t.length,e.length),a=0;r>a;a++)e[a][n]=t[a]},a.minExtend=function(t,e){var n={};\"object\"!=typeof e&&(e={});var r,o,i,l=3,s=Object.keys(t);for(r=0;r<s.length;r++)o=s[r],i=t[o],\"_\"!==o.charAt(0)&&\"function\"!=typeof i&&(\"module\"===o?n[o]=i:Array.isArray(i)?n[o]=i.slice(0,l):i&&\"object\"==typeof i?n[o]=a.minExtend(t[o],e[o]):n[o]=i);for(s=Object.keys(e),r=0;r<s.length;r++)o=s[r],i=e[o],\"object\"==typeof i&&o in n&&\"object\"==typeof n[o]||(n[o]=i);return n},a.titleCase=function(t){return t.charAt(0).toUpperCase()+t.substr(1)},a.containsAny=function(t,e){for(var n=0;n<e.length;n++)if(-1!==t.indexOf(e[n]))return!0;return!1},a.getPlotDiv=function(t){for(;t&&t.removeAttribute;t=t.parentNode)if(a.isPlotDiv(t))return t},a.isPlotDiv=function(t){var e=r.select(t);return e.size()&&e.classed(\"js-plotly-plot\")},a.removeElement=function(t){var e=t&&t.parentNode;e&&e.removeChild(t)},a.add"
+,
+"StyleRule=function(t,e){if(!a.styleSheet){var n=document.createElement(\"style\");n.appendChild(document.createTextNode(\"\")),document.head.appendChild(n),a.styleSheet=n.sheet}var r=a.styleSheet;r.insertRule?r.insertRule(t+\"{\"+e+\"}\",0):r.addRule?r.addRule(t,e,0):a.warn(\"addStyleRule failed\")},a.getTranslate=function(t){var e=/.*\\btranslate\\((\\d*\\.?\\d*)[^\\d]*(\\d*\\.?\\d*)[^\\d].*/,n=t.attr?\"attr\":\"getAttribute\",r=t[n](\"transform\")||\"\",a=r.replace(e,function(t,e,n){return[e,n].join(\" \")}).split(\" \");return{x:+a[0]||0,y:+a[1]||0}},a.setTranslate=function(t,e,n){var r=/(\\btranslate\\(.*?\\);?)/,a=t.attr?\"attr\":\"getAttribute\",o=t.attr?\"attr\":\"setAttribute\",i=t[a](\"transform\")||\"\";return e=e||0,n=n||0,i=i.replace(r,\"\").trim(),i+=\" translate(\"+e+\", \"+n+\")\",i=i.trim(),t[o](\"transform\",i),i},a.getScale=function(t){var e=/.*\\bscale\\((\\d*\\.?\\d*)[^\\d]*(\\d*\\.?\\d*)[^\\d].*/,n=t.attr?\"attr\":\"getAttribute\",r=t[n](\"transform\")||\"\",a=r.replace(e,function(t,e,n){return[e,n].join(\" \")}).split(\" \");return{x:+a[0]||1,y:+a[1]||1}},a.setScale=function(t,e,n){var r=/(\\bscale\\(.*?\\);?)/,a=t.attr?\"attr\":\"getAttribute\",o=t.attr?\"attr\":\"setAttribute\",i=t[a](\"transform\")||\"\";return e=e||1,n=n||1,i=i.replace(r,\"\").trim(),i+=\" scale(\"+e+\", \"+n+\")\",i=i.trim(),t[o](\"transform\",i),i},a.setPointGroupScale=function(t,e,n){var r,a,o;return e=e||1,n=n||1,a=1===e&&1===n?\"\":\" scale(\"+e+\",\"+n+\")\",o=/\\s*sc.*/,t.each(function(){r=(this.getAttribute(\"transform\")||\"\").replace(o,\"\"),r+=a,r=r.trim(),this.setAttribute(\"transform\",r)}),a},a.isIE=function(){return\"undefined\"!=typeof window.navigator.msSaveBlob},a.objectFromPath=function(t,e){for(var n,r=t.split(\".\"),a=n={},o=0;o<r.length;o++){var i=r[o],l=null,s=r[o].match(/(.*)\\[([0-9]+)\\]/);s?(i=s[1],l=s[2],n=n[i]=[],o===r.length-1?n[l]=e:n[l]={},n=n[l]):(o===r.length-1?n[i]=e:n[i]={},n=n[i])}return a},a.numSeparate=function(t,e){if(\"string\"!=typeof e||0===e.length)throw new Error(\"Separator string required for formatting!\");\"number\"==typeof t&&(t=String(t));var n=/(\\d+)(\\d{3})/,r=e.charAt(0),a=e.charAt(1),o=t.split(\".\"),i=o[0],l=o.length>1?r+o[1]:\"\";\n"
+,
+"if(a&&(o.length>1||i.length>4))for(;n.test(i);)i=i.replace(n,\"$1\"+a+\"$2\");return i+l}},{\"./coerce\":85,\"./dates\":86,\"./extend\":88,\"./is_plain_object\":90,\"./loggers\":91,\"./matrix\":92,\"./nested_property\":93,\"./notifier\":94,\"./search\":97,\"./stats\":99,d3:9}],90:[function(t,e,n){\"use strict\";e.exports=function(t){return\"[object Object]\"===Object.prototype.toString.call(t)&&Object.getPrototypeOf(t)===Object.prototype}},{}],91:[function(t,e,n){\"use strict\";var r=t(\"../plot_api/plot_config\"),a=e.exports={};a.log=function(){if(r.logging>1){for(var t=[\"LOG:\"],e=0;e<arguments.length;e++)t.push(arguments[e]);console.trace?console.trace.apply(console,t):console.log.apply(console,t)}},a.warn=function(){if(r.logging>0){for(var t=[\"WARN:\"],e=0;e<arguments.length;e++)t.push(arguments[e]);console.trace?console.trace.apply(console,t):console.log.apply(console,t)}},a.error=function(){if(r.logging>0){for(var t=[\"ERROR:\"],e=0;e<arguments.length;e++)t.push(arguments[e]);console.error.apply(console,arguments)}}},{\"../plot_api/plot_config\":102}],92:[function(t,e,n){\"use strict\";n.init2dArray=function(t,e){for(var n=new Array(t),r=0;t>r;r++)n[r]=new Array(e);return n},n.transposeRagged=function(t){var e,n,r=0,a=t.length;for(e=0;a>e;e++)r=Math.max(r,t[e].length);var o=new Array(r);for(e=0;r>e;e++)for(o[e]=new Array(a),n=0;a>n;n++)o[e][n]=t[n][e];return o},n.dot=function(t,e){if(!t.length||!e.length||t.length!==e.length)return null;var r,a,o=t.length;if(t[0].length)for(r=new Array(o),a=0;o>a;a++)r[a]=n.dot(t[a],e);else if(e[0].length){var i=n.transposeRagged(e);for(r=new Array(i.length),a=0;a<i.length;a++)r[a]=n.dot(t,i[a])}else for(r=0,a=0;o>a;a++)r+=t[a]*e[a];return r},n.translationMatrix=function(t,e){return[[1,0,t],[0,1,e],[0,0,1]]},n.rotationMatrix=function(t){var e=t*Math.PI/180;return[[Math.cos(e),-Math.sin(e),0],[Math.sin(e),Math.cos(e),0],[0,0,1]]},n.rotationXYMatrix=function(t,e,r){return n.dot(n.dot(n.translationMatrix(e,r),n.rotationMatrix(t)),n.translationMatrix(-e,-r))},n.apply2DTransform=function(t){return function(){var e=arguments;3===e.length&&(e=e[0]);var r=1===arguments.length?e[0]:[e[0],e[1]];return n.dot(t,[r[0],r[1],1]).slice(0,2)}},n.apply2DTransform2=function(t){var e=n.apply2DTransform(t);return function(t){return e(t.slice(0,2)).concat(e(t.slice(2,4)))}}},{}],93:[function(t,e,n){\"use strict\";function r(t,e){return function(){var n,a,o,i,l,s=t;for(i=0;i<e.length-1;i++){if(n=e[i],-1===n){for(a=!0,o=[],l=0;l<s.length;l++)o[l]=r(s[l],e.slice(i+1))(),o[l]!==o[0]&&(a=!1);return a?o[0]:o}if(\"number\"==typeof n&&!Array.isArray(s))return;if(s=s[n],\"object\"!=typeof s||null===s)return}if(\"object\"==typeof s&&null!==s&&(o=s[e[i]],null!==o))return o}}function a(t,e){var n=[\"annotations\",\"shapes\",\"range\",\"domain\",\"buttons\"],r=-1===n.indexOf(e);return Array.isArray(t)&&r}function o(t,e){return function(n){var r,o,u=t,f=[t],d=c(n)&&!a(n,e[e.length-1]);for(o=0;o<e.length-1;o++){if(r=e[o],\"number\"==typeof r&&!Array.isArray(u))throw\"array index but container is not an array\";if(-1===r){if(d=!i(u,e.slice(o+1),n))break;return}if(!l(u,r,e[o+1],d))break;if(u=u[r],\"object\"!=typeof u||null===u)throw\"container is not an object\";f.push(u)}d?(o===e.length-1&&delete u[e[o]],s(f)):u[e[o]]=n}}function i(t,e,n){var r,a=Array.isArray(n),i=!0,s=n,u=a?!1:c(n),f=e[0];for(r=0;r<t.length;r++)a&&(s=n[r%n.length],u=c(s)),u&&(i=!1),l(t,r,f,u)&&o(t[r],e)(s);return i}function l(t,e,n,r){if(void 0===t[e]){if(r)return!1;\"number\"==typeof n?t[e]=[]:t[e]={}}return!0}function s(t){var e,n,r,o,i;for(e=t.length-1;e>=0;e--){if(r=t[e],i=!1,Array.isArray(r))for(n=r.length-1;n>=0;n--)c(r[n])?i?r[n]=void 0:r.pop():i=!0;else if(\"object\"==typeof r&&null!==r)for(o=Object.keys(r),i=!1,n=o.length-1;n>=0;n--)c(r[o[n]])&&!a(r[o[n]],o[n])?delete r[o[n]]:i=!0;if(i)return}}function c(t){return void 0===t||null===t?!0:\"object\"!=typeof t?!1:Array.isArray(t)?!t.length:!Object.keys(t).length}function u(t,e,n){return{set:function(){throw\"bad container\"},get:function(){},astr:e,parts:n,obj:t}}var f=t(\"fast-isnumeric\");e.exports=function(t,e){if(f(e))e=String(e);else if(\"string\"!=typeof e||\"[-1]\"===e.substr(e.length-4))throw\"bad property string\";for(var n,a,i,l=0,s=e.split(\".\");l<s.length;){if(n=String(s[l]).match(/^([^\\[\\]]*)((\\[\\-?[0-9]*\\])+)$/)){if(n[1])s[l]=n[1];else{if(0!==l)throw\"bad property string\";s.splice(0,1)}for(a=n[2].substr(1,n[2].length-2).split(\"][\"),i=0;i<a.length;i++)l++,s.splice(l,0,Number(a[i]))}l++}return\"object\"!=typeof t?u(t,e,s):{set:o(t,s),get:r(t,s),astr:e,parts:s,obj:t}}},{\"fast-isnumeric\":11}],94:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"fast-isnumeric\"),o=[];e.exports=function(t,e){function n(t){t.duration(700).style(\"opacity\",0).each(\"end\",function(t){var e=o.indexOf(t);-1!==e&&o.splice(e,1),r.select(this).remove()})}if(-1===o.indexOf(t)){o.push(t);var i=1e3;a(e)?i=e:\"long\"===e&&(i=3e3);var l=r.select(\"body\").selectAll(\".plotly-notifier\").data([0]);l.enter().append(\"div\").classed(\"plotly-notifier\",!0);var s=l.selectAll(\".notifier-note\").data(o);s.enter().append(\"div\").classed(\"notifier-note\",!0).style(\"opacity\",0).each(function(t){var e=r.select(this);e.append(\"button\").classed(\"notifier-close\",!0).html(\"&times;\").on(\"click\",function(){e.transition().call(n)}),e.append(\"p\").html(t),e.transition().duration(700).style(\"opacity\",1).transition().delay(i).call(n)})}}},{d3:9,\"fast-isnumeric\":11}],95:[function(t,e,n){\"use strict\";var r=t(\"./matrix\").dot,a=e.exports={};a.tester=function(t){function e(t,e){var n=t[0],r=t[1];return a>n||n>o||i>r||r>l?!1:!e||!c(t)}function n(t,e){var n=t[0],s=t[1];if(a>n||n>o||i>s||s>l)return!1;var c,u,f,d,h,p=r.length,g=r[0][0],v=r[0][1],m=0;for(c=1;p>c;c++)if(u=g,f=v,g=r[c][0],v=r[c][1],d=Math.min(u,g),!(d>n||n>Math.max(u,g)||s>Math.max(f,v)))if(s<Math.min(f,v))n!==d&&m++;else{if(h=g===u?s:f+(n-u)*(v-f)/(g-u),s===h)return 1!==c||!e;h>=s&&n!==d&&m++}return m%2===1}var r=t.slice(),a=r[0][0],o=a,i=r[0][1],l=i;r.push(r[0]);for(var s=1;s<r.length;s++)a=Math.min(a,r[s][0]),o=Math.max(o,r[s][0]),i=Math.min(i,r[s][1]),l=Math.max(l,r[s][1]);var c,u=!1;return 5===r.length&&(r[0][0]===r[1][0]?r[2][0]===r[3][0]&&r[0][1]===r[3][1]&&r[1][1]===r[2][1]&&(u=!0,c=function(t){return t[0]===r[0][0]}):r[0][1]===r[1][1]&&r[2][1]===r[3][1]&&r[0][0]===r[3][0]&&r[1][0]===r[2][0]&&(u=!0,c=function(t){return t[1]===r[0][1]})),{xmin:a,xmax:o,ymin:i,ymax:l,pts:r,contains:u?e:n,isRect:u}};var o=a.isSegmentBent=function(t,e,n,a){var o,i,l,s=t[e],c=[t[n][0]-s[0],t[n][1]-s[1]],u=r(c,c),f=Math.sqrt(u),d=[-c[1]/f,c[0]/f];for(o=e+1;n>o;o++)if(i=[t[o][0]-s[0],t[o][1]-s[1]],l=r(i,c),0>l||l>u||Math.abs(r(i,d))>a)return!0;return!1};a.filter=function(t,e){function n(n){t.push(n);var l=r.length,s=a;r.splice(i+1);for(var c=s+1;c<t.length;c++)(c===t.length-1||o(t,s,c+1,e))&&(r.push(t[c]),r.length<l-2&&(a=c,i=r.length-1),s=c)}var r=[t[0]],a=0,i=0;if(t.length>1){var l=t.pop();n(l)}return{addPt:n,raw:t,filtered:r}}},{\"./matrix\":92}],96:[function(t,e,n){\"use strict\";function r(t,e){for(var n,r=[],o=0;o<e.length;o++)n=e[o],n===t?r[o]=n:\"object\"==typeof n?r[o]=Array.isArray(n)?a.extendDeep([],n):a.extendDeepAll({},n):r[o]=n;return r}var a=t(\"../lib\"),o=t(\"../plot_api/plot_config\"),i={};i.add=function(t,e,n,r,a){var i,l;return t.undoQueue=t.undoQueue||{index:0,queue:[],sequence:!1},l=t.undoQueue.index,t.autoplay?void(t.undoQueue.inSequence||(t.autoplay=!1)):(!t.undoQueue.sequence||t.undoQueue.beginSequence?(i={undo:{calls:[],args:[]},redo:{calls:[],args:[]}},t.undoQueue.queue.splice(l,t.undoQueue.queue.length-l,i),t.undoQueue.index+=1):i=t.undoQueue.queue[l-1],t.undoQueue.beginSequence=!1,i&&(i.undo.calls.unshift(e),i.undo.args.unshift(n),i.redo.calls.push(r),i.redo.args.push(a)),void(t.undoQueue.queue.length>o.queueLength&&(t.undoQueue.queue.shift(),t.undoQueue.index--)))},i.startSequence=function(t){t.undoQueue=t.undoQueue||{index:0,queue:[],sequence:!1},t.undoQueue.sequence=!0,t.undoQueue.beginSequence=!0},i.stopSequence=function(t){t.undoQueue=t.undoQueue||{index:0,queue:[],sequence:!1},t.undoQueue.sequence=!1,t.undoQueue.beginSequence=!1},i.undo=function(t){var e,n;if(t.framework&&t.framework.isPolar)return void t.framework.undo();if(!(void 0===t.undoQueue||isNaN(t.undoQueue.index)||t.undoQueue.index<=0)){for(t.undoQueue.index--,e=t.undoQueue.queue[t.undoQueue.index],t.undoQueue.inSequence=!0,n=0;n<e.undo.calls.length;n++)i.plotDo(t,e.undo.calls[n],e.undo.args[n]);t.undoQueue.inSequence=!1,t.autoplay=!1}},i.redo=function(t){var e,n;if(t.framework&&t.framework.isPolar)return void t.framework.redo();if(!(void 0===t.undoQueue||isNaN(t.undoQueue.index)||t.undoQueue.index>=t.undoQueue.queue.length)){for(e=t.undoQueue.queue[t.undoQueue.index],t.undoQueue.inSequence=!0,n=0;n<e.redo.calls.length;n++)i.plotDo(t,e.redo.calls[n],e.redo.args[n]);t.undoQueue.inSequence=!1,t.autoplay=!1,t.undoQueue.index++}},i.plotDo=function(t,e,n){t.autoplay=!0,n=r(t,n),e.apply(null,n)},e.exports=i},{\"../lib\":89,\"../plot_api/plot_config\":102}],97:[function(t,e,n){\"use strict\";function r(t,e){return e>t}function a(t,e){return e>=t}function o(t,e){return t>e}function i(t,e){return t>=e}var l=t(\"fast-isnumeric\"),s=t(\"../lib\");n.findBin=function(t,e,n){if(l(e.start))return n?Math.ceil((t-e.start)/e.size)-1:Math.floor((t-e.start)/e.size);var c,u,f=0,d=e.length,h=0;for(u=e[e.length-1]>=e[0]?n?r:a:n?i:o;d>f&&h++<100;)c=Math.floor((f+d)/2),u(e[c],t)?f=c+1:d=c;return h>90&&s.log(\"Long binary search...\"),f-1},n.sorterAsc=function(t,e){return t-e},n.sorterDes=function(t,e){return e-t},n.distinctVals=function(t){var e=t.slice();e.sort(n.sorterAsc);for(var r=e.length-1,a=e[r]-e[0]||1,o=a/(r||1)/1e4,i=[e[0]],l=0;r>l;l++)e[l+1]>e[l]+o&&(a=Math.min(a,e[l+1]-e[l]),i.push(e[l+1]));return{vals:i,minDiff:a}},n.roundUp=function(t,e,n){for(var r,a=0,o=e.length-1,i=0,l=n?0:1,s=n?1:0,c=n?Math.ceil:Math.floor;o>a&&i++<100;)r=c((a+o)/2),e[r]<=t?a=r+l:o=r-s;return e[a]}},{\"../lib\":89,\"fast-isnumeric\":11}],98:[function(t,e,n){\"use strict\";e.exports=function(t,e){(t.attr(\"class\")||\"\").split(\" \").forEach(function(e){0===e.inde"
+,
+"xOf(\"cursor-\")&&t.classed(e,!1)}),e&&t.classed(\"cursor-\"+e,!0)}},{}],99:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\");n.aggNums=function(t,e,a,o){var i,l;if(o||(o=a.length),r(e)||(e=!1),Array.isArray(a[0])){for(l=new Array(o),i=0;o>i;i++)l[i]=n.aggNums(t,e,a[i]);a=l}for(i=0;o>i;i++)r(e)?r(a[i])&&(e=t(+e,+a[i])):e=a[i];return e},n.len=function(t){return n.aggNums(function(t){return t+1},0,t)},n.mean=function(t,e){return e||(e=n.len(t)),n.aggNums(function(t,e){return t+e},0,t)/e},n.variance=function(t,e,a){return e||(e=n.len(t)),r(a)||(a=n.mean(t,e)),n.aggNums(function(t,e){return t+Math.pow(e-a,2)},0,t)/e},n.stdev=function(t,e,r){return Math.sqrt(n.variance(t,e,r))},n.interp=function(t,e){if(!r(e))throw\"n should be a finite number\";if(e=e*t.length-.5,0>e)return t[0];if(e>t.length-1)return t[t.length-1];var n=e%1;return n*t[Math.ceil(e)]+(1-n)*t[Math.floor(e)]}},{\"fast-isnumeric\":11}],100:[function(t,e,n){\"use strict\";function r(t,e){return t.node().getBoundingClientRect()[e]}function a(t){return t.replace(/(<|&lt;|&#60;)/g,\"\\\\lt \").replace(/(>|&gt;|&#62;)/g,\"\\\\gt \")}function o(t,e,n){var r=\"math-output-\"+s.Lib.randstr([],64),o=c.select(\"body\").append(\"div\").attr({id:r}).style({visibility:\"hidden\",position:\"absolute\"}).style({\"font-size\":e.fontSize+\"px\"}).text(a(t));MathJax.Hub.Queue([\"Typeset\",MathJax.Hub,o.node()],function(){var e=c.select(\"body\").select(\"#MathJax_SVG_glyphs\");if(o.select(\".MathJax_SVG\").empty()||!o.select(\"svg\").node())u.log(\"There was an error in the tex syntax.\",t),n();else{var r=o.select(\"svg\").node().getBoundingClientRect();n(o.select(\".MathJax_SVG\"),e,r)}o.remove()})}function i(t){for(var e=s.util.html_entity_decode(t),n=e.split(/(<[^<>]*>)/).map(function(t){var e=t.match(/<(\\/?)([^ >]*)\\s*(.*)>/i),n=e&&e[2].toLowerCase(),r=h[n];if(void 0!==r){var a=e[1],o=e[3],i=o.match(/^style\\s*=\\s*\"([^\"]+)\"\\s*/i);if(\"a\"===n){if(a)return\"</a>\";if(\"href\"!==o.substr(0,4).toLowerCase())return\"<a>\";var l=o.substr(4).replace(/[\"']/g,\"\").replace(/=/,\"\").replace(/&/g,\"&amp;\"),c=document.createElement(\"a\");return c.href=l,-1===p.indexOf(c.protocol)?\"<a>\":'<a xlink:show=\"new\" xlink:href=\"'+l+'\">'}if(\"br\"===n)return\"<br>\";if(a)return\"sup\"===n?'</tspan><tspan dy=\"0.42em\">&#x200b;</tspan>':\"sub\"===n?'</tspan><tspan dy=\"-0.21em\">&#x200b;</tspan>':\"</tspan>\";var u=\"<tspan\";return\"sup\"!==n&&\"sub\"!==n||(u=\"&#x200b;\"+u),i&&(i=i[1].replace(/(^|;)\\s*color:/,\"$1 fill:\"),r=(r?r+\";\":\"\")+i),u+(r?' style=\"'+r+'\"':\"\")+\">\"}return s.util.xml_entity_encode(t).replace(/</g,\"&lt;\")}),r=[],a=n.indexOf(\"<br>\");a>0;a=n.indexOf(\"<br>\",a+1))r.push(a);var o=0;r.forEach(function(t){for(var e=t+o,r=n.slice(0,e),a=\"\",i=r.length-1;i>=0;i--){var l=r[i].match(/<(\\/?).*>/i);if(l&&\"<br>\"!==r[i]){l[1]||(a=r[i]);break}}a&&(n.splice(e+1,0,a),n.splice(e,0,\"</tspan>\"),o+=2)});var i=n.join(\"\"),l=i.split(/<br>/gi);return l.length>1&&(n=l.map(function(t,e){return'<tspan class=\"line\" dy=\"'+1.3*e+'em\">'+t+\"</tspan>\"})),n.join(\"\")}function l(t,e,n){var r,a,o,i=n.horizontalAlign,l=n.verticalAlign||\"top\",s=t.node().getBoundingClientRect(),c=e.node().getBoundingClientRect();return a=\"bottom\"===l?function(){return s.bottom-r.height}:\"middle\"===l?function(){return s.top+(s.height-r.height)/2}:function(){return s.top},o=\"right\"===i?function(){return s.right-r.width}:\"center\"===i?function(){return s.left+(s.width-r.width)/2}:function(){return s.left},function(){return r=this.node().getBoundingClientRect(),this.style({top:a()-c.top+\"px\",left:o()-c.left+\"px\",\"z-index\":1e3}),this}}var s=t(\"../plotly\"),c=t(\"d3\"),u=t(\"../lib\"),f=t(\"../constants/xmlns_namespaces\"),d=e.exports={};c.selection.prototype.appendSVG=function(t){for(var e=['<svg xmlns=\"',f.svg,'\" ','xmlns:xlink=\"',f.xlink,'\">',t,\"</svg>\"].join(\"\"),n=(new DOMParser).parseFromString(e,\"application/xml\"),r=n.documentElement.firstChild;r;)this.node().appendChild(this.node().ownerDocument.importNode(r,!0)),r=r.nextSibling;return n.querySelector(\"parsererror\")?(u.log(n.querySelector(\"parsererror div\").textContent),null):c.select(this.node().lastChild)},d.html_entity_decode=function(t){var e=c.select(\"body\").append(\"div\").style({display:\"none\"}).html(\"\"),n=t.replace(/(&[^;]*;)/gi,function(t){return\"&lt;\"===t?\"&#60;\":\"&rt;\"===t?\"&#62;\":e.html(t).text()});return e.remove(),n},d.xml_entity_encode=function(t){return t.replace(/&(?!\\w+;|\\#[0-9]+;| \\#x[0-9A-F]+;)/g,\"&amp;\")},d.convertToTspans=function(t,e){function n(){h.empty()||(p=u.attr(\"class\")+\"-math\",h.select(\"svg.\"+p).remove()),t.text(\"\").style({visibility:\"visible\",\"white-space\":\"pre\"}),d=t.appendSVG(l),d||t.text(a),t.select(\"a\").size()&&t.style(\"pointer-events\",\"all\"),e&&e.call(u)}var a=t.text(),l=i(a),u=t,f=!u.attr(\"data-notex\")&&l.match(/([^$]*)([$]+[^$]*[$]+)([^$]*)/),d=a,h=c.select(u.node().parentNode);if(!h.empty()){var p=u.attr(\"class\")?u.attr(\"class\").split(\" \")[0]:\"text\";p+=\"-math\",h.selectAll(\"svg.\"+p).remove(),h.selectAll(\"g.\"+p+\"-group\").remove(),t.style({visibility:null});for(var g=t.node();g&&g.removeAttribute;g=g.parentNode)g.removeAttribute(\"data-bb\");if(f){var v=s.Lib.getPlotDiv(u.node());(v&&v._promises||[]).push(new Promise(function(t){u.style({visibility:\"hidden\"});var a={fontSize:parseInt(u.style(\"font-size\"),10)};o(f[2],a,function(a,o,i){h.selectAll(\"svg.\"+p).remove(),h.selectAll(\"g.\"+p+\"-group\").remove();var l=a&&a.select(\"svg\");if(!l||!l.node())return n(),void t();var s=h.append(\"g\").classed(p+\"-group\",!0).attr({\"pointer-events\":\"none\"});s.node().appendChild(l.node()),o&&o.node()&&l.node().insertBefore(o.node().cloneNode(!0),l.node().firstChild),l.attr({\"class\":p,height:i.height,preserveAspectRatio:\"xMinYMin meet\"}).style({overflow:\"visible\",\"pointer-events\":\"none\"});var c=u.style(\"fill\")||\"black\";l.select(\"g\").attr({fill:c,stroke:c});var f=r(l,\"width\"),d=r(l,\"height\"),g=+u.attr(\"x\")-f*{start:0,middle:.5,end:1}[u.attr(\"text-anchor\")||\"start\"],v=parseInt(u.style(\"font-size\"),10)||r(u,\"height\"),m=-v/4;\"y\"===p[0]?(s.attr({transform:\"rotate(\"+[-90,+u.attr(\"x\"),+u.attr(\"y\")]+\") translate(\"+[-f/2,m-d/2]+\")\"}),l.attr({x:+u.attr(\"x\"),y:+u.attr(\"y\")})):\"l\"===p[0]?l.attr({x:u.attr(\"x\"),y:m-d/2}):\"a\"===p[0]?l.attr({x:0,y:m}):l.attr({x:g,y:+u.attr(\"y\")+m-d/2}),e&&e.call(u,s),t(s)})}))}else n();return t}};var h={sup:'font-size:70%\" dy=\"-0.6em',sub:'font-size:70%\" dy=\"0.3em',b:\"font-weight:bold\",i:\"font-style:italic\",a:\"\",span:\"\",br:\"\",em:\"font-style:italic;font-weight:bold\"},p=[\"http:\",\"https:\",\"mailto:\"],g=new RegExp(\"</?(\"+Object.keys(h).join(\"|\")+\")( [^>]*)?/?>\",\"g\");d.plainText=function(t){return(t||\"\").replace(g,\" \")},d.makeEditable=function(t,e,n){function r(){o(),i.style({opacity:0});var t,e=d.attr(\"class\");t=e?\".\"+e.split(\" \")[0]+\"-math-group\":\"[class*=-math-group]\",t&&c.select(i.node().parentNode).select(t).style({opacity:0})}function a(t){var e=t.node(),n=document.createRange();n.selectNodeContents(e);var r=window.getSelection();r.removeAllRanges(),r.addRange(n),e.focus()}function o(){var t=c.select(s.Lib.getPlotDiv(i.node())),e=t.select(\".svg-container\"),r=e.append(\"div\");r.classed(\"plugin-editable editable\",!0).style({position:\"absolute\",\"font-family\":i.style(\"font-family\")||\"Arial\",\"font-size\":i.style(\"font-size\")||12,color:n.fill||i.style(\"fill\")||\"black\",opacity:1,\"background-color\":n.background||\"transparent\",outline:\"#ffffff33 1px solid\",margin:[-parseFloat(i.style(\"font-size\"))/8+1,0,0,-1].join(\"px \")+\"px\",padding:\"0\",\"box-sizing\":\"border-box\"}).attr({contenteditable:!0}).text(n.text||i.attr(\"data-unformatted\")).call(l(i,e,n)).on(\"blur\",function(){i.text(this.textContent).style({opacity:1});var t,e=c.select(this).attr(\"class\");t=e?\".\"+e.split(\" \")[0]+\"-math-group\":\"[class*=-math-group]\",t&&c.select(i.node().parentNode).select(t).style({opacity:0});var n=this.textContent;c.select(this).transition().duration(0).remove(),c.select(document).on(\"mouseup\",null),u.edit.call(i,n)}).on(\"focus\",function(){var t=this;c.select(document).on(\"mouseup\",function(){return c.event.target===t?!1:void(document.activeElement===r.node()&&r.node().blur())})}).on(\"keyup\",function(){27===c.event.which?(i.style({opacity:1}),c.select(this).style({opacity:0}).on(\"blur\",function(){return!1}).transition().remove(),u.cancel.call(i,this.textContent)):(u.input.call(i,this.textContent),c.select(this).call(l(i,e,n)))}).on(\"keydown\",function(){13===c.event.which&&this.blur()}).call(a)}n||(n={});var i=this,u=c.dispatch(\"edit\",\"input\",\"cancel\"),f=c.select(this.node()).style({\"pointer-events\":\"all\"}),d=e||f;return e&&f.style({\"pointer-events\":\"none\"}),n.immediate?r():d.on(\"click\",r),c.rebind(this,u,\"on\")}},{\"../constants/xmlns_namespaces\":82,\"../lib\":89,\"../plotly\":107,d3:9}],101:[function(t,e,n){\"use strict\";function r(t){var e;if(\"string\"==typeof t){if(e=document.getElementById(t),null===e)throw new Error(\"No DOM element with id '\"+t+\"' exists on the page.\");return e}if(null===t||void 0===t)throw new Error(\"DOM element provided is null or undefined\");return t}function a(t,e){t._fullLayout._paperdiv.style(\"background\",\"white\"),O.defaultConfig.setBackground(t,e)}function o(t,e){t._context||(t._context=P.extendFlat({},O.defaultConfig));var n=t._context;e&&(Object.keys(e).forEach(function(t){t in n&&(\"setBackground\"===t&&\"opaque\"===e[t]?n[t]=a:n[t]=e[t])}),e.plot3dPixelRatio&&!n.plotGlPixelRatio&&(n.plotGlPixelRatio=n.plot3dPixelRatio)),n.staticPlot&&(n.editable=!1,n.autosizable=!1,n.scrollZoom=!1,n.doubleClick=!1,n.showTips=!1,n.showLink=!1,n.displayModeBar=!1)}function i(t,e,n){var r=S.select(t).selectAll(\".plot-container\").data([0]);r.enter().insert(\"div\",\":first-child\").classed(\"plot-container plotly\",!0);var a=r.selectAll(\".svg-container\").data([0]);a.enter().append(\"div\").classed(\"svg-container\",!0).style(\"position\",\"relative\"),a.html(\"\"),e&&(t.data=e),n&&(t.layout=n),O.micropolar.manager.fillLayout(t),\"initial\"===t._fullLayout.autosize&&t._context.autosizable&&(w(t,{}),t._fullLayout.autosize=n.autosize=!0),a.style({width:t._fullLayout.width+\"px\",height:t._fullLayout.height+\"px\"}),t.framework=O.micropolar.manager.framework(t),t.framework({data:t.data,layout:"
+,
+"t.layout},a.node()),t.framework.setUndoPoint();var o=t.framework.svg(),i=1,l=t._fullLayout.title;\"\"!==l&&l||(i=0);var s=\"Click to enter title\",c=function(){this.call(O.util.convertToTspans)},u=o.select(\".title-group text\").call(c);if(t._context.editable){u.attr({\"data-unformatted\":l}),l&&l!==s||(i=.2,u.attr({\"data-unformatted\":s}).text(s).style({opacity:i}).on(\"mouseover.opacity\",function(){S.select(this).transition().duration(100).style(\"opacity\",1)}).on(\"mouseout.opacity\",function(){S.select(this).transition().duration(1e3).style(\"opacity\",0)}));var f=function(){this.call(O.util.makeEditable).on(\"edit\",function(e){t.framework({layout:{title:e}}),this.attr({\"data-unformatted\":e}).text(e).call(c),this.call(f)}).on(\"cancel\",function(){var t=this.attr(\"data-unformatted\");this.text(t).call(c)})};u.call(f)}return t._context.setBackground(t,t._fullLayout.paper_bgcolor),I.addLinks(t),Promise.resolve()}function l(t){var e,n;t||(t={}),t.xaxis1&&(t.xaxis||(t.xaxis=t.xaxis1),delete t.xaxis1),t.yaxis1&&(t.yaxis||(t.yaxis=t.yaxis1),delete t.yaxis1);var r=O.Axes.list({_fullLayout:t});for(e=0;e<r.length;e++){var a=r[e];a.anchor&&\"free\"!==a.anchor&&(a.anchor=O.Axes.cleanId(a.anchor)),a.overlaying&&(a.overlaying=O.Axes.cleanId(a.overlaying)),a.type||(a.isdate?a.type=\"date\":a.islog?a.type=\"log\":a.isdate===!1&&a.islog===!1&&(a.type=\"linear\")),\"withzero\"!==a.autorange&&\"tozero\"!==a.autorange||(a.autorange=!0,a.rangemode=\"tozero\"),delete a.islog,delete a.isdate,delete a.categories,f(a,\"domain\")&&delete a.domain,void 0!==a.autotick&&(void 0===a.tickmode&&(a.tickmode=a.autotick?\"auto\":\"linear\"),delete a.autotick)}void 0===t.annotations||Array.isArray(t.annotations)||(P.warn(\"Annotations must be an array.\"),delete t.annotations);var o=(t.annotations||[]).length;for(e=0;o>e;e++){var i=t.annotations[e];i.ref&&(\"paper\"===i.ref?(i.xref=\"paper\",i.yref=\"paper\"):\"data\"===i.ref&&(i.xref=\"x\",i.yref=\"y\"),delete i.ref),s(i,\"xref\"),s(i,\"yref\")}void 0===t.shapes||Array.isArray(t.shapes)||(P.warn(\"Shapes must be an array.\"),delete t.shapes);var l=(t.shapes||[]).length;for(e=0;l>e;e++){var c=t.shapes[e];s(c,\"xref\"),s(c,\"yref\")}var u=t.legend;u&&(u.x>3?(u.x=1.02,u.xanchor=\"left\"):u.x<-2&&(u.x=-.02,u.xanchor=\"right\"),u.y>3?(u.y=1.02,u.yanchor=\"bottom\"):u.y<-2&&(u.y=-.02,u.yanchor=\"top\")),\"rotate\"===t.dragmode&&(t.dragmode=\"orbit\"),t.scene1&&(t.scene||(t.scene=t.scene1),delete t.scene1);var d=I.getSubplotIds(t,\"gl3d\");for(e=0;e<d.length;e++){var h=t[d[e]],p=h.cameraposition;if(Array.isArray(p)&&4===p[0].length){var g=p[0],v=p[1],m=p[2],y=E([],g),x=[];for(n=0;3>n;++n)x[n]=v[e]+m*y[2+4*n];h.camera={eye:{x:x[0],y:x[1],z:x[2]},center:{x:v[0],y:v[1],z:v[2]},up:{x:y[1],y:y[5],z:y[9]}},delete h.cameraposition}}return j.clean(t),t}function s(t,e){var n=t[e],r=e.charAt(0);n&&\"paper\"!==n&&(t[e]=O.Axes.cleanId(n,r))}function c(t,e){for(var n=[],r=(t.concat(Array.isArray(e)?e:[]).filter(function(t){return\"uid\"in t}).map(function(t){return t.uid})),a=0;a<t.length;a++){var o,i=t[a];if(!(\"uid\"in i)||-1!==n.indexOf(i.uid)){var l;for(o=0;100>o&&(l=P.randstr(r),-1!==n.indexOf(l));o++);i.uid=P.randstr(r),r.push(i.uid)}if(n.push(i.uid),\"histogramy\"===i.type&&\"xbins\"in i&&!(\"ybins\"in i)&&(i.ybins=i.xbins,delete i.xbins),i.error_y&&\"opacity\"in i.error_y){var s=j.defaults,c=i.error_y.color||(I.traceIs(i,\"bar\")?j.defaultLine:s[a%s.length]);i.error_y.color=j.addOpacity(j.rgb(c),j.opacity(c)*i.error_y.opacity),delete i.error_y.opacity}if(\"bardir\"in i&&(\"h\"!==i.bardir||!I.traceIs(i,\"bar\")&&\"histogram\"!==i.type.substr(0,9)||(i.orientation=\"h\",b(i)),delete i.bardir),\"histogramy\"===i.type&&b(i),\"histogramx\"!==i.type&&\"histogramy\"!==i.type||(i.type=\"histogram\"),\"scl\"in i&&(i.colorscale=i.scl,delete i.scl),\"reversescl\"in i&&(i.reversescale=i.reversescl,delete i.reversescl),i.xaxis&&(i.xaxis=O.Axes.cleanId(i.xaxis,\"x\")),i.yaxis&&(i.yaxis=O.Axes.cleanId(i.yaxis,\"y\")),I.traceIs(i,\"gl3d\")&&i.scene&&(i.scene=I.subplotsRegistry.gl3d.cleanId(i.scene)),I.traceIs(i,\"pie\")||(Array.isArray(i.textposition)?i.textposition=i.textposition.map(u):i.textposition&&(i.textposition=u(i.textposition))),I.traceIs(i,\"2dMap\")&&(\"YIGnBu\"===i.colorscale&&(i.colorscale=\"YlGnBu\"),\"YIOrRd\"===i.colorscale&&(i.colorscale=\"YlOrRd\")),I.traceIs(i,\"markerColorscale\")&&i.marker){var d=i.marker;\"YIGnBu\"===d.colorscale&&(d.colorscale=\"YlGnBu\"),\"YIOrRd\"===d.colorscale&&(d.colorscale=\"YlOrRd\")}if(\"surface\"===i.type&&P.isPlainObject(i.contours)){var h=[\"x\",\"y\",\"z\"];for(o=0;o<h.length;o++){var p=i.contours[h[o]];P.isPlainObject(p)&&(p.highlightColor&&(p.highlightcolor=p.highlightColor,delete p.highlightColor),p.highlightWidth&&(p.highlightwidth=p.highlightWidth,delete p.highlightWidth))}}f(i,\"line\")&&delete i.line,\"marker\"in i&&(f(i.marker,\"line\")&&delete i.marker.line,f(i,\"marker\")&&delete i.marker),j.clean(i)}}function u(t){var e=\"middle\",n=\"center\";return-1!==t.indexOf(\"top\")?e=\"top\":-1!==t.indexOf(\"bottom\")&&(e=\"bottom\"),-1!==t.indexOf(\"left\")?n=\"left\":-1!==t.indexOf(\"right\")&&(n=\"right\"),e+\" \"+n}function f(t,e){return e in t&&\"object\"==typeof t[e]&&0===Object.keys(t[e]).length}function d(t){var e,n=O.Axes.list(t),r=t._fullData,a=t._fullLayout,o=t.calcdata=new Array(r.length);for(t.firstscatter=!0,t.numboxes=0,t._hmpixcount=0,t._hmlumcount=0,a._piecolormap={},a._piedefaultcolorcount=0,e=0;e<n.length;e++)n[e]._categories=n[e]._initialCategories.slice();for(e=0;e<r.length;e++){var i=r[e],l=i._module,s=[];l&&i.visible===!0&&l.calc&&(s=l.calc(t,i)),Array.isArray(s)&&s[0]||(s=[{x:!1,y:!1}]),s[0].t||(s[0].t={}),s[0].trace=i,o[e]=s}}function h(t,e){var n,r,a=e+1,o=[];for(n=0;n<t.length;n++)r=t[n],0>r?o.push(a+r):o.push(r);return o}function p(t,e,n){var r,a;for(r=0;r<e.length;r++){if(a=e[r],a!==parseInt(a,10))throw new Error(\"all values in \"+n+\" must be integers\");if(a>=t.data.length||a<-t.data.length)throw new Error(n+\" must be valid indices for gd.data.\");if(e.indexOf(a,r+1)>-1||a>=0&&e.indexOf(-t.data.length+a)>-1||0>a&&e.indexOf(t.data.length+a)>-1)throw new Error(\"each index in \"+n+\" must be unique.\")}}function g(t,e,n){if(!Array.isArray(t.data))throw new Error(\"gd.data must be an array.\");if(\"undefined\"==typeof e)throw new Error(\"currentIndices is a required argument.\");if(Array.isArray(e)||(e=[e]),p(t,e,\"currentIndices\"),\"undefined\"==typeof n||Array.isArray(n)||(n=[n]),\"undefined\"!=typeof n&&p(t,n,\"newIndices\"),\"undefined\"!=typeof n&&e.length!==n.length)throw new Error(\"current and new indices must be of equal length.\")}function v(t,e,n){var r,a;if(!Array.isArray(t.data))throw new Error(\"gd.data must be an array.\");if(\"undefined\"==typeof e)throw new Error(\"traces must be defined.\");for(Array.isArray(e)||(e=[e]),r=0;r<e.length;r++)if(a=e[r],\"object\"!=typeof a||Array.isArray(a)||null===a)throw new Error(\"all values in traces array must be non-array objects\");if(\"undefined\"==typeof n||Array.isArray(n)||(n=[n]),\"undefined\"!=typeof n&&n.length!==e.length)throw new Error(\"if indices is specified, traces.length must equal indices.length\")}function m(t,e,n,r){var a=P.isPlainObject(r);if(!Array.isArray(t.data))throw new Error(\"gd.data must be an array\");if(!P.isPlainObject(e))throw new Error(\"update must be a key:value object\");if(\"undefined\"==typeof n)throw new Error(\"indices must be an integer or array of integers\");p(t,n,\"indices\");for(var o in e){if(!Array.isArray(e[o])||e[o].length!==n.length)throw new Error(\"attribute \"+o+\" must be an array of length equal to indices array length\");if(a&&(!(o in r)||!Array.isArray(r[o])||r[o].length!==e[o].length))throw new Error(\"when maxPoints is set as a key:value object it must contain a 1:1 corrispondence with the keys and number of traces in the update object\")}}function y(t,e,n,r){var a,o,i,l,s,c=P.isPlainObject(r),u=[];Array.isArray(n)||(n=[n]),n=h(n,t.data.length-1);for(var f in e)for(var d=0;d<n.length;d++){if(a=t.data[n[d]],i=P.nestedProperty(a,f),o=i.get(),l=e[f][d],!Array.isArray(l))throw new Error(\"attribute: \"+f+\" index: \"+d+\" must be an array\");if(!Array.isArray(o))throw new Error(\"cannot extend missing or non-array attribute: \"+f);s=c?r[f][d]:r,C(s)||(s=-1),u.push({prop:i,target:o,insert:l,maxp:Math.floor(s)})}return u}function x(t,e,n,r,a,o){m(t,e,n,r);for(var i,l,s,c=y(t,e,n,r),u=[],f={},d={},h=0;h<c.length;h++)l=c[h].prop,s=c[h].maxp,i=a(c[h].target,c[h].insert),s>=0&&s<i.length&&(u=o(i,s)),s=c[h].target.length,l.set(i),Array.isArray(f[l.astr])||(f[l.astr]=[]),Array.isArray(d[l.astr])||(d[l.astr]=[]),f[l.astr].push(u),d[l.astr].push(s);return{update:f,maxPoints:d}}function b(t){var e;if(P.swapAttrs(t,[\"?\",\"?0\",\"d?\",\"?bins\",\"nbins?\",\"autobin?\",\"?src\",\"error_?\"]),Array.isArray(t.z)&&Array.isArray(t.z[0])&&(t.transpose?delete t.transpose:t.transpose=!0),t.error_x&&t.error_y){var n=t.error_y,r=\"copy_ystyle\"in n?n.copy_ystyle:!(n.color||n.thickness||n.width);P.swapAttrs(t,[\"error_?.copy_ystyle\"]),r&&P.swapAttrs(t,[\"error_?.color\",\"error_?.thickness\",\"error_?.width\"])}if(t.hoverinfo){var a=t.hoverinfo.split(\"+\");for(e=0;e<a.length;e++)\"x\"===a[e]?a[e]=\"y\":\"y\"===a[e]&&(a[e]=\"x\");t.hoverinfo=a.join(\"+\")}}function _(t){var e,n={left:0,right:0,bottom:0,top:0};if(t)for(e in t)t.hasOwnProperty(e)&&(n.left+=t[e].left||0,n.right+=t[e].right||0,n.bottom+=t[e].bottom||0,n.top+=t[e].top||0);return n}function w(t,e){var n,r,a,o=t._fullLayout,i=t._context;if(t.emit(\"plotly_autosize\"),t._context.fillFrame)a=window.innerWidth,r=window.innerHeight,document.body.style.overflow=\"hidden\";else if(C(i.frameMargins)&&i.frameMargins>0){var l=_(t._boundingBoxMargins),s=l.left+l.right,c=l.bottom+l.top,u=o._container.node().getBoundingClientRect(),f=1-2*i.frameMargins;a=Math.round(f*(u.width-s)),r=Math.round(f*(u.height-c))}else n=window.getComputedStyle(t),r=parseFloat(n.height)||o.height,a=parseFloat(n.width)||o.width;return Math.abs(o.width-a)>1||Math.abs(o.height-r)>1?(o.height=t.layout.height=r,o.width=t.layout.width=a):\"initial\"!==o.autosize&&(delete e.autosize,o.autosize=t.layout.autosize=!0),I.sanitizeMargins(o),e}function k(t){var e=S.select(t),n=t._fullLayout;if(n._container=e.selectAll(\".pl"
+,
+"ot-container\").data([0]),n._container.enter().insert(\"div\",\":first-child\").classed(\"plot-container\",!0).classed(\"plotly\",!0),n._paperdiv=n._container.selectAll(\".svg-container\").data([0]),n._paperdiv.enter().append(\"div\").classed(\"svg-container\",!0).style(\"position\",\"relative\"),\"initial\"===n.autosize&&(w(t,{}),n.autosize=!0,t.layout.autosize=!0),n._glcontainer=n._paperdiv.selectAll(\".gl-container\").data([0]),n._glcontainer.enter().append(\"div\").classed(\"gl-container\",!0),n._geocontainer=n._paperdiv.selectAll(\".geo-container\").data([0]),n._geocontainer.enter().append(\"div\").classed(\"geo-container\",!0),n._paperdiv.selectAll(\".main-svg\").remove(),n._paper=n._paperdiv.insert(\"svg\",\":first-child\").classed(\"main-svg\",!0),n._toppaper=n._paperdiv.append(\"svg\").classed(\"main-svg\",!0),!n._uid){var r=[];S.selectAll(\"defs\").each(function(){this.id&&r.push(this.id.split(\"-\")[1])}),n._uid=P.randstr(r)}n._paperdiv.selectAll(\".main-svg\").attr(G.svgAttrs),n._defs=n._paper.append(\"defs\").attr(\"id\",\"defs-\"+n._uid),n._topdefs=n._toppaper.append(\"defs\").attr(\"id\",\"topdefs-\"+n._uid),n._draggers=n._paper.append(\"g\").classed(\"draglayer\",!0);var a=n._paper.append(\"g\").classed(\"layer-below\",!0);n._imageLowerLayer=a.append(\"g\").classed(\"imagelayer\",!0),n._shapeLowerLayer=a.append(\"g\").classed(\"shapelayer\",!0);var o=O.Axes.getSubplots(t);o.join(\"\")!==Object.keys(t._fullLayout._plots||{}).join(\"\")&&M(t,o),n._has(\"cartesian\")&&A(t,o),n._ternarylayer=n._paper.append(\"g\").classed(\"ternarylayer\",!0);var i=n._paper.selectAll(\".layer-subplot\");n._imageSubplotLayer=i.selectAll(\".imagelayer\"),n._shapeSubplotLayer=i.selectAll(\".shapelayer\");var l=n._paper.append(\"g\").classed(\"layer-above\",!0);n._imageUpperLayer=l.append(\"g\").classed(\"imagelayer\",!0),n._shapeUpperLayer=l.append(\"g\").classed(\"shapelayer\",!0),n._pielayer=n._paper.append(\"g\").classed(\"pielayer\",!0),n._glimages=n._paper.append(\"g\").classed(\"glimages\",!0),n._geoimages=n._paper.append(\"g\").classed(\"geoimages\",!0),n._infolayer=n._toppaper.append(\"g\").classed(\"infolayer\",!0),\n"
+,
+"n._zoomlayer=n._toppaper.append(\"g\").classed(\"zoomlayer\",!0),n._hoverlayer=n._toppaper.append(\"g\").classed(\"hoverlayer\",!0),t.emit(\"plotly_framework\");var s=P.syncOrAsync([L,function(){return O.Axes.doTicks(t,\"redraw\")},R.init],t);return s&&s.then&&t._promises.push(s),s}function M(t,e){function n(e,n){return function(){return O.Axes.getFromId(t,e,n)}}for(var r,a,o=t._fullLayout._plots={},i=0;i<e.length;i++)r=e[i],a=o[r]={},a.id=r,a.x=n(r,\"x\"),a.y=n(r,\"y\"),a.xaxis=a.x(),a.yaxis=a.y()}function A(t,e){function n(t){t.append(\"g\").classed(\"imagelayer\",!0),t.append(\"g\").classed(\"maplayer\",!0),t.append(\"g\").classed(\"barlayer\",!0),t.append(\"g\").classed(\"boxlayer\",!0),t.append(\"g\").classed(\"scatterlayer\",!0)}var r=t._fullLayout,a=[];r._paper.selectAll(\"g.subplot\").data(e).enter().append(\"g\").classed(\"subplot\",!0).each(function(o){var i=r._plots[o],l=i.plotgroup=S.select(this).classed(o,!0),s=i.xaxis,c=i.yaxis;i.overlays=[];var u=O.Axes.getFromId(t,s.overlaying)||s;u!==s&&u.overlaying&&(u=s,s.overlaying=!1);var f=O.Axes.getFromId(t,c.overlaying)||c;f!==c&&f.overlaying&&(f=c,c.overlaying=!1);var d=u._id+f._id;if(d!==o&&-1!==e.indexOf(d))i.mainplot=d,a.push(i),s.domain=u.domain.slice(),c.domain=f.domain.slice();else{i.bg=l.append(\"rect\").style(\"stroke-width\",0);var h=l.append(\"g\").classed(\"layer-subplot\",!0);i.shapelayer=h.append(\"g\").classed(\"shapelayer\",!0),i.imagelayer=h.append(\"g\").classed(\"imagelayer\",!0),i.gridlayer=l.append(\"g\"),i.overgrid=l.append(\"g\"),i.zerolinelayer=l.append(\"g\"),i.overzero=l.append(\"g\"),i.plot=l.append(\"g\").call(n),i.overplot=l.append(\"g\"),i.xlines=l.append(\"path\"),i.ylines=l.append(\"path\"),i.overlines=l.append(\"g\"),i.xaxislayer=l.append(\"g\"),i.yaxislayer=l.append(\"g\"),i.overaxes=l.append(\"g\")}i.draglayer=r._draggers.append(\"g\")}),a.forEach(function(t){var e=r._plots[t.mainplot];e.overlays.push(t),t.gridlayer=e.overgrid.append(\"g\"),t.zerolinelayer=e.overzero.append(\"g\"),t.plot=e.overplot.append(\"g\").call(n),t.xlines=e.overlines.append(\"path\"),t.ylines=e.overlines.append(\"path\"),t.xaxislayer=e.overaxes.append(\"g\"),t.yaxislayer=e.overaxes.append(\"g\")}),e.forEach(function(t){var e=r._plots[t];e.xlines.style(\"fill\",\"none\").classed(\"crisp\",!0),e.ylines.style(\"fill\",\"none\").classed(\"crisp\",!0)})}function L(t){return P.syncOrAsync([I.doAutoMargin,T],t)}function T(t){var e,n=t._fullLayout,r=n._size,a=O.Axes.list(t);for(e=0;e<a.length;e++)a[e]._linepositions={};n._paperdiv.style({width:n.width+\"px\",height:n.height+\"px\"}).selectAll(\".main-svg\").call(q.setSize,n.width,n.height),t._context.setBackground(t,n.paper_bgcolor);var o=[];return n._paper.selectAll(\"g.subplot\").each(function(e){var a=n._plots[e],i=O.Axes.getFromId(t,e,\"x\"),l=O.Axes.getFromId(t,e,\"y\");i.setScale(),l.setScale(),a.bg&&a.bg.call(q.setRect,i._offset-r.p,l._offset-r.p,i._length+2*r.p,l._length+2*r.p).call(j.fill,n.plot_bgcolor),a.clipId=\"clip\"+n._uid+e+\"plot\";var s=n._defs.selectAll(\"g.clips\").selectAll(\"#\"+a.clipId).data([0]);s.enter().append(\"clipPath\").attr({\"class\":\"plotclip\",id:a.clipId}).append(\"rect\"),s.selectAll(\"rect\").attr({width:i._length,height:l._length}),a.plot.call(P.setTranslate,i._offset,l._offset),a.plot.call(q.setClipUrl,a.clipId);var c=q.crispRound(t,i.linewidth,1),u=q.crispRound(t,l.linewidth,1),f=r.p+u,d=\"M\"+-f+\",\",h=\"h\"+(i._length+2*f),p=\"free\"===i.anchor&&-1===o.indexOf(i._id),g=r.h*(1-(i.position||0))+c/2%1,v=i.anchor===l._id&&(i.mirror||\"top\"!==i.side)||\"all\"===i.mirror||\"allticks\"===i.mirror||i.mirrors&&i.mirrors[l._id+\"bottom\"],m=l._length+r.p+c/2,y=i.anchor===l._id&&(i.mirror||\"top\"===i.side)||\"all\"===i.mirror||\"allticks\"===i.mirror||i.mirrors&&i.mirrors[l._id+\"top\"],x=-r.p-c/2,b=r.p,_=v?0:c,w=y?0:c,k=\",\"+(-b-w)+\"v\"+(l._length+2*b+w+_),M=\"free\"===l.anchor&&-1===o.indexOf(l._id),A=r.w*(l.position||0)+u/2%1,L=l.anchor===i._id&&(l.mirror||\"right\"!==l.side)||\"all\"===l.mirror||\"allticks\"===l.mirror||l.mirrors&&l.mirrors[i._id+\"left\"],T=-r.p-u/2,z=l.anchor===i._id&&(l.mirror||\"right\"===l.side)||\"all\"===l.mirror||\"allticks\"===l.mirror||l.mirrors&&l.mirrors[i._id+\"right\"],S=i._length+r.p+u/2;i._linepositions[e]=[v?m:void 0,y?x:void 0,p?g:void 0],i.anchor===l._id?i._linepositions[e][3]=\"top\"===i.side?x:m:p&&(i._linepositions[e][3]=g),l._linepositions[e]=[L?T:void 0,z?S:void 0,M?A:void 0],l.anchor===i._id?l._linepositions[e][3]=\"right\"===l.side?S:T:M&&(l._linepositions[e][3]=A);var E=\"translate(\"+i._offset+\",\"+l._offset+\")\",C=E,N=E;p&&(C=\"translate(\"+i._offset+\",\"+r.t+\")\",x+=l._offset-r.t,m+=l._offset-r.t),M&&(N=\"translate(\"+r.l+\",\"+l._offset+\")\",T+=i._offset-r.l,S+=i._offset-r.l),a.xlines.attr(\"transform\",C).attr(\"d\",(v?d+m+h:\"\")+(y?d+x+h:\"\")+(p?d+g+h:\"\")||\"M0,0\").style(\"stroke-width\",c+\"px\").call(j.stroke,i.showline?i.linecolor:\"rgba(0,0,0,0)\"),a.ylines.attr(\"transform\",N).attr(\"d\",(L?\"M\"+T+k:\"\")+(z?\"M\"+S+k:\"\")+(M?\"M\"+A+k:\"\")||\"M0,0\").attr(\"stroke-width\",u+\"px\").call(j.stroke,l.showline?l.linecolor:\"rgba(0,0,0,0)\"),a.xaxislayer.attr(\"transform\",C),a.yaxislayer.attr(\"transform\",N),a.gridlayer.attr(\"transform\",E),a.zerolinelayer.attr(\"transform\",E),a.draglayer.attr(\"transform\",E),p&&o.push(i._id),M&&o.push(l._id)}),O.Axes.makeClipPaths(t),z(t),X(t),t._promises.length&&Promise.all(t._promises)}function z(t){var e=t._fullLayout;U.draw(t,\"gtitle\",{propContainer:e,propName:\"title\",dfltName:\"Plot\",attributes:{x:e.width/2,y:e._size.t/2,\"text-anchor\":\"middle\"}})}var S=t(\"d3\"),E=t(\"gl-mat4/fromQuat\"),C=t(\"fast-isnumeric\"),O=t(\"../plotly\"),P=t(\"../lib\"),N=t(\"../lib/events\"),D=t(\"../lib/queue\"),I=t(\"../plots/plots\"),R=t(\"../plots/cartesian/graph_interact\"),j=t(\"../components/color\"),q=t(\"../components/drawing\"),F=t(\"../components/errorbars\"),B=t(\"../components/images\"),H=t(\"../components/legend\"),V=t(\"../components/rangeslider\"),Z=t(\"../components/rangeselector\"),Y=t(\"../components/shapes\"),U=t(\"../components/titles\"),X=t(\"../components/modebar/manage\"),G=t(\"../constants/xmlns_namespaces\");O.plot=function(t,e,n,a){function s(){var e,n,r,a=t.calcdata;for(H.draw(t),Z.draw(t),e=0;e<a.length;e++)n=a[e],r=n[0].trace,r.visible===!0&&r._module.colorbar?r._module.colorbar(t,n):I.autoMargin(t,\"cb\"+r.uid);return I.doAutoMargin(t),I.previousPromises(t)}function u(){var e=JSON.stringify(A._size)===E?[]:[s,L];return P.syncOrAsync(e.concat(R.init),t)}function f(){if(T){for(var e,n,r=I.getSubplotIds(A,\"cartesian\"),a=A._modules,o=0;o<r.length;o++){e=A._plots[r[o]];for(var i=0;i<a.length;i++)n=a[i],n.setPositions&&n.setPositions(t,e)}return F.calc(t),P.syncOrAsync([Y.calcAutorange,O.Annotations.calcAutorange,h],t)}}function h(){for(var e=O.Axes.list(t,\"\",!0),n=0;n<e.length;n++)O.Axes.doAutoRange(e[n])}function p(){return O.Axes.doTicks(t,\"redraw\")}function g(){var e,n=t.calcdata;for(e=0;e<n.length;e++){var r=n[e][0].trace,a=r.visible===!0,o=r.uid;a&&I.traceIs(r,\"2dMap\")||A._paper.selectAll(\".hm\"+o+\",.contour\"+o+\",#clip\"+o).remove(),a&&r._module.colorbar||A._infolayer.selectAll(\".cb\"+o).remove()}var i=A._basePlotModules;for(e=0;e<i.length;e++)i[e].plot(t);return I.style(t),Y.drawAll(t),O.Annotations.drawAll(t),I.addLinks(t),t._replotting=!1,I.previousPromises(t)}function v(){Y.drawAll(t),B.draw(t),O.Annotations.drawAll(t),H.draw(t),V.draw(t),Z.draw(t)}function m(){t.emit(\"plotly_afterplot\")}t=r(t),N.init(t);var y=N.triggerHandler(t,\"plotly_beforeplot\",[e,n,a]);if(y===!1)return Promise.reject();e||n||P.isPlotDiv(t)||P.warn(\"Calling Plotly.plot as if redrawing but this container doesn't yet have a plot.\",t),o(t,a),n||(n={}),S.select(t).classed(\"js-plotly-plot\",!0),q.makeTester(t),t._promises=[];var x=0===(t.data||[]).length&&Array.isArray(e);if(Array.isArray(e)&&(c(e,t.data),x?t.data=e:t.data.push.apply(t.data,e),t.empty=!1),t.layout&&!x||(t.layout=l(n)),t._dragging)return t._replotPending=!0,Promise.reject();if(t._replotPending=!1,I.supplyDefaults(t),e&&e[0]&&e[0].r)return i(t,e,n);t._replotting=!0;var b=t._fullData.length>0,_=O.Axes.getSubplots(t).join(\"\"),w=Object.keys(t._fullLayout._plots||{}).join(\"\"),M=w===_;b?t.framework===k&&!x&&M||(t.framework=k,k(t)):M?x&&k(t):(t.framework=k,k(t)),x&&O.Axes.saveRangeInitial(t);var A=t._fullLayout,T=!t.calcdata||t.calcdata.length!==(t.data||[]).length;T&&d(t);for(var z=0;z<t.calcdata.length;z++)t.calcdata[z][0].trace=t._fullData[z];var E=JSON.stringify(A._size);return P.syncOrAsync([I.previousPromises,s,u,f,L,p,g,v],t,m),Promise.all(t._promises).then(function(){return t},function(){P.log(\"Clearing previous rejected promises from queue.\"),t._promises=[]})},O.redraw=function(t){return t=r(t),P.isPlotDiv(t)?(t.calcdata=void 0,O.plot(t).then(function(){return t.emit(\"plotly_redraw\"),t})):void P.warn(\"This element is not a Plotly plot.\",t)},O.newPlot=function(t,e,n,a){return t=r(t),I.cleanPlot([],{},t._fullData||{},t._fullLayout||{}),I.purge(t),O.plot(t,e,n,a)},O.extendTraces=function $(t,e,n,a){t=r(t);var o=x(t,e,n,a,function(t,e){return t.concat(e)},function(t,e){return t.splice(0,t.length-e)}),i=O.redraw(t),l=[t,o.update,n,o.maxPoints];return D.add(t,O.prependTraces,l,$,arguments),i},O.prependTraces=function Q(t,e,n,a){t=r(t);var o=x(t,e,n,a,function(t,e){return e.concat(t)},function(t,e){return t.splice(e,t.length)}),i=O.redraw(t),l=[t,o.update,n,o.maxPoints];return D.add(t,O.extendTraces,l,Q,arguments),i},O.addTraces=function W(t,e,n){t=r(t);var a,o,i=[],l=O.deleteTraces,s=W,u=[t,i],f=[t,e];for(v(t,e,n),Array.isArray(e)||(e=[e]),c(e,t.data),a=0;a<e.length;a+=1)t.data.push(e[a]);for(a=0;a<e.length;a++)i.push(-e.length+a);if(\"undefined\"==typeof n)return o=O.redraw(t),D.add(t,l,u,s,f),o;Array.isArray(n)||(n=[n]);try{g(t,i,n)}catch(d){throw t.data.splice(t.data.length-e.length,e.length),d}return D.startSequence(t),D.add(t,l,u,s,f),o=O.moveTraces(t,i,n),D.stopSequence(t),o},O.deleteTraces=function J(t,e){t=r(t);var n,a,o=[],i=O.addTraces,l=J,s=[t,o,e],c=[t,e];if(\"undefined\"==typeof e)throw new Error(\"indices must be an integer or array of integers.\");for(Array.isArray(e)||(e=[e]),p(t,e,\"indices\"),e=h(e,t.data.length-1),e.sort(P.sorterDes),n=0;n<e.length;n+=1)a=t.data.splice(e[n],1)[0],o.push(a);var u=O.redraw("
+,
+"t);return D.add(t,i,s,l,c),u},O.moveTraces=function K(t,e,n){t=r(t);var a,o=[],i=[],l=K,s=K,c=[t,n,e],u=[t,e,n];if(g(t,e,n),e=Array.isArray(e)?e:[e],\"undefined\"==typeof n)for(n=[],a=0;a<e.length;a++)n.push(-e.length+a);for(n=Array.isArray(n)?n:[n],e=h(e,t.data.length-1),n=h(n,t.data.length-1),a=0;a<t.data.length;a++)-1===e.indexOf(a)&&o.push(t.data[a]);for(a=0;a<e.length;a++)i.push({newIndex:n[a],trace:t.data[e[a]]});for(i.sort(function(t,e){return t.newIndex-e.newIndex}),a=0;a<i.length;a+=1)o.splice(i[a].newIndex,0,i[a].trace);t.data=o;var f=O.redraw(t);return D.add(t,l,c,s,u),f},O.restyle=function tt(t,e,n,a){function o(){return a.map(function(){})}function i(t){var e=O.Axes.id2name(t);-1===p.indexOf(e)&&p.push(e)}function l(t){return\"LAYOUT\"+t+\".autorange\"}function s(t){return\"LAYOUT\"+t+\".range\"}function c(e,n,r){if(Array.isArray(e))return void e.forEach(function(t){c(t,n,r)});if(!(e in d)){var i;i=\"LAYOUT\"===e.substr(0,6)?P.nestedProperty(t.layout,e.replace(\"LAYOUT\",\"\")):P.nestedProperty(t.data[a[r]],e),e in L||(L[e]=o()),void 0===L[e][r]&&(L[e][r]=i.get()),void 0!==n&&i.set(n)}}t=r(t);var u,f=t._fullLayout,d={};if(\"string\"==typeof e)d[e]=n;else{if(!P.isPlainObject(e))return P.warn(\"Restyle fail.\",e,n,a),Promise.reject();d=e,void 0===a&&(a=n)}Object.keys(d).length&&(t.changed=!0),C(a)?a=[a]:Array.isArray(a)&&a.length||(a=t.data.map(function(t,e){return e}));var h=[\"mode\",\"visible\",\"type\",\"orientation\",\"fill\",\"histfunc\",\"histnorm\",\"text\",\"x\",\"y\",\"z\",\"a\",\"b\",\"c\",\"xtype\",\"x0\",\"dx\",\"ytype\",\"y0\",\"dy\",\"xaxis\",\"yaxis\",\"line.width\",\"connectgaps\",\"transpose\",\"zsmooth\",\"showscale\",\"marker.showscale\",\"zauto\",\"marker.cauto\",\"autocolorscale\",\"marker.autocolorscale\",\"colorscale\",\"marker.colorscale\",\"reversescale\",\"marker.reversescale\",\"autobinx\",\"nbinsx\",\"xbins\",\"xbins.start\",\"xbins.end\",\"xbins.size\",\"autobiny\",\"nbinsy\",\"ybins\",\"ybins.start\",\"ybins.end\",\"ybins.size\",\"autocontour\",\"ncontours\",\"contours\",\"contours.coloring\",\"error_y\",\"error_y.visible\",\"error_y.value\",\"error_y.type\",\"error_y.traceref\",\"error_y.array\",\"error_y.symmetric\",\"error_y.arrayminus\",\"error_y.valueminus\",\"error_y.tracerefminus\",\"error_x\",\"error_x.visible\",\"error_x.value\",\"error_x.type\",\"error_x.traceref\",\"error_x.array\",\"error_x.symmetric\",\"error_x.arrayminus\",\"error_x.valueminus\",\"error_x.tracerefminus\",\"swapxy\",\"swapxyaxes\",\"orientationaxes\",\"marker.colors\",\"values\",\"labels\",\"label0\",\"dlabel\",\"sort\",\"textinfo\",\"textposition\",\"textfont.size\",\"textfont.family\",\"textfont.color\",\"insidetextfont.size\",\"insidetextfont.family\",\"insidetextfont.color\",\"outsidetextfont.size\",\"outsidetextfont.family\",\"outsidetextfont.color\",\"hole\",\"scalegroup\",\"domain\",\"domain.x\",\"domain.y\",\"domain.x[0]\",\"domain.x[1]\",\"domain.y[0]\",\"domain.y[1]\",\"tilt\",\"tiltaxis\",\"depth\",\"direction\",\"rotation\",\"pull\",\"line.showscale\",\"line.cauto\",\"line.autocolorscale\",\"line.reversescale\",\"marker.line.showscale\",\"marker.line.cauto\",\"marker.line.autocolorscale\",\"marker.line.reversescale\"];for(u=0;u<a.length;u++)if(I.traceIs(t._fullData[a[u]],\"box\")){h.push(\"name\");break}var p,g=[\"marker\",\"marker.size\",\"textfont\",\"boxpoints\",\"jitter\",\"pointpos\",\"whiskerwidth\",\"boxmean\"],v=[\"zmin\",\"zmax\",\"zauto\",\"marker.cmin\",\"marker.cmax\",\"marker.cauto\",\"line.cmin\",\"line.cmax\",\"marker.line.cmin\",\"marker.line.cmax\",\"contours.start\",\"contours.end\",\"contours.size\",\"contours.showlines\",\"line\",\"line.smoothing\",\"line.shape\",\"error_y.width\",\"error_x.width\",\"error_x.copy_ystyle\",\"marker.maxdisplayed\"],m=[\"type\",\"x\",\"y\",\"x0\",\"y0\",\"orientation\",\"xaxis\",\"yaxis\"],y=!1,x=!1,_=!1,w=!1,k=!1,M=!1,A={},L={},T={},z=[\"cartesian\",\"pie\",\"ternary\"];f._basePlotModules.forEach(function(t){-1===z.indexOf(t.name)&&(y=!0)});var S=[\"zmin\",\"zmax\"],E=[\"xbins.start\",\"xbins.end\",\"xbins.size\"],N=[\"ybins.start\",\"ybins.end\",\"ybins.size\"],R=[\"contours.start\",\"contours.end\",\"contours.size\"];for(var j in d){var q,F,B,V,Z,Y=d[j];if(A[j]=Y,\"LAYOUT\"!==j.substr(0,6)){for(\"transforms\"===j.substr(0,10)&&(y=!0),L[j]=o(),u=0;u<a.length;u++){if(q=t.data[a[u]],F=t._fullData[a[u]],B=P.nestedProperty(q,j),V=B.get(),Z=Array.isArray(Y)?Y[u%Y.length]:Y,-1!==S.indexOf(j))c(\"zauto\",!1,u);else if(\"colorscale\"===j)c(\"autocolorscale\",!1,u);else if(\"autocolorscale\"===j)c(\"colorscale\",void 0,u);else if(\"marker.colorscale\"===j)c(\"marker.autocolorscale\",!1,u);else if(\"marker.autocolorscale\"===j)c(\"marker.colorscale\",void 0,u);else if(\"zauto\"===j)c(S,void 0,u);else if(-1!==E.indexOf(j))c(\"autobinx\",!1,u);else if(\"autobinx\"===j)c(E,void 0,u);else if(-1!==N.indexOf(j))c(\"autobiny\",!1,u);else if(\"autobiny\"===j)c(N,void 0,u);else if(-1!==R.indexOf(j))c(\"autocontour\",!1,u);else if(\"autocontour\"===j)c(R,void 0,u);else if(-1!==[\"x0\",\"dx\"].indexOf(j)&&F.x&&\"scaled\"!==F.xtype)c(\"xtype\",\"scaled\",u);else if(-1!==[\"y0\",\"dy\"].indexOf(j)&&F.y&&\"scaled\"!==F.ytype)c(\"ytype\",\"scaled\",u);else if(\"colorbar.thicknessmode\"===j&&B.get()!==Z&&-1!==[\"fraction\",\"pixels\"].indexOf(Z)&&F.colorbar){var U=-1!==[\"top\",\"bottom\"].indexOf(F.colorbar.orient)?f.height-f.margin.t-f.margin.b:f.width-f.margin.l-f.margin.r;c(\"colorbar.thickness\",F.colorbar.thickness*(\"fraction\"===Z?1/U:U),u)}else if(\"colorbar.lenmode\"===j&&B.get()!==Z&&-1!==[\"fraction\",\"pixels\"].indexOf(Z)&&F.colorbar){var X=-1!==[\"top\",\"bottom\"].indexOf(F.colorbar.orient)?f.width-f.margin.l-f.margin.r:f.height-f.margin.t-f.margin.b;c(\"colorbar.len\",F.colorbar.len*(\"fraction\"===Z?1/X:X),u)}else\"colorbar.tick0\"===j||\"colorbar.dtick\"===j?c(\"colorbar.tickmode\",\"linear\",u):\"colorbar.tickmode\"===j&&c([\"colorbar.tick0\",\"colorbar.dtick\"],void 0,u);if(\"type\"===j&&\"pie\"===Z!=(\"pie\"===V)){var G=\"x\",$=\"y\";\"bar\"!==Z&&\"bar\"!==V||\"h\"!==q.orientation||(G=\"y\",$=\"x\"),P.swapAttrs(q,[\"?\",\"?src\"],\"labels\",G),P.swapAttrs(q,[\"d?\",\"?0\"],\"label\",G),P.swapAttrs(q,[\"?\",\"?src\"],\"values\",$),\"pie\"===V?(P.nestedProperty(q,\"marker.color\").set(P.nestedProperty(q,\"marker.colors\").get()),f._pielayer.selectAll(\"g.trace\").remove()):I.traceIs(q,\"cartesian\")&&(P.nestedProperty(q,\"marker.colors\").set(P.nestedProperty(q,\"marker.color\").get()),T[q.xaxis||\"x\"]=!0,T[q.yaxis||\"y\"]=!0)}L[j][u]=V;var Q=[\"swapxy\",\"swapxyaxes\",\"orientation\",\"orientationaxes\"];if(-1!==Q.indexOf(j)){if(\"orientation\"===j){if(B.set(Z),B.get()===L[j][u])continue}else\"orientationaxes\"===j&&(q.orientation={v:\"h\",h:\"v\"}[F.orientation]);b(q)}else B.set(Z)}if(-1!==[\"swapxyaxes\",\"orientationaxes\"].indexOf(j)&&O.Axes.swap(t,a),\"orientationaxes\"===j){var W=P.nestedProperty(t.layout,\"hovermode\");\"x\"===W.get()?W.set(\"y\"):\"y\"===W.get()&&W.set(\"x\")}if(-1!==a.indexOf(0)&&-1!==m.indexOf(j)&&(O.Axes.clearTypes(t,a),y=!0),-1!==[\"autobinx\",\"autobiny\",\"zauto\"].indexOf(j)&&Z===!1||(k=!0),(-1!==[\"colorbar\",\"line\"].indexOf(B.parts[0])||\"marker\"===B.parts[0]&&\"colorbar\"===B.parts[1])&&(M=!0),-1!==h.indexOf(j)){if(-1!==[\"orientation\",\"type\"].indexOf(j)){for(p=[],u=0;u<a.length;u++){var J=t.data[a[u]];I.traceIs(J,\"cartesian\")&&(i(J.xaxis||\"x\"),i(J.yaxis||\"y\"),\"type\"===e&&c([\"autobinx\",\"autobiny\"],!0,u))}c(p.map(l),!0,0),c(p.map(s),[0,1],0)}y=!0}else-1!==v.indexOf(j)?_=!0:-1!==g.indexOf(j)&&(x=!0)}else B=P.nestedProperty(t.layout,j.replace(\"LAYOUT\",\"\")),L[j]=[B.get()],B.set(Array.isArray(Y)?Y[0]:Y),y=!0}var K=Object.keys(T);t:for(u=0;u<K.length;u++){for(var et=K[u],nt=et.charAt(0),rt=nt+\"axis\",at=0;at<t.data.length;at++)if(I.traceIs(t.data[at],\"cartesian\")&&(t.data[at][rt]||nt)===et)continue t;c(\"LAYOUT\"+O.Axes.id2name(et),null,0)}D.add(t,tt,[t,L,a],tt,[t,A,a]);var ot=!1;O.Axes.list(t).forEach(function(t){t.autorange&&(ot=!0)}),(y||w||x&&ot)&&(t.calcdata=void 0);var it;w?it=[function(){var e=t.layout;return t.layout=void 0,O.plot(t,\"\",e)}]:y||_||x?it=[O.plot]:(I.supplyDefaults(t),it=[I.previousPromises],k&&it.push(function(){var e,n,r;for(e=0;e<t.calcdata.length;e++)n=t.calcdata[e],r=(((n[0]||{}).trace||{})._module||{}).arraysToCalcdata,r&&r(n);return I.style(t),H.draw(t),I.previousPromises(t)}),M&&it.push(function(){return t.calcdata.forEach(function(t){if((t[0].t||{}).cb){var e=t[0].trace,n=t[0].t.cb;I.traceIs(e,\"contour\")&&n.line({width:e.contours.showlines!==!1?e.line.width:0,dash:e.line.dash,color:\"line\"===e.contours.coloring?n._opts.line.color:e.line.color}),I.traceIs(e,\"markerColorscale\")?n.options(e.marker.colorbar)():n.options(e.colorbar)()}}),I.previousPromises(t)}));var lt=P.syncOrAsync(it,t);return lt&&lt.then||(lt=Promise.resolve()),lt.then(function(){return t.emit(\"plotly_restyle\",P.extendDeep([],[A,a])),t})},O.relayout=function et(t,e,n){function a(t,e){if(Array.isArray(t))return void t.forEach(function(t){a(t,e)});if(!(t in v)){var n=P.nestedProperty(p,t);t in T||(T[t]=n.get()),void 0!==e&&n.set(e)}}function o(t,e){var n=O.Axes.id2name(t[e+\"ref\"]||e);return(g[n]||{}).autorange}function i(t){var e=t[\"xaxis.range\"]?t[\"xaxis.range\"][0]:t[\"xaxis.range[0]\"],n=t[\"xaxis.range\"]?t[\"xaxis.range\"][1]:t[\"xaxis.range[1]\"],r=g.xaxis&&g.xaxis.rangeslider?g.xaxis.rangeslider:{};r.visible&&(e||n?g.xaxis.rangeslider.setRange(e,n):t[\"xaxis.autorange\"]&&g.xaxis.rangeslider.setRange())}if(t=r(t),t.framework&&t.framework.isPolar)return Promise.resolve(t);var l,s,c,u,f,d,h,p=t.layout,g=t._fullLayout,v={},m=!1,y=!1,x=!1,b=!1,_=!1,k=!1;if(\"string\"==typeof e)v[e]=n;else{if(!P.isPlainObject(e))return P.warn(\"Relayout fail.\",e,n),Promise.reject();v=e}for(Object.keys(v).length&&(t.changed=!0),c=Object.keys(v),s=O.Axes.list(t),h=0;h<c.length;h++){if(0===c[h].indexOf(\"allaxes\")){for(var M=0;M<s.length;M++)f=s[M]._id.substr(1),d=-1!==f.indexOf(\"scene\")?f+\".\":\"\",l=c[h].replace(\"allaxes\",d+s[M]._name),v[l]||(v[l]=v[c[h]]);delete v[c[h]]}c[h].match(/^annotations\\[[0-9-]+\\].ref$/)&&(u=v[c[h]].split(\"y\"),v[c[h].replace(\"ref\",\"xref\")]=u[0],v[c[h].replace(\"ref\",\"yref\")]=2===u.length?\"y\"+u[1]:\"paper\",delete v[c[h]])}var A={},T={},S=[\"height\",\"width\"];for(var E in v){var C=P.nestedProperty(p,E),N=v[E],R=C.parts.length,j=\"string\"==typeof C.parts[R-1]?R-1:R-2,q=C.parts[j],F=C.parts[j-1]+\".\"+q,V=C.parts.slice(0,j).join(\".\"),Z=P.nestedProperty(t.layout,V).get(),Y=P.nestedProperty(g,V).get();if(A[E]=N,T[E]=\"reverse\"===q?N:C.get(),-1!==S.indexOf(E)?a(\"autosize\",!1):\""
+,
+"autosize\"===E?a(S,void 0):F.match(/^[xyz]axis[0-9]*\\.range(\\[[0|1]\\])?$/)?a(V+\".autorange\",!1):F.match(/^[xyz]axis[0-9]*\\.autorange$/)?a([V+\".range[0]\",V+\".range[1]\"],void 0):F.match(/^aspectratio\\.[xyz]$/)?a(C.parts[0]+\".aspectmode\",\"manual\"):F.match(/^aspectmode$/)?a([V+\".x\",V+\".y\",V+\".z\"],void 0):\"tick0\"===q||\"dtick\"===q?a(V+\".tickmode\",\"linear\"):\"tickmode\"===q?a([V+\".tick0\",V+\".dtick\"],void 0):/[xy]axis[0-9]*?$/.test(q)&&!Object.keys(N||{}).length?_=!0:/[xy]axis[0-9]*\\.categoryorder$/.test(F)?_=!0:/[xy]axis[0-9]*\\.categoryarray/.test(F)&&(_=!0),-1!==F.indexOf(\"rangeslider\")&&(_=!0),\"type\"===q&&\"log\"===Y.type!=(\"log\"===N)){var U=Z;if(U&&U.range)if(Y.autorange)\"log\"===N&&(U.range=U.range[1]>U.range[0]?[1,2]:[2,1]);else{var G=U.range[0],$=U.range[1];\"log\"===N?(0>=G&&0>=$&&a(V+\".autorange\",!0),0>=G?G=$/1e6:0>=$&&($=G/1e6),a(V+\".range[0]\",Math.log(G)/Math.LN10),a(V+\".range[1]\",Math.log($)/Math.LN10)):(a(V+\".range[0]\",Math.pow(10,G)),a(V+\".range[1]\",Math.pow(10,$)))}else a(V+\".autorange\",!0)}if(\"reverse\"===q)Z.range?Z.range.reverse():(a(V+\".autorange\",!0),Z.range=[1,0]),Y.autorange?_=!0:b=!0;else if(\"annotations\"===C.parts[0]||\"shapes\"===C.parts[0]){var Q=C.parts[1],W=C.parts[0],J=p[W]||[],K=O[P.titleCase(W)],tt=J[Q]||{};2===C.parts.length&&(\"add\"===v[E]||P.isPlainObject(v[E])?T[E]=\"remove\":\"remove\"===v[E]?-1===Q?(T[W]=J,delete T[E]):T[E]=tt:P.log(\"???\",v)),!o(tt,\"x\")&&!o(tt,\"y\")||P.containsAny(E,[\"color\",\"opacity\",\"align\",\"dash\"])||(_=!0),K.draw(t,Q,C.parts.slice(2).join(\".\"),v[E]),delete v[E]}else if(\"images\"===C.parts[0]){var nt=P.objectFromPath(E,N);P.extendDeepAll(t.layout,nt),B.supplyLayoutDefaults(t.layout,t._fullLayout),B.draw(t)}else if(\"mapbox\"===C.parts[0]&&\"layers\"===C.parts[1]){P.extendDeepAll(t.layout,P.objectFromPath(E,N));var rt=(t._fullLayout.mapbox||{}).layers||[],at=C.parts[2]+1-rt.length;for(h=0;at>h;h++)rt.push({});b=!0}else 0===C.parts[0].indexOf(\"scene\")?b=!0:0===C.parts[0].indexOf(\"geo\")?b=!0:0===C.parts[0].indexOf(\"ternary\")?b=!0:!g._has(\"gl2d\")||-1===E.indexOf(\"axis\")&&\"plot_bgcolor\"!==C.parts[0]?\"hiddenlabels\"===E?_=!0:-1!==C.parts[0].indexOf(\"legend\")?m=!0:-1!==E.indexOf(\"title\")?y=!0:-1!==C.parts[0].indexOf(\"bgcolor\")?x=!0:C.parts.length>1&&P.containsAny(C.parts[1],[\"tick\",\"exponent\",\"grid\",\"zeroline\"])?y=!0:-1!==E.indexOf(\".linewidth\")&&-1!==E.indexOf(\"axis\")?y=x=!0:C.parts.length>1&&-1!==C.parts[1].indexOf(\"line\")?x=!0:C.parts.length>1&&\"mirror\"===C.parts[1]?y=x=!0:\"margin.pad\"===E?y=x=!0:\"margin\"===C.parts[0]||\"autorange\"===C.parts[1]||\"rangemode\"===C.parts[1]||\"type\"===C.parts[1]||\"domain\"===C.parts[1]||E.match(/^(bar|box|font)/)?_=!0:-1!==[\"hovermode\",\"dragmode\"].indexOf(E)?k=!0:-1===[\"hovermode\",\"dragmode\",\"height\",\"width\",\"autosize\"].indexOf(E)&&(b=!0):b=!0,C.set(N)}D.add(t,et,[t,T],et,[t,A]),v.autosize&&(v=w(t,v)),(v.height||v.width||v.autosize)&&(_=!0);var ot=Object.keys(v),it=[I.previousPromises];if(b||_)it.push(function(){return t.layout=void 0,_&&(t.calcdata=void 0),O.plot(t,\"\",p)});else if(ot.length&&(I.supplyDefaults(t),g=t._fullLayout,m&&it.push(function(){return H.draw(t),I.previousPromises(t)}),x&&it.push(L),y&&it.push(function(){return O.Axes.doTicks(t,\"redraw\"),z(t),I.previousPromises(t)}),k)){var lt;for(X(t),O.Fx.supplyLayoutDefaults(t.layout,g,t._fullData),O.Fx.init(t),lt=I.getSubplotIds(g,\"gl3d\"),h=0;h<lt.length;h++)f=g[lt[h]]._scene,f.updateFx(g.dragmode,g.hovermode);for(lt=I.getSubplotIds(g,\"gl2d\"),h=0;h<lt.length;h++)f=g._plots[lt[h]]._scene2d,f.updateFx(g);for(lt=I.getSubplotIds(g,\"geo\"),h=0;h<lt.length;h++){var st=g[lt[h]]._geo;st.updateFx(g.hovermode)}}var ct=P.syncOrAsync(it,t);return ct&&ct.then||(ct=Promise.resolve(t)),ct.then(function(){var e=P.extendDeep({},A);return i(e),t.emit(\"plotly_relayout\",e),t})},O.purge=function(t){t=r(t);var e=t._fullLayout||{},n=t._fullData||[];return I.cleanPlot([],{},n,e),I.purge(t),N.purge(t),e._container&&e._container.remove(),delete t._context,delete t._replotPending,delete t._mouseDownTime,delete t._hmpixcount,delete t._hmlumcount,t}},{\"../components/color\":18,\"../components/drawing\":41,\"../components/errorbars\":47,\"../components/images\":53,\"../components/legend\":61,\"../components/modebar/manage\":65,\"../components/rangeselector\":72,\"../components/rangeslider\":77,\"../components/shapes\":80,\"../components/titles\":81,\"../constants/xmlns_namespaces\":82,\"../lib\":89,\"../lib/events\":87,\"../lib/queue\":96,\"../plotly\":107,\"../plots/cartesian/graph_interact\":117,\"../plots/plots\":130,d3:9,\"fast-isnumeric\":11,\"gl-mat4/fromQuat\":12}],102:[function(t,e,n){\"use strict\";function r(t,e){try{t._fullLayout._paper.style(\"background\",e)}catch(n){a.error(n)}}var a=t(\"../lib\");e.exports={staticPlot:!1,editable:!1,queueLength:0,autosizable:!1,fillFrame:!1,frameMargins:0,scrollZoom:!1,doubleClick:\"reset+autosize\",showTips:!0,showLink:!1,sendData:!0,linkText:\"Edit chart\",showSources:!1,displayModeBar:\"hover\",modeBarButtonsToRemove:[],modeBarButtonsToAdd:[],modeBarButtons:!1,displaylogo:!0,plotGlPixelRatio:2,setBackground:r,topojsonURL:\"https://cdn.plot.ly/\",mapboxAccessToken:null,logging:!1}},{\"../lib\":89}],103:[function(t,e,n){\"use strict\";function r(t){var e=m.attributes,n=c({type:t}),r=f(t),a=h(t),o={},i={};o.type=null,b(o,e),o=l(n.attributes,o,\"attributes\",t),void 0!==a.attributes&&b(o,a.attributes),o.type=t,o=u(o),s(o),z.traces[t]=x({},r,{attributes:o}),void 0!==n.layoutAttributes&&(i=l(n.layoutAttributes,i,\"layoutAttributes\",t),s(i),z.traces[t].layoutAttributes=i)}function a(){var t=m.layoutAttributes,e={};e=l(t,e,\"layoutAttributes\",\"*\"),e=d(e),e=p(e),e=u(e),s(e),g(e),z.layout={layoutAttributes:e}}function o(t){var e=m.transformsRegistry[t],n={};n=l(n,e.attributes||{},\"attributes\",\"*\"),n=u(n),s(n),g(n),z.transforms[t]={attributes:n}}function i(){z.defs={valObjects:y.valObjects,metaKeys:T.concat([\"description\",\"role\"])}}function l(t,e,n,r){var a,o,i,s,u;return Object.keys(t).forEach(function(f){return f===w?void Object.keys(t[f]).forEach(function(s){a=c({module:t[f][s]}),void 0!==a&&(o=a[n],i=l(o,{},n,r),y.nestedProperty(e,s).set(b({},i)))}):f===k?void Object.keys(t[f]).forEach(function(a){a===r&&(s=c({module:t[f][a]}),void 0!==s&&(u=s[n],u=l(u,{},n,r),_(e,u)))}):void(e[f]=y.isPlainObject(t[f])?_({},t[f]):t[f])}),e}function s(t){function e(t){return{valType:\"string\"}}function n(t,n,r){C.isValObject(t)?\"data_array\"===t.valType?(t.role=\"data\",r[n+\"src\"]=e(n)):t.arrayOk===!0&&(r[n+\"src\"]=e(n)):y.isPlainObject(t)&&(t.role=\"object\")}C.crawl(t,n)}function c(t){if(\"type\"in t)return\"area\"===t.type?{attributes:S}:m.getModule({type:t.type});var e=m.subplotsRegistry,n=t.module;return e[n]?e[n]:\"module\"in t?v[n]:void 0}function u(t){return Object.keys(t).forEach(function(e){\"_\"===e.charAt(0)&&-1===T.indexOf(e)&&delete t[e]}),t}function f(t){return\"area\"===t?{}:m.modules[t].meta||{}}function d(t){return x(t,{radialaxis:E.radialaxis,angularaxis:E.angularaxis}),x(t,E.layout),t}function h(t){if(\"area\"===t)return{};var e=m.subplotsRegistry,n=Object.keys(e).filter(function(e){return m.traceIs({type:t},e)})[0];return void 0===n?{}:e[n]}function p(t){var e=m.subplotsRegistry;return Object.keys(t).forEach(function(n){Object.keys(e).forEach(function(r){var a,o=e[r];o.attrRegex&&(a=\"cartesian\"===r||\"gl2d\"===r?o.attrRegex.x.test(n)||o.attrRegex.y.test(n):o.attrRegex.test(n),a&&(t[n][M]=!0))})}),t}function g(t){function e(t,e,n){if(t[A]===!0){var r=e.substr(0,e.length-1);delete t[A],n[e]={items:{}},n[e].items[r]=t,n[e].role=\"object\"}}C.crawl(t,e)}var v=t(\"../plotly\"),m=t(\"../plots/plots\"),y=t(\"../lib\"),x=y.extendFlat,b=y.extendDeep,_=y.extendDeepAll,w=\"_nestedModules\",k=\"_composedModules\",M=\"_isSubplotObj\",A=\"_isLinkedToArray\",L=\"_deprecated\",T=[M,A,L],z={traces:{},layout:{},transforms:{},defs:{}},S=t(\"../plots/polar/area_attributes\"),E=t(\"../plots/polar/axis_attributes\"),C=e.exports={};C.get=function(){return m.allTypes.concat(\"area\").forEach(r),a(),Object.keys(m.transformsRegistry).forEach(o),i(),z},C.crawl=function(t,e){Object.keys(t).forEach(function(n){var r=t[n];-1===T.indexOf(n)&&(e(r,n,t),C.isValObject(r)||y.isPlainObject(r)&&C.crawl(r,e))})},C.isValObject=function(t){return t&&void 0!==t.valType}},{\"../lib\":89,\"../plotly\":107,\"../plots/plots\":130,\"../plots/polar/area_attributes\":131,\"../plots/polar/axis_attributes\":132}],104:[function(t,e,n){\"use strict\";var r=t(\"../plotly\"),a=t(\"../lib\");e.exports=function(t){return a.extendFlat(r.defaultConfig,t)}},{\"../lib\":89,\"../plotly\":107}],105:[function(t,e,n){\"use strict\";function r(e,n){var r=t(\"../snapshot\"),l=new Promise(function(t,l){function s(){var t=r.getDelay(f._fullLayout);return new Promise(function(e,a){setTimeout(function(){var t=r.toSVG(f),o=document.createElement(\"canvas\");o.id=i.randstr(),r.svgToImg({format:n.format,width:f._fullLayout.width,height:f._fullLayout.height,canvas:o,svg:t,promise:!0}).then(function(t){f&&document.body.removeChild(f),e(t)}).catch(function(t){a(t)})},t)})}n=n||{},n.format=n.format||\"png\";var c=function(t){return void 0===t||null===t?!0:!!(a(t)&&t>1)};c(n.width)&&c(n.height)||l(new Error(\"Height and width should be pixel values.\"));var u=r.clone(e,{format:\"png\",height:n.height,width:n.width}),f=u.td;f.style.position=\"absolute\",f.style.left=\"-5000px\",document.body.appendChild(f);var d=r.getRedrawFunc(f);o.plot(f,u.data,u.layout,u.config).then(d).then(s).then(function(e){t(e)}).catch(function(t){l(t)})});return l}var a=t(\"fast-isnumeric\"),o=t(\"../plotly\"),i=t(\"../lib\");e.exports=r},{\"../lib\":89,\"../plotly\":107,\"../snapshot\":139,\"fast-isnumeric\":11}],106:[function(t,e,n){\"use strict\";function r(t,e,n,a,o,c){c=c||[];for(var u=Object.keys(t),d=0;d<u.length;d++){var h=u[d];if(\"transforms\"!==h){var v=c.slice();v.push(h);var m=t[h],y=e[h],x=s(n,h);if(l(n,h))if(p(m)&&p(y))r(m,y,x,a,o,v);else if(x.items&&g(m))for(var b=h.substr(0,h.length-1),_=0;_<m.length;_++){var w=x.items[b],k=v.slice();k.push(_),r(m[_],y[_],w,a,o,k)}else!p(m)&&p(y)?a.push(i(\"object\",o,v,m)):!g(m)&&g(y)&&\"info_array\"!==x.valType?a.push(i(\"array\",o,v,m)):h in e?f.validate(m,x)||a.push(i(\"value\",o,v,m)):a.push(i(\"u"
+,
+"nused\",o,v,m));else a.push(i(\"schema\",o,v))}}return a}function a(t,e){for(var n=0;n<e.length;n++){var r=e[n].type,a=t.traces[r].layoutAttributes;a&&f.extendFlat(t.layout.layoutAttributes,a)}return t.layout.layoutAttributes}function o(t){return g(t)?\"In data trace \"+t[1]+\", \":\"In \"+t+\", \"}function i(t,e,n,r){n=n||\"\";var a,o;g(e)?(a=e[0],o=e[1]):(a=e,o=null);var i=u(n),l=v[t](e,i,r);return f.log(l),{code:t,container:a,trace:o,path:n,astr:i,msg:l}}function l(t,e){var n=c(e),r=n.keyMinusId,a=n.id;return r in t&&t[r]._isSubplotObj&&a?!0:e in t}function s(t,e){var n=c(e);return t[n.keyMinusId]}function c(t){var e=/([2-9]|[1-9][0-9]+)$/,n=t.split(e)[0],r=t.substr(n.length,t.length);return{keyMinusId:n,id:r}}function u(t){if(!g(t))return String(t);for(var e=\"\",n=0;n<t.length;n++){var r=t[n];\"number\"==typeof r?e=e.substr(0,e.length-1)+\"[\"+r+\"]\":e+=r,n<t.length-1&&(e+=\".\")}return e}var f=t(\"../lib\"),d=t(\"../plots/plots\"),h=t(\"./plot_schema\"),p=f.isPlainObject,g=Array.isArray;e.exports=function(t,e){var n,o,l=h.get(),s=[],c={};g(t)?(c.data=f.extendDeep([],t),n=t):(c.data=[],n=[],s.push(i(\"array\",\"data\"))),p(e)?(c.layout=f.extendDeep({},e),o=e):(c.layout={},o={},arguments.length>1&&s.push(i(\"object\",\"layout\"))),d.supplyDefaults(c);for(var u=c._fullData,v=n.length,m=0;v>m;m++){var y=n[m],x=[\"data\",m];if(p(y)){var b=u[m],_=b.type,w=l.traces[_].attributes;w.type={valType:\"enumerated\",values:[_]},b.visible===!1&&y.visible!==!1&&s.push(i(\"invisible\",x)),r(y,b,w,s,x);var k=y.transforms,M=b.transforms;if(k){g(k)||s.push(i(\"array\",x,[\"transforms\"])),x.push(\"transforms\");for(var A=0;A<k.length;A++){var L=[\"transforms\",A],T=k[A].type;if(p(k[A])){var z=l.transforms[T]?l.transforms[T].attributes:{};z.type={valType:\"enumerated\",values:Object.keys(l.transforms)},r(k[A],M[A],z,s,x,L)}else s.push(i(\"object\",x,L))}}}else s.push(i(\"object\",x))}var S=c._fullLayout,E=a(l,u);return r(o,S,E,s,\"layout\"),0===s.length?void 0:s};var v={object:function(t,e){var n;return n=\"layout\"===t&&\"\"===e?\"The layout argument\":\"data\"===t[0]&&\"\"===e?\"Trace \"+t[1]+\" in the data argument\":o(t)+\"key \"+e,\n"
+,
+"n+\" must be linked to an object container\"},array:function(t,e){var n;return n=\"data\"===t?\"The data argument\":o(t)+\"key \"+e,n+\" must be linked to an array container\"},schema:function(t,e){return o(t)+\"key \"+e+\" is not part of the schema\"},unused:function(t,e,n){var r=p(n)?\"container\":\"key\";return o(t)+r+\" \"+e+\" did not get coerced\"},invisible:function(t){return\"Trace \"+t[1]+\" got defaulted to be not visible\"},value:function(t,e,n){return[o(t)+\"key \"+e,\"is set to an invalid value (\"+n+\")\"].join(\" \")}}},{\"../lib\":89,\"../plots/plots\":130,\"./plot_schema\":103}],107:[function(t,e,n){\"use strict\";t(\"es6-promise\").polyfill();var r=n.Lib=t(\"./lib\");n.util=t(\"./lib/svg_text_utils\"),n.Queue=t(\"./lib/queue\"),t(\"../build/plotcss\"),n.MathJaxConfig=t(\"./fonts/mathjax_config\"),n.defaultConfig=t(\"./plot_api/plot_config\");var a=n.Plots=t(\"./plots/plots\");n.Axes=t(\"./plots/cartesian/axes\"),n.Fx=t(\"./plots/cartesian/graph_interact\"),n.micropolar=t(\"./plots/polar/micropolar\"),n.Color=t(\"./components/color\"),n.Drawing=t(\"./components/drawing\"),n.Colorscale=t(\"./components/colorscale\"),n.Colorbar=t(\"./components/colorbar\"),n.ErrorBars=t(\"./components/errorbars\"),n.Annotations=t(\"./components/annotations\"),n.Shapes=t(\"./components/shapes\"),n.Legend=t(\"./components/legend\"),n.Images=t(\"./components/images\"),n.ModeBar=t(\"./components/modebar\"),n.register=function(t){if(!t)throw new Error(\"No argument passed to Plotly.register.\");t&&!Array.isArray(t)&&(t=[t]);for(var e=0;e<t.length;e++){var n=t[e];if(!n)throw new Error(\"Invalid module was attempted to be registered!\");switch(n.moduleType){case\"trace\":a.register(n,n.name,n.categories,n.meta),a.subplotsRegistry[n.basePlotModule.name]||a.registerSubplot(n.basePlotModule);break;case\"transform\":if(\"string\"!=typeof n.name)throw new Error(\"Transform module *name* must be a string.\");var o=\"Transform module \"+n.name;if(\"function\"!=typeof n.transform)throw new Error(o+\" is missing a *transform* function.\");r.isPlainObject(n.attributes)||r.log(o+\" registered without an *attributes* object.\"),\"function\"!=typeof n.supplyDefaults&&r.log(o+\" registered without a *supplyDefaults* function.\"),a.transformsRegistry[n.name]=n;break;default:throw new Error(\"Invalid module was attempted to be registered!\")}}},n.register(t(\"./traces/scatter\")),t(\"./plot_api/plot_api\"),n.PlotSchema=t(\"./plot_api/plot_schema\"),n.Snapshot=t(\"./snapshot\")},{\"../build/plotcss\":1,\"./components/annotations\":16,\"./components/color\":18,\"./components/colorbar\":23,\"./components/colorscale\":32,\"./components/drawing\":41,\"./components/errorbars\":47,\"./components/images\":53,\"./components/legend\":61,\"./components/modebar\":64,\"./components/shapes\":80,\"./fonts/mathjax_config\":84,\"./lib\":89,\"./lib/queue\":96,\"./lib/svg_text_utils\":100,\"./plot_api/plot_api\":101,\"./plot_api/plot_config\":102,\"./plot_api/plot_schema\":103,\"./plots/cartesian/axes\":110,\"./plots/cartesian/graph_interact\":117,\"./plots/plots\":130,\"./plots/polar/micropolar\":133,\"./snapshot\":139,\"./traces/scatter\":177,\"es6-promise\":10}],108:[function(t,e,n){\"use strict\";e.exports={type:{valType:\"enumerated\",values:[],dflt:\"scatter\"},visible:{valType:\"enumerated\",values:[!0,!1,\"legendonly\"],dflt:!0},showlegend:{valType:\"boolean\",dflt:!0},legendgroup:{valType:\"string\",dflt:\"\"},opacity:{valType:\"number\",min:0,max:1,dflt:1},name:{valType:\"string\"},uid:{valType:\"string\",dflt:\"\"},hoverinfo:{valType:\"flaglist\",flags:[\"x\",\"y\",\"z\",\"text\",\"name\"],extras:[\"all\",\"none\"],dflt:\"all\"},stream:{token:{valType:\"string\",noBlank:!0,strict:!0},maxpoints:{valType:\"number\",min:0}}}},{}],109:[function(t,e,n){\"use strict\";e.exports={xaxis:{valType:\"subplotid\",dflt:\"x\"},yaxis:{valType:\"subplotid\",dflt:\"y\"}}},{}],110:[function(t,e,n){\"use strict\";function r(t){var e,n,r=t.tickvals,a=t.ticktext,o=new Array(r.length),i=1.0001*t.range[0]-1e-4*t.range[1],s=1.0001*t.range[1]-1e-4*t.range[0],c=Math.min(i,s),u=Math.max(i,s),f=0;for(Array.isArray(a)||(a=[]),n=0;n<r.length;n++)e=t.d2l(r[n]),e>c&&u>e&&(void 0===a[n]?o[f]=L.tickText(t,e):o[f]=l(t,e,String(a[n])),f++);return f<r.length&&o.splice(f,r.length-f),o}function a(t,e,n){return e*_.roundUp(t/e,n)}function o(t){var e,n=t.dtick;if(t._tickexponent=0,x(n)||\"string\"==typeof n||(n=1),\"category\"===t.type)t._tickround=null;else if(x(n)||\"L\"===n.charAt(0))if(\"date\"===t.type)n>=864e5?t._tickround=\"d\":n>=36e5?t._tickround=\"H\":n>=6e4?t._tickround=\"M\":n>=1e3?t._tickround=\"S\":t._tickround=3-Math.round(Math.log(n/2)/Math.LN10);else{x(n)||(n=Number(n.substr(1))),t._tickround=2-Math.floor(Math.log(n)/Math.LN10+.01),e=\"log\"===t.type?Math.pow(10,Math.max(t.range[0],t.range[1])):Math.max(Math.abs(t.range[0]),Math.abs(t.range[1]));var r=Math.floor(Math.log(e)/Math.LN10+.01);Math.abs(r)>3&&(\"SI\"===t.exponentformat||\"B\"===t.exponentformat?t._tickexponent=3*Math.round((r-1)/3):t._tickexponent=r)}else\"M\"===n.charAt(0)?t._tickround=2===n.length?\"m\":\"y\":t._tickround=null}function i(t,e){var n=t.match(B),r=new Date(e);if(n){var a=Math.min(+n[1]||6,6),o=String(e/1e3%1+2.0000005).substr(2,a).replace(/0+$/,\"\")||\"0\";return y.time.format(t.replace(B,o))(r)}return y.time.format(t)(r)}function l(t,e,n){var r=t.tickfont||t._gd._fullLayout.font;return{x:e,dx:0,dy:0,text:n||\"\",fontSize:r.size,font:r.family,fontColor:r.color}}function s(t,e,n,r){var a,o=e.x,l=t._tickround,s=new Date(o),c=\"\";n&&t.hoverformat?a=i(t.hoverformat,o):t.tickformat?a=i(t.tickformat,o):(r&&(x(l)?l+=2:l={y:\"m\",m:\"d\",d:\"H\",H:\"M\",M:\"S\",S:2}[l]),\"y\"===l?a=D(s):\"m\"===l?a=I(s):(o!==t._tmin||n||(c=\"<br>\"+D(s)),\"d\"===l?a=R(s):\"H\"===l?a=j(s):(o!==t._tmin||n||(c=\"<br>\"+R(s)+\", \"+D(s)),a=q(s),\"M\"!==l&&(a+=F(s),\"S\"!==l&&(a+=d(m(o/1e3,1),t,\"none\",n).substr(1)))))),e.text=a+c}function c(t,e,n,r,a){var o=t.dtick,i=e.x;if(!r||\"string\"==typeof o&&\"L\"===o.charAt(0)||(o=\"L3\"),t.tickformat||\"string\"==typeof o&&\"L\"===o.charAt(0))e.text=d(Math.pow(10,i),t,a,r);else if(x(o)||\"D\"===o.charAt(0)&&m(i+.01,1)<.1)if(-1!==[\"e\",\"E\",\"power\"].indexOf(t.exponentformat)){var l=Math.round(i);0===l?e.text=1:1===l?e.text=\"10\":l>1?e.text=\"10<sup>\"+l+\"</sup>\":e.text=\"10<sup>\\u2212\"+-l+\"</sup>\",e.fontSize*=1.25}else e.text=d(Math.pow(10,i),t,\"\",\"fakehover\"),\"D1\"===o&&\"y\"===t._id.charAt(0)&&(e.dy-=e.fontSize/6);else{if(\"D\"!==o.charAt(0))throw\"unrecognized dtick \"+String(o);e.text=String(Math.round(Math.pow(10,m(i,1)))),e.fontSize*=.75}if(\"D1\"===t.dtick){var s=String(e.text).charAt(0);\"0\"!==s&&\"1\"!==s||(\"y\"===t._id.charAt(0)?e.dx-=e.fontSize/4:(e.dy+=e.fontSize/2,e.dx+=(t.range[1]>t.range[0]?1:-1)*e.fontSize*(0>i?.5:.25)))}}function u(t,e){var n=t._categories[Math.round(e.x)];void 0===n&&(n=\"\"),e.text=String(n)}function f(t,e,n,r,a){\"all\"===t.showexponent&&Math.abs(e.x/t.dtick)<1e-6&&(a=\"hide\"),e.text=d(e.x,t,a,r)}function d(t,e,n,r){var a=0>t,i=e._tickround,l=n||e.exponentformat||\"B\",s=e._tickexponent,c=e.tickformat;if(r){var u={exponentformat:e.exponentformat,dtick:\"none\"===e.showexponent?e.dtick:x(t)?Math.abs(t)||1:1,range:\"none\"===e.showexponent?e.range:[0,t||1]};o(u),i=(Number(u._tickround)||0)+4,s=u._tickexponent,e.hoverformat&&(c=e.hoverformat)}if(c)return y.format(c)(t).replace(/-/g,\"\\u2212\");var f=Math.pow(10,-i)/2;if(\"none\"===l&&(s=0),t=Math.abs(t),f>t)t=\"0\",a=!1;else{if(t+=f,s&&(t*=Math.pow(10,-s),i+=s),0===i)t=String(Math.floor(t));else if(0>i){t=String(Math.round(t)),t=t.substr(0,t.length+i);for(var d=i;0>d;d++)t+=\"0\"}else{t=String(t);var h=t.indexOf(\".\")+1;h&&(t=t.substr(0,h+i).replace(/\\.?0+$/,\"\"))}t=_.numSeparate(t,e._gd._fullLayout.separators)}if(s&&\"hide\"!==l){var p;p=0>s?\"\\u2212\"+-s:\"power\"!==l?\"+\"+s:String(s),\"e\"===l||(\"SI\"===l||\"B\"===l)&&(s>12||-15>s)?t+=\"e\"+p:\"E\"===l?t+=\"E\"+p:\"power\"===l?t+=\"&times;10<sup>\"+p+\"</sup>\":\"B\"===l&&9===s?t+=\"B\":\"SI\"!==l&&\"B\"!==l||(t+=H[s/3+5])}return a?\"\\u2212\"+t:t}function h(t,e){var n,r,a=[];for(n=0;n<e.length;n++){var o=[],i=t._fullData[e[n]].xaxis,l=t._fullData[e[n]].yaxis;if(i&&l){for(r=0;r<a.length;r++)-1===a[r].x.indexOf(i)&&-1===a[r].y.indexOf(l)||o.push(r);if(o.length){var s,c=a[o[0]];if(o.length>1)for(r=1;r<o.length;r++)s=a[o[r]],p(c.x,s.x),p(c.y,s.y);p(c.x,[i]),p(c.y,[l])}else a.push({x:[i],y:[l]})}}return a}function p(t,e){for(var n=0;n<e.length;n++)-1===t.indexOf(e[n])&&t.push(e[n])}function g(t,e,n){var r,a,o=[],i=[],l=t.layout;for(r=0;r<e.length;r++)o.push(L.getFromId(t,e[r]));for(r=0;r<n.length;r++)i.push(L.getFromId(t,n[r]));var s=Object.keys(o[0]),c=[\"anchor\",\"domain\",\"overlaying\",\"position\",\"side\",\"tickangle\"],u=[\"linear\",\"log\"];for(r=0;r<s.length;r++){var f=s[r],d=o[0][f],h=i[0][f],p=!0,g=!1,m=!1;if(\"_\"!==f.charAt(0)&&\"function\"!=typeof d&&-1===c.indexOf(f)){for(a=1;a<o.length&&p;a++){var y=o[a][f];\"type\"===f&&-1!==u.indexOf(d)&&-1!==u.indexOf(y)&&d!==y?g=!0:y!==d&&(p=!1)}for(a=1;a<i.length&&p;a++){var x=i[a][f];\"type\"===f&&-1!==u.indexOf(h)&&-1!==u.indexOf(x)&&h!==x?m=!0:i[a][f]!==h&&(p=!1)}p&&(g&&(l[o[0]._name].type=\"linear\"),m&&(l[i[0]._name].type=\"linear\"),v(l,f,o,i))}}for(r=0;r<t._fullLayout.annotations.length;r++){var b=t._fullLayout.annotations[r];-1!==e.indexOf(b.xref)&&-1!==n.indexOf(b.yref)&&_.swapAttrs(l.annotations[r],[\"?\"])}}function v(t,e,n,r){var a,o=_.nestedProperty,i=o(t[n[0]._name],e).get(),l=o(t[r[0]._name],e).get();for(\"title\"===e&&(\"Click to enter X axis title\"===i&&(i=\"Click to enter Y axis title\"),\"Click to enter Y axis title\"===l&&(l=\"Click to enter X axis title\")),a=0;a<n.length;a++)o(t,n[a]._name+\".\"+e).set(l);for(a=0;a<r.length;a++)o(t,r[a]._name+\".\"+e).set(i)}function m(t,e){return(t%e+e)%e}var y=t(\"d3\"),x=t(\"fast-isnumeric\"),b=t(\"../../plotly\"),_=t(\"../../lib\"),w=t(\"../../lib/svg_text_utils\"),k=t(\"../../components/titles\"),M=t(\"../../components/color\"),A=t(\"../../components/drawing\"),L=e.exports={};L.layoutAttributes=t(\"./layout_attributes\"),L.supplyLayoutDefaults=t(\"./layout_defaults\"),L.setConvert=t(\"./set_convert\");var T=t(\"./axis_ids\");L.id2name=T.id2name,L.cleanId=T.cleanId,L.list=T.list,L.listIds=T.listIds,L.getFromId=T.getFromId,L.getFromTrace=T.getFromTrace,L.coerceRef=function(t,e,n,r,a){var o=n._fullLayout._has(\"gl2d\")?[]:L.listIds(n,r),i=r+\"ref\",l={};return l[i]={va"
+,
+"lType:\"enumerated\",values:o.concat([\"paper\"]),dflt:a||o[0]||\"paper\"},_.coerce(t,e,l,i)},L.coerceARef=function(t,e,n,r,a){var o=n._fullLayout._has(\"gl2d\")?[]:L.listIds(n,r),i=\"a\"+r+\"ref\",l={};return l[i]={valType:\"enumerated\",values:o.concat([\"pixel\"]),dflt:a||\"pixel\"||o[0]},_.coerce(t,e,l,i)},L.clearTypes=function(t,e){Array.isArray(e)&&e.length||(e=t._fullData.map(function(t,e){return e})),e.forEach(function(e){var n=t.data[e];delete(L.getFromId(t,n.xaxis)||{}).type,delete(L.getFromId(t,n.yaxis)||{}).type})},L.counterLetter=function(t){var e=t.charAt(0);return\"x\"===e?\"y\":\"y\"===e?\"x\":void 0},L.minDtick=function(t,e,n,r){-1===[\"log\",\"category\"].indexOf(t.type)&&r?null===t._minDtick?(t._minDtick=e,t._forceTick0=n):t._minDtick&&((t._minDtick/e+1e-6)%1<2e-6&&((n-t._forceTick0)/e%1+1.000001)%1<2e-6?(t._minDtick=e,t._forceTick0=n):((e/t._minDtick+1e-6)%1>2e-6||((n-t._forceTick0)/t._minDtick%1+1.000001)%1>2e-6)&&(t._minDtick=0)):t._minDtick=0},L.getAutoRange=function(t){var e,n=[],r=t._min[0].val,a=t._max[0].val;for(e=1;e<t._min.length&&r===a;e++)r=Math.min(r,t._min[e].val);for(e=1;e<t._max.length&&r===a;e++)a=Math.max(a,t._max[e].val);var o,i,l,s,c,u,f,d=0,h=t.range&&t.range[1]<t.range[0];for(\"reversed\"===t.autorange&&(h=!0,t.autorange=!0),e=0;e<t._min.length;e++)for(i=t._min[e],o=0;o<t._max.length;o++)l=t._max[o],f=l.val-i.val,u=t._length-i.pad-l.pad,f>0&&u>0&&f/u>d&&(s=i,c=l,d=f/u);return r===a?n=h?[r+1,\"normal\"!==t.rangemode?0:r-1]:[\"normal\"!==t.rangemode?0:r-1,r+1]:d&&(\"linear\"!==t.type&&\"-\"!==t.type||(\"tozero\"===t.rangemode&&s.val>=0?s={val:0,pad:0}:\"nonnegative\"===t.rangemode&&(s.val-d*s.pad<0&&(s={val:0,pad:0}),c.val<0&&(c={val:1,pad:0})),d=(c.val-s.val)/(t._length-s.pad-c.pad)),n=[s.val-d*s.pad,c.val+d*c.pad],n[0]===n[1]&&(n=[n[0]-1,n[0]+1]),h&&n.reverse()),n},L.doAutoRange=function(t){t._length||t.setScale();var e=t._min&&t._max&&t._min.length&&t._max.length;if(t.autorange&&e){t.range=L.getAutoRange(t);var n=t._gd.layout[t._name];n||(t._gd.layout[t._name]=n={}),n!==t&&(n.range=t.range.slice(),n.autorange=t.autorange)}},L.saveRangeInitial=function(t,e){for(var n=L.list(t,\"\",!0),r=!1,a=0;a<n.length;a++){var o=n[a],i=void 0===o._rangeInitial,l=i||!(o.range[0]===o._rangeInitial[0]&&o.range[1]===o._rangeInitial[1]);(i&&o.autorange===!1||e&&l)&&(o._rangeInitial=o.range.slice(),r=!0)}return r};var z=Number.MAX_VALUE/2;L.expand=function(t,e,n){function r(t){if(Array.isArray(t))return function(e){return Math.max(Number(t[e]||0),0)};var e=Math.max(Number(t||0),0);return function(){return e}}function a(n){function r(t){return x(t)&&Math.abs(t)<z}if(s=e[n],x(s)){if(f=b(n)+m,d=_(n)+m,p=s-k(n),g=s+w(n),\"log\"===t.type&&g/10>p&&(p=g/10),c=t.c2l(p),u=t.c2l(g),y&&(c=Math.min(0,c),u=Math.max(0,u)),r(c)){for(h=!0,i=0;i<t._min.length&&h;i++)l=t._min[i],l.val<=c&&l.pad>=d?h=!1:l.val>=c&&l.pad<=d&&(t._min.splice(i,1),i--);h&&t._min.push({val:c,pad:y&&0===c?0:d})}if(r(u)){for(h=!0,i=0;i<t._max.length&&h;i++)l=t._max[i],l.val>=u&&l.pad>=f?h=!1:l.val<=u&&l.pad<=f&&(t._max.splice(i,1),i--);h&&t._max.push({val:u,pad:y&&0===u?0:f})}}}if((t.autorange||t._needsExpand)&&e){t._min||(t._min=[]),t._max||(t._max=[]),n||(n={}),t._m||t.setScale();var o,i,l,s,c,u,f,d,h,p,g,v=e.length,m=n.padded?.05*t._length:0,y=n.tozero&&(\"linear\"===t.type||\"-\"===t.type),b=r((t._m>0?n.ppadplus:n.ppadminus)||n.ppad||0),_=r((t._m>0?n.ppadminus:n.ppadplus)||n.ppad||0),w=r(n.vpadplus||n.vpad),k=r(n.vpadminus||n.vpad);for(o=0;6>o;o++)a(o);for(o=v-1;o>5;o--)a(o)}},L.autoBin=function(t,e,n,r){function a(t){return(1+100*(t-h)/f.dtick)%100<2}var o=_.aggNums(Math.min,null,t),i=_.aggNums(Math.max,null,t);if(\"category\"===e.type)return{start:o-.5,end:i+.5,size:1};var l;if(n)l=(i-o)/n;else{var s=_.distinctVals(t),c=Math.pow(10,Math.floor(Math.log(s.minDiff)/Math.LN10)),u=c*_.roundUp(s.minDiff/c,[.9,1.9,4.9,9.9],!0);l=Math.max(u,2*_.stdev(t)/Math.pow(t.length,r?.25:.4))}var f={type:\"log\"===e.type?\"linear\":e.type,range:[o,i]};L.autoTicks(f,l);var d,h=L.tickIncrement(L.tickFirst(f),f.dtick,\"reverse\");if(\"number\"==typeof f.dtick){for(var p=0,g=0,v=0,m=0,y=0;y<t.length;y++)t[y]%1===0?v++:x(t[y])||m++,a(t[y])&&p++,a(t[y]+f.dtick/2)&&g++;var b=t.length-m;if(v===b&&\"date\"!==e.type)f.dtick<1?h=o-.5*f.dtick:h-=.5;else if(.1*b>g&&(p>.3*b||a(o)||a(i))){var w=f.dtick/2;h+=o>h+w?w:-w}var k=1+Math.floor((i-h)/f.dtick);d=h+k*f.dtick}else for(d=h;i>=d;)d=L.tickIncrement(d,f.dtick);return{start:h,end:d,size:f.dtick}},L.calcTicks=function(t){if(\"array\"===t.tickmode)return r(t);if(\"auto\"===t.tickmode||!t.dtick){var e,n=t.nticks;n||(\"category\"===t.type?(e=t.tickfont?1.2*(t.tickfont.size||12):15,n=t._length/e):(e=\"y\"===t._id.charAt(0)?40:80,n=_.constrain(t._length/e,4,9)+1)),L.autoTicks(t,Math.abs(t.range[1]-t.range[0])/n),t._minDtick>0&&t.dtick<2*t._minDtick&&(t.dtick=t._minDtick,t.tick0=t._forceTick0)}t.tick0||(t.tick0=\"date\"===t.type?new Date(2e3,0,1).getTime():0),o(t),t._tmin=L.tickFirst(t);var a=t.range[1]<t.range[0],i=[],l=1.0001*t.range[1]-1e-4*t.range[0];\"category\"===t.type&&(l=a?Math.max(-.5,l):Math.min(t._categories.length-.5,l));for(var s=t._tmin;(a?s>=l:l>=s)&&(i.push(s),!(i.length>1e3));s=L.tickIncrement(s,t.dtick,a));t._tmax=i[i.length-1];for(var c=new Array(i.length),u=0;u<i.length;u++)c[u]=L.tickText(t,i[u]);return c};var S=[2,5,10],E=[1,2,3,6,12],C=[1,2,5,10,15,30],O=[1,2,3,7,14],P=[-.046,0,.301,.477,.602,.699,.778,.845,.903,.954,1],N=[-.301,0,.301,.699,1];L.autoTicks=function(t,e){var n;if(\"date\"===t.type)t.tick0=new Date(2e3,0,1).getTime(),e>157788e5?(e/=315576e5,n=Math.pow(10,Math.floor(Math.log(e)/Math.LN10)),t.dtick=\"M\"+12*a(e,n,S)):e>12096e5?(e/=26298e5,t.dtick=\"M\"+a(e,1,E)):e>432e5?(t.dtick=a(e,864e5,O),t.tick0=new Date(2e3,0,2).getTime()):e>18e5?t.dtick=a(e,36e5,E):e>3e4?t.dtick=a(e,6e4,C):e>500?t.dtick=a(e,1e3,C):(n=Math.pow(10,Math.floor(Math.log(e)/Math.LN10)),t.dtick=a(e,n,S));else if(\"log\"===t.type)if(t.tick0=0,e>.7)t.dtick=Math.ceil(e);else if(Math.abs(t.range[1]-t.range[0])<1){var r=1.5*Math.abs((t.range[1]-t.range[0])/e);e=Math.abs(Math.pow(10,t.range[1])-Math.pow(10,t.range[0]))/r,n=Math.pow(10,Math.floor(Math.log(e)/Math.LN10)),t.dtick=\"L\"+a(e,n,S)}else t.dtick=e>.3?\"D2\":\"D1\";else\"category\"===t.type?(t.tick0=0,t.dtick=Math.ceil(Math.max(e,1))):(t.tick0=0,n=Math.pow(10,Math.floor(Math.log(e)/Math.LN10)),t.dtick=a(e,n,S));if(0===t.dtick&&(t.dtick=1),!x(t.dtick)&&\"string\"!=typeof t.dtick){var o=t.dtick;throw t.dtick=1,\"ax.dtick error: \"+String(o)}},L.tickIncrement=function(t,e,n){var r=n?-1:1;if(x(e))return t+r*e;var a=e.charAt(0),o=r*Number(e.substr(1));if(\"M\"===a){var i=new Date(t);return i.setMonth(i.getMonth()+o)}if(\"L\"===a)return Math.log(Math.pow(10,t)+o)/Math.LN10;if(\"D\"===a){var l=\"D2\"===e?N:P,s=t+.01*r,c=_.roundUp(m(s,1),l,n);return Math.floor(s)+Math.log(y.round(Math.pow(10,c),1))/Math.LN10}throw\"unrecognized dtick \"+String(e)},L.tickFirst=function(t){var e=t.range[1]<t.range[0],n=e?Math.floor:Math.ceil,r=1.0001*t.range[0]-1e-4*t.range[1],a=t.dtick,o=t.tick0;if(x(a)){var i=n((r-o)/a)*a+o;return\"category\"===t.type&&(i=_.constrain(i,0,t._categories.length-1)),i}var l,s,c,u=a.charAt(0),f=Number(a.substr(1));if(\"M\"===u){for(l=new Date(o),r=new Date(r),s=12*(r.getFullYear()-l.getFullYear())+r.getMonth()-l.getMonth(),c=l.setMonth(l.getMonth()+(Math.round(s/f)+(e?1:-1))*f);e?c>r:r>c;)c=L.tickIncrement(c,a,e);return c}if(\"L\"===u)return Math.log(n((Math.pow(10,r)-o)/f)*f+o)/Math.LN10;if(\"D\"===u){var d=\"D2\"===a?N:P,h=_.roundUp(m(r,1),d,e);return Math.floor(r)+Math.log(y.round(Math.pow(10,h),1))/Math.LN10}throw\"unrecognized dtick \"+String(a)};var D=y.time.format(\"%Y\"),I=y.time.format(\"%b %Y\"),R=y.time.format(\"%b %-d\"),j=y.time.format(\"%b %-d %Hh\"),q=y.time.format(\"%H:%M\"),F=y.time.format(\":%S\"),B=/%(\\d?)f/g;L.tickText=function(t,e,n){function r(r){var a;return void 0===r?!0:n?\"none\"===r:(a={first:t._tmin,last:t._tmax}[r],\"all\"!==r&&e!==a)}var a,o,i=l(t,e),d=\"array\"===t.tickmode,h=n||d;if(d&&Array.isArray(t.ticktext)){var p=Math.abs(t.range[1]-t.range[0])/1e4;for(o=0;o<t.ticktext.length&&!(Math.abs(e-t.d2l(t.tickvals[o]))<p);o++);if(o<t.ticktext.length)return i.text=String(t.ticktext[o]),i}return a=\"none\"!==t.exponentformat&&r(t.showexponent)?\"hide\":\"\",\"date\"===t.type?s(t,i,n,h):\"log\"===t.type?c(t,i,n,h,a):\"category\"===t.type?u(t,i):f(t,i,n,h,a),t.tickprefix&&!r(t.showtickprefix)&&(i.text=t.tickprefix+i.text),t.ticksuffix&&!r(t.showticksuffix)&&(i.text+=t.ticksuffix),i};var H=[\"f\",\"p\",\"n\",\"&mu;\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\"];L.subplotMatch=/^x([0-9]*)y([0-9]*)$/,L.getSubplots=function(t,e){function n(t,e){return-1!==t.indexOf(e._id)}var r,a,o,i=[],l=t.data||[];for(r=0;r<l.length;r++){var s=l[r];if(s.visible!==!1&&\"legendonly\"!==s.visible&&(b.Plots.traceIs(s,\"cartesian\")||b.Plots.traceIs(s,\"gl2d\"))){var c=s.xaxis||\"x\",u=s.yaxis||\"y\";o=c+u,-1===i.indexOf(o)&&i.push(o)}}var f=L.list(t,\"\",!0);for(r=0;r<f.length;r++){var d=f[r],h=d._id.charAt(0),p=\"free\"===d.anchor?\"x\"===h?\"y\":\"x\":d.anchor,g=L.getFromId(t,p),v=!1;for(a=0;a<i.length;a++)if(n(i[a],d)){v=!0;break}\"free\"===d.anchor&&v||g&&(o=\"x\"===h?d._id+g._id:g._id+d._id,-1===i.indexOf(o)&&i.push(o))}var m=L.subplotMatch,y=[];for(r=0;r<i.length;r++)o=i[r],m.test(o)&&y.push(o);return y.sort(function(t,e){var n=t.match(m),r=e.match(m);return n[1]===r[1]?+(n[2]||1)-(r[2]||1):+(n[1]||0)-(r[1]||0)}),e?L.findSubplotsWithAxis(y,e):y},L.findSubplotsWithAxis=function(t,e){for(var n=new RegExp(\"x\"===e._id.charAt(0)?\"^\"+e._id+\"y\":e._id+\"$\"),r=[],a=0;a<t.length;a++){var o=t[a];n.test(o)&&r.push(o)}return r},L.makeClipPaths=function(t){var e,n,r=t._fullLayout,a=r._defs,o={_offset:0,_length:r.width,_id:\"\"},i={_offset:0,_length:r.height,_id:\"\"},l=L.list(t,\"x\",!0),s=L.list(t,\"y\",!0),c=[];for(e=0;e<l.length;e++)for(c.push({x:l[e],y:i}),n=0;n<s.length;n++)0===e&&c.push({x:o,y:s[n]}),c.push({x:l[e],y:s[n]});var u=a.selectAll(\"g.clips\").data([0]);u.enter().append(\"g\").classed(\"clips\",!0);var f=u.selectAll(\".axesclip\").data(c,function(t){return t.x._id+t.y._id});f.enter().append(\"clipPath\").classed(\"axesclip\",!0).attr(\"id\",function(t){return\"clip\"+r"
+,
+"._uid+t.x._id+t.y._id}).append(\"rect\"),f.exit().remove(),f.each(function(t){y.select(this).select(\"rect\").attr({x:t.x._offset||0,y:t.y._offset||0,width:t.x._length||1,height:t.y._length||1})})},L.doTicks=function(t,e,n){function r(t){var e=c.l2p(t.x);return e>1&&e<c._length-1}function a(t,e){var n=t.selectAll(\"path.\"+E).data(\"inside\"===c.ticks?H:z,S);e&&c.ticks?(n.enter().append(\"path\").classed(E,1).classed(\"ticks\",1).classed(\"crisp\",1).call(M.stroke,c.tickcolor).style(\"stroke-width\",j+\"px\").attr(\"d\",e),n.attr(\"transform\",h),n.exit().remove()):n.remove()}function o(n,r){function a(t,e){t.each(function(t){var n=p(e),r=y.select(this),a=r.select(\".text-math-group\"),o=h(t)+(x(e)&&0!==+e?\" rotate(\"+e+\",\"+f(t)+\",\"+(d(t)-t.fontSize/2)+\")\":\"\");if(a.empty()){var i=r.select(\"text\").attr({transform:o,\"text-anchor\":n});i.empty()||i.selectAll(\"tspan.line\").attr({x:i.attr(\"x\"),y:i.attr(\"y\")})}else{var l=A.bBox(a.node()).width*{end:-.5,start:.5}[n];a.attr(\"transform\",o+(l?\"translate(\"+l+\",0)\":\"\"))}})}function o(){return L.length&&Promise.all(L)}function l(){if(a(u,c.tickangle),\"x\"===v&&!x(c.tickangle)&&(\"log\"!==c.type||\"D\"!==String(c.dtick).charAt(0))){var t=[];for(u.each(function(e){var n=y.select(this),r=n.select(\".text-math-group\"),a=c.l2p(e.x);r.empty()&&(r=n.select(\"text\"));var o=A.bBox(r.node());t.push({top:0,bottom:10,height:10,left:a-o.width/2,right:a+o.width/2+2,width:o.width+2})}),g=0;g<t.length-1;g++)if(_.bBoxIntersect(t[g],t[g+1])){M=30;break}if(M){var n=Math.abs((z[z.length-1].x-z[0].x)*c._m)/(z.length-1);2.5*k>n&&(M=90),a(u,M)}c._lastangle=M}return i(e),e+\" done\"}function s(){c._boundingBox=n.node().getBoundingClientRect()}var u=n.selectAll(\"g.\"+E).data(z,S);if(!c.showticklabels||!x(r))return u.remove(),void i(e);var f,d,p,m,b;\"x\"===v?(b=\"bottom\"===F?1:-1,f=function(t){return t.dx+D*b},m=r+(N+P)*b,d=function(t){return t.dy+m+t.fontSize*(\"bottom\"===F?1:-.5)},p=function(t){return x(t)&&0!==t&&180!==t?0>t*b?\"end\":\"start\":\"middle\"}):(b=\"right\"===F?1:-1,d=function(t){return t.dy+t.fontSize/2-D*b},f=function(t){return t.dx+r+(N+P+(90===Math.abs(c.tickangle)?t.fontSize/2:0))*b},p=function(t){return x(t)&&90===Math.abs(t)?\"middle\":\"right\"===F?\"start\":\"end\"});var k=0,M=0,L=[];u.enter().append(\"g\").classed(E,1).append(\"text\").attr(\"text-anchor\",\"middle\").each(function(e){var n=y.select(this),r=t._promises.length;n.call(A.setPosition,f(e),d(e)).call(A.font,e.font,e.fontSize,e.fontColor).text(e.text).call(w.convertToTspans),r=t._promises[r],r?L.push(t._promises.pop().then(function(){a(n,c.tickangle)})):a(n,c.tickangle)}),u.exit().remove(),u.each(function(t){k=Math.max(k,t.fontSize)}),a(u,c._lastangle||c.tickangle);var T=_.syncOrAsync([o,l,s]);return T&&T.then&&t._promises.push(T),T}function i(e){if(!n){var r,a,o,i,l=T.getFromId(t,e),s=y.select(t).selectAll(\"g.\"+e+\"tick\"),c={selection:s,side:l.side},f=e.charAt(0),d=t._fullLayout._size,h=1.5,p=l.titlefont.size;if(s.size()){var g=y.select(s.node().parentNode).attr(\"transform\").match(/translate\\(([-\\.\\d]+),([-\\.\\d]+)\\)/);g&&(c.offsetLeft=+g[1],c.offsetTop=+g[2])}\"x\"===f?(a=\"free\"===l.anchor?{_offset:d.t+(1-(l.position||0))*d.h,_length:0}:T.getFromId(t,l.anchor),o=l._offset+l._length/2,i=a._offset+(\"top\"===l.side?-10-p*(h+(l.showticklabels?1:0)):a._length+10+p*(h+(l.showticklabels?1.5:.5))),l.rangeslider&&l.rangeslider.visible&&l._boundingBox&&(i+=(u.height-u.margin.b-u.margin.t)*l.rangeslider.thickness+l._boundingBox.height),c.side||(c.side=\"bottom\")):(a=\"free\"===l.anchor?{_offset:d.l+(l.position||0)*d.w,_length:0}:T.getFromId(t,l.anchor),i=l._offset+l._length/2,o=a._offset+(\"right\"===l.side?a._length+10+p*(h+(l.showticklabels?1:.5)):-10-p*(h+(l.showticklabels?.5:0))),r={rotate:\"-90\",offset:0},c.side||(c.side=\"left\")),k.draw(t,e+\"title\",{propContainer:l,propName:l._name+\".title\",dfltName:f.toUpperCase()+\" axis\",avoid:c,transform:r,attributes:{x:o,y:i,\"text-anchor\":\"middle\"}})}}function l(t,e){return t.visible!==!0||t.xaxis+t.yaxis!==e?!1:b.Plots.traceIs(t,\"bar\")&&t.orientation==={x:\"h\",y:\"v\"}[v]?!0:t.fill&&t.fill.charAt(t.fill.length-1)===v}function s(e,n,a){var o=e.gridlayer,i=e.zerolinelayer,s=e[\"hidegrid\"+v]?[]:H,u=c._gridpath||\"M0,0\"+(\"x\"===v?\"v\":\"h\")+n._length,f=o.selectAll(\"path.\"+C).data(c.showgrid===!1?[]:s,S);if(f.enter().append(\"path\").classed(C,1).classed(\"crisp\",1).attr(\"d\",u).each(function(t){c.zeroline&&(\"linear\"===c.type||\"-\"===c.type)&&Math.abs(t.x)<c.dtick/100&&y.select(this).remove()}),f.attr(\"transform\",h).call(M.stroke,c.gridcolor||\"#ddd\").style(\"stroke-width\",I+\"px\"),f.exit().remove(),i){for(var d=!1,p=0;p<t._fullData.length;p++)if(l(t._fullData[p],a)){d=!0;break}var g=c.range[0]*c.range[1]<=0&&c.zeroline&&(\"linear\"===c.type||\"-\"===c.type)&&s.length&&(d||r({x:0})||!c.showline),m=i.selectAll(\"path.\"+O).data(g?[{x:0}]:[]);m.enter().append(\"path\").classed(O,1).classed(\"zl\",1).classed(\"crisp\",1).attr(\"d\",u),m.attr(\"transform\",h).call(M.stroke,c.zerolinecolor||M.defaultLine).style(\"stroke-width\",R+\"px\"),m.exit().remove()}}var c,u=t._fullLayout,f=!1;if(\"object\"==typeof e)c=e,e=c._id,f=!0;else if(c=L.getFromId(t,e),\"redraw\"===e&&u._paper.selectAll(\"g.subplot\").each(function(t){var e=u._plots[t],n=e.x(),r=e.y();e.xaxislayer.selectAll(\".\"+n._id+\"tick\").remove(),e.yaxislayer.selectAll(\".\"+r._id+\"tick\").remove(),e.gridlayer.selectAll(\"path\").remove(),e.zerolinelayer.selectAll(\"path\").remove()}),!e||\"redraw\"===e)return _.syncOrAsync(L.list(t,\"\",!0).map(function(n){return function(){if(n._id){var r=L.doTicks(t,n._id);return\"redraw\"===e&&(n._r=n.range.slice()),r}}}));c.tickformat||(-1===[\"none\",\"e\",\"E\",\"power\",\"SI\",\"B\"].indexOf(c.exponentformat)&&(c.exponentformat=\"e\"),-1===[\"all\",\"first\",\"last\",\"none\"].indexOf(c.showexponent)&&(c.showexponent=\"all\")),c.range=[+c.range[0],+c.range[1]],c.setScale();var d,h,p,g,v=e.charAt(0),m=L.counterLetter(e),z=L.calcTicks(c),S=function(t){return t.text+t.x+c.mirror},E=e+\"tick\",C=e+\"grid\",O=e+\"zl\",P=(c.linewidth||1)/2,N=(\"outside\"===c.ticks?c.ticklen:1)+(c.linewidth||0),D=0,I=A.crispRound(t,c.gridwidth,1),R=A.crispRound(t,c.zerolinewidth,I),j=A.crispRound(t,c.tickwidth,1);if(c._counterangle&&\"outside\"===c.ticks){var q=c._counterangle*Math.PI/180;N=c.ticklen*Math.cos(q)+(c.linewidth||0),D=c.ticklen*Math.sin(q)}if(\"x\"===v)d=[\"bottom\",\"top\"],h=function(t){return\"translate(\"+c.l2p(t.x)+\",0)\"},p=function(t,e){if(c._counterangle){var n=c._counterangle*Math.PI/180;return\"M0,\"+t+\"l\"+Math.sin(n)*e+\",\"+Math.cos(n)*e}return\"M0,\"+t+\"v\"+e};else{if(\"y\"!==v)return void _.warn(\"Unrecognized doTicks axis:\",e);d=[\"left\",\"right\"],h=function(t){return\"translate(0,\"+c.l2p(t.x)+\")\"},p=function(t,e){if(c._counterangle){var n=c._counterangle*Math.PI/180;return\"M\"+t+\",0l\"+Math.cos(n)*e+\",\"+-Math.sin(n)*e}return\"M\"+t+\",0h\"+e}}var F=c.side||d[0],B=[-1,1,F===d[1]?1:-1];\"inside\"!==c.ticks==(\"x\"===v)&&(B=B.map(function(t){return-t}));var H=z.filter(r);if(f){if(a(c._axislayer,p(c._pos+P*B[2],B[2]*c.ticklen)),c._counteraxis){var V={gridlayer:c._gridlayer,zerolinelayer:c._zerolinelayer};s(V,c._counteraxis)}return o(c._axislayer,c._pos)}var Z=L.getSubplots(t,c).map(function(t){var e=u._plots[t];if(u._has(\"cartesian\")){var n=e[v+\"axislayer\"],r=c._linepositions[t]||[],i=e[m](),l=i._id===c.anchor,f=[!1,!1,!1],h=\"\";if(\"allticks\"===c.mirror?f=[!0,!0,!1]:l&&(\"ticks\"===c.mirror?f=[!0,!0,!1]:f[d.indexOf(F)]=!0),c.mirrors)for(g=0;2>g;g++){var y=c.mirrors[i._id+d[g]];\"ticks\"!==y&&\"labels\"!==y||(f[g]=!0)}return void 0!==r[2]&&(f[2]=!0),f.forEach(function(t,e){var n=r[e],a=B[e];t&&x(n)&&(h+=p(n+P*a,a*c.ticklen))}),a(n,h),s(e,i,t),o(n,r[3])}}).filter(function(t){return t&&t.then});return Z.length?Promise.all(Z):0},L.swap=function(t,e){for(var n=h(t,e),r=0;r<n.length;r++)g(t,n[r].x,n[r].y)}},{\"../../components/color\":18,\"../../components/drawing\":41,\"../../components/titles\":81,\"../../lib\":89,\"../../lib/svg_text_utils\":100,\"../../plotly\":107,\"./axis_ids\":112,\"./layout_attributes\":119,\"./layout_defaults\":120,\"./set_convert\":124,d3:9,\"fast-isnumeric\":11}],111:[function(t,e,n){\"use strict\";function r(t,e){if(\"-\"===t.type){var n=t._id,r=n.charAt(0);-1!==n.indexOf(\"scene\")&&(n=r);var s=l(e,n,r);if(s){if(\"histogram\"===s.type&&r==={v:\"y\",h:\"x\"}[s.orientation||\"v\"])return void(t.type=\"linear\");if(o(s,r)){for(var c,u=a(s),f=[],d=0;d<e.length;d++)c=e[d],p.traceIs(c,\"box\")&&(c[r+\"axis\"]||r)===n&&(void 0!==c[u]?f.push(c[u][0]):void 0!==c.name?f.push(c.name):f.push(\"text\"));t.type=i(f)}else t.type=i(s[r]||[s[r+\"0\"]])}}}function a(t){return{v:\"x\",h:\"y\"}[t.orientation||\"v\"]}function o(t,e){var n=a(t);return p.traceIs(t,\"box\")&&e===n&&void 0===t[n]&&void 0===t[n+\"0\"]}function i(t){return c(t)?\"date\":u(t)?\"category\":s(t)?\"linear\":\"-\"}function l(t,e,n){for(var r=0;r<t.length;r++){var a=t[r];if((a[n+\"axis\"]||n)===e){if(o(a,n))return a;if((a[n]||[]).length||a[n+\"0\"])return a}}}function s(t){if(!t)return!1;for(var e=0;e<t.length;e++)if(f(t[e]))return!0;return!1}function c(t){for(var e,n=0,r=0,a=Math.max(1,(t.length-1)/1e3),o=0;o<t.length;o+=a)e=t[Math.round(o)],h.isDateTime(e)&&(n+=1),f(e)&&(r+=1);return n>2*r}function u(t){for(var e,n=Math.max(1,(t.length-1)/1e3),r=0,a=0,o=0;o<t.length;o+=n)e=k(t[Math.round(o)]),f(e)?r++:\"string\"==typeof e&&\"\"!==e&&\"None\"!==e&&a++;return a>2*r}var f=t(\"fast-isnumeric\"),d=t(\"tinycolor2\").mix,h=t(\"../../lib\"),p=t(\"../plots\"),g=t(\"../../components/color/attributes\").lightFraction,v=t(\"./layout_attributes\"),m=t(\"./tick_value_defaults\"),y=t(\"./tick_mark_defaults\"),x=t(\"./tick_label_defaults\"),b=t(\"./category_order_defaults\"),_=t(\"./set_convert\"),w=t(\"./ordered_categories\"),k=t(\"./clean_datum\"),M=t(\"./axis_ids\");e.exports=function(t,e,n,a){function o(n,r){return h.coerce2(t,e,v,n,r)}var i=a.letter,l=a.font||{},s=\"Click to enter \"+(a.title||i.toUpperCase()+\" axis\")+\" title\";a.name&&(e._name=a.name,e._id=M.name2id(a.name));var c=n(\"type\");\"-\"===c&&(r(e,a.data),\"-\"===e.type?e.type=\"linear\":c=t.type=e.type),_(e);var u=n(\"color\"),p=u===t.color?u:l.color;n(\"title\",s),h.coerceFont(n,\"titlefont\",{family:l.family,size:Math.round(1.2*l.size),color:p});var k=2===(t.range||[]).length&&"
+,
+"f(t.range[0])&&f(t.range[1]),A=n(\"autorange\",!k);A&&n(\"rangemode\");var L=n(\"range\",[-1,\"x\"===i?6:4]);L[0]===L[1]&&(e.range=[L[0]-1,L[0]+1]),h.noneOrAll(t.range,e.range,[0,1]),n(\"fixedrange\"),m(t,e,n,c),x(t,e,n,c,a),y(t,e,n,a),b(t,e,n);var T=o(\"linecolor\",u),z=o(\"linewidth\"),S=n(\"showline\",!!T||!!z);S||(delete e.linecolor,delete e.linewidth),(S||e.ticks)&&n(\"mirror\");var E=o(\"gridcolor\",d(u,a.bgColor,g).toRgbString()),C=o(\"gridwidth\"),O=n(\"showgrid\",a.showGrid||!!E||!!C);O||(delete e.gridcolor,delete e.gridwidth);var P=o(\"zerolinecolor\",u),N=o(\"zerolinewidth\"),D=n(\"zeroline\",a.showGrid||!!P||!!N);return D||(delete e.zerolinecolor,delete e.zerolinewidth),e._initialCategories=\"category\"===c?w(i,e.categoryorder,e.categoryarray,a.data):[],e}},{\"../../components/color/attributes\":17,\"../../lib\":89,\"../plots\":130,\"./axis_ids\":112,\"./category_order_defaults\":113,\"./clean_datum\":114,\"./layout_attributes\":119,\"./ordered_categories\":121,\"./set_convert\":124,\"./tick_label_defaults\":125,\"./tick_mark_defaults\":126,\"./tick_value_defaults\":127,\"fast-isnumeric\":11,tinycolor2:13}],112:[function(t,e,n){\"use strict\";function r(t,e,n){function r(t,n){for(var r=Object.keys(t),a=/^[xyz]axis[0-9]*/,o=[],i=0;i<r.length;i++){var l=r[i];e&&l.charAt(0)!==e||a.test(l)&&o.push(n+l)}return o.sort()}var o=t._fullLayout;if(!o)return[];var i=r(o,\"\");if(n)return i;for(var l=a.getSubplotIds(o,\"gl3d\")||[],s=0;s<l.length;s++){var c=l[s];i=i.concat(r(o[c],c+\".\"))}return i}var a=t(\"../plots\"),o=t(\"../../lib\"),i=t(\"./constants\");n.id2name=function(t){if(\"string\"==typeof t&&t.match(i.AX_ID_PATTERN)){var e=t.substr(1);return\"1\"===e&&(e=\"\"),t.charAt(0)+\"axis\"+e}},n.name2id=function(t){if(t.match(i.AX_NAME_PATTERN)){var e=t.substr(5);return\"1\"===e&&(e=\"\"),t.charAt(0)+e}},n.cleanId=function(t,e){if(t.match(i.AX_ID_PATTERN)&&(!e||t.charAt(0)===e)){var n=t.substr(1).replace(/^0+/,\"\");return\"1\"===n&&(n=\"\"),t.charAt(0)+n}},n.list=function(t,e,n){return r(t,e,n).map(function(e){return o.nestedProperty(t._fullLayout,e).get();\n"
+,
+"})},n.listIds=function(t,e){return r(t,e,!0).map(n.name2id)},n.getFromId=function(t,e,r){var a=t._fullLayout;return\"x\"===r?e=e.replace(/y[0-9]*/,\"\"):\"y\"===r&&(e=e.replace(/x[0-9]*/,\"\")),a[n.id2name(e)]},n.getFromTrace=function(t,e,r){var o=t._fullLayout,i=null;if(a.traceIs(e,\"gl3d\")){var l=e.scene;\"scene\"===l.substr(0,5)&&(i=o[l][r+\"axis\"])}else i=n.getFromId(t,e[r+\"axis\"]||r);return i}},{\"../../lib\":89,\"../plots\":130,\"./constants\":115}],113:[function(t,e,n){\"use strict\";e.exports=function(t,e,n){if(\"category\"===e.type){var r,a=t.categoryarray,o=Array.isArray(a)&&a.length>0;o&&(r=\"array\");var i=n(\"categoryorder\",r);\"array\"===i&&n(\"categoryarray\"),o||\"array\"!==i||(e.categoryorder=\"trace\")}}},{}],114:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../lib\");e.exports=function(t){try{if(\"object\"==typeof t&&null!==t&&t.getTime)return a.ms2DateTime(t);if(\"string\"!=typeof t&&!r(t))return\"\";t=t.toString().replace(/['\"%,$# ]/g,\"\")}catch(e){a.error(e,t)}return t}},{\"../../lib\":89,\"fast-isnumeric\":11}],115:[function(t,e,n){\"use strict\";e.exports={idRegex:{x:/^x([2-9]|[1-9][0-9]+)?$/,y:/^y([2-9]|[1-9][0-9]+)?$/},attrRegex:{x:/^xaxis([2-9]|[1-9][0-9]+)?$/,y:/^yaxis([2-9]|[1-9][0-9]+)?$/},BADNUM:void 0,xAxisMatch:/^xaxis[0-9]*$/,yAxisMatch:/^yaxis[0-9]*$/,AX_ID_PATTERN:/^[xyz][0-9]*$/,AX_NAME_PATTERN:/^[xyz]axis[0-9]*$/,DBLCLICKDELAY:300,MINDRAG:8,MINSELECT:12,MINZOOM:20,DRAGGERSIZE:20,MAXDIST:20,YANGLE:60,HOVERARROWSIZE:6,HOVERTEXTPAD:3,HOVERFONTSIZE:13,HOVERFONT:\"Arial, sans-serif\",HOVERMINTIME:50,BENDPX:1.5,REDRAWDELAY:50}},{}],116:[function(t,e,n){\"use strict\";function r(t,e){var n,r=t.range[e],a=Math.abs(r-t.range[1-e]);return\"date\"===t.type?u.ms2DateTime(r,a):\"log\"===t.type?(n=Math.ceil(Math.max(0,-Math.log(a)/Math.LN10))+3,l.format(\".\"+n+\"g\")(Math.pow(10,r))):(n=Math.floor(Math.log(Math.abs(r))/Math.LN10)-Math.floor(Math.log(a)/Math.LN10)+4,l.format(\".\"+String(n)+\"g\")(r))}function a(t,e){return t?\"nsew\"===t?\"pan\"===e?\"move\":\"crosshair\":t.toLowerCase()+\"-resize\":\"pointer\"}function o(t){l.select(t).selectAll(\".zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners\").remove()}function i(t){var e=[\"lasso\",\"select\"];return-1!==e.indexOf(t)}var l=t(\"d3\"),s=t(\"tinycolor2\"),c=t(\"../../plotly\"),u=t(\"../../lib\"),f=t(\"../../lib/svg_text_utils\"),d=t(\"../../components/color\"),h=t(\"../../components/drawing\"),p=t(\"../../lib/setcursor\"),g=t(\"../../components/dragelement\"),v=t(\"./axes\"),m=t(\"./select\"),y=t(\"./constants\"),x=!0;e.exports=function(t,e,n,l,b,_,w,k){function M(t,e){for(var n=0;n<t.length;n++)if(!t[n].fixedrange)return e;return\"\"}function A(t){t[0]=Number(t[0]),t[1]=Number(t[1])}function L(e,n,r){var a=nt.getBoundingClientRect();at=n-a.left,ot=r-a.top,it={l:at,r:at,w:0,t:ot,b:ot,h:0},lt=t._hmpixcount?t._hmlumcount/t._hmpixcount:s(t._fullLayout.plot_bgcolor).getLuminance(),st=\"M0,0H\"+H+\"V\"+V+\"H0V0\",ct=!1,ut=\"xy\",ft=ht.append(\"path\").attr(\"class\",\"zoombox\").style({fill:lt>.2?\"rgba(0,0,0,0)\":\"rgba(255,255,255,0)\",\"stroke-width\":0}).attr(\"transform\",\"translate(\"+pt+\", \"+gt+\")\").attr(\"d\",st+\"Z\"),dt=ht.append(\"path\").attr(\"class\",\"zoombox-corners\").style({fill:d.background,stroke:d.defaultLine,\"stroke-width\":1,opacity:0}).attr(\"transform\",\"translate(\"+pt+\", \"+gt+\")\").attr(\"d\",\"M0,0Z\"),T();for(var o=0;o<Q.length;o++)A(Q[o].range)}function T(){ht.selectAll(\".select-outline\").remove()}function z(t,e){var n=Math.max(0,Math.min(H,t+at)),r=Math.max(0,Math.min(V,e+ot)),a=Math.abs(n-at),o=Math.abs(r-ot),i=Math.floor(Math.min(o,a,Y)/2);it.l=Math.min(at,n),it.r=Math.max(at,n),it.t=Math.min(ot,r),it.b=Math.max(ot,r),!J||o<Math.min(Math.max(.6*a,Z),Y)?Z>a?(ut=\"\",it.r=it.l,it.t=it.b,dt.attr(\"d\",\"M0,0Z\")):(it.t=0,it.b=V,ut=\"x\",dt.attr(\"d\",\"M\"+(it.l-.5)+\",\"+(ot-Y-.5)+\"h-3v\"+(2*Y+1)+\"h3ZM\"+(it.r+.5)+\",\"+(ot-Y-.5)+\"h3v\"+(2*Y+1)+\"h-3Z\")):!W||a<Math.min(.6*o,Y)?(it.l=0,it.r=H,ut=\"y\",dt.attr(\"d\",\"M\"+(at-Y-.5)+\",\"+(it.t-.5)+\"v-3h\"+(2*Y+1)+\"v3ZM\"+(at-Y-.5)+\",\"+(it.b+.5)+\"v3h\"+(2*Y+1)+\"v-3Z\")):(ut=\"xy\",dt.attr(\"d\",\"M\"+(it.l-3.5)+\",\"+(it.t-.5+i)+\"h3v\"+-i+\"h\"+i+\"v-3h-\"+(i+3)+\"ZM\"+(it.r+3.5)+\",\"+(it.t-.5+i)+\"h-3v\"+-i+\"h\"+-i+\"v-3h\"+(i+3)+\"ZM\"+(it.r+3.5)+\",\"+(it.b+.5-i)+\"h-3v\"+i+\"h\"+-i+\"v3h\"+(i+3)+\"ZM\"+(it.l-3.5)+\",\"+(it.b+.5-i)+\"h3v\"+i+\"h\"+i+\"v3h-\"+(i+3)+\"Z\")),it.w=it.r-it.l,it.h=it.b-it.t,ft.attr(\"d\",st+\"M\"+it.l+\",\"+it.t+\"v\"+it.h+\"h\"+it.w+\"v-\"+it.h+\"h-\"+it.w+\"Z\"),ct||(ft.transition().style(\"fill\",lt>.2?\"rgba(0,0,0,0.4)\":\"rgba(255,255,255,0.3)\").duration(200),dt.transition().style(\"opacity\",1).duration(200),ct=!0)}function S(t,e,n){var r,a,o;for(r=0;r<t.length;r++)a=t[r],a.fixedrange||(o=a.range,a.range=[o[0]+(o[1]-o[0])*e,o[0]+(o[1]-o[0])*n])}function E(e,n){return Math.min(it.h,it.w)<2*Z?(2===n&&D(),o(t)):(\"xy\"!==ut&&\"x\"!==ut||S(F,it.l/H,it.r/H),\"xy\"!==ut&&\"y\"!==ut||S(B,(V-it.b)/V,(V-it.t)/V),o(t),I(ut),void(x&&t.data&&t._context.showTips&&(u.notifier(\"Double-click to<br>zoom back out\",\"long\"),x=!1)))}function C(e,n){var a=1===(w+k).length;if(e)I();else if(2!==n||a){if(1===n&&a){var o=w?B[0]:F[0],i=\"s\"===w||\"w\"===k?0:1,l=o._name+\".range[\"+i+\"]\",s=r(o,i),u=\"left\",d=\"middle\";if(o.fixedrange)return;w?(d=\"n\"===w?\"top\":\"bottom\",\"right\"===o.side&&(u=\"right\")):\"e\"===k&&(u=\"right\"),et.call(f.makeEditable,null,{immediate:!0,background:j.paper_bgcolor,text:String(s),fill:o.tickfont?o.tickfont.color:\"#444\",horizontalAlign:u,verticalAlign:d}).on(\"edit\",function(e){var n=\"category\"===o.type?o.c2l(e):o.d2l(e);void 0!==n&&c.relayout(t,l,n)})}}else D()}function O(e){function n(t,e,n){if(!t.fixedrange){A(t.range);var r=t.range,a=r[0]+(r[1]-r[0])*e;t.range=[a+(r[0]-a)*n,a+(r[1]-a)*n]}}if(t._context.scrollZoom||j._enablescrollzoom){var r=t.querySelector(\".plotly\");if(!(r.scrollHeight-r.clientHeight>10||r.scrollWidth-r.clientWidth>10)){clearTimeout(mt);var a=-e.deltaY;if(isFinite(a)||(a=e.wheelDelta/10),!isFinite(a))return void u.log(\"Did not find wheel motion attributes: \",e);var o,i=Math.exp(-Math.min(Math.max(a,-20),20)/100),l=xt.draglayer.select(\".nsewdrag\").node().getBoundingClientRect(),s=(e.clientX-l.left)/l.width,c=vt[0]+vt[2]*s,f=(l.bottom-e.clientY)/l.height,d=vt[1]+vt[3]*(1-f);if(k){for(o=0;o<F.length;o++)n(F[o],s,i);vt[2]*=i,vt[0]=c-vt[2]*s}if(w){for(o=0;o<B.length;o++)n(B[o],f,i);vt[3]*=i,vt[1]=d-vt[3]*(1-f)}return R(vt),N(w,k),mt=setTimeout(function(){vt=[0,0,H,V],I()},yt),u.pauseEvent(e)}}}function P(t,e){function n(t,e){for(var n=0;n<t.length;n++){var r=t[n];r.fixedrange||(r.range=[r._r[0]-e/r._m,r._r[1]-e/r._m])}}function r(t){return 1-(t>=0?Math.min(t,.9):1/(1/Math.max(t,-.3)+3.222))}function a(t,e,n){for(var a=1-e,o=0,i=0;i<t.length;i++){var l=t[i];l.fixedrange||(o=i,l.range[e]=l._r[a]+(l._r[e]-l._r[a])/r(n/l._length))}return t[o]._length*(t[o]._r[e]-t[o].range[e])/(t[o]._r[e]-t[o]._r[a])}return\"ew\"===W||\"ns\"===J?(W&&n(F,t),J&&n(B,e),R([W?-t:0,J?-e:0,H,V]),void N(J,W)):(\"w\"===W?t=a(F,0,t):\"e\"===W?t=a(F,1,-t):W||(t=0),\"n\"===J?e=a(B,1,e):\"s\"===J?e=a(B,0,-e):J||(e=0),R([\"w\"===W?t:0,\"n\"===J?e:0,H-t,V-e]),void N(J,W))}function N(e,n){function r(t){for(o=0;o<t.length;o++)t[o].fixedrange||i.push(t[o]._id)}function a(r,a){var l;for(o=0;o<r.length;o++)l=r[o],(n&&-1!==i.indexOf(l.xref)||e&&-1!==i.indexOf(l.yref))&&a.draw(t,o)}var o,i=[];for(n&&r(F),e&&r(B),o=0;o<i.length;o++)v.doTicks(t,i[o],!0);a(j.annotations||[],c.Annotations),a(j.shapes||[],c.Shapes),a(j.images||[],c.Images)}function D(){var e,n,r,a=t._context.doubleClick,o=(W?F:[]).concat(J?B:[]),i={};if(\"autosize\"===a)for(n=0;n<o.length;n++)e=o[n],e.fixedrange||(i[e._name+\".autorange\"]=!0);else if(\"reset\"===a)for(n=0;n<o.length;n++)e=o[n],e._rangeInitial?(r=e._rangeInitial.slice(),i[e._name+\".range[0]\"]=r[0],i[e._name+\".range[1]\"]=r[1]):i[e._name+\".autorange\"]=!0;else if(\"reset+autosize\"===a)for(n=0;n<o.length;n++)e=o[n],e.fixedrange||(void 0===e._rangeInitial||e.range[0]===e._rangeInitial[0]&&e.range[1]===e._rangeInitial[1]?i[e._name+\".autorange\"]=!0:(r=e._rangeInitial.slice(),i[e._name+\".range[0]\"]=r[0],i[e._name+\".range[1]\"]=r[1]));t.emit(\"plotly_doubleclick\",null),c.relayout(t,i)}function I(e){for(var n={},r=0;r<Q.length;r++){var a=Q[r];e&&-1===e.indexOf(a._id.charAt(0))||(a._r[0]!==a.range[0]&&(n[a._name+\".range[0]\"]=a.range[0]),a._r[1]!==a.range[1]&&(n[a._name+\".range[1]\"]=a.range[1]),a.range=a._r.slice())}R([0,0,H,V]),c.relayout(t,n)}function R(t){for(var e=j._plots,n=Object.keys(e),r=0;r<n.length;r++){var a=e[n[r]],o=a.x(),i=a.y(),l=k&&-1!==F.indexOf(o)&&!o.fixedrange,s=w&&-1!==B.indexOf(i)&&!i.fixedrange,c=l?o._length/t[2]:1,f=s?i._length/t[3]:1,d=l?t[0]:0,h=s?t[1]:0,p=l?t[0]/t[2]*o._length:0,g=s?t[1]/t[3]*i._length:0,v=o._offset-p,m=i._offset-g;j._defs.selectAll(\"#\"+a.clipId).call(u.setTranslate,d,h).call(u.setScale,1/c,1/f),a.plot.call(u.setTranslate,v,m).call(u.setScale,c,f).selectAll(\".points\").selectAll(\".point\").call(u.setPointGroupScale,1/c,1/f)}}for(var j=t._fullLayout,q=[e].concat(w&&k?e.overlays:[]),F=[e.x()],B=[e.y()],H=F[0]._length,V=B[0]._length,Z=y.MINDRAG,Y=y.MINZOOM,U=w+k===\"nsew\",X=1;X<q.length;X++){var G=q[X].x(),$=q[X].y();-1===F.indexOf(G)&&F.push(G),-1===B.indexOf($)&&B.push($)}var Q=F.concat(B),W=M(F,k),J=M(B,w),K=a(J+W,j.dragmode),tt=w+k+\"drag\",et=e.draglayer.selectAll(\".\"+tt).data([0]);et.enter().append(\"rect\").classed(\"drag\",!0).classed(tt,!0).style({fill:\"transparent\",\"stroke-width\":0}).attr(\"data-subplot\",e.id),et.call(h.setRect,n,l,b,_).call(p,K);var nt=et.node();if(!J&&!W&&!i(j.dragmode))return nt.onmousedown=null,nt.style.pointerEvents=U?\"all\":\"none\",nt;var rt={element:nt,gd:t,plotinfo:e,xaxes:F,yaxes:B,doubleclick:D,prepFn:function(e,n,r){var a=t._fullLayout.dragmode;U?e.shiftKey&&(a=\"pan\"===a?\"zoom\":\"pan\"):a=\"pan\",\"lasso\"===a?rt.minDrag=1:rt.minDrag=void 0,\"zoom\"===a?(rt.moveFn=z,rt.doneFn=E,L(e,n,r)):\"pan\"===a?(rt.moveFn=P,rt.doneFn=C,T()):i(a)&&m(e,n,r,rt,a)}};g.init(rt);var at,ot,it,lt,st,ct,ut,ft,dt,ht=t._fullLayout._zoomlayer,pt=e.x()._offset,gt=e.y()._offset,vt=[0,0,H,V],mt=null,yt=y.REDRAWDELAY,xt=e.mainplot?j._plots[e.mainplot]:e;return w.length*k.length!==1&&(void 0!==nt.onwheel?nt.onwheel=O:void 0!==nt.onmousewheel&&(nt.onmousewheel="
+,
+"O)),nt}},{\"../../components/color\":18,\"../../components/dragelement\":39,\"../../components/drawing\":41,\"../../lib\":89,\"../../lib/setcursor\":98,\"../../lib/svg_text_utils\":100,\"../../plotly\":107,\"./axes\":110,\"./constants\":115,\"./select\":123,d3:9,tinycolor2:13}],117:[function(t,e,n){\"use strict\";function r(t,e){for(var n=[],r=t.length;r>0;r--)n.push(e);return n}function a(t,e){for(var n=[],r=0;r<t.length;r++)n.push(t[r].p2c(e));return n}function o(t,e){return function(n){var r=t(n),a=e(n);return Math.sqrt(r*r+a*a)}}function i(t,e,n){if(\"pie\"===n)return void t.emit(\"plotly_hover\",{points:[e]});n||(n=\"xy\");var o=Array.isArray(n)?n:[n],i=t._fullLayout,h=i._plots||[],p=h[n];if(p){var y=p.overlays.map(function(t){return t.id});o=o.concat(y)}for(var b=o.length,M=new Array(b),A=new Array(b),L=0;b>L;L++){var T=o[L],z=h[T];if(z)M[L]=w.getFromId(t,z.xaxis._id),A[L]=w.getFromId(t,z.yaxis._id);else{var S=i[T]._subplot;M[L]=S.xaxis,A[L]=S.yaxis}}var E=e.hovermode||i.hovermode;if(-1===[\"x\",\"y\",\"closest\"].indexOf(E)||!t.calcdata||t.querySelector(\".zoombox\")||t._dragging)return _.unhoverRaw(t,e);var C,O,P,N,D,I,R,j,q,F,B,H,V=[],Z=[];if(Array.isArray(e))for(E=\"array\",P=0;P<e.length;P++)D=t.calcdata[e[P].curveNumber||0],Z.push(D);else{for(N=0;N<t.calcdata.length;N++)D=t.calcdata[N],I=D[0].trace,-1!==o.indexOf(l(I))&&Z.push(D);var Y,U;if(e.target&&\"clientX\"in e&&\"clientY\"in e){if(m.triggerHandler(t,\"plotly_beforehover\",e)===!1)return;var X=e.target.getBoundingClientRect();if(Y=e.clientX-X.left,U=e.clientY-X.top,0>Y||Y>X.width||0>U||U>X.height)return _.unhoverRaw(t,e)}else Y=\"xpx\"in e?e.xpx:M[0]._length/2,U=\"ypx\"in e?e.ypx:A[0]._length/2;if(C=\"xval\"in e?r(o,e.xval):a(M,Y),O=\"yval\"in e?r(o,e.yval):a(A,U),!g(C[0])||!g(O[0]))return v.warn(\"Plotly.Fx.hover failed\",e,t),_.unhoverRaw(t,e)}var G=1/0;for(N=0;N<Z.length;N++)if(D=Z[N],D&&D[0]&&D[0].trace&&D[0].trace.visible===!0){if(I=D[0].trace,R=o.indexOf(l(I)),j=E,B={cd:D,trace:I,xa:M[R],ya:A[R],name:t.data.length>1||-1!==I.hoverinfo.indexOf(\"name\")?I.name:void 0,index:!1,distance:Math.min(G,k.MAXDIST),color:x.defaultLine,x0:void 0,x1:void 0,y0:void 0,y1:void 0,xLabelVal:void 0,yLabelVal:void 0,zLabelVal:void 0,text:void 0},H=V.length,\"array\"===j){var $=e[N];\"pointNumber\"in $?(B.index=$.pointNumber,j=\"closest\"):(j=\"\",\"xval\"in $&&(q=$.xval,j=\"x\"),\"yval\"in $&&(F=$.yval,j=j?\"closest\":\"y\"))}else q=C[R],F=O[R];if(I._module&&I._module.hoverPoints){var Q=I._module.hoverPoints(B,q,F,j);if(Q)for(var W,J=0;J<Q.length;J++)W=Q[J],g(W.x0)&&g(W.y0)&&V.push(s(W,E))}else v.log(\"Unrecognized trace type in hover:\",I);\"closest\"===E&&V.length>H&&(V.splice(0,H),G=V[0].distance)}if(0===V.length)return _.unhoverRaw(t,e);var K=\"y\"===E&&Z.length>1;V.sort(function(t,e){return t.distance-e.distance});var tt=x.combine(i.plot_bgcolor||x.background,i.paper_bgcolor),et={hovermode:E,rotateLabels:K,bgColor:tt,container:i._hoverlayer,outerContainer:i._paperdiv},nt=c(V,et);u(V,K?\"xa\":\"ya\"),f(nt,K);var rt=t._hoverdata,at=[];for(P=0;P<V.length;P++){var ot=V[P],it={data:ot.trace._input,fullData:ot.trace,curveNumber:ot.trace.index,pointNumber:ot.index,x:ot.xVal,y:ot.yVal,xaxis:ot.xa,yaxis:ot.ya};void 0!==ot.zLabelVal&&(it.z=ot.zLabelVal),at.push(it)}t._hoverdata=at,d(t,e,rt)&&(rt&&t.emit(\"plotly_unhover\",{points:rt}),t.emit(\"plotly_hover\",{points:t._hoverdata,xaxes:M,yaxes:A,xvals:C,yvals:O}))}function l(t){return t.subplot||t.xaxis+t.yaxis}function s(t,e){t.posref=\"y\"===e?(t.x0+t.x1)/2:(t.y0+t.y1)/2,t.x0=v.constrain(t.x0,0,t.xa._length),t.x1=v.constrain(t.x1,0,t.xa._length),t.y0=v.constrain(t.y0,0,t.ya._length),t.y1=v.constrain(t.y1,0,t.ya._length);var n;if(void 0!==t.xLabelVal){n=\"log\"===t.xa.type&&t.xLabelVal<=0;var r=w.tickText(t.xa,t.xa.c2l(n?-t.xLabelVal:t.xLabelVal),\"hover\");n?0===t.xLabelVal?t.xLabel=\"0\":t.xLabel=\"-\"+r.text:t.xLabel=r.text,t.xVal=t.xa.c2d(t.xLabelVal)}if(void 0!==t.yLabelVal){n=\"log\"===t.ya.type&&t.yLabelVal<=0;var a=w.tickText(t.ya,t.ya.c2l(n?-t.yLabelVal:t.yLabelVal),\"hover\");n?0===t.yLabelVal?t.yLabel=\"0\":t.yLabel=\"-\"+a.text:t.yLabel=a.text,t.yVal=t.ya.c2d(t.yLabelVal)}if(void 0!==t.zLabelVal&&(t.zLabel=String(t.zLabelVal)),!(isNaN(t.xerr)||\"log\"===t.xa.type&&t.xerr<=0)){var o=w.tickText(t.xa,t.xa.c2l(t.xerr),\"hover\").text;void 0!==t.xerrneg?t.xLabel+=\" +\"+o+\" / -\"+w.tickText(t.xa,t.xa.c2l(t.xerrneg),\"hover\").text:t.xLabel+=\" &plusmn; \"+o,\"x\"===e&&(t.distance+=1)}if(!(isNaN(t.yerr)||\"log\"===t.ya.type&&t.yerr<=0)){var i=w.tickText(t.ya,t.ya.c2l(t.yerr),\"hover\").text;void 0!==t.yerrneg?t.yLabel+=\" +\"+i+\" / -\"+w.tickText(t.ya,t.ya.c2l(t.yerrneg),\"hover\").text:t.yLabel+=\" &plusmn; \"+i,\"y\"===e&&(t.distance+=1)}var l=t.trace.hoverinfo;return\"all\"!==l&&(l=l.split(\"+\"),-1===l.indexOf(\"x\")&&(t.xLabel=void 0),-1===l.indexOf(\"y\")&&(t.yLabel=void 0),-1===l.indexOf(\"z\")&&(t.zLabel=void 0),-1===l.indexOf(\"text\")&&(t.text=void 0),-1===l.indexOf(\"name\")&&(t.name=void 0)),t}function c(t,e){var n,r,a=e.hovermode,o=e.rotateLabels,i=e.bgColor,l=e.container,s=e.outerContainer,c=t[0],u=c.xa,f=c.ya,d=\"y\"===a?\"yLabel\":\"xLabel\",g=c[d],v=(String(g)||\"\").split(\" \")[0],m=s.node().getBoundingClientRect(),_=m.top,w=m.width,M=m.height,A=c.distance<=k.MAXDIST&&(\"x\"===a||\"y\"===a);for(n=0;n<t.length;n++){r=t[n].trace.hoverinfo;var T=r.split(\"+\");if(-1===T.indexOf(\"all\")&&-1===T.indexOf(a)){A=!1;break}}var z=l.selectAll(\"g.axistext\").data(A?[0]:[]);z.enter().append(\"g\").classed(\"axistext\",!0),z.exit().remove(),z.each(function(){var e=h.select(this),n=e.selectAll(\"path\").data([0]),r=e.selectAll(\"text\").data([0]);n.enter().append(\"path\").style({fill:x.defaultLine,\"stroke-width\":\"1px\",stroke:x.background}),r.enter().append(\"text\").call(b.font,N,P,x.background).attr(\"data-notex\",1),r.text(g).call(y.convertToTspans).call(b.setPosition,0,0).selectAll(\"tspan.line\").call(b.setPosition,0,0),e.attr(\"transform\",\"\");var o=r.node().getBoundingClientRect();if(\"x\"===a){r.attr(\"text-anchor\",\"middle\").call(b.setPosition,0,\"top\"===u.side?_-o.bottom-C-O:_-o.top+C+O).selectAll(\"tspan.line\").attr({x:r.attr(\"x\"),y:r.attr(\"y\")});var i=\"top\"===u.side?\"-\":\"\";n.attr(\"d\",\"M0,0L\"+C+\",\"+i+C+\"H\"+(O+o.width/2)+\"v\"+i+(2*O+o.height)+\"H-\"+(O+o.width/2)+\"V\"+i+C+\"H-\"+C+\"Z\"),e.attr(\"transform\",\"translate(\"+(u._offset+(c.x0+c.x1)/2)+\",\"+(f._offset+(\"top\"===u.side?0:f._length))+\")\")}else{r.attr(\"text-anchor\",\"right\"===f.side?\"start\":\"end\").call(b.setPosition,(\"right\"===f.side?1:-1)*(O+C),_-o.top-o.height/2).selectAll(\"tspan.line\").attr({x:r.attr(\"x\"),y:r.attr(\"y\")});var l=\"right\"===f.side?\"\":\"-\";n.attr(\"d\",\"M0,0L\"+l+C+\",\"+C+\"V\"+(O+o.height/2)+\"h\"+l+(2*O+o.width)+\"V-\"+(O+o.height/2)+\"H\"+l+C+\"V-\"+C+\"Z\"),e.attr(\"transform\",\"translate(\"+(u._offset+(\"right\"===f.side?u._length:0))+\",\"+(f._offset+(c.y0+c.y1)/2)+\")\")}t=t.filter(function(t){return void 0!==t.zLabelVal||(t[d]||\"\").split(\" \")[0]===v})});var S=l.selectAll(\"g.hovertext\").data(t,function(t){return[t.trace.index,t.index,t.x0,t.y0,t.name,t.attr,t.xa,t.ya||\"\"].join(\",\")});return S.enter().append(\"g\").classed(\"hovertext\",!0).each(function(){var t=h.select(this);t.append(\"rect\").call(x.fill,x.addOpacity(i,.8)),t.append(\"text\").classed(\"name\",!0).call(b.font,N,P),t.append(\"path\").style(\"stroke-width\",\"1px\"),t.append(\"text\").classed(\"nums\",!0).call(b.font,N,P)}),S.exit().remove(),S.each(function(t){var e=h.select(this).attr(\"transform\",\"\"),n=\"\",r=\"\",l=x.opacity(t.color)?t.color:x.defaultLine,s=x.combine(l,i),c=p(s).getBrightness()>128?\"#000\":x.background;if(t.name&&void 0===t.zLabelVal){var u=document.createElement(\"p\");u.innerHTML=t.name,n=u.textContent||\"\",n.length>15&&(n=n.substr(0,12)+\"...\")}void 0!==t.extraText&&(r+=t.extraText),void 0!==t.zLabel?(void 0!==t.xLabel&&(r+=\"x: \"+t.xLabel+\"<br>\"),void 0!==t.yLabel&&(r+=\"y: \"+t.yLabel+\"<br>\"),r+=(r?\"z: \":\"\")+t.zLabel):A&&t[a+\"Label\"]===g?r=t[(\"x\"===a?\"y\":\"x\")+\"Label\"]||\"\":void 0===t.xLabel?void 0!==t.yLabel&&(r=t.yLabel):r=void 0===t.yLabel?t.xLabel:\"(\"+t.xLabel+\", \"+t.yLabel+\")\",t.text&&!Array.isArray(t.text)&&(r+=(r?\"<br>\":\"\")+t.text),\"\"===r&&(\"\"===n&&e.remove(),r=n);var f=e.select(\"text.nums\").style(\"fill\",c).call(b.setPosition,0,0).text(r).attr(\"data-notex\",1).call(y.convertToTspans);f.selectAll(\"tspan.line\").call(b.setPosition,0,0);var d=e.select(\"text.name\"),v=0;n&&n!==r?(d.style(\"fill\",s).text(n).call(b.setPosition,0,0).attr(\"data-notex\",1).call(y.convertToTspans),d.selectAll(\"tspan.line\").call(b.setPosition,0,0),v=d.node().getBoundingClientRect().width+2*O):(d.remove(),e.select(\"rect\").remove()),e.select(\"path\").style({fill:s,stroke:c});var m,k,T=f.node().getBoundingClientRect(),z=t.xa._offset+(t.x0+t.x1)/2,S=t.ya._offset+(t.y0+t.y1)/2,E=Math.abs(t.x1-t.x0),P=Math.abs(t.y1-t.y0),N=T.width+C+O+v;t.ty0=_-T.top,t.bx=T.width+2*O,t.by=T.height+2*O,t.anchor=\"start\",t.txwidth=T.width,t.tx2width=v,t.offset=0,o?(t.pos=z,m=M>=S+P/2+N,k=S-P/2-N>=0,\"top\"!==t.idealAlign&&m||!k?m?(S+=P/2,t.anchor=\"start\"):t.anchor=\"middle\":(S-=P/2,t.anchor=\"end\")):(t.pos=S,m=w>=z+E/2+N,k=z-E/2-N>=0,\"left\"!==t.idealAlign&&m||!k?m?(z+=E/2,t.anchor=\"start\"):t.anchor=\"middle\":(z-=E/2,t.anchor=\"end\")),f.attr(\"text-anchor\",t.anchor),v&&d.attr(\"text-anchor\",t.anchor),e.attr(\"transform\",\"translate(\"+z+\",\"+S+\")\"+(o?\"rotate(\"+L+\")\":\"\"))}),S}function u(t,e){function n(t){var e=t[0],n=t[t.length-1];if(a=e.pmin-e.pos-e.dp+e.size,o=n.pos+n.dp+n.size-e.pmax,a>.01){for(l=t.length-1;l>=0;l--)t[l].dp+=a;r=!1}if(!(.01>o)){if(-.01>a){for(l=t.length-1;l>=0;l--)t[l].dp-=o;r=!1}if(r){var c=0;for(i=0;i<t.length;i++)s=t[i],s.pos+s.dp+s.size>e.pmax&&c++;for(i=t.length-1;i>=0&&!(0>=c);i--)s=t[i],s.pos>e.pmax-1&&(s.del=!0,c--);for(i=0;i<t.length&&!(0>=c);i++)if(s=t[i],s.pos<e.pmin+1)for(s.del=!0,c--,o=2*s.size,l=t.length-1;l>=0;l--)t[l].dp-=o;for(i=t.length-1;i>=0&&!(0>=c);i--)s=t[i],s.pos+s.dp+s.size>e.pmax&&(s.del=!0,c--)}}}for(var r,a,o,i,l,s,c,u=0,f=t.map(function(t,n){var r=t[e];return[{i:n,dp:0,pos:t.pos,posref:t.posref,size:t.by*(\"x\"===r._id.charAt(0)?z:1)/2,pmin:r._offset,pmax:r._offset+r._length}]}).sort(function(t,e){return t[0].posref-e[0].posref});!r&&u<=t.length;){for(u++,r=!0,i=0;i<f.length-1;){var d=f[i],h=f[i+1"
+,
+"],p=d[d.length-1],g=h[0];if(a=p.pos+p.dp+p.size-g.pos-g.dp+g.size,a>.01&&p.pmin===g.pmin&&p.pmax===g.pmax){for(l=h.length-1;l>=0;l--)h[l].dp+=a;for(d.push.apply(d,h),f.splice(i+1,1),c=0,l=d.length-1;l>=0;l--)c+=d[l].dp;for(o=c/d.length,l=d.length-1;l>=0;l--)d[l].dp-=o;r=!1}else i++}f.forEach(n)}for(i=f.length-1;i>=0;i--){var v=f[i];for(l=v.length-1;l>=0;l--){var m=v[l],y=t[m.i];y.offset=m.dp,y.del=m.del}}}function f(t,e){t.each(function(t){var n=h.select(this);if(t.del)return void n.remove();var r=\"end\"===t.anchor?-1:1,a=n.select(\"text.nums\"),o={start:1,end:-1,middle:0}[t.anchor],i=o*(C+O),l=i+o*(t.txwidth+O),s=0,c=t.offset;\"middle\"===t.anchor&&(i-=t.tx2width/2,l-=t.tx2width/2),e&&(c*=-E,s=t.offset*S),n.select(\"path\").attr(\"d\",\"middle\"===t.anchor?\"M-\"+t.bx/2+\",-\"+t.by/2+\"h\"+t.bx+\"v\"+t.by+\"h-\"+t.bx+\"Z\":\"M0,0L\"+(r*C+s)+\",\"+(C+c)+\"v\"+(t.by/2-C)+\"h\"+r*t.bx+\"v-\"+t.by+\"H\"+(r*C+s)+\"V\"+(c-C)+\"Z\"),a.call(b.setPosition,i+s,c+t.ty0-t.by/2+O).selectAll(\"tspan.line\").attr({x:a.attr(\"x\"),y:a.attr(\"y\")}),t.tx2width&&(n.select(\"text.name, text.name tspan.line\").call(b.setPosition,l+o*O+s,c+t.ty0-t.by/2+O),n.select(\"rect\").call(b.setRect,l+(o-1)*t.tx2width/2+s,c-t.by/2-1,t.tx2width,t.by+2))})}function d(t,e,n){if(!e.target)return!1;if(!n||n.length!==t._hoverdata.length)return!0;for(var r=n.length-1;r>=0;r--){var a=n[r],o=t._hoverdata[r];if(a.curveNumber!==o.curveNumber||String(a.pointNumber)!==String(o.pointNumber))return!0}return!1}var h=t(\"d3\"),p=t(\"tinycolor2\"),g=t(\"fast-isnumeric\"),v=t(\"../../lib\"),m=t(\"../../lib/events\"),y=t(\"../../lib/svg_text_utils\"),x=t(\"../../components/color\"),b=t(\"../../components/drawing\"),_=t(\"../../components/dragelement\"),w=t(\"./axes\"),k=t(\"./constants\"),M=t(\"./dragbox\"),A=e.exports={};A.unhover=_.unhover,A.layoutAttributes={dragmode:{valType:\"enumerated\",values:[\"zoom\",\"pan\",\"select\",\"lasso\",\"orbit\",\"turntable\"],dflt:\"zoom\"},hovermode:{valType:\"enumerated\",values:[\"x\",\"y\",\"closest\",!1]}},A.supplyLayoutDefaults=function(t,e,n){function r(n,r){return v.coerce(t,e,A.layoutAttributes,n,r)}r(\"dragmode\");var a;if(e._has(\"cartesian\")){var o=e._isHoriz=A.isHoriz(n);a=o?\"y\":\"x\"}else a=\"closest\";r(\"hovermode\",a)},A.isHoriz=function(t){for(var e=!0,n=0;n<t.length;n++){var r=t[n];if(\"h\"!==r.orientation){e=!1;break}}return e},A.init=function(t){var e=t._fullLayout;if(e._has(\"cartesian\")&&!t._context.staticPlot){var n=Object.keys(e._plots||{}).sort(function(t,n){if((e._plots[t].mainplot&&!0)===(e._plots[n].mainplot&&!0)){var r=t.split(\"y\"),a=n.split(\"y\");return r[0]===a[0]?Number(r[1]||1)-Number(a[1]||1):Number(r[0]||1)-Number(a[0]||1)}return e._plots[t].mainplot?1:-1});n.forEach(function(n){var r=e._plots[n];if(e._has(\"cartesian\")){var a=r.x(),o=r.y(),i=(a._linepositions[n]||[])[3],l=(o._linepositions[n]||[])[3],s=k.DRAGGERSIZE;if(g(i)&&\"top\"===a.side&&(i-=s),g(l)&&\"right\"!==o.side&&(l-=s),!r.mainplot){var c=M(t,r,0,0,a._length,o._length,\"ns\",\"ew\");c.onmousemove=function(r){A.hover(t,r,n),e._lasthover=c,e._hoversubplot=n},c.onmouseout=function(e){t._dragging||_.unhover(t,e)},c.onclick=function(e){A.click(t,e)},M(t,r,-s,-s,s,s,\"n\",\"w\"),M(t,r,a._length,-s,s,s,\"n\",\"e\"),M(t,r,-s,o._length,s,s,\"s\",\"w\"),M(t,r,a._length,o._length,s,s,\"s\",\"e\")}g(i)&&(\"free\"===a.anchor&&(i-=e._size.h*(1-o.domain[1])),M(t,r,.1*a._length,i,.8*a._length,s,\"\",\"ew\"),M(t,r,0,i,.1*a._length,s,\"\",\"w\"),M(t,r,.9*a._length,i,.1*a._length,s,\"\",\"e\")),g(l)&&(\"free\"===o.anchor&&(l-=e._size.w*a.domain[0]),M(t,r,l,.1*o._length,s,.8*o._length,\"ns\",\"\"),M(t,r,l,.9*o._length,s,.1*o._length,\"s\",\"\"),M(t,r,l,0,s,.1*o._length,\"n\",\"\"))}});var r=e._hoverlayer.node();r.onmousemove=function(n){n.target=e._lasthover,A.hover(t,n,e._hoversubplot)},r.onclick=function(n){n.target=e._lasthover,A.click(t,n)},r.onmousedown=function(t){e._lasthover.onmousedown(t)}}};var L=k.YANGLE,T=Math.PI*L/180,z=1/Math.sin(T),S=Math.cos(T),E=Math.sin(T),C=k.HOVERARROWSIZE,O=k.HOVERTEXTPAD,P=k.HOVERFONTSIZE,N=k.HOVERFONT;A.hover=function(t,e,n){return\"string\"==typeof t&&(t=document.getElementById(t)),void 0===t._lastHoverTime&&(t._lastHoverTime=0),void 0!==t._hoverTimer&&(clearTimeout(t._hoverTimer),t._hoverTimer=void 0),Date.now()>t._lastHoverTime+k.HOVERMINTIME?(i(t,e,n),void(t._lastHoverTime=Date.now())):void(t._hoverTimer=setTimeout(function(){i(t,e,n),t._lastHoverTime=Date.now(),t._hoverTimer=void 0},k.HOVERMINTIME))},A.getDistanceFunction=function(t,e,n,r){return\"closest\"===t?r||o(e,n):\"x\"===t?e:n},A.getClosest=function(t,e,n){if(n.index!==!1)n.index>=0&&n.index<t.length?n.distance=0:n.index=!1;else for(var r=0;r<t.length;r++){var a=e(t[r]);a<=n.distance&&(n.index=r,n.distance=a)}return n},A.loneHover=function(t,e){var n={color:t.color||x.defaultLine,x0:t.x0||t.x||0,x1:t.x1||t.x||0,y0:t.y0||t.y||0,y1:t.y1||t.y||0,xLabel:t.xLabel,yLabel:t.yLabel,zLabel:t.zLabel,text:t.text,name:t.name,idealAlign:t.idealAlign,trace:{index:0,hoverinfo:\"\"},xa:{_offset:0},ya:{_offset:0},index:0},r=h.select(e.container),a=e.outerContainer?h.select(e.outerContainer):r,o={hovermode:\"closest\",rotateLabels:!1,bgColor:e.bgColor||x.background,container:r,outerContainer:a},i=c([n],o);return f(i,o.rotateLabels),i.node()},A.loneUnhover=function(t){var e=t instanceof h.selection?t:h.select(t);e.selectAll(\"g.hovertext\").remove()},A.click=function(t,e){t._hoverdata&&e&&e.target&&(t.emit(\"plotly_click\",{points:t._hoverdata}),e.stopImmediatePropagation&&e.stopImmediatePropagation())},A.inbox=function(t,e){return 0>t*e||0===t?k.MAXDIST*(.6-.3/Math.max(3,Math.abs(t-e))):1/0}},{\"../../components/color\":18,\"../../components/dragelement\":39,\"../../components/drawing\":41,\"../../lib\":89,\"../../lib/events\":87,\"../../lib/svg_text_utils\":100,\"./axes\":110,\"./constants\":115,\"./dragbox\":116,d3:9,\"fast-isnumeric\":11,tinycolor2:13}],118:[function(t,e,n){\"use strict\";var r=t(\"../plots\"),a=t(\"./constants\");n.name=\"cartesian\",n.attr=[\"xaxis\",\"yaxis\"],n.idRoot=[\"x\",\"y\"],n.idRegex=a.idRegex,n.attrRegex=a.attrRegex,n.attributes=t(\"./attributes\"),n.plot=function(t){function e(t,e){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=a[0].trace;o.xaxis+o.yaxis===e&&n.push(a)}return n}function n(t,e){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=a[0].trace;o._module===e&&o.visible===!0&&n.push(a)}return n}for(var a=t._fullLayout,o=r.getSubplotIds(a,\"cartesian\"),i=t.calcdata,l=a._modules,s=0;s<o.length;s++){var c=o[s],u=a._plots[c],f=e(i,c);u.plot&&u.plot.selectAll(\"g.trace\").remove();for(var d=0;d<l.length;d++){var h=l[d];if(\"cartesian\"===h.basePlotModule.name){var p=n(f,h);h.plot(t,u,p)}}}}},{\"../plots\":130,\"./attributes\":109,\"./constants\":115}],119:[function(t,e,n){\"use strict\";var r=t(\"../font_attributes\"),a=t(\"../../components/color/attributes\"),o=t(\"../../lib/extend\").extendFlat,i=t(\"../../components/rangeslider/attributes\"),l=t(\"../../components/rangeselector/attributes\"),s=t(\"./constants\");e.exports={color:{valType:\"color\",dflt:a.defaultLine},title:{valType:\"string\"},titlefont:o({},r,{}),type:{valType:\"enumerated\",values:[\"-\",\"linear\",\"log\",\"date\",\"category\"],dflt:\"-\"},autorange:{valType:\"enumerated\",values:[!0,!1,\"reversed\"],dflt:!0},rangemode:{valType:\"enumerated\",values:[\"normal\",\"tozero\",\"nonnegative\"],dflt:\"normal\"},range:{valType:\"info_array\",items:[{valType:\"number\"},{valType:\"number\"}]},rangeslider:i,rangeselector:l,fixedrange:{valType:\"boolean\",dflt:!1},tickmode:{valType:\"enumerated\",values:[\"auto\",\"linear\",\"array\"]},nticks:{valType:\"integer\",min:0,dflt:0},tick0:{valType:\"number\",dflt:0},dtick:{valType:\"any\",dflt:1},tickvals:{valType:\"data_array\"},ticktext:{valType:\"data_array\"},ticks:{valType:\"enumerated\",values:[\"outside\",\"inside\",\"\"]},mirror:{valType:\"enumerated\",values:[!0,\"ticks\",!1,\"all\",\"allticks\"],dflt:!1},ticklen:{valType:\"number\",min:0,dflt:5},tickwidth:{valType:\"number\",min:0,dflt:1},tickcolor:{valType:\"color\",dflt:a.defaultLine},showticklabels:{valType:\"boolean\",dflt:!0},tickfont:o({},r,{}),tickangle:{valType:\"angle\",dflt:\"auto\"},tickprefix:{valType:\"string\",dflt:\"\"},showtickprefix:{valType:\"enumerated\",values:[\"all\",\"first\",\"last\",\"none\"],dflt:\"all\"},ticksuffix:{valType:\"string\",dflt:\"\"},showticksuffix:{valType:\"enumerated\",values:[\"all\",\"first\",\"last\",\"none\"],dflt:\"all\"},showexponent:{valType:\"enumerated\",values:[\"all\",\"first\",\"last\",\"none\"],dflt:\"all\"},exponentformat:{valType:\"enumerated\",values:[\"none\",\"e\",\"E\",\"power\",\"SI\",\"B\"],dflt:\"B\"},tickformat:{valType:\"string\",dflt:\"\"},hoverformat:{valType:\"string\",dflt:\"\"},showline:{valType:\"boolean\",dflt:!1},linecolor:{valType:\"color\",dflt:a.defaultLine},linewidth:{valType:\"number\",min:0,dflt:1},showgrid:{valType:\"boolean\"},gridcolor:{valType:\"color\",dflt:a.lightLine},gridwidth:{valType:\"number\",min:0,dflt:1},zeroline:{valType:\"boolean\"},zerolinecolor:{valType:\"color\",dflt:a.defaultLine},zerolinewidth:{valType:\"number\",dflt:1},anchor:{valType:\"enumerated\",values:[\"free\",s.idRegex.x.toString(),s.idRegex.y.toString()]},side:{valType:\"enumerated\",values:[\"top\",\"bottom\",\"left\",\"right\"]},overlaying:{valType:\"enumerated\",values:[\"free\",s.idRegex.x.toString(),s.idRegex.y.toString()]},domain:{valType:\"info_array\",items:[{valType:\"number\",min:0,max:1},{valType:\"number\",min:0,max:1}],dflt:[0,1]},position:{valType:\"number\",min:0,max:1,dflt:0},categoryorder:{valType:\"enumerated\",values:[\"trace\",\"category ascending\",\"category descending\",\"array\"],dflt:\"trace\"},categoryarray:{valType:\"data_array\"},_deprecated:{autotick:{valType:\"boolean\"}}}},{\"../../components/color/attributes\":17,\"../../components/rangeselector/attributes\":66,\"../../components/rangeslider/attributes\":73,\"../../lib/extend\":88,\"../font_attributes\":128,\"./constants\":115}],120:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"../plots\"),o=t(\"../../components/color\"),i=t(\"../../components/rangeslider\"),l=t(\"../../components/rangeselector\"),s=t(\"./constants\"),c=t(\"./layout_attributes\"),u=t(\"./axis_defaults\"),f=t(\"./position_defaults\"),d=t(\"./axis_ids\");e.exports=function(t,e,n){function h(t,e){var n=Number(t.substr(5)||1),r=Number(e.substr(5)||1);return n-r}var p,g=Object.keys(t),v=[],m=[],y=[],x=[],b={},_={};for(p=0;"
+,
+"p<n.length;p++){var w,k,M=n[p];if(a.traceIs(M,\"cartesian\"))w=v,k=m;else{if(!a.traceIs(M,\"gl2d\"))continue;w=y,k=x}var A=d.id2name(M.xaxis),L=d.id2name(M.yaxis);if(A&&-1===w.indexOf(A)&&w.push(A),L&&-1===k.indexOf(L)&&k.push(L),a.traceIs(M,\"2dMap\")&&(b[A]=!0,b[L]=!0),a.traceIs(M,\"oriented\")){var T=\"h\"===M.orientation?L:A;_[T]=!0}}var z=e._has(\"gl3d\")||e._has(\"geo\");if(!z)for(p=0;p<g.length;p++){var S=g[p];-1===y.indexOf(S)&&-1===v.indexOf(S)&&s.xAxisMatch.test(S)?v.push(S):-1===x.indexOf(S)&&-1===m.indexOf(S)&&s.yAxisMatch.test(S)&&m.push(S)}v.length&&m.length&&r.pushUnique(e._basePlotModules,a.subplotsRegistry.cartesian);var E=v.concat(y).sort(h),C=m.concat(x).sort(h),O=E.concat(C),P=o.background;E.length&&C.length&&(P=r.coerce(t,e,a.layoutAttributes,\"plot_bgcolor\"));var N=o.combine(P,e.paper_bgcolor);O.forEach(function(a){function o(t,e){return r.coerce(l,s,c,t,e)}var i=a.charAt(0),l=t[a]||{},s={},h={letter:i,font:e.font,outerTicks:b[a],showGrid:!_[a],name:a,data:n,bgColor:N},p={letter:i,counterAxes:{x:C,y:E}[i].map(d.name2id),overlayableAxes:{x:E,y:C}[i].filter(function(e){return e!==a&&!(t[e]||{}).overlaying}).map(d.name2id)};u(l,s,o,h),f(l,s,o,p),e[a]=s,t[a]||\"-\"===l.type||(t[a]={type:l.type})}),O.forEach(function(n){var r=n.charAt(0),a=t[n],o=e[n],s={x:C,y:E}[r];i.supplyLayoutDefaults(t,e,n,s),\"x\"===r&&\"date\"===o.type&&l.supplyLayoutDefaults(a,o,e,s)})}},{\"../../components/color\":18,\"../../components/rangeselector\":72,\"../../components/rangeslider\":77,\"../../lib\":89,\"../plots\":130,\"./axis_defaults\":111,\"./axis_ids\":112,\"./constants\":115,\"./layout_attributes\":119,\"./position_defaults\":122}],121:[function(t,e,n){\"use strict\";function r(t,e,n){var r,o,i,l,s,c=[],u=n.map(function(e){return e[t]}),f=a.bisector(e).left;for(r=0;r<u.length;r++)for(i=u[r],o=0;o<i.length;o++)l=i[o],null!==l&&void 0!==l&&(s=f(c,l),s<c.length-1&&c[s]===l||c.splice(s,0,l));return c}var a=t(\"d3\");e.exports=function(t,e,n,o){switch(e){case\"array\":return Array.isArray(n)?n.slice():[];case\"category ascending\":\n"
+,
+"return r(t,a.ascending,o);case\"category descending\":return r(t,a.descending,o);case\"trace\":return[];default:return[]}}},{d3:9}],122:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../lib\");e.exports=function(t,e,n,o){var i=o.counterAxes||[],l=o.overlayableAxes||[],s=o.letter,c=a.coerce(t,e,{anchor:{valType:\"enumerated\",values:[\"free\"].concat(i),dflt:r(t.position)?\"free\":i[0]||\"free\"}},\"anchor\");\"free\"===c&&n(\"position\"),a.coerce(t,e,{side:{valType:\"enumerated\",values:\"x\"===s?[\"bottom\",\"top\"]:[\"left\",\"right\"],dflt:\"x\"===s?\"bottom\":\"left\"}},\"side\");var u=!1;if(l.length&&(u=a.coerce(t,e,{overlaying:{valType:\"enumerated\",values:[!1].concat(l),dflt:!1}},\"overlaying\")),!u){var f=n(\"domain\");f[0]>f[1]-.01&&(e.domain=[0,1]),a.noneOrAll(t.domain,e.domain,[0,1])}return e}},{\"../../lib\":89,\"fast-isnumeric\":11}],123:[function(t,e,n){\"use strict\";function r(t){return t._id}var a=t(\"../../lib/polygon\"),o=t(\"../../components/color\"),i=t(\"./axes\"),l=t(\"./constants\"),s=a.filter,c=a.tester,u=l.MINSELECT;e.exports=function(t,e,n,a,f){function d(t){var e=\"y\"===t._id.charAt(0)?1:0;return function(n){return t.p2d(n[e])}}function h(t,e){return t-e}var p,g=a.gd._fullLayout._zoomlayer,v=a.element.getBoundingClientRect(),m=a.plotinfo.x()._offset,y=a.plotinfo.y()._offset,x=e-v.left,b=n-v.top,_=x,w=b,k=\"M\"+x+\",\"+b,M=a.xaxes[0]._length,A=a.yaxes[0]._length,L=a.xaxes.map(r),T=a.yaxes.map(r),z=a.xaxes.concat(a.yaxes);\"lasso\"===f&&(p=s([[x,b]],l.BENDPX));var S=g.selectAll(\"path.select-outline\").data([1,2]);S.enter().append(\"path\").attr(\"class\",function(t){return\"select-outline select-outline-\"+t}).attr(\"transform\",\"translate(\"+m+\", \"+y+\")\").attr(\"d\",k+\"Z\");var E,C,O,P,N,D=g.append(\"path\").attr(\"class\",\"zoombox-corners\").style({fill:o.background,stroke:o.defaultLine,\"stroke-width\":1}).attr(\"transform\",\"translate(\"+m+\", \"+y+\")\").attr(\"d\",\"M0,0Z\"),I=[],R=a.gd,j=[];for(E=0;E<R.calcdata.length;E++)if(C=R.calcdata[E],O=C[0].trace,O._module&&O._module.selectPoints)if(a.subplot){if(O.subplot!==a.subplot)continue;I.push({selectPoints:O._module.selectPoints,cd:C,xaxis:a.xaxes[0],yaxis:a.yaxes[0]})}else{if(-1===L.indexOf(O.xaxis))continue;if(-1===T.indexOf(O.yaxis))continue;I.push({selectPoints:O._module.selectPoints,cd:C,xaxis:i.getFromId(R,O.xaxis),yaxis:i.getFromId(R,O.yaxis)})}a.moveFn=function(t,e){var n,r;_=Math.max(0,Math.min(M,t+x)),w=Math.max(0,Math.min(A,e+b));var o=Math.abs(_-x),i=Math.abs(w-b);for(\"select\"===f?(i<Math.min(.6*o,u)?(n=c([[x,0],[x,A],[_,A],[_,0]]),D.attr(\"d\",\"M\"+n.xmin+\",\"+(b-u)+\"h-4v\"+2*u+\"h4ZM\"+(n.xmax-1)+\",\"+(b-u)+\"h4v\"+2*u+\"h-4Z\")):o<Math.min(.6*i,u)?(n=c([[0,b],[0,w],[M,w],[M,b]]),D.attr(\"d\",\"M\"+(x-u)+\",\"+n.ymin+\"v-4h\"+2*u+\"v4ZM\"+(x-u)+\",\"+(n.ymax-1)+\"v4h\"+2*u+\"v-4Z\")):(n=c([[x,b],[x,w],[_,w],[_,b]]),D.attr(\"d\",\"M0,0Z\")),S.attr(\"d\",\"M\"+n.xmin+\",\"+n.ymin+\"H\"+(n.xmax-1)+\"V\"+(n.ymax-1)+\"H\"+n.xmin+\"Z\")):\"lasso\"===f&&(p.addPt([_,w]),n=c(p.filtered),S.attr(\"d\",\"M\"+p.filtered.join(\"L\")+\"Z\")),j=[],E=0;E<I.length;E++)P=I[E],[].push.apply(j,P.selectPoints(P,n));if(N={points:j},\"select\"===f){var l,s=N.range={};for(E=0;E<z.length;E++)r=z[E],l=r._id.charAt(0),s[r._id]=[r.p2d(n[l+\"min\"]),r.p2d(n[l+\"max\"])].sort(h)}else{var g=N.lassoPoints={};for(E=0;E<z.length;E++)r=z[E],g[r._id]=p.filtered.map(d(r))}a.gd.emit(\"plotly_selecting\",N)},a.doneFn=function(t,e){if(D.remove(),t||2!==e)a.gd.emit(\"plotly_selected\",N);else{for(S.remove(),E=0;E<I.length;E++)P=I[E],P.selectPoints(P,!1);R.emit(\"plotly_deselect\",null)}}}},{\"../../components/color\":18,\"../../lib/polygon\":95,\"./axes\":110,\"./constants\":115}],124:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"fast-isnumeric\"),o=t(\"../../lib\"),i=t(\"./constants\"),l=t(\"./clean_datum\"),s=t(\"./axis_ids\");e.exports=function(t){function e(e,n){if(e>0)return Math.log(e)/Math.LN10;if(0>=e&&n&&t.range&&2===t.range.length){var r=t.range[0],a=t.range[1];return.5*(r+a-3*u*Math.abs(r-a))}return i.BADNUM}function n(t){return Math.pow(10,t)}function c(t){return a(t)?Number(t):i.BADNUM}var u=10;if(t.c2l=\"log\"===t.type?e:c,t.l2c=\"log\"===t.type?n:c,t.l2d=function(e){return t.c2d(t.l2c(e))},t.p2d=function(e){return t.l2d(t.p2l(e))},t.setScale=function(){var e,n=t._gd._fullLayout._size;if(t._categories||(t._categories=[]),t.overlaying){var r=s.getFromId(t._gd,t.overlaying);t.domain=r.domain}for(t.range&&2===t.range.length&&t.range[0]!==t.range[1]||(t.range=[-1,1]),e=0;2>e;e++)a(t.range[e])||(t.range[e]=a(t.range[1-e])?t.range[1-e]*(e?10:.1):e?1:-1),t.range[e]<-(Number.MAX_VALUE/2)?t.range[e]=-(Number.MAX_VALUE/2):t.range[e]>Number.MAX_VALUE/2&&(t.range[e]=Number.MAX_VALUE/2);if(\"y\"===t._id.charAt(0)?(t._offset=n.t+(1-t.domain[1])*n.h,t._length=n.h*(t.domain[1]-t.domain[0]),t._m=t._length/(t.range[0]-t.range[1]),t._b=-t._m*t.range[1]):(t._offset=n.l+t.domain[0]*n.w,t._length=n.w*(t.domain[1]-t.domain[0]),t._m=t._length/(t.range[1]-t.range[0]),t._b=-t._m*t.range[0]),!isFinite(t._m)||!isFinite(t._b))throw o.notifier(\"Something went wrong with axis scaling\",\"long\"),t._gd._replotting=!1,new Error(\"axis scaling\")},t.l2p=function(e){return a(e)?r.round(t._b+t._m*e,2):i.BADNUM},t.p2l=function(e){return(e-t._b)/t._m},t.c2p=function(e,n){return t.l2p(t.c2l(e,n))},t.p2c=function(e){return t.l2c(t.p2l(e))},-1!==[\"linear\",\"log\",\"-\"].indexOf(t.type))t.c2d=c,t.d2c=function(t){return t=l(t),a(t)?Number(t):i.BADNUM},t.d2l=function(e,n){return\"log\"===t.type?t.c2l(t.d2c(e),n):t.d2c(e)};else if(\"date\"===t.type){if(t.c2d=function(t){return a(t)?o.ms2DateTime(t):i.BADNUM},t.d2c=function(t){return a(t)?Number(t):o.dateTime2ms(t)},t.d2l=t.d2c,t.range&&t.range.length>1)try{var f=t.range.map(o.dateTime2ms);!a(t.range[0])&&a(f[0])&&(t.range[0]=f[0]),!a(t.range[1])&&a(f[1])&&(t.range[1]=f[1])}catch(d){o.error(d,t.range)}}else\"category\"===t.type&&(t.c2d=function(e){return t._categories[Math.round(e)]},t.d2c=function(e){null!==e&&void 0!==e&&-1===t._categories.indexOf(e)&&t._categories.push(e);var n=t._categories.indexOf(e);return-1===n?i.BADNUM:n},t.d2l=t.d2c);t.makeCalcdata=function(e,n){var r,a,o;if(n in e)for(r=e[n],a=new Array(r.length),o=0;o<r.length;o++)a[o]=t.d2c(r[o]);else{var i=n+\"0\"in e?t.d2c(e[n+\"0\"]):0,l=e[\"d\"+n]?Number(e[\"d\"+n]):1;for(r=e[{x:\"y\",y:\"x\"}[n]],a=new Array(r.length),o=0;o<r.length;o++)a[o]=i+o*l}return a},t._min=[],t._max=[],t._minDtick=null,t._forceTick0=null}},{\"../../lib\":89,\"./axis_ids\":112,\"./clean_datum\":114,\"./constants\":115,d3:9,\"fast-isnumeric\":11}],125:[function(t,e,n){\"use strict\";function r(t){var e=[\"showexponent\",\"showtickprefix\",\"showticksuffix\"],n=e.filter(function(e){return void 0!==t[e]}),r=function(e){return t[e]===t[n[0]]};return n.every(r)||1===n.length?t[n[0]]:void 0}var a=t(\"../../lib\");e.exports=function(t,e,n,o,i){var l=r(t),s=n(\"tickprefix\");s&&n(\"showtickprefix\",l);var c=n(\"ticksuffix\");c&&n(\"showticksuffix\",l);var u=n(\"showticklabels\");if(u){var f=i.font||{},d=e.color===t.color?e.color:f.color;if(a.coerceFont(n,\"tickfont\",{family:f.family,size:f.size,color:d}),n(\"tickangle\"),\"category\"!==o){var h=n(\"tickformat\");h||\"date\"===o||(n(\"showexponent\",l),n(\"exponentformat\"))}}\"category\"===o||i.noHover||n(\"hoverformat\")}},{\"../../lib\":89}],126:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./layout_attributes\");e.exports=function(t,e,n,o){var i=r.coerce2(t,e,a,\"ticklen\"),l=r.coerce2(t,e,a,\"tickwidth\"),s=r.coerce2(t,e,a,\"tickcolor\",e.color),c=n(\"ticks\",o.outerTicks||i||l||s?\"outside\":\"\");c||(delete e.ticklen,delete e.tickwidth,delete e.tickcolor)}},{\"../../lib\":89,\"./layout_attributes\":119}],127:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\");e.exports=function(t,e,n,a){var o=\"auto\";\"array\"!==t.tickmode||\"log\"!==a&&\"date\"!==a||(t.tickmode=\"auto\"),Array.isArray(t.tickvals)?o=\"array\":t.dtick&&r(t.dtick)&&(o=\"linear\");var i=n(\"tickmode\",o);if(\"auto\"===i)n(\"nticks\");else if(\"linear\"===i)n(\"tick0\"),n(\"dtick\");else{var l=n(\"tickvals\");void 0===l?e.tickmode=\"auto\":n(\"ticktext\")}}},{\"fast-isnumeric\":11}],128:[function(t,e,n){\"use strict\";e.exports={family:{valType:\"string\",noBlank:!0,strict:!0},size:{valType:\"number\",min:1},color:{valType:\"color\"}}},{}],129:[function(t,e,n){\"use strict\";var r=t(\"../plotly\"),a=t(\"./font_attributes\"),o=t(\"../components/color/attributes\"),i=r.Lib.extendFlat;e.exports={font:{family:i({},a.family,{dflt:'\"Open Sans\", verdana, arial, sans-serif'}),size:i({},a.size,{dflt:12}),color:i({},a.color,{dflt:o.defaultLine})},title:{valType:\"string\",dflt:\"Click to enter Plot title\"},titlefont:i({},a,{}),autosize:{valType:\"enumerated\",values:[!0,!1,\"initial\"]},width:{valType:\"number\",min:10,dflt:700},height:{valType:\"number\",min:10,dflt:450},margin:{l:{valType:\"number\",min:0,dflt:80},r:{valType:\"number\",min:0,dflt:80},t:{valType:\"number\",min:0,dflt:100},b:{valType:\"number\",min:0,dflt:80},pad:{valType:\"number\",min:0,dflt:0},autoexpand:{valType:\"boolean\",dflt:!0}},paper_bgcolor:{valType:\"color\",dflt:o.background},plot_bgcolor:{valType:\"color\",dflt:o.background},separators:{valType:\"string\",dflt:\".,\"},hidesources:{valType:\"boolean\",dflt:!1},smith:{valType:\"enumerated\",values:[!1],dflt:!1},showlegend:{valType:\"boolean\"},_composedModules:{\"*\":\"Fx\"},_nestedModules:{xaxis:\"Axes\",yaxis:\"Axes\",scene:\"gl3d\",geo:\"geo\",legend:\"Legend\",annotations:\"Annotations\",shapes:\"Shapes\",images:\"Images\",ternary:\"ternary\",mapbox:\"mapbox\"}}},{\"../components/color/attributes\":17,\"../plotly\":107,\"./font_attributes\":128}],130:[function(t,e,n){\"use strict\";function r(t){return\"object\"==typeof t&&(t=t.type),t}function a(t,e){e.text(\"\");var n=e.append(\"a\").attr({\"xlink:xlink:href\":\"#\",\"class\":\"link--impt link--embedview\",\"font-weight\":\"bold\"}).text(t._context.linkText+\" \"+String.fromCharCode(187));if(t._context.sendData)n.on(\"click\",function(){h.sendDataToCloud(t)});else{var r=window.location.pathname.split(\"/\"),a=window.location.search;n.attr({\"xlink:xlink:show\":\"new\",\"xlink:xlink:href\":\"/\"+r[2].split(\".\")[0]+\"/\"+r[1]+a})}}function o(t,e){for(var n=f.isPlainObject,r=Array.isArray,a=Object.keys(e),i=0;i<a.length;i++){var l=a[i],s=e[l],c=t[l];if(\"_\"===l.charAt(0)||\"function\"==typeof s){if(l in t)co"
+,
+"ntinue;t[l]=s}else if(r(s)&&r(c))for(var u=0;u<s.length;u++)n(s[u])&&n(c[u])&&o(c[u],s[u]);else n(s)&&n(c)&&(o(c,s),Object.keys(c).length||delete t[l])}}function i(t,e,n){if(Array.isArray(t.transforms))for(var r=t.transforms,a=e.transforms=[],o=0;o<r.length;o++){var i,l=r[o],s=l.type,c=y[s];c||f.warn(\"Unrecognized transform type \"+s+\".\"),c&&c.supplyDefaults?(i=c.supplyDefaults(l,e,n),i.type=s):i=f.extendFlat({},l),a.push(i)}}function l(t,e,n){for(var r=t.transforms,a=[t],o=0;o<r.length;o++){var i=r[o],l=i.type,s=y[l];s&&(a=s.transform(a,{transform:i,fullTrace:t,fullData:e,layout:n}))}return a}var s=t(\"d3\"),c=t(\"fast-isnumeric\"),u=t(\"../plotly\"),f=t(\"../lib\"),d=t(\"../components/color\"),h=e.exports={},p=h.modules={},g=h.allTypes=[],v=h.allCategories={},m=h.subplotsRegistry={},y=h.transformsRegistry={};h.attributes=t(\"./attributes\"),h.attributes.type.values=g,h.fontAttrs=t(\"./font_attributes\"),h.layoutAttributes=t(\"./layout_attributes\"),h.fontWeight=\"normal\",h.register=function(t,e,n,r){if(p[e])return void f.log(\"Type \"+e+\" already registered\");for(var a={},o=0;o<n.length;o++)a[n[o]]=!0,v[n[o]]=!0;p[e]={_module:t,categories:a},r&&Object.keys(r).length&&(p[e].meta=r),g.push(e)},h.getModule=function(t){if(void 0!==t.r)return f.warn(\"Tried to put a polar trace on an incompatible graph of cartesian data. Ignoring this dataset.\",t),!1;var e=p[r(t)];return e?e._module:!1},h.traceIs=function(t,e){if(t=r(t),\"various\"===t)return!1;var n=p[t];return n||(void 0!==t&&f.log(\"Unrecognized trace type \"+t+\".\"),n=p[h.attributes.type.dflt]),!!n.categories[e]},h.registerSubplot=function(t){var e=t.name;return m[e]?void f.log(\"Plot type \"+e+\" already registered.\"):void(m[e]=t)},h.findSubplotIds=function(t,e){var n=[];if(void 0===h.subplotsRegistry[e])return n;for(var r=h.subplotsRegistry[e].attr,a=0;a<t.length;a++){var o=t[a];h.traceIs(o,e)&&-1===n.indexOf(o[r])&&n.push(o[r])}return n},h.getSubplotIds=function(t,e){var n=h.subplotsRegistry[e];if(void 0===n)return[];if(!(\"cartesian\"!==e||t._has&&t._has(\"cartesian\")))return[];if(!(\"gl2d\"!==e||t._has&&t._has(\"gl2d\")))return[];if(\"cartesian\"===e||\"gl2d\"===e)return Object.keys(t._plots||{});for(var r=n.idRegex,a=Object.keys(t),o=[],i=0;i<a.length;i++){var l=a[i];r.test(l)&&o.push(l)}var s=n.idRoot.length;return o.sort(function(t,e){var n=+(t.substr(s)||1),r=+(e.substr(s)||1);return n-r}),o},h.getSubplotData=function(t,e,n){if(void 0===h.subplotsRegistry[e])return[];for(var r,a=h.subplotsRegistry[e].attr,o=[],i=0;i<t.length;i++)if(r=t[i],\"gl2d\"===e&&h.traceIs(r,\"gl2d\")){var l=u.Axes.subplotMatch,s=\"x\"+n.match(l)[1],c=\"y\"+n.match(l)[2];r[a[0]]===s&&r[a[1]]===c&&o.push(r)}else r[a]===n&&o.push(r);return o},h.redrawText=function(t){return t.data&&t.data[0]&&t.data[0].r?void 0:new Promise(function(e){setTimeout(function(){u.Annotations.drawAll(t),u.Legend.draw(t),(t.calcdata||[]).forEach(function(t){t[0]&&t[0].t&&t[0].t.cb&&t[0].t.cb()}),e(h.previousPromises(t))},300)})},h.resize=function(t){return new Promise(function(e,n){t&&\"none\"!==s.select(t).style(\"display\")||n(new Error(\"Resize must be passed a plot div element.\")),t._redrawTimer&&clearTimeout(t._redrawTimer),t._redrawTimer=setTimeout(function(){if((t._fullLayout||{}).autosize){var n=t.changed;t.autoplay=!0,u.relayout(t,{autosize:!0}),t.changed=n,e(t)}},100)})},h.previousPromises=function(t){return(t._promises||[]).length?Promise.all(t._promises).then(function(){t._promises=[]}):void 0},h.addLinks=function(t){var e=t._fullLayout,n=e._paper.selectAll(\"text.js-plot-link-container\").data([0]);n.enter().append(\"text\").classed(\"js-plot-link-container\",!0).style({\"font-family\":'\"Open Sans\", Arial, sans-serif',\"font-size\":\"12px\",fill:d.defaultLine,\"pointer-events\":\"all\"}).each(function(){var t=s.select(this);t.append(\"tspan\").classed(\"js-link-to-tool\",!0),t.append(\"tspan\").classed(\"js-link-spacer\",!0),t.append(\"tspan\").classed(\"js-sourcelinks\",!0)});var r=n.node(),o={y:e._paper.attr(\"height\")-9};document.body.contains(r)&&r.getComputedTextLength()>=e.width-20?(o[\"text-anchor\"]=\"start\",o.x=5):(o[\"text-anchor\"]=\"end\",o.x=e._paper.attr(\"width\")-7),n.attr(o);var i=n.select(\".js-link-to-tool\"),l=n.select(\".js-link-spacer\"),c=n.select(\".js-sourcelinks\");t._context.showSources&&t._context.showSources(t),t._context.showLink&&a(t,i),l.text(i.text()&&c.text()?\" - \":\"\")},h.sendDataToCloud=function(t){t.emit(\"plotly_beforeexport\");var e=window.PLOTLYENV&&window.PLOTLYENV.BASE_URL||\"https://plot.ly\",n=s.select(t).append(\"div\").attr(\"id\",\"hiddenform\").style(\"display\",\"none\"),r=n.append(\"form\").attr({action:e+\"/external\",method:\"post\",target:\"_blank\"}),a=r.append(\"input\").attr({type:\"text\",name:\"data\"});return a.node().value=h.graphJson(t,!1,\"keepdata\"),r.node().submit(),n.remove(),t.emit(\"plotly_afterexport\"),!1},h.supplyDefaults=function(t){var e,n=t._fullLayout||{},r=t._fullLayout={},a=t.layout||{},i=t._fullData||[],l=t._fullData=[],s=t.data||[];h.supplyLayoutGlobalDefaults(a,r),r._dataLength=s.length,h.supplyDataDefaults(s,l,r),r._has=h._hasPlotType.bind(r);var c=r._modules;for(e=0;e<c.length;e++){var f=c[e];f.cleanData&&f.cleanData(l)}if(i.length===s.length)for(e=0;e<l.length;e++)o(l[e],i[e]);h.supplyLayoutModuleDefaults(a,r,l),r._hasCartesian=r._has(\"cartesian\"),r._hasGeo=r._has(\"geo\"),r._hasGL3D=r._has(\"gl3d\"),r._hasGL2D=r._has(\"gl2d\"),r._hasTernary=r._has(\"ternary\"),r._hasPie=r._has(\"pie\"),h.cleanPlot(l,r,i,n),o(r,n),h.doAutoMargin(t);var d=u.Axes.list(t);for(e=0;e<d.length;e++){var p=d[e];p._gd=t,p.setScale()}if((t.calcdata||[]).length===l.length)for(e=0;e<l.length;e++){var g=l[e];(t.calcdata[e][0]||{}).trace=g}},h._hasPlotType=function(t){for(var e=this._basePlotModules||[],n=0;n<e.length;n++){var r=e[n];if(r.name===t)return!0}return!1},h.cleanPlot=function(t,e,n,r){var a,o,i=r._basePlotModules||[];for(a=0;a<i.length;a++){var l=i[a];l.clean&&l.clean(t,e,n,r)}var s=!!r._paper,c=!!r._infolayer;t:for(a=0;a<n.length;a++){var u=n[a],f=u.uid;for(o=0;o<t.length;o++){var d=t[o];if(f===d.uid)continue t}s&&r._paper.selectAll(\".hm\"+f+\",.contour\"+f+\",#clip\"+f).remove(),c&&r._infolayer.selectAll(\".cb\"+f).remove()}},h.supplyDataDefaults=function(t,e,n){function r(t){e.push(t);var n=t._module;n&&(f.pushUnique(a,n),f.pushUnique(o,t._module.basePlotModule),i++)}for(var a=n._modules=[],o=n._basePlotModules=[],i=0,s=0;s<t.length;s++){var c=t[s],u=h.supplyTraceDefaults(c,i,n);if(u.transforms&&u.transforms.length)for(var d=l(u,e,n),p=0;p<d.length;p++){var g=d[p],v=h.supplyTraceDefaults(g,i,n);g.uid=v.uid=u.uid+p,v.index=s,v._input=c,v._fullInput=u,v._expandedIndex=i,v._expandedInput=g,r(v)}else u.index=s,u._input=c,u._expandedIndex=i,r(u)}},h.supplyTraceDefaults=function(t,e,n){function r(e,n){return f.coerce(t,o,h.attributes,e,n)}function a(e,n){return h.traceIs(o,e)?f.coerce(t,o,h.subplotsRegistry[e].attributes,n):void 0}var o={},l=d.defaults[e%d.defaults.length],s=r(\"visible\");r(\"type\"),r(\"uid\");for(var c=Object.keys(m),u=0;u<c.length;u++){var p=c[u];if(-1===[\"cartesian\",\"gl2d\"].indexOf(p)){var g=m[p].attr;g&&a(p,g)}}if(s){var v=h.getModule(o);o._module=v,r(\"hoverinfo\",1===n._dataLength?\"x+y+z+text\":void 0),v&&v.supplyDefaults(t,o,l,n),r(\"name\",\"trace \"+e),h.traceIs(o,\"noOpacity\")||r(\"opacity\"),a(\"cartesian\",\"xaxis\"),a(\"cartesian\",\"yaxis\"),a(\"gl2d\",\"xaxis\"),a(\"gl2d\",\"yaxis\"),h.traceIs(o,\"showLegend\")&&(r(\"showlegend\"),r(\"legendgroup\")),i(t,o,n)}return o},h.supplyLayoutGlobalDefaults=function(t,e){function n(n,r){return f.coerce(t,e,h.layoutAttributes,n,r)}var r=f.coerceFont(n,\"font\");n(\"title\"),f.coerceFont(n,\"titlefont\",{family:r.family,size:Math.round(1.4*r.size),color:r.color});var a=n(\"autosize\",t.width&&t.height?!1:\"initial\");n(\"width\"),n(\"height\"),n(\"margin.l\"),n(\"margin.r\"),n(\"margin.t\"),n(\"margin.b\"),n(\"margin.pad\"),n(\"margin.autoexpand\"),\"initial\"!==a&&h.sanitizeMargins(e),n(\"paper_bgcolor\"),n(\"separators\"),n(\"hidesources\"),n(\"smith\")},h.supplyLayoutModuleDefaults=function(t,e,n){var r,a;u.Axes.supplyLayoutDefaults(t,e,n);var o=e._basePlotModules;for(r=0;r<o.length;r++)a=o[r],\"cartesian\"!==a.name&&a.supplyLayoutDefaults&&a.supplyLayoutDefaults(t,e,n);var i=e._modules;for(r=0;r<i.length;r++)a=i[r],a.supplyLayoutDefaults&&a.supplyLayoutDefaults(t,e,n);var l=[\"Fx\",\"Annotations\",\"Shapes\",\"Legend\",\"Images\"];for(r=0;r<l.length;r++)a=l[r],u[a]&&u[a].supplyLayoutDefaults(t,e,n)},h.purge=function(t){var e=t._fullLayout||{};void 0!==e._glcontainer&&e._glcontainer.remove(),void 0!==e._geocontainer&&e._geocontainer.remove(),e._modeBar&&e._modeBar.destroy(),delete t.data,delete t.layout,delete t._fullData,delete t._fullLayout,delete t.calcdata,delete t.framework,delete t.empty,delete t.fid,delete t.undoqueue,delete t.undonum,delete t.autoplay,delete t.changed,delete t._tester,delete t._testref,delete t._promises,delete t._redrawTimer,delete t._replotting,delete t.firstscatter,delete t.hmlumcount,delete t.hmpixcount,delete t.numboxes,delete t._hoverTimer,delete t._lastHoverTime,t.removeAllListeners&&t.removeAllListeners()},h.style=function(t){for(var e=t._fullLayout._modules,n=0;n<e.length;n++){var r=e[n];r.style&&r.style(t)}},h.sanitizeMargins=function(t){if(t&&t.margin){var e,n=t.width,r=t.height,a=t.margin,o=n-(a.l+a.r),i=r-(a.t+a.b);0>o&&(e=(n-1)/(a.l+a.r),a.l=Math.floor(e*a.l),a.r=Math.floor(e*a.r)),0>i&&(e=(r-1)/(a.t+a.b),a.t=Math.floor(e*a.t),a.b=Math.floor(e*a.b))}},h.autoMargin=function(t,e,n){var r=t._fullLayout;if(r._pushmargin||(r._pushmargin={}),r.margin.autoexpand!==!1){if(n){var a=void 0===n.pad?12:n.pad;n.l+n.r>.5*r.width&&(n.l=n.r=0),n.b+n.t>.5*r.height&&(n.b=n.t=0),r._pushmargin[e]={l:{val:n.x,size:n.l+a},r:{val:n.x,size:n.r+a},b:{val:n.y,size:n.b+a},t:{val:n.y,size:n.t+a}}}else delete r._pushmargin[e];t._replotting||h.doAutoMargin(t)}},h.doAutoMargin=function(t){var e=t._fullLayout;e._size||(e._size={}),e._pushmargin||(e._pushmargin={});var n=e._size,r=JSON.stringify(n),a=Math.max(e.margin.l||0,0),o=Math.max(e.margin.r||0,0),i=Math.max(e.margin.t||0,0),l=Math.max(e.margin.b||0,0),s=e._pushmargin;return e.margin.autoexpand!==!1&&(s.base={l:{val:0,size"
+,
+":a},r:{val:1,size:o},t:{val:1,size:i},b:{val:0,size:l}},Object.keys(s).forEach(function(t){var n=s[t].l||{},r=s[t].b||{},u=n.val,f=n.size,d=r.val,h=r.size;Object.keys(s).forEach(function(t){if(c(f)&&s[t].r){var n=s[t].r.val,r=s[t].r.size;if(n>u){var p=(f*n+(r-e.width)*u)/(n-u),g=(r*(1-u)+(f-e.width)*(1-n))/(n-u);p>=0&&g>=0&&p+g>a+o&&(a=p,o=g)}}if(c(h)&&s[t].t){var v=s[t].t.val,m=s[t].t.size;if(v>d){var y=(h*v+(m-e.height)*d)/(v-d),x=(m*(1-d)+(h-e.height)*(1-v))/(v-d);y>=0&&x>=0&&y+x>l+i&&(l=y,i=x)}}})})),n.l=Math.round(a),n.r=Math.round(o),n.t=Math.round(i),n.b=Math.round(l),n.p=Math.round(e.margin.pad),n.w=Math.round(e.width)-n.l-n.r,n.h=Math.round(e.height)-n.t-n.b,t._replotting||\"{}\"===r||r===JSON.stringify(e._size)?void 0:u.plot(t)},h.graphJson=function(t,e,n,r,a){function o(t){if(\"function\"==typeof t)return null;if(f.isPlainObject(t)){var e,r,a={};for(e in t)if(\"function\"!=typeof t[e]&&-1===[\"_\",\"[\"].indexOf(e.charAt(0))){if(\"keepdata\"===n){if(\"src\"===e.substr(e.length-3))continue}else if(\"keepstream\"===n){if(r=t[e+\"src\"],\"string\"==typeof r&&r.indexOf(\":\")>0&&!f.isPlainObject(t.stream))continue}else if(\"keepall\"!==n&&(r=t[e+\"src\"],\"string\"==typeof r&&r.indexOf(\":\")>0))continue;a[e]=o(t[e])}return a}return Array.isArray(t)?t.map(o):t&&t.getTime?f.ms2DateTime(t):t}(a&&e&&!t._fullData||a&&!e&&!t._fullLayout)&&h.supplyDefaults(t);var i=a?t._fullData:t.data,l=a?t._fullLayout:t.layout,s={data:(i||[]).map(function(t){var n=o(t);return e&&delete n.fit,n})};return e||(s.layout=o(l)),t.framework&&t.framework.isPolar&&(s=t.framework.getConfig()),\"object\"===r?s:JSON.stringify(s)}},{\"../components/color\":18,\"../lib\":89,\"../plotly\":107,\"./attributes\":108,\"./font_attributes\":128,\"./layout_attributes\":129,d3:9,\"fast-isnumeric\":11}],131:[function(t,e,n){\"use strict\";var r=t(\"../../traces/scatter/attributes\"),a=r.marker;e.exports={r:r.r,t:r.t,marker:{color:a.color,size:a.size,symbol:a.symbol,opacity:a.opacity}}},{\"../../traces/scatter/attributes\":167}],132:[function(t,e,n){\"use strict\";function r(t,e){var n={showline:{valType:\"boolean\"},showticklabels:{valType:\"boolean\"},tickorientation:{valType:\"enumerated\",values:[\"horizontal\",\"vertical\"]},ticklen:{valType:\"number\",min:0},tickcolor:{valType:\"color\"},ticksuffix:{valType:\"string\"},endpadding:{valType:\"number\"},visible:{valType:\"boolean\"}};return o({},e,n)}var a=t(\"../cartesian/layout_attributes\"),o=t(\"../../lib/extend\").extendFlat,i=o({},a.domain,{});e.exports={radialaxis:r(\"radial\",{range:{valType:\"info_array\",items:[{valType:\"number\"},{valType:\"number\"}]},domain:i,orientation:{valType:\"number\"}}),angularaxis:r(\"angular\",{range:{valType:\"info_array\",items:[{valType:\"number\",dflt:0},{valType:\"number\",dflt:360}]},domain:i}),layout:{direction:{valType:\"enumerated\",values:[\"clockwise\",\"counterclockwise\"]},orientation:{valType:\"angle\"}}}},{\"../../lib/extend\":88,\"../cartesian/layout_attributes\":119}],133:[function(t,e,n){var r=t(\"../../plotly\"),a=t(\"d3\"),o=e.exports={version:\"0.2.2\",manager:t(\"./micropolar_manager\")},i=r.Lib.extendDeepAll;o.Axis=function(){function t(t){n=t||n;var c=s.data,f=s.layout;return(\"string\"==typeof n||n.nodeName)&&(n=a.select(n)),n.datum(c).each(function(t,n){function s(t,e){return l(t)%360+f.orientation}var c=t.slice();u={data:o.util.cloneJson(c),layout:o.util.cloneJson(f)};var d=0;c.forEach(function(t,e){t.color||(t.color=f.defaultColorRange[d],d=(d+1)%f.defaultColorRange.length),t.strokeColor||(t.strokeColor=\"LinePlot\"===t.geometry?t.color:a.rgb(t.color).darker().toString()),u.data[e].color=t.color,u.data[e].strokeColor=t.strokeColor,u.data[e].strokeDash=t.strokeDash,u.data[e].strokeSize=t.strokeSize});var h=c.filter(function(t,e){var n=t.visible;return\"undefined\"==typeof n||n===!0}),p=!1,g=h.map(function(t,e){return p=p||\"undefined\"!=typeof t.groupId,t});if(p){var v=a.nest().key(function(t,e){return\"undefined\"!=typeof t.groupId?t.groupId:\"unstacked\"}).entries(g),m=[],y=v.map(function(t,e){if(\"unstacked\"===t.key)return t.values;var n=t.values[0].r.map(function(t,e){return 0});return t.values.forEach(function(t,e,r){t.yStack=[n],m.push(n),n=o.util.sumArrays(t.r,n)}),t.values});h=a.merge(y)}h.forEach(function(t,e){t.t=Array.isArray(t.t[0])?t.t:[t.t],t.r=Array.isArray(t.r[0])?t.r:[t.r]});var x=Math.min(f.width-f.margin.left-f.margin.right,f.height-f.margin.top-f.margin.bottom)/2;x=Math.max(10,x);var b,_=[f.margin.left+x,f.margin.top+x];if(p){var w=a.max(o.util.sumArrays(o.util.arrayLast(h).r[0],o.util.arrayLast(m)));b=[0,w]}else b=a.extent(o.util.flattenArray(h.map(function(t,e){return t.r})));f.radialAxis.domain!=o.DATAEXTENT&&(b[0]=0),r=a.scale.linear().domain(f.radialAxis.domain!=o.DATAEXTENT&&f.radialAxis.domain?f.radialAxis.domain:b).range([0,x]),u.layout.radialAxis.domain=r.domain();var k,M=o.util.flattenArray(h.map(function(t,e){return t.t})),A=\"string\"==typeof M[0];A&&(M=o.util.deduplicate(M),k=M.slice(),M=a.range(M.length),h=h.map(function(t,e){var n=t;return t.t=[M],p&&(n.yStack=t.yStack),n}));var L=h.filter(function(t,e){return\"LinePlot\"===t.geometry||\"DotPlot\"===t.geometry}).length===h.length,T=null===f.needsEndSpacing?A||!L:f.needsEndSpacing,z=f.angularAxis.domain&&f.angularAxis.domain!=o.DATAEXTENT&&!A&&f.angularAxis.domain[0]>=0,S=z?f.angularAxis.domain:a.extent(M),E=Math.abs(M[1]-M[0]);L&&!A&&(E=0);var C=S.slice();T&&A&&(C[1]+=E);var O=f.angularAxis.ticksCount||4;O>8&&(O=O/(O/8)+O%8),f.angularAxis.ticksStep&&(O=(C[1]-C[0])/O);var P=f.angularAxis.ticksStep||(C[1]-C[0])/(O*(f.minorTicks+1));k&&(P=Math.max(Math.round(P),1)),C[2]||(C[2]=P);var N=a.range.apply(this,C);if(N=N.map(function(t,e){return parseFloat(t.toPrecision(12))}),l=a.scale.linear().domain(C.slice(0,2)).range(\"clockwise\"===f.direction?[0,360]:[360,0]),u.layout.angularAxis.domain=l.domain(),u.layout.angularAxis.endPadding=T?E:0,e=a.select(this).select(\"svg.chart-root\"),\"undefined\"==typeof e||e.empty()){var D=\"<svg xmlns='http://www.w3.org/2000/svg' class='chart-root'>' + '<g class='outer-group'>' + '<g class='chart-group'>' + '<circle class='background-circle'></circle>' + '<g class='geometry-group'></g>' + '<g class='radial axis-group'>' + '<circle class='outside-circle'></circle>' + '</g>' + '<g class='angular axis-group'></g>' + '<g class='guides-group'><line></line><circle r='0'></circle></g>' + '</g>' + '<g class='legend-group'></g>' + '<g class='tooltips-group'></g>' + '<g class='title-group'><text></text></g>' + '</g>' + '</svg>\",I=(new DOMParser).parseFromString(D,\"application/xml\"),R=this.appendChild(this.ownerDocument.importNode(I.documentElement,!0));e=a.select(R)}e.select(\".guides-group\").style({\"pointer-events\":\"none\"}),e.select(\".angular.axis-group\").style({\"pointer-events\":\"none\"}),e.select(\".radial.axis-group\").style({\"pointer-events\":\"none\"});var j,q=e.select(\".chart-group\"),F={fill:\"none\",stroke:f.tickColor},B={\"font-size\":f.font.size,\"font-family\":f.font.family,fill:f.font.color,\"text-shadow\":[\"-1px 0px\",\"1px -1px\",\"-1px 1px\",\"1px 1px\"].map(function(t,e){return\" \"+t+\" 0 \"+f.font.outlineColor}).join(\",\")};if(f.showLegend){j=e.select(\".legend-group\").attr({transform:\"translate(\"+[x,f.margin.top]+\")\"}).style({display:\"block\"});var H=h.map(function(t,e){var n=o.util.cloneJson(t);return n.symbol=\"DotPlot\"===t.geometry?t.dotType||\"circle\":\"LinePlot\"!=t.geometry?\"square\":\"line\",n.visibleInLegend=\"undefined\"==typeof t.visibleInLegend||t.visibleInLegend,n.color=\"LinePlot\"===t.geometry?t.strokeColor:t.color,n});o.Legend().config({data:h.map(function(t,e){return t.name||\"Element\"+e}),legendConfig:i({},o.Legend.defaultConfig().legendConfig,{container:j,elements:H,reverseOrder:f.legend.reverseOrder})})();var V=j.node().getBBox();x=Math.min(f.width-V.width-f.margin.left-f.margin.right,f.height-f.margin.top-f.margin.bottom)/2,x=Math.max(10,x),_=[f.margin.left+x,f.margin.top+x],r.range([0,x]),u.layout.radialAxis.domain=r.domain(),j.attr(\"transform\",\"translate(\"+[_[0]+x,_[1]-x]+\")\")}else j=e.select(\".legend-group\").style({display:\"none\"});e.attr({width:f.width,height:f.height}).style({opacity:f.opacity}),q.attr(\"transform\",\"translate(\"+_+\")\").style({cursor:\"crosshair\"});var Z=[(f.width-(f.margin.left+f.margin.right+2*x+(V?V.width:0)))/2,(f.height-(f.margin.top+f.margin.bottom+2*x))/2];if(Z[0]=Math.max(0,Z[0]),Z[1]=Math.max(0,Z[1]),e.select(\".outer-group\").attr(\"transform\",\"translate(\"+Z+\")\"),f.title){var Y=e.select(\"g.title-group text\").style(B).text(f.title),U=Y.node().getBBox();Y.attr({x:_[0]-U.width/2,y:_[1]-x-20})}var X=e.select(\".radial.axis-group\");if(f.radialAxis.gridLinesVisible){var G=X.selectAll(\"circle.grid-circle\").data(r.ticks(5));G.enter().append(\"circle\").attr({\"class\":\"grid-circle\"}).style(F),G.attr(\"r\",r),G.exit().remove()}X.select(\"circle.outside-circle\").attr({r:x}).style(F);var $=e.select(\"circle.background-circle\").attr({r:x}).style({fill:f.backgroundColor,stroke:f.stroke});if(f.radialAxis.visible){var Q=a.svg.axis().scale(r).ticks(5).tickSize(5);X.call(Q).attr({transform:\"rotate(\"+f.radialAxis.orientation+\")\"}),X.selectAll(\".domain\").style(F),X.selectAll(\"g>text\").text(function(t,e){return this.textContent+f.radialAxis.ticksSuffix}).style(B).style({\"text-anchor\":\"start\"}).attr({x:0,y:0,dx:0,dy:0,transform:function(t,e){return\"horizontal\"===f.radialAxis.tickOrientation?\"rotate(\"+-f.radialAxis.orientation+\") translate(\"+[0,B[\"font-size\"]]+\")\":\"translate(\"+[0,B[\"font-size\"]]+\")\"}}),X.selectAll(\"g>line\").style({stroke:\"black\"})}var W=e.select(\".angular.axis-group\").selectAll(\"g.angular-tick\").data(N),J=W.enter().append(\"g\").classed(\"angular-tick\",!0);W.attr({transform:function(t,e){return\"rotate(\"+s(t,e)+\")\"}}).style({display:f.angularAxis.visible?\"block\":\"none\"}),W.exit().remove(),J.append(\"line\").classed(\"grid-line\",!0).classed(\"major\",function(t,e){return e%(f.minorTicks+1)==0}).classed(\"minor\",function(t,e){return!(e%(f.minorTicks+1)==0)}).style(F),J.selectAll(\".minor\").style({stroke:f.minorTickColor}),W.select(\"line.grid-line\").attr({x1:f.tickLength?x-f.tickLength:0,x2:x}).style({display:f.angularAxis.gridLinesVisible?\"block\":\"none\"}),J.append(\"text\""
+,
+").classed(\"axis-text\",!0).style(B);var K=W.select(\"text.axis-text\").attr({x:x+f.labelOffset,dy:\".35em\",transform:function(t,e){var n=s(t,e),r=x+f.labelOffset,a=f.angularAxis.tickOrientation;return\"horizontal\"==a?\"rotate(\"+-n+\" \"+r+\" 0)\":\"radial\"==a?270>n&&n>90?\"rotate(180 \"+r+\" 0)\":null:\"rotate(\"+(180>=n&&n>0?-90:90)+\" \"+r+\" 0)\"}}).style({\"text-anchor\":\"middle\",display:f.angularAxis.labelsVisible?\"block\":\"none\"}).text(function(t,e){return e%(f.minorTicks+1)!=0?\"\":k?k[t]+f.angularAxis.ticksSuffix:t+f.angularAxis.ticksSuffix}).style(B);f.angularAxis.rewriteTicks&&K.text(function(t,e){return e%(f.minorTicks+1)!=0?\"\":f.angularAxis.rewriteTicks(this.textContent,e)});var tt=a.max(q.selectAll(\".angular-tick text\")[0].map(function(t,e){return t.getCTM().e+t.getBBox().width}));j.attr({transform:\"translate(\"+[x+tt,f.margin.top]+\")\"});var et=e.select(\"g.geometry-group\").selectAll(\"g\").size()>0,nt=e.select(\"g.geometry-group\").selectAll(\"g.geometry\").data(h);if(nt.enter().append(\"g\").attr({\"class\":function(t,e){return\"geometry geometry\"+e}}),nt.exit().remove(),h[0]||et){var rt=[];h.forEach(function(t,e){var n={};n.radialScale=r,n.angularScale=l,n.container=nt.filter(function(t,n){return n==e}),n.geometry=t.geometry,n.orientation=f.orientation,n.direction=f.direction,n.index=e,rt.push({data:t,geometryConfig:n})});var at=a.nest().key(function(t,e){return\"undefined\"!=typeof t.data.groupId||\"unstacked\"}).entries(rt),ot=[];at.forEach(function(t,e){\"unstacked\"===t.key?ot=ot.concat(t.values.map(function(t,e){return[t]})):ot.push(t.values)}),ot.forEach(function(t,e){var n;n=Array.isArray(t)?t[0].geometryConfig.geometry:t.geometryConfig.geometry;var r=t.map(function(t,e){return i(o[n].defaultConfig(),t)});o[n]().config(r)()})}var it,lt,st=e.select(\".guides-group\"),ct=e.select(\".tooltips-group\"),ut=o.tooltipPanel().config({container:ct,fontSize:8})(),ft=o.tooltipPanel().config({container:ct,fontSize:8})(),dt=o.tooltipPanel().config({container:ct,hasTick:!0})();if(!A){var ht=st.select(\"line\").attr({\n"
+,
+"x1:0,y1:0,y2:0}).style({stroke:\"grey\",\"pointer-events\":\"none\"});q.on(\"mousemove.angular-guide\",function(t,e){var n=o.util.getMousePos($).angle;ht.attr({x2:-x,transform:\"rotate(\"+n+\")\"}).style({opacity:.5});var r=(n+180+360-f.orientation)%360;it=l.invert(r);var a=o.util.convertToCartesian(x+12,n+180);ut.text(o.util.round(it)).move([a[0]+_[0],a[1]+_[1]])}).on(\"mouseout.angular-guide\",function(t,e){st.select(\"line\").style({opacity:0})})}var pt=st.select(\"circle\").style({stroke:\"grey\",fill:\"none\"});q.on(\"mousemove.radial-guide\",function(t,e){var n=o.util.getMousePos($).radius;pt.attr({r:n}).style({opacity:.5}),lt=r.invert(o.util.getMousePos($).radius);var a=o.util.convertToCartesian(n,f.radialAxis.orientation);ft.text(o.util.round(lt)).move([a[0]+_[0],a[1]+_[1]])}).on(\"mouseout.radial-guide\",function(t,e){pt.style({opacity:0}),dt.hide(),ut.hide(),ft.hide()}),e.selectAll(\".geometry-group .mark\").on(\"mouseover.tooltip\",function(t,n){var r=a.select(this),i=r.style(\"fill\"),l=\"black\",s=r.style(\"opacity\")||1;if(r.attr({\"data-opacity\":s}),\"none\"!=i){r.attr({\"data-fill\":i}),l=a.hsl(i).darker().toString(),r.style({fill:l,opacity:1});var c={t:o.util.round(t[0]),r:o.util.round(t[1])};A&&(c.t=k[t[0]]);var u=\"t: \"+c.t+\", r: \"+c.r,f=this.getBoundingClientRect(),d=e.node().getBoundingClientRect(),h=[f.left+f.width/2-Z[0]-d.left,f.top+f.height/2-Z[1]-d.top];dt.config({color:l}).text(u),dt.move(h)}else i=r.style(\"stroke\"),r.attr({\"data-stroke\":i}),l=a.hsl(i).darker().toString(),r.style({stroke:l,opacity:1})}).on(\"mousemove.tooltip\",function(t,e){return 0!=a.event.which?!1:void(a.select(this).attr(\"data-fill\")&&dt.show())}).on(\"mouseout.tooltip\",function(t,e){dt.hide();var n=a.select(this),r=n.attr(\"data-fill\");r?n.style({fill:r,opacity:n.attr(\"data-opacity\")}):n.style({stroke:n.attr(\"data-stroke\"),opacity:n.attr(\"data-opacity\")})})}),d}var e,n,r,l,s={data:[],layout:{}},c={},u={},f=a.dispatch(\"hover\"),d={};return d.render=function(e){return t(e),this},d.config=function(t){if(!arguments.length)return s;var e=o.util.cloneJson(t);return e.data.forEach(function(t,e){s.data[e]||(s.data[e]={}),i(s.data[e],o.Axis.defaultConfig().data[0]),i(s.data[e],t)}),i(s.layout,o.Axis.defaultConfig().layout),i(s.layout,e.layout),this},d.getLiveConfig=function(){return u},d.getinputConfig=function(){return c},d.radialScale=function(t){return r},d.angularScale=function(t){return l},d.svg=function(){return e},a.rebind(d,f,\"on\"),d},o.Axis.defaultConfig=function(t,e){var n={data:[{t:[1,2,3,4],r:[10,11,12,13],name:\"Line1\",geometry:\"LinePlot\",color:null,strokeDash:\"solid\",strokeColor:null,strokeSize:\"1\",visibleInLegend:!0,opacity:1}],layout:{defaultColorRange:a.scale.category10().range(),title:null,height:450,width:500,margin:{top:40,right:40,bottom:40,left:40},font:{size:12,color:\"gray\",outlineColor:\"white\",family:\"Tahoma, sans-serif\"},direction:\"clockwise\",orientation:0,labelOffset:10,radialAxis:{domain:null,orientation:-45,ticksSuffix:\"\",visible:!0,gridLinesVisible:!0,tickOrientation:\"horizontal\",rewriteTicks:null},angularAxis:{domain:[0,360],ticksSuffix:\"\",visible:!0,gridLinesVisible:!0,labelsVisible:!0,tickOrientation:\"horizontal\",rewriteTicks:null,ticksCount:null,ticksStep:null},minorTicks:0,tickLength:null,tickColor:\"silver\",minorTickColor:\"#eee\",backgroundColor:\"none\",needsEndSpacing:null,showLegend:!0,legend:{reverseOrder:!1},opacity:1}};return n},o.util={},o.DATAEXTENT=\"dataExtent\",o.AREA=\"AreaChart\",o.LINE=\"LinePlot\",o.DOT=\"DotPlot\",o.BAR=\"BarChart\",o.util._override=function(t,e){for(var n in t)n in e&&(e[n]=t[n])},o.util._extend=function(t,e){for(var n in t)e[n]=t[n]},o.util._rndSnd=function(){return 2*Math.random()-1+(2*Math.random()-1)+(2*Math.random()-1)},o.util.dataFromEquation2=function(t,e){var n=e||6,r=a.range(0,360+n,n).map(function(e,n){var r=e*Math.PI/180,a=t(r);return[e,a]});return r},o.util.dataFromEquation=function(t,e,n){var r=e||6,o=[],i=[];a.range(0,360+r,r).forEach(function(e,n){var r=e*Math.PI/180,a=t(r);o.push(e),i.push(a)});var l={t:o,r:i};return n&&(l.name=n),l},o.util.ensureArray=function(t,e){if(\"undefined\"==typeof t)return null;var n=[].concat(t);return a.range(e).map(function(t,e){return n[e]||n[0]})},o.util.fillArrays=function(t,e,n){return e.forEach(function(e,r){t[e]=o.util.ensureArray(t[e],n)}),t},o.util.cloneJson=function(t){return JSON.parse(JSON.stringify(t))},o.util.validateKeys=function(t,e){\"string\"==typeof e&&(e=e.split(\".\"));var n=e.shift();return t[n]&&(!e.length||objHasKeys(t[n],e))},o.util.sumArrays=function(t,e){return a.zip(t,e).map(function(t,e){return a.sum(t)})},o.util.arrayLast=function(t){return t[t.length-1]},o.util.arrayEqual=function(t,e){for(var n=Math.max(t.length,e.length,1);n-- >=0&&t[n]===e[n];);return-2===n},o.util.flattenArray=function(t){for(var e=[];!o.util.arrayEqual(e,t);)e=t,t=[].concat.apply([],t);return t},o.util.deduplicate=function(t){return t.filter(function(t,e,n){return n.indexOf(t)==e})},o.util.convertToCartesian=function(t,e){var n=e*Math.PI/180,r=t*Math.cos(n),a=t*Math.sin(n);return[r,a]},o.util.round=function(t,e){var n=e||2,r=Math.pow(10,n);return Math.round(t*r)/r},o.util.getMousePos=function(t){var e=a.mouse(t.node()),n=e[0],r=e[1],o={};return o.x=n,o.y=r,o.pos=e,o.angle=180*(Math.atan2(r,n)+Math.PI)/Math.PI,o.radius=Math.sqrt(n*n+r*r),o},o.util.duplicatesCount=function(t){for(var e,n={},r={},a=0,o=t.length;o>a;a++)e=t[a],e in n?(n[e]++,r[e]=n[e]):n[e]=1;return r},o.util.duplicates=function(t){return Object.keys(o.util.duplicatesCount(t))},o.util.translator=function(t,e,n,r){if(r){var a=n.slice();n=e,e=a}var o=e.reduce(function(t,e){return\"undefined\"!=typeof t?t[e]:void 0},t);\"undefined\"!=typeof o&&(e.reduce(function(t,n,r){return\"undefined\"!=typeof t?(r===e.length-1&&delete t[n],t[n]):void 0},t),n.reduce(function(t,e,r){return\"undefined\"==typeof t[e]&&(t[e]={}),r===n.length-1&&(t[e]=o),t[e]},t))},o.PolyChart=function(){function t(){var t=n[0].geometryConfig,e=t.container;\"string\"==typeof e&&(e=a.select(e)),e.datum(n).each(function(e,n){function r(e,n){var r=t.radialScale(e[1]),a=(t.angularScale(e[0])+t.orientation)*Math.PI/180;return{r:r,t:a}}function o(t){var e=t.r*Math.cos(t.t),n=t.r*Math.sin(t.t);return{x:e,y:n}}var i=!!e[0].data.yStack,s=e.map(function(t,e){return i?a.zip(t.data.t[0],t.data.r[0],t.data.yStack[0]):a.zip(t.data.t[0],t.data.r[0])}),c=t.angularScale,u=t.radialScale.domain()[0],f={};f.bar=function(n,r,o){var i=e[o].data,l=t.radialScale(n[1])-t.radialScale(0),s=t.radialScale(n[2]||0),u=i.barWidth;a.select(this).attr({\"class\":\"mark bar\",d:\"M\"+[[l+s,-u/2],[l+s,u/2],[s,u/2],[s,-u/2]].join(\"L\")+\"Z\",transform:function(e,n){return\"rotate(\"+(t.orientation+c(e[0]))+\")\"}})},f.dot=function(t,n,i){var l=t[2]?[t[0],t[1]+t[2]]:t,s=a.svg.symbol().size(e[i].data.dotSize).type(e[i].data.dotType)(t,n);a.select(this).attr({\"class\":\"mark dot\",d:s,transform:function(t,e){var n=o(r(l));return\"translate(\"+[n.x,n.y]+\")\"}})};var d=a.svg.line.radial().interpolate(e[0].data.lineInterpolation).radius(function(e){return t.radialScale(e[1])}).angle(function(e){return t.angularScale(e[0])*Math.PI/180});f.line=function(n,r,o){var i=n[2]?s[o].map(function(t,e){return[t[0],t[1]+t[2]]}):s[o];if(a.select(this).each(f.dot).style({opacity:function(t,n){return+e[o].data.dotVisible},fill:v.stroke(n,r,o)}).attr({\"class\":\"mark dot\"}),!(r>0)){var l=a.select(this.parentNode).selectAll(\"path.line\").data([0]);l.enter().insert(\"path\"),l.attr({\"class\":\"line\",d:d(i),transform:function(e,n){return\"rotate(\"+(t.orientation+90)+\")\"},\"pointer-events\":\"none\"}).style({fill:function(t,e){return v.fill(n,r,o)},\"fill-opacity\":0,stroke:function(t,e){return v.stroke(n,r,o)},\"stroke-width\":function(t,e){return v[\"stroke-width\"](n,r,o)},\"stroke-dasharray\":function(t,e){return v[\"stroke-dasharray\"](n,r,o)},opacity:function(t,e){return v.opacity(n,r,o)},display:function(t,e){return v.display(n,r,o)}})}};var h=t.angularScale.range(),p=Math.abs(h[1]-h[0])/s[0].length*Math.PI/180,g=a.svg.arc().startAngle(function(t){return-p/2}).endAngle(function(t){return p/2}).innerRadius(function(e){return t.radialScale(u+(e[2]||0))}).outerRadius(function(e){return t.radialScale(u+(e[2]||0))+t.radialScale(e[1])});f.arc=function(e,n,r){a.select(this).attr({\"class\":\"mark arc\",d:g,transform:function(e,n){return\"rotate(\"+(t.orientation+c(e[0])+90)+\")\"}})};var v={fill:function(t,n,r){return e[r].data.color},stroke:function(t,n,r){return e[r].data.strokeColor},\"stroke-width\":function(t,n,r){return e[r].data.strokeSize+\"px\"},\"stroke-dasharray\":function(t,n,r){return l[e[r].data.strokeDash]},opacity:function(t,n,r){return e[r].data.opacity},display:function(t,n,r){return\"undefined\"==typeof e[r].data.visible||e[r].data.visible?\"block\":\"none\"}},m=a.select(this).selectAll(\"g.layer\").data(s);m.enter().append(\"g\").attr({\"class\":\"layer\"});var y=m.selectAll(\"path.mark\").data(function(t,e){return t});y.enter().append(\"path\").attr({\"class\":\"mark\"}),y.style(v).each(f[t.geometryType]),y.exit().remove(),m.exit().remove()})}var e,n=[o.PolyChart.defaultConfig()],r=a.dispatch(\"hover\"),l={solid:\"none\",dash:[5,2],dot:[2,5]};return t.config=function(t){return arguments.length?(t.forEach(function(t,e){n[e]||(n[e]={}),i(n[e],o.PolyChart.defaultConfig()),i(n[e],t)}),this):n},t.getColorScale=function(){return e},a.rebind(t,r,\"on\"),t},o.PolyChart.defaultConfig=function(){var t={data:{name:\"geom1\",t:[[1,2,3,4]],r:[[1,2,3,4]],dotType:\"circle\",dotSize:64,dotVisible:!1,barWidth:20,color:\"#ffa500\",strokeSize:1,strokeColor:\"silver\",strokeDash:\"solid\",opacity:1,index:0,visible:!0,visibleInLegend:!0},geometryConfig:{geometry:\"LinePlot\",geometryType:\"arc\",direction:\"clockwise\",orientation:0,container:\"body\",radialScale:null,angularScale:null,colorScale:a.scale.category20()}};return t},o.BarChart=function(){return o.PolyChart()},o.BarChart.defaultConfig=function(){var t={geometryConfig:{geometryType:\"bar\"}};return t},o.AreaChart=function(){return o.PolyChart()},o.AreaChart.defaultConfig=function(){var t={geometryConfig:{geometryType:\"arc\"}};return t},o.DotPlot=function(){ret"
+,
+"urn o.PolyChart()},o.DotPlot.defaultConfig=function(){var t={geometryConfig:{geometryType:\"dot\",dotType:\"circle\"}};return t},o.LinePlot=function(){return o.PolyChart()},o.LinePlot.defaultConfig=function(){var t={geometryConfig:{geometryType:\"line\"}};return t},o.Legend=function(){function t(){var n=e.legendConfig,r=e.data.map(function(t,e){return[].concat(t).map(function(t,r){var a=i({},n.elements[e]);return a.name=t,a.color=[].concat(n.elements[e].color)[r],a})}),o=a.merge(r);o=o.filter(function(t,e){return n.elements[e]&&(n.elements[e].visibleInLegend||\"undefined\"==typeof n.elements[e].visibleInLegend)}),n.reverseOrder&&(o=o.reverse());var l=n.container;(\"string\"==typeof l||l.nodeName)&&(l=a.select(l));var s=o.map(function(t,e){return t.color}),c=n.fontSize,u=null==n.isContinuous?\"number\"==typeof o[0]:n.isContinuous,f=u?n.height:c*o.length,d=l.classed(\"legend-group\",!0),h=d.selectAll(\"svg\").data([0]),p=h.enter().append(\"svg\").attr({width:300,height:f+c,xmlns:\"http://www.w3.org/2000/svg\",\"xmlns:xlink\":\"http://www.w3.org/1999/xlink\",version:\"1.1\"});p.append(\"g\").classed(\"legend-axis\",!0),p.append(\"g\").classed(\"legend-marks\",!0);var g=a.range(o.length),v=a.scale[u?\"linear\":\"ordinal\"]().domain(g).range(s),m=a.scale[u?\"linear\":\"ordinal\"]().domain(g)[u?\"range\":\"rangePoints\"]([0,f]),y=function(t,e){var n=3*e;return\"line\"===t?\"M\"+[[-e/2,-e/12],[e/2,-e/12],[e/2,e/12],[-e/2,e/12]]+\"Z\":-1!=a.svg.symbolTypes.indexOf(t)?a.svg.symbol().type(t).size(n)():a.svg.symbol().type(\"square\").size(n)()};if(u){var x=h.select(\".legend-marks\").append(\"defs\").append(\"linearGradient\").attr({id:\"grad1\",x1:\"0%\",y1:\"0%\",x2:\"0%\",y2:\"100%\"}).selectAll(\"stop\").data(s);x.enter().append(\"stop\"),x.attr({offset:function(t,e){return e/(s.length-1)*100+\"%\"}}).style({\"stop-color\":function(t,e){return t}}),h.append(\"rect\").classed(\"legend-mark\",!0).attr({height:n.height,width:n.colorBandWidth,fill:\"url(#grad1)\"})}else{var b=h.select(\".legend-marks\").selectAll(\"path.legend-mark\").data(o);b.enter().append(\"path\").classed(\"legend-mark\",!0),b.attr({transform:function(t,e){return\"translate(\"+[c/2,m(e)+c/2]+\")\"},d:function(t,e){var n=t.symbol;return y(n,c)},fill:function(t,e){return v(e)}}),b.exit().remove()}var _=a.svg.axis().scale(m).orient(\"right\"),w=h.select(\"g.legend-axis\").attr({transform:\"translate(\"+[u?n.colorBandWidth:c,c/2]+\")\"}).call(_);return w.selectAll(\".domain\").style({fill:\"none\",stroke:\"none\"}),w.selectAll(\"line\").style({fill:\"none\",stroke:u?n.textColor:\"none\"}),w.selectAll(\"text\").style({fill:n.textColor,\"font-size\":n.fontSize}).text(function(t,e){return o[e].name}),t}var e=o.Legend.defaultConfig(),n=a.dispatch(\"hover\");return t.config=function(t){return arguments.length?(i(e,t),this):e},a.rebind(t,n,\"on\"),t},o.Legend.defaultConfig=function(t,e){var n={data:[\"a\",\"b\",\"c\"],legendConfig:{elements:[{symbol:\"line\",color:\"red\"},{symbol:\"square\",color:\"yellow\"},{symbol:\"diamond\",color:\"limegreen\"}],height:150,colorBandWidth:30,fontSize:12,container:\"body\",isContinuous:null,textColor:\"grey\",reverseOrder:!1}};return n},o.tooltipPanel=function(){var t,e,n,r={container:null,hasTick:!1,fontSize:12,color:\"white\",padding:5},l=\"tooltip-\"+o.tooltipPanel.uid++,s=10,c=function(){t=r.container.selectAll(\"g.\"+l).data([0]);var a=t.enter().append(\"g\").classed(l,!0).style({\"pointer-events\":\"none\",display:\"none\"});return n=a.append(\"path\").style({fill:\"white\",\"fill-opacity\":.9}).attr({d:\"M0 0\"}),e=a.append(\"text\").attr({dx:r.padding+s,dy:.3*+r.fontSize}),c};return c.text=function(o){var i=a.hsl(r.color).l,l=i>=.5?\"#aaa\":\"white\",u=i>=.5?\"black\":\"white\",f=o||\"\";e.style({fill:u,\"font-size\":r.fontSize+\"px\"}).text(f);var d=r.padding,h=e.node().getBBox(),p={fill:r.color,stroke:l,\"stroke-width\":\"2px\"},g=h.width+2*d+s,v=h.height+2*d;return n.attr({d:\"M\"+[[s,-v/2],[s,-v/4],[r.hasTick?0:s,0],[s,v/4],[s,v/2],[g,v/2],[g,-v/2]].join(\"L\")+\"Z\"}).style(p),t.attr({transform:\"translate(\"+[s,-v/2+2*d]+\")\"}),t.style({display:\"block\"}),c},c.move=function(e){return t?(t.attr({transform:\"translate(\"+[e[0],e[1]]+\")\"}).style({display:\"block\"}),c):void 0},c.hide=function(){return t?(t.style({display:\"none\"}),c):void 0},c.show=function(){return t?(t.style({display:\"block\"}),c):void 0},c.config=function(t){return i(r,t),c},c},o.tooltipPanel.uid=1,o.adapter={},o.adapter.plotly=function(){var t={};return t.convert=function(t,e){var n={};if(t.data&&(n.data=t.data.map(function(t,n){var r=i({},t),a=[[r,[\"marker\",\"color\"],[\"color\"]],[r,[\"marker\",\"opacity\"],[\"opacity\"]],[r,[\"marker\",\"line\",\"color\"],[\"strokeColor\"]],[r,[\"marker\",\"line\",\"dash\"],[\"strokeDash\"]],[r,[\"marker\",\"line\",\"width\"],[\"strokeSize\"]],[r,[\"marker\",\"symbol\"],[\"dotType\"]],[r,[\"marker\",\"size\"],[\"dotSize\"]],[r,[\"marker\",\"barWidth\"],[\"barWidth\"]],[r,[\"line\",\"interpolation\"],[\"lineInterpolation\"]],[r,[\"showlegend\"],[\"visibleInLegend\"]]];return a.forEach(function(t,n){o.util.translator.apply(null,t.concat(e))}),e||delete r.marker,e&&delete r.groupId,e?(\"LinePlot\"===r.geometry?(r.type=\"scatter\",r.dotVisible===!0?(delete r.dotVisible,r.mode=\"lines+markers\"):r.mode=\"lines\"):\"DotPlot\"===r.geometry?(r.type=\"scatter\",r.mode=\"markers\"):\"AreaChart\"===r.geometry?r.type=\"area\":\"BarChart\"===r.geometry&&(r.type=\"bar\"),delete r.geometry):(\"scatter\"===r.type?\"lines\"===r.mode?r.geometry=\"LinePlot\":\"markers\"===r.mode?r.geometry=\"DotPlot\":\"lines+markers\"===r.mode&&(r.geometry=\"LinePlot\",r.dotVisible=!0):\"area\"===r.type?r.geometry=\"AreaChart\":\"bar\"===r.type&&(r.geometry=\"BarChart\"),delete r.mode,delete r.type),r}),!e&&t.layout&&\"stack\"===t.layout.barmode)){var r=o.util.duplicates(n.data.map(function(t,e){return t.geometry}));n.data.forEach(function(t,e){var a=r.indexOf(t.geometry);-1!=a&&(n.data[e].groupId=a)})}if(t.layout){var l=i({},t.layout),s=[[l,[\"plot_bgcolor\"],[\"backgroundColor\"]],[l,[\"showlegend\"],[\"showLegend\"]],[l,[\"radialaxis\"],[\"radialAxis\"]],[l,[\"angularaxis\"],[\"angularAxis\"]],[l.angularaxis,[\"showline\"],[\"gridLinesVisible\"]],[l.angularaxis,[\"showticklabels\"],[\"labelsVisible\"]],[l.angularaxis,[\"nticks\"],[\"ticksCount\"]],[l.angularaxis,[\"tickorientation\"],[\"tickOrientation\"]],[l.angularaxis,[\"ticksuffix\"],[\"ticksSuffix\"]],[l.angularaxis,[\"range\"],[\"domain\"]],[l.angularaxis,[\"endpadding\"],[\"endPadding\"]],[l.radialaxis,[\"showline\"],[\"gridLinesVisible\"]],[l.radialaxis,[\"tickorientation\"],[\"tickOrientation\"]],[l.radialaxis,[\"ticksuffix\"],[\"ticksSuffix\"]],[l.radialaxis,[\"range\"],[\"domain\"]],[l.angularAxis,[\"showline\"],[\"gridLinesVisible\"]],[l.angularAxis,[\"showticklabels\"],[\"labelsVisible\"]],[l.angularAxis,[\"nticks\"],[\"ticksCount\"]],[l.angularAxis,[\"tickorientation\"],[\"tickOrientation\"]],[l.angularAxis,[\"ticksuffix\"],[\"ticksSuffix\"]],[l.angularAxis,[\"range\"],[\"domain\"]],[l.angularAxis,[\"endpadding\"],[\"endPadding\"]],[l.radialAxis,[\"showline\"],[\"gridLinesVisible\"]],[l.radialAxis,[\"tickorientation\"],[\"tickOrientation\"]],[l.radialAxis,[\"ticksuffix\"],[\"ticksSuffix\"]],[l.radialAxis,[\"range\"],[\"domain\"]],[l.font,[\"outlinecolor\"],[\"outlineColor\"]],[l.legend,[\"traceorder\"],[\"reverseOrder\"]],[l,[\"labeloffset\"],[\"labelOffset\"]],[l,[\"defaultcolorrange\"],[\"defaultColorRange\"]]];if(s.forEach(function(t,n){o.util.translator.apply(null,t.concat(e))}),e?(\"undefined\"!=typeof l.tickLength&&(l.angularaxis.ticklen=l.tickLength,delete l.tickLength),l.tickColor&&(l.angularaxis.tickcolor=l.tickColor,delete l.tickColor)):(l.angularAxis&&\"undefined\"!=typeof l.angularAxis.ticklen&&(l.tickLength=l.angularAxis.ticklen),l.angularAxis&&\"undefined\"!=typeof l.angularAxis.tickcolor&&(l.tickColor=l.angularAxis.tickcolor)),l.legend&&\"boolean\"!=typeof l.legend.reverseOrder&&(l.legend.reverseOrder=\"normal\"!=l.legend.reverseOrder),l.legend&&\"boolean\"==typeof l.legend.traceorder&&(l.legend.traceorder=l.legend.traceorder?\"reversed\":\"normal\",delete l.legend.reverseOrder),l.margin&&\"undefined\"!=typeof l.margin.t){var c=[\"t\",\"r\",\"b\",\"l\",\"pad\"],u=[\"top\",\"right\",\"bottom\",\"left\",\"pad\"],f={};a.entries(l.margin).forEach(function(t,e){f[u[c.indexOf(t.key)]]=t.value}),l.margin=f}e&&(delete l.needsEndSpacing,delete l.minorTickColor,delete l.minorTicks,delete l.angularaxis.ticksCount,delete l.angularaxis.ticksCount,delete l.angularaxis.ticksStep,delete l.angularaxis.rewriteTicks,delete l.angularaxis.nticks,delete l.radialaxis.ticksCount,delete l.radialaxis.ticksCount,delete l.radialaxis.ticksStep,delete l.radialaxis.rewriteTicks,delete l.radialaxis.nticks),n.layout=l}return n},t}},{\"../../plotly\":107,\"./micropolar_manager\":134,d3:9}],134:[function(t,e,n){\"use strict\";var r=t(\"../../plotly\"),a=t(\"d3\"),o=t(\"./undo_manager\"),i=e.exports={},l=r.Lib.extendDeepAll;i.framework=function(t){function e(e,o){return o&&(f=o),a.select(a.select(f).node().parentNode).selectAll(\".svg-container>*:not(.chart-root)\").remove(),n=n?l(n,e):e,c||(c=r.micropolar.Axis()),u=r.micropolar.adapter.plotly().convert(n),c.config(u).render(f),t.data=n.data,t.layout=n.layout,i.fillLayout(t),n}var n,s,c,u,f,d=new o;return e.isPolar=!0,e.svg=function(){return c.svg()},e.getConfig=function(){return n},e.getLiveConfig=function(){return r.micropolar.adapter.plotly().convert(c.getLiveConfig(),!0)},e.getLiveScales=function(){return{t:c.angularScale(),r:c.radialScale()}},e.setUndoPoint=function(){var t=this,e=r.micropolar.util.cloneJson(n);!function(e,n){d.add({undo:function(){n&&t(n)},redo:function(){t(e)}})}(e,s),s=r.micropolar.util.cloneJson(e)},e.undo=function(){d.undo()},e.redo=function(){d.redo()},e},i.fillLayout=function(t){var e=a.select(t).selectAll(\".plot-container\"),n=e.selectAll(\".svg-container\"),o=t.framework&&t.framework.svg&&t.framework.svg(),i={width:800,height:600,paper_bgcolor:r.Color.background,_container:e,_paperdiv:n,_paper:o};t._fullLayout=l(i,t.layout)}},{\"../../plotly\":107,\"./undo_manager\":135,d3:9}],135:[function(t,e,n){\"use strict\";e.exports=function(){function t(t,e){return t?(a=!0,t[e](),a=!1,this):this}var e,n=[],r=-1,a=!1;return{add:function(t){return a?this:(n.splice(r+1,n.length-r),n.push(t),r=n.length-1,this)},setCallback:function(t){e=t},undo:function(){var a=n[r];return a?(t(a,\"undo\"),r-=1,e&&e(a.undo),thi"
+,
+"s):this},redo:function(){var a=n[r+1];return a?(t(a,\"redo\"),r+=1,e&&e(a.redo),this):this},clear:function(){n=[],r=-1},hasUndo:function(){return-1!==r},hasRedo:function(){return r<n.length-1},getCommands:function(){return n},getPreviousCommand:function(){return n[r-1]},getIndex:function(){return r}}}},{}],136:[function(t,e,n){\"use strict\";function r(t){var e;switch(t){case\"themes__thumb\":e={autosize:!0,width:150,height:150,title:\"\",showlegend:!1,margin:{l:5,r:5,t:5,b:5,pad:0},annotations:[]};break;case\"thumbnail\":e={title:\"\",hidesources:!0,showlegend:!1,borderwidth:0,bordercolor:\"\",margin:{l:1,r:1,t:1,b:1,pad:0},annotations:[]};break;default:e={}}return e}function a(t){var e=[\"xaxis\",\"yaxis\",\"zaxis\"];return e.indexOf(t.slice(0,5))>-1}var o=t(\"../plotly\"),i=o.Lib.extendFlat,l=o.Lib.extendDeep;e.exports=function(t,e){t.framework&&t.framework.isPolar&&(t=t.framework.getConfig());var n,s=t.data,c=t.layout,u=l([],s),f=l({},c,r(e.tileClass));if(e.width&&(f.width=e.width),e.height&&(f.height=e.height),\"thumbnail\"===e.tileClass||\"themes__thumb\"===e.tileClass){f.annotations=[];var d=Object.keys(f);for(n=0;n<d.length;n++)a(d[n])&&(f[d[n]].title=\"\");for(n=0;n<u.length;n++){var h=u[n];h.showscale=!1,h.marker&&(h.marker.showscale=!1),\"pie\"===h.type&&(h.textposition=\"none\")}}if(Array.isArray(e.annotations))for(n=0;n<e.annotations.length;n++)f.annotations.push(e.annotations[n]);var p=o.Plots.getSubplotIds(f,\"gl3d\");if(p.length){var g={};for(\"thumbnail\"===e.tileClass&&(g={title:\"\",showaxeslabels:!1,showticklabels:!1,linetickenable:!1}),n=0;n<p.length;n++){var v=p[n];i(f[v].xaxis,g),i(f[v].yaxis,g),i(f[v].zaxis,g),f[v]._scene=null}}var m=document.createElement(\"div\");e.tileClass&&(m.className=e.tileClass);var y={td:m,layout:f,data:u,config:{staticPlot:void 0===e.staticPlot?!0:e.staticPlot,plotGlPixelRatio:void 0===e.plotGlPixelRatio?2:e.plotGlPixelRatio,displaylogo:e.displaylogo||!1,showLink:e.showLink||!1,showTips:e.showTips||!1}};return\"transparent\"!==e.setBackground&&(y.config.setBackground=e.setBackground||\"opaque\"),y.td.defaultLayout=r(e.tileClass),y}},{\"../plotly\":107}],137:[function(t,e,n){\"use strict\";function r(t,e){return e=e||{},e.format=e.format||\"png\",new Promise(function(n,r){t._snapshotInProgress&&r(new Error(\"Snapshotting already in progress.\")),o.isIE()&&\"svg\"!==e.format&&r(new Error(\"Sorry IE does not support downloading from canvas. Try {format:'svg'} instead.\")),t._snapshotInProgress=!0;var l=a(t,e),s=e.filename||t.fn||\"newplot\";s+=\".\"+e.format,l.then(function(e){return t._snapshotInProgress=!1,i(e,s)}).then(function(t){n(t)}).catch(function(e){t._snapshotInProgress=!1,r(e)})})}var a=t(\"../plot_api/to_image\"),o=t(\"../lib\"),i=t(\"./filesaver\");e.exports=r},{\"../lib\":89,\"../plot_api/to_image\":105,\"./filesaver\":138}],138:[function(t,e,n){\"use strict\";var r=function(t,e){var n=document.createElement(\"a\"),r=\"download\"in n,a=/Version\\/[\\d\\.]+.*Safari/.test(navigator.userAgent),o=new Promise(function(o,i){\"undefined\"!=typeof navigator&&/MSIE [1-9]\\./.test(navigator.userAgent)&&i(new Error(\"IE < 10 unsupported\")),a&&(document.location.href=\"data:application/octet-stream\"+t.slice(t.search(/[,;]/)),o(e)),e||(e=\"download\"),r&&(n.href=t,n.download=e,document.body.appendChild(n),n.click(),document.body.removeChild(n),o(e)),\"undefined\"!=typeof navigator&&navigator.msSaveBlob&&(navigator.msSaveBlob(new Blob([t]),e),o(e)),i(new Error(\"download error\"))});return o};e.exports=r},{}],139:[function(t,e,n){\"use strict\";function r(t){return t._has&&(t._has(\"gl3d\")||t._has(\"gl2d\"))?500:0}function a(t){return t.data&&t.data[0]&&t.data[0].r?void 0:function(){(t.calcdata||[]).forEach(function(t){t[0]&&t[0].t&&t[0].t.cb&&t[0].t.cb()})}}var o={getDelay:r,getRedrawFunc:a,clone:t(\"./cloneplot\"),toSVG:t(\"./tosvg\"),svgToImg:t(\"./svgtoimg\"),toImage:t(\"./toimage\"),downloadImage:t(\"./download\")};e.exports=o},{\"./cloneplot\":136,\"./download\":137,\"./svgtoimg\":140,\"./toimage\":141,\"./tosvg\":142}],140:[function(t,e,n){\"use strict\";function r(t){var e=t.emitter||new o,n=new Promise(function(r,o){var i=window.Image,l=t.svg,s=t.format||\"png\";if(a.isIE()&&(l=l.replace(/\"/gi,\"'\"),l=l.replace(/(\\('#)(.*)('\\))/gi,'(\"$2\")'),l=l.replace(/(\\\\')/gi,'\"'),\"svg\"!==s)){var c=new Error(\"Sorry IE does not support downloading from canvas. Try {format:'svg'} instead.\");return o(c),t.promise?n:e.emit(\"error\",c)}var u=t.canvas,f=u.getContext(\"2d\"),d=new i,h=\"data:image/svg+xml,\"+encodeURIComponent(l);u.height=t.height||150,u.width=t.width||300,d.onload=function(){var n;switch(\"svg\"!==s&&f.drawImage(d,0,0),s){case\"jpeg\":n=u.toDataURL(\"image/jpeg\");break;case\"png\":n=u.toDataURL(\"image/png\");break;case\"webp\":n=u.toDataURL(\"image/webp\");break;case\"svg\":n=h;break;default:if(o(new Error(\"Image format is not jpeg, png or svg\")),!t.promise)return e.emit(\"error\",\"Image format is not jpeg, png or svg\")}r(n),t.promise||e.emit(\"success\",n)},d.onerror=function(n){return o(n),t.promise?void 0:e.emit(\"error\",n)},d.src=h});return t.promise?n:e}var a=t(\"../lib\"),o=t(\"events\").EventEmitter;e.exports=r},{\"../lib\":89,events:7}],141:[function(t,e,n){\"use strict\";function r(t,e){function n(){var t=r.getDelay(c._fullLayout);setTimeout(function(){var t=o.Snapshot.toSVG(c),n=document.createElement(\"canvas\");n.id=i.randstr(),l=o.Snapshot.svgToImg({format:e.format,width:c._fullLayout.width,height:c._fullLayout.height,canvas:n,emitter:l,svg:t}),l.clean=function(){c&&document.body.removeChild(c)}},t)}var r=o.Snapshot,l=new a,s=r.clone(t,{format:\"png\"}),c=s.td;c.style.position=\"absolute\",c.style.left=\"-5000px\",document.body.appendChild(c);var u=r.getRedrawFunc(c);return o.plot(c,s.data,s.layout,s.config).then(u).then(n).catch(function(t){l.emit(\"error\",t)}),l}var a=t(\"events\").EventEmitter,o=t(\"../plotly\"),i=t(\"../lib\");e.exports=r},{\"../lib\":89,\"../plotly\":107,events:7}],142:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"../lib/svg_text_utils\"),o=t(\"../components/drawing\"),i=t(\"../components/color\"),l=t(\"../constants/xmlns_namespaces\");e.exports=function(t,e){var n,s=t._fullLayout,c=s._paper,u=s._toppaper;c.insert(\"rect\",\":first-child\").call(o.setRect,0,0,s.width,s.height).call(i.fill,s.paper_bgcolor);var f=s._basePlotModules||[];for(n=0;n<f.length;n++){var d=f[n];d.toSVG&&d.toSVG(t)}if(u){var h=u.node().childNodes,p=Array.prototype.slice.call(h);for(n=0;n<p.length;n++){var g=p[n];g.childNodes.length&&c.node().appendChild(g)}}s._draggers&&s._draggers.remove(),c.node().style.background=\"\",c.selectAll(\"text\").attr(\"data-unformatted\",null).each(function(){var t=r.select(this);if(\"hidden\"===t.style(\"visibility\"))return void t.remove();var e=t.style(\"font-family\");e&&-1!==e.indexOf('\"')&&t.style(\"font-family\",e.replace(/\"/g,\"TOBESTRIPPED\"))}),\"pdf\"!==e&&\"eps\"!==e||c.selectAll(\"#MathJax_SVG_glyphs path\").attr(\"stroke-width\",0),c.node().setAttributeNS(l.xmlns,\"xmlns\",l.svg),c.node().setAttributeNS(l.xmlns,\"xmlns:xlink\",l.xlink);var v=(new window.XMLSerializer).serializeToString(c.node());return v=a.html_entity_decode(v),v=a.xml_entity_encode(v),v=v.replace(/(\"TOBESTRIPPED)|(TOBESTRIPPED\")/g,\"'\")}},{\"../components/color\":18,\"../components/drawing\":41,\"../constants/xmlns_namespaces\":82,\"../lib/svg_text_utils\":100,d3:9}],143:[function(t,e,n){\"use strict\";var r=t(\"../../lib\").mergeArray;e.exports=function(t){var e=t[0].trace,n=e.marker,a=n.line;r(e.text,t,\"tx\"),r(n.opacity,t,\"mo\"),r(n.color,t,\"mc\"),r(a.color,t,\"mlc\"),r(a.width,t,\"mlw\")}},{\"../../lib\":89}],144:[function(t,e,n){\"use strict\";var r=t(\"../scatter/attributes\"),a=t(\"../../components/colorscale/color_attributes\"),o=t(\"../../lib/extend\").extendFlat,i=r.marker,l=i.line,s=o({},l.width,{dflt:0}),c=o({},{width:s},a(\"marker.line\")),u=o({},{showscale:i.showscale,line:c},a(\"marker\"));e.exports={x:r.x,x0:r.x0,dx:r.dx,y:r.y,y0:r.y0,dy:r.dy,text:r.text,orientation:{valType:\"enumerated\",values:[\"v\",\"h\"]},marker:u,r:r.r,t:r.t,_nestedModules:{error_y:\"ErrorBars\",error_x:\"ErrorBars\",\"marker.colorbar\":\"Colorbar\"},_deprecated:{bardir:{valType:\"enumerated\",values:[\"v\",\"h\"]}}}},{\"../../components/colorscale/color_attributes\":26,\"../../lib/extend\":88,\"../scatter/attributes\":167}],145:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../plots/cartesian/axes\"),o=t(\"../../components/colorscale/has_colorscale\"),i=t(\"../../components/colorscale/calc\");e.exports=function(t,e){var n,l,s,c=a.getFromId(t,e.xaxis||\"x\"),u=a.getFromId(t,e.yaxis||\"y\"),f=e.orientation||(e.x&&!e.y?\"h\":\"v\");\"h\"===f?(l=c.makeCalcdata(e,\"x\"),n=u.makeCalcdata(e,\"y\")):(l=u.makeCalcdata(e,\"y\"),n=c.makeCalcdata(e,\"x\"));var d=Math.min(n.length,l.length),h=[];for(s=0;d>s;s++)r(n[s])&&h.push({p:n[s],s:l[s],b:0});return o(e,\"marker\")&&i(e,e.marker.color,\"marker\",\"c\"),o(e,\"marker.line\")&&i(e,e.marker.line.color,\"marker.line\",\"c\"),h}},{\"../../components/colorscale/calc\":25,\"../../components/colorscale/has_colorscale\":31,\"../../plots/cartesian/axes\":110,\"fast-isnumeric\":11}],146:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"../../components/color\"),o=t(\"../scatter/xy_defaults\"),i=t(\"../bar/style_defaults\"),l=t(\"../../components/errorbars/defaults\"),s=t(\"./attributes\");e.exports=function(t,e,n,c){function u(n,a){return r.coerce(t,e,s,n,a)}var f=o(t,e,u);return f?(u(\"orientation\",e.x&&!e.y?\"h\":\"v\"),u(\"text\"),i(t,e,u,n,c),l(t,e,a.defaultLine,{axis:\"y\"}),void l(t,e,a.defaultLine,{axis:\"x\",inherit:\"y\"})):void(e.visible=!1)}},{\"../../components/color\":18,\"../../components/errorbars/defaults\":46,\"../../lib\":89,\"../bar/style_defaults\":154,\"../scatter/xy_defaults\":188,\"./attributes\":144}],147:[function(t,e,n){\"use strict\";var r=t(\"../../plots/cartesian/graph_interact\"),a=t(\"../../components/errorbars\"),o=t(\"../../components/color\");e.exports=function(t,e,n,i){var l,s=t.cd,c=s[0].trace,u=s[0].t,f=t.xa,d=t.ya,h=\"closest\"===i?u.barwidth/2:u.dbar*(1-f._gd._fullLayout.bargap)/2;l=\"closest\"!==i?function(t){return t.p}:\"h\"===c.orientation?function(t){return t.y}:function(t){return t.x};var p,g;\"h\"===c.orientation?(p=function(t){return r.inbox(t.b-e,t.x-e)+(t.x-e)/(t.x-t.b)},g=function(t){var e=l(t)-n;ret"
+,
+"urn r.inbox(e-h,e+h)}):(g=function(t){return r.inbox(t.b-n,t.y-n)+(t.y-n)/(t.y-t.b)},p=function(t){var n=l(t)-e;return r.inbox(n-h,n+h)});var v=r.getDistanceFunction(i,p,g);if(r.getClosest(s,v,t),t.index!==!1){var m=s[t.index],y=m.mcc||c.marker.color,x=m.mlcc||c.marker.line.color,b=m.mlw||c.marker.line.width;return o.opacity(y)?t.color=y:o.opacity(x)&&b&&(t.color=x),\"h\"===c.orientation?(t.x0=t.x1=f.c2p(m.x,!0),t.xLabelVal=m.s,t.y0=d.c2p(l(m)-h,!0),t.y1=d.c2p(l(m)+h,!0),t.yLabelVal=m.p):(t.y0=t.y1=d.c2p(m.y,!0),t.yLabelVal=m.s,t.x0=f.c2p(l(m)-h,!0),t.x1=f.c2p(l(m)+h,!0),t.xLabelVal=m.p),m.tx&&(t.text=m.tx),a.hoverInfo(m,c,t),[t]}}},{\"../../components/color\":18,\"../../components/errorbars\":47,\"../../plots/cartesian/graph_interact\":117}],148:[function(t,e,n){\"use strict\";var r={};r.attributes=t(\"./attributes\"),r.layoutAttributes=t(\"./layout_attributes\"),r.supplyDefaults=t(\"./defaults\"),r.supplyLayoutDefaults=t(\"./layout_defaults\"),r.calc=t(\"./calc\"),r.setPositions=t(\"./set_positions\"),r.colorbar=t(\"../scatter/colorbar\"),r.arraysToCalcdata=t(\"./arrays_to_calcdata\"),r.plot=t(\"./plot\"),r.style=t(\"./style\"),r.hoverPoints=t(\"./hover\"),r.moduleType=\"trace\",r.name=\"bar\",r.basePlotModule=t(\"../../plots/cartesian\"),r.categories=[\"cartesian\",\"bar\",\"oriented\",\"markerColorscale\",\"errorBarsOK\",\"showLegend\"],r.meta={},e.exports=r},{\"../../plots/cartesian\":118,\"../scatter/colorbar\":170,\"./arrays_to_calcdata\":143,\"./attributes\":144,\"./calc\":145,\"./defaults\":146,\"./hover\":147,\"./layout_attributes\":149,\"./layout_defaults\":150,\"./plot\":151,\"./set_positions\":152,\"./style\":153}],149:[function(t,e,n){\"use strict\";e.exports={barmode:{valType:\"enumerated\",values:[\"stack\",\"group\",\"overlay\",\"relative\"],dflt:\"group\"},barnorm:{valType:\"enumerated\",values:[\"\",\"fraction\",\"percent\"],dflt:\"\"},bargap:{valType:\"number\",min:0,max:1},bargroupgap:{valType:\"number\",min:0,max:1,dflt:0}}},{}],150:[function(t,e,n){\"use strict\";var r=t(\"../../plots/plots\"),a=t(\"../../plots/cartesian/axes\"),o=t(\"../../lib\"),i=t(\"./layout_attributes\");\n"
+,
+"e.exports=function(t,e,n){function l(n,r){return o.coerce(t,e,i,n,r)}for(var s=!1,c=!1,u=!1,f={},d=0;d<n.length;d++){var h=n[d];if(r.traceIs(h,\"bar\")){if(s=!0,\"overlay\"!==t.barmode&&\"stack\"!==t.barmode){var p=h.xaxis+h.yaxis;f[p]&&(u=!0),f[p]=!0}if(h.visible&&\"histogram\"===h.type){var g=a.getFromId({_fullLayout:e},h[\"v\"===h.orientation?\"xaxis\":\"yaxis\"]);\"category\"!==g.type&&(c=!0)}}}if(s){var v=l(\"barmode\");\"overlay\"!==v&&l(\"barnorm\"),l(\"bargap\",c&&!u?0:.2),l(\"bargroupgap\")}}},{\"../../lib\":89,\"../../plots/cartesian/axes\":110,\"../../plots/plots\":130,\"./layout_attributes\":149}],151:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"fast-isnumeric\"),o=t(\"../../lib\"),i=t(\"../../components/color\"),l=t(\"../../components/errorbars\"),s=t(\"./arrays_to_calcdata\");e.exports=function(t,e,n){var c=e.x(),u=e.y(),f=t._fullLayout,d=e.plot.select(\".barlayer\").selectAll(\"g.trace.bars\").data(n).enter().append(\"g\").attr(\"class\",\"trace bars\");d.append(\"g\").attr(\"class\",\"points\").each(function(e){var n=e[0].t,l=e[0].trace;s(e),r.select(this).selectAll(\"path\").data(o.identity).enter().append(\"path\").each(function(e){function o(t){return 0===f.bargap&&0===f.bargroupgap?r.round(Math.round(t)-m,2):t}function s(t,e){return Math.abs(t-e)>=2?o(t):t>e?Math.ceil(t):Math.floor(t)}var d,h,p,g;if(\"h\"===l.orientation?(p=u.c2p(n.poffset+e.p,!0),g=u.c2p(n.poffset+e.p+n.barwidth,!0),d=c.c2p(e.b,!0),h=c.c2p(e.s+e.b,!0)):(d=c.c2p(n.poffset+e.p,!0),h=c.c2p(n.poffset+e.p+n.barwidth,!0),g=u.c2p(e.s+e.b,!0),p=u.c2p(e.b,!0)),!(a(d)&&a(h)&&a(p)&&a(g)&&d!==h&&p!==g))return void r.select(this).remove();var v=(e.mlw+1||l.marker.line.width+1||(e.trace?e.trace.marker.line.width:0)+1)-1,m=r.round(v/2%1,2);if(!t._context.staticPlot){var y=i.opacity(e.mc||l.marker.color),x=1>y||v>.01?o:s;d=x(d,h),h=x(h,d),p=x(p,g),g=x(g,p)}r.select(this).attr(\"d\",\"M\"+d+\",\"+p+\"V\"+g+\"H\"+h+\"V\"+p+\"Z\")})}),d.call(l.plot,e)}},{\"../../components/color\":18,\"../../components/errorbars\":47,\"../../lib\":89,\"./arrays_to_calcdata\":143,d3:9,\"fast-isnumeric\":11}],152:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../plots/plots\"),o=t(\"../../plots/cartesian/axes\"),i=t(\"../../lib\");e.exports=function(t,e){var n,l,s=t._fullLayout,c=e.x(),u=e.y();[\"v\",\"h\"].forEach(function(f){function d(e){function n(t){t[p]=t.p+d}var r=[];e.forEach(function(e){t.calcdata[e].forEach(function(t){r.push(t.p)})});var a=i.distinctVals(r),l=a.vals,c=a.minDiff,u=!1,f=[];\"group\"===s.barmode&&e.forEach(function(e){u||(t.calcdata[e].forEach(function(t){u||f.forEach(function(e){Math.abs(t.p-e)<c&&(u=!0)})}),u||t.calcdata[e].forEach(function(t){f.push(t.p)}))}),o.minDtick(v,c,l[0],u),o.expand(v,l,{vpad:c/2}),c*=1-s.bargap,u&&(c/=h.length);for(var d,g=0;g<e.length;g++){var m=t.calcdata[e[g]][0].t;m.barwidth=c*(1-s.bargroupgap),m.poffset=((u?(2*g+1-e.length)*c:0)-m.barwidth)/2,m.dbar=a.minDiff,d=m.poffset+m.barwidth/2,t.calcdata[e[g]].forEach(n)}}var h=[],p={v:\"x\",h:\"y\"}[f],g={v:\"y\",h:\"x\"}[f],v=e[p](),m=e[g]();if(t._fullData.forEach(function(t,e){t.visible===!0&&a.traceIs(t,\"bar\")&&t.orientation===f&&t.xaxis===c._id&&t.yaxis===u._id&&h.push(e)}),h.length){\"overlay\"===s.barmode?h.forEach(function(t){d([t])}):d(h);var y=\"stack\"===s.barmode,x=\"relative\"===s.barmode,b=s.barnorm;if(y||x||b){var _,w,k,M=m.l2c(m.c2l(0)),A=M,L={},T=t.calcdata[h[0]][0].t.barwidth/100,z=0,S=!0;for(n=0;n<h.length;n++)for(w=t.calcdata[h[n]],l=0;l<w.length;l++)if(r(w[l].s)){z=Math.round(w[l].p/T),x&&w[l].s<0&&(z=\"-\"+z);var E=L[z]||0;(y||x)&&(w[l].b=E),_=w[l].b+w[l].s,L[z]=E+w[l].s,(y||x)&&(w[l][g]=_,!b&&r(m.c2l(_))&&(M=Math.max(M,_),A=Math.min(A,_)))}if(b){var C=\"fraction\"===b?1:100,O=!1,P=C/1e9;for(S=!1,A=0,M=y?C:0,n=0;n<h.length;n++)for(w=t.calcdata[h[n]],l=0;l<w.length;l++)O=x&&w[l].s<0,z=Math.round(w[l].p/T),O&&(z=\"-\"+z),k=C/L[z],O&&(k*=-1),w[l].b*=k,w[l].s*=k,_=w[l].b+w[l].s,w[l][g]=_,r(m.c2l(_))&&(A-P>_&&(S=!0,A=_),_>M+P&&(S=!0,M=_))}o.expand(m,[A,M],{tozero:!0,padded:S})}else{var N=function(t){return t[g]=t.s,t.s};for(n=0;n<h.length;n++)o.expand(m,t.calcdata[h[n]].map(N),{tozero:!0,padded:!0})}}})}},{\"../../lib\":89,\"../../plots/cartesian/axes\":110,\"../../plots/plots\":130,\"fast-isnumeric\":11}],153:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"../../components/color\"),o=t(\"../../components/drawing\"),i=t(\"../../components/errorbars\");e.exports=function(t){var e=r.select(t).selectAll(\"g.trace.bars\"),n=e.size(),l=t._fullLayout;e.style(\"opacity\",function(t){return t[0].trace.opacity}).each(function(t){(\"stack\"===l.barmode&&n>1||0===l.bargap&&0===l.bargroupgap&&!t[0].trace.marker.line.width)&&r.select(this).attr(\"shape-rendering\",\"crispEdges\")}),e.selectAll(\"g.points\").each(function(t){var e=t[0].trace,n=e.marker,i=n.line,l=(e._input||{}).marker||{},s=o.tryColorscale(n,l,\"\"),c=o.tryColorscale(n,l,\"line.\");r.select(this).selectAll(\"path\").each(function(t){var e,o,l=(t.mlw+1||i.width+1)-1,u=r.select(this);e=\"mc\"in t?t.mcc=s(t.mc):Array.isArray(n.color)?a.defaultLine:n.color,u.style(\"stroke-width\",l+\"px\").call(a.fill,e),l&&(o=\"mlc\"in t?t.mlcc=c(t.mlc):Array.isArray(i.color)?a.defaultLine:i.color,u.call(a.stroke,o))})}),e.call(i.style)}},{\"../../components/color\":18,\"../../components/drawing\":41,\"../../components/errorbars\":47,d3:9}],154:[function(t,e,n){\"use strict\";var r=t(\"../../components/color\"),a=t(\"../../components/colorscale/has_colorscale\"),o=t(\"../../components/colorscale/defaults\");e.exports=function(t,e,n,i,l){n(\"marker.color\",i),a(t,\"marker\")&&o(t,e,l,n,{prefix:\"marker.\",cLetter:\"c\"}),n(\"marker.line.color\",r.defaultLine),a(t,\"marker.line\")&&o(t,e,l,n,{prefix:\"marker.line.\",cLetter:\"c\"}),n(\"marker.line.width\")}},{\"../../components/color\":18,\"../../components/colorscale/defaults\":28,\"../../components/colorscale/has_colorscale\":31}],155:[function(t,e,n){\"use strict\";var r=t(\"../../components/color/attributes\"),a=t(\"../../plots/font_attributes\"),o=t(\"../../plots/attributes\"),i=t(\"../../lib/extend\").extendFlat;e.exports={labels:{valType:\"data_array\"},label0:{valType:\"number\",dflt:0},dlabel:{valType:\"number\",dflt:1},values:{valType:\"data_array\"},marker:{colors:{valType:\"data_array\"},line:{color:{valType:\"color\",dflt:r.defaultLine,arrayOk:!0},width:{valType:\"number\",min:0,dflt:0,arrayOk:!0}}},text:{valType:\"data_array\"},scalegroup:{valType:\"string\",dflt:\"\"},textinfo:{valType:\"flaglist\",flags:[\"label\",\"text\",\"value\",\"percent\"],extras:[\"none\"]},hoverinfo:i({},o.hoverinfo,{flags:[\"label\",\"text\",\"value\",\"percent\",\"name\"]}),textposition:{valType:\"enumerated\",values:[\"inside\",\"outside\",\"auto\",\"none\"],dflt:\"auto\",arrayOk:!0},textfont:i({},a,{}),insidetextfont:i({},a,{}),outsidetextfont:i({},a,{}),domain:{x:{valType:\"info_array\",items:[{valType:\"number\",min:0,max:1},{valType:\"number\",min:0,max:1}],dflt:[0,1]},y:{valType:\"info_array\",items:[{valType:\"number\",min:0,max:1},{valType:\"number\",min:0,max:1}],dflt:[0,1]}},hole:{valType:\"number\",min:0,max:1,dflt:0},sort:{valType:\"boolean\",dflt:!0},direction:{valType:\"enumerated\",values:[\"clockwise\",\"counterclockwise\"],dflt:\"counterclockwise\"},rotation:{valType:\"number\",min:-360,max:360,dflt:0},pull:{valType:\"number\",min:0,max:1,dflt:0,arrayOk:!0}}},{\"../../components/color/attributes\":17,\"../../lib/extend\":88,\"../../plots/attributes\":108,\"../../plots/font_attributes\":128}],156:[function(t,e,n){\"use strict\";function r(t,e){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=a[0].trace;o._module===e&&o.visible===!0&&n.push(a)}return n}var a=t(\"../../plots/plots\");n.name=\"pie\",n.plot=function(t){var e=a.getModule(\"pie\"),n=r(t.calcdata,e);n.length&&e.plot(t,n)},n.clean=function(t,e,n,r){var a=r._has&&r._has(\"pie\"),o=e._has&&e._has(\"pie\");a&&!o&&r._pielayer.selectAll(\"g.trace\").remove()}},{\"../../plots/plots\":130}],157:[function(t,e,n){\"use strict\";function r(t){if(!s){var e=i.defaults;s=e.slice();var n;for(n=0;n<e.length;n++)s.push(o(e[n]).lighten(20).toHexString());for(n=0;n<i.defaults.length;n++)s.push(o(e[n]).darken(20).toHexString())}return s[t%s.length]}var a=t(\"fast-isnumeric\"),o=t(\"tinycolor2\"),i=t(\"../../components/color\"),l=t(\"./helpers\");e.exports=function(t,e){var n,s,c,u,f,d,h=e.values,p=e.labels,g=[],v=t._fullLayout,m=v._piecolormap,y={},x=!1,b=0,_=v.hiddenlabels||[];if(e.dlabel)for(p=new Array(h.length),n=0;n<h.length;n++)p[n]=String(e.label0+n*e.dlabel);for(n=0;n<h.length;n++)s=h[n],a(s)&&(s=+s,0>s||(c=p[n],void 0!==c&&\"\"!==c||(c=n),c=String(c),void 0===y[c]&&(y[c]=!0,u=o(e.marker.colors[n]),u.isValid()?(u=i.addOpacity(u,u.getAlpha()),m[c]||(m[c]=u)):m[c]?u=m[c]:(u=!1,x=!0),f=-1!==_.indexOf(c),f||(b+=s),g.push({v:s,label:c,color:u,i:n,hidden:f}))));if(e.sort&&g.sort(function(t,e){return e.v-t.v}),x)for(n=0;n<g.length;n++)d=g[n],d.color===!1&&(m[d.label]=d.color=r(v._piedefaultcolorcount),v._piedefaultcolorcount++);if(g[0]&&(g[0].vTotal=b),e.textinfo&&\"none\"!==e.textinfo){var w,k=-1!==e.textinfo.indexOf(\"label\"),M=-1!==e.textinfo.indexOf(\"text\"),A=-1!==e.textinfo.indexOf(\"value\"),L=-1!==e.textinfo.indexOf(\"percent\"),T=v.separators;for(n=0;n<g.length;n++)d=g[n],w=k?[d.label]:[],M&&e.text[d.i]&&w.push(e.text[d.i]),A&&w.push(l.formatPieValue(d.v,T)),L&&w.push(l.formatPiePercent(d.v/b,T)),d.text=w.join(\"<br>\")}return g};var s},{\"../../components/color\":18,\"./helpers\":159,\"fast-isnumeric\":11,tinycolor2:13}],158:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./attributes\");e.exports=function(t,e,n,o){function i(n,o){return r.coerce(t,e,a,n,o)}var l=r.coerceFont,s=i(\"values\");if(!Array.isArray(s)||!s.length)return void(e.visible=!1);var c=i(\"labels\");Array.isArray(c)||(i(\"label0\"),i(\"dlabel\"));var u=i(\"marker.line.width\");u&&i(\"marker.line.color\");var f=i(\"marker.colors\");Array.isArray(f)||(e.marker.colors=[]),i(\"scalegroup\");var d=i(\"text\"),h=i(\"textinfo\",Array.isArray(d)?\"text+percent\":\"percent\");if(i(\"hoverinfo\",1===o._dataLength?\"label+text+value+percent\":void 0),h&&\"none\"!==h){var p=i(\"textposition\"),g=Array.isArray(p)||\"auto\"===p,v=g||\"inside\"===p,m=g||\"outside\"===p;if(v||m){var y=l(i,\"textfont\",o.font);v&&l(i,\"insidetextfont\",y),m&&l(i,\"outsidetextfont\",y)}}i(\"domain.x\"),i(\"domain.y\"),i(\"hol"
+,
+"e\"),i(\"sort\"),i(\"direction\"),i(\"rotation\"),i(\"pull\")}},{\"../../lib\":89,\"./attributes\":155}],159:[function(t,e,n){\"use strict\";var r=t(\"../../lib\");n.formatPiePercent=function(t,e){var n=(100*t).toPrecision(3);return-1!==n.lastIndexOf(\".\")&&(n=n.replace(/[.]?0+$/,\"\")),r.numSeparate(n,e)+\"%\"},n.formatPieValue=function(t,e){var n=t.toPrecision(10);return-1!==n.lastIndexOf(\".\")&&(n=n.replace(/[.]?0+$/,\"\")),r.numSeparate(n,e)}},{\"../../lib\":89}],160:[function(t,e,n){\"use strict\";var r={};r.attributes=t(\"./attributes\"),r.supplyDefaults=t(\"./defaults\"),r.supplyLayoutDefaults=t(\"./layout_defaults\"),r.layoutAttributes=t(\"./layout_attributes\"),r.calc=t(\"./calc\"),r.plot=t(\"./plot\"),r.style=t(\"./style\"),r.styleOne=t(\"./style_one\"),r.moduleType=\"trace\",r.name=\"pie\",r.basePlotModule=t(\"./base_plot\"),r.categories=[\"pie\",\"showLegend\"],r.meta={},e.exports=r},{\"./attributes\":155,\"./base_plot\":156,\"./calc\":157,\"./defaults\":158,\"./layout_attributes\":161,\"./layout_defaults\":162,\"./plot\":163,\"./style\":164,\"./style_one\":165}],161:[function(t,e,n){\"use strict\";e.exports={hiddenlabels:{valType:\"data_array\"}}},{}],162:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./layout_attributes\");e.exports=function(t,e){function n(n,o){return r.coerce(t,e,a,n,o)}n(\"hiddenlabels\")}},{\"../../lib\":89,\"./layout_attributes\":161}],163:[function(t,e,n){\"use strict\";function r(t,e,n){var r=Math.sqrt(t.width*t.width+t.height*t.height),o=t.width/t.height,i=Math.PI*Math.min(e.v/n.vTotal,.5),l=1-n.trace.hole,s=a(e,n),c={scale:s*n.r*2/r,rCenter:1-s,rotate:0};if(c.scale>=1)return c;var u=o+1/(2*Math.tan(i)),f=n.r*Math.min(1/(Math.sqrt(u*u+.5)+u),l/(Math.sqrt(o*o+l/2)+o)),d={scale:2*f/t.height,rCenter:Math.cos(f/n.r)-f*o/n.r,rotate:(180/Math.PI*e.midangle+720)%180-90},h=1/o,p=h+1/(2*Math.tan(i)),g=n.r*Math.min(1/(Math.sqrt(p*p+.5)+p),l/(Math.sqrt(h*h+l/2)+h)),v={scale:2*g/t.width,rCenter:Math.cos(g/n.r)-g/o/n.r,rotate:(180/Math.PI*e.midangle+810)%180-90},m=v.scale>d.scale?v:d;return c.scale<1&&m.scale>c.scale?m:c}function a(t,e){if(t.v===e.vTotal&&!e.trace.hole)return 1;var n=Math.PI*Math.min(t.v/e.vTotal,.5);return Math.min(1/(1+1/Math.sin(n)),(1-e.trace.hole)/2)}function o(t,e){var n=e.pxmid[0],r=e.pxmid[1],a=t.width/2,o=t.height/2;return 0>n&&(a*=-1),0>r&&(o*=-1),{scale:1,rCenter:1,rotate:0,x:a+Math.abs(o)*(a>0?1:-1)/2,y:o/(1+n*n/(r*r)),outside:!0}}function i(t,e){function n(t,e){return t.pxmid[1]-e.pxmid[1]}function r(t,e){return e.pxmid[1]-t.pxmid[1]}function a(t,n){n||(n={});var r,a,o,l,d,h,g=n.labelExtraY+(i?n.yLabelMax:n.yLabelMin),v=i?t.yLabelMin:t.yLabelMax,m=i?t.yLabelMax:t.yLabelMin,y=t.cyFinal+c(t.px0[1],t.px1[1]),x=g-v;if(x*f>0&&(t.labelExtraY=x),Array.isArray(e.pull))for(a=0;a<p.length;a++)o=p[a],o===t||(e.pull[t.i]||0)>=e.pull[o.i]||((t.pxmid[1]-o.pxmid[1])*f>0?(l=o.cyFinal+c(o.px0[1],o.px1[1]),x=l-v-t.labelExtraY,x*f>0&&(t.labelExtraY+=x)):(m+t.labelExtraY-y)*f>0&&(r=3*u*Math.abs(a-p.indexOf(t)),d=o.cxFinal+s(o.px0[0],o.px1[0]),h=d+r-(t.cxFinal+t.pxmid[0])-t.labelExtraX,h*u>0&&(t.labelExtraX+=h)))}var o,i,l,s,c,u,f,d,h,p,g,v,m;for(i=0;2>i;i++)for(l=i?n:r,c=i?Math.max:Math.min,f=i?1:-1,o=0;2>o;o++){for(s=o?Math.max:Math.min,u=o?1:-1,d=t[i][o],d.sort(l),h=t[1-i][o],p=h.concat(d),v=[],g=0;g<d.length;g++)void 0!==d[g].yLabelMid&&v.push(d[g]);for(m=!1,g=0;i&&g<h.length;g++)if(void 0!==h[g].yLabelMid){m=h[g];break}for(g=0;g<v.length;g++){var y=g&&v[g-1];m&&!g&&(y=m),a(v[g],y)}}}function l(t,e){var n,r,a,o,i,l,s,u,f,d,h=[];for(a=0;a<t.length;a++){if(i=t[a][0],l=i.trace,n=e.w*(l.domain.x[1]-l.domain.x[0]),r=e.h*(l.domain.y[1]-l.domain.y[0]),s=l.tiltaxis*Math.PI/180,u=l.pull,Array.isArray(u))for(u=0,o=0;o<l.pull.length;o++)l.pull[o]>u&&(u=l.pull[o]);i.r=Math.min(n/c(l.tilt,Math.sin(s),l.depth),r/c(l.tilt,Math.cos(s),l.depth))/(2+2*u),i.cx=e.l+e.w*(l.domain.x[1]+l.domain.x[0])/2,i.cy=e.t+e.h*(2-l.domain.y[1]-l.domain.y[0])/2,l.scalegroup&&-1===h.indexOf(l.scalegroup)&&h.push(l.scalegroup)}for(o=0;o<h.length;o++){for(d=1/0,f=h[o],a=0;a<t.length;a++)i=t[a][0],i.trace.scalegroup===f&&(d=Math.min(d,i.r*i.r/i.vTotal));for(a=0;a<t.length;a++)i=t[a][0],i.trace.scalegroup===f&&(i.r=Math.sqrt(d*i.vTotal))}}function s(t){function e(t){var e=f.r*Math.sin(t),n=-f.r*Math.cos(t);return h?[e*(1-l*r*r)+n*i*l,e*i*l+n*(1-l*a*a),Math.sin(o)*(n*a-e*r)]:[e,n]}var n,r,a,o,i,l,s,c,u,f=t[0],d=f.trace,h=d.tilt,p=d.rotation*Math.PI/180,g=2*Math.PI/f.vTotal,v=\"px0\",m=\"px1\";if(\"counterclockwise\"===d.direction){for(s=0;s<t.length&&t[s].hidden;s++);if(s===t.length)return;p+=g*t[s].v,g*=-1,v=\"px1\",m=\"px0\"}for(h&&(o=h*Math.PI/180,n=d.tiltaxis*Math.PI/180,i=Math.sin(n)*Math.cos(n),l=1-Math.cos(o),r=Math.sin(n),a=Math.cos(n)),u=e(p),s=0;s<t.length;s++)c=t[s],c.hidden||(c[v]=u,p+=g*c.v/2,c.pxmid=e(p),c.midangle=p,p+=g*c.v/2,u=e(p),c[m]=u,c.largeArc=c.v>f.vTotal/2?1:0)}function c(t,e,n){if(!t)return 1;var r=Math.sin(t*Math.PI/180);return Math.max(.01,n*r*Math.abs(e)+2*Math.sqrt(1-r*r*e*e))}var u=t(\"d3\"),f=t(\"../../plots/cartesian/graph_interact\"),d=t(\"../../components/color\"),h=t(\"../../components/drawing\"),p=t(\"../../lib/svg_text_utils\"),g=t(\"./helpers\");e.exports=function(t,e){var n=t._fullLayout;l(e,n._size);var c=n._pielayer.selectAll(\"g.trace\").data(e);c.enter().append(\"g\").attr({\"stroke-linejoin\":\"round\",\"class\":\"trace\"}),c.exit().remove(),c.order(),c.each(function(e){var l=u.select(this),c=e[0],v=c.trace,m=0,y=(v.depth||0)*c.r*Math.sin(m)/2,x=v.tiltaxis||0,b=x*Math.PI/180,_=[y*Math.sin(b),y*Math.cos(b)],w=c.r*Math.cos(m),k=l.selectAll(\"g.part\").data(v.tilt?[\"top\",\"sides\"]:[\"top\"]);k.enter().append(\"g\").attr(\"class\",function(t){return t+\" part\"}),k.exit().remove(),k.order(),s(e),l.selectAll(\".top\").each(function(){var l=u.select(this).selectAll(\"g.slice\").data(e);l.enter().append(\"g\").classed(\"slice\",!0),l.exit().remove();var s=[[[],[]],[[],[]]],m=!1;l.each(function(i){function l(e){var r=t._fullLayout,o=t._fullData[v.index],l=o.hoverinfo;if(\"all\"===l&&(l=\"label+text+value+percent+name\"),!t._dragging&&r.hovermode!==!1&&\"none\"!==l&&l){var s=a(i,c),u=k+i.pxmid[0]*(1-s),d=M+i.pxmid[1]*(1-s),h=n.separators,p=[];-1!==l.indexOf(\"label\")&&p.push(i.label),o.text&&o.text[i.i]&&-1!==l.indexOf(\"text\")&&p.push(o.text[i.i]),-1!==l.indexOf(\"value\")&&p.push(g.formatPieValue(i.v,h)),-1!==l.indexOf(\"percent\")&&p.push(g.formatPiePercent(i.v/c.vTotal,h)),f.loneHover({x0:u-s*c.r,x1:u+s*c.r,y:d,text:p.join(\"<br>\"),name:-1!==l.indexOf(\"name\")?o.name:void 0,color:i.color,idealAlign:i.pxmid[0]<0?\"left\":\"right\"},{container:r._hoverlayer.node(),outerContainer:r._paper.node()}),f.hover(t,e,\"pie\"),T=!0}}function d(e){t.emit(\"plotly_unhover\",{points:[e]}),T&&(f.loneUnhover(n._hoverlayer.node()),T=!1)}function y(){t._hoverdata=[i],t._hoverdata.trace=e.trace,f.click(t,{target:!0})}function b(t,e,n,r){return\"a\"+r*c.r+\",\"+r*w+\" \"+x+\" \"+i.largeArc+(n?\" 1 \":\" 0 \")+r*(e[0]-t[0])+\",\"+r*(e[1]-t[1])}if(i.hidden)return void u.select(this).selectAll(\"path,g\").remove();s[i.pxmid[1]<0?0:1][i.pxmid[0]<0?0:1].push(i);var k=c.cx+_[0],M=c.cy+_[1],A=u.select(this),L=A.selectAll(\"path.surface\").data([i]),T=!1;if(L.enter().append(\"path\").classed(\"surface\",!0).style({\"pointer-events\":\"all\"}),A.select(\"path.textline\").remove(),A.on(\"mouseover\",l).on(\"mouseout\",d).on(\"click\",y),v.pull){var z=+(Array.isArray(v.pull)?v.pull[i.i]:v.pull)||0;z>0&&(k+=z*i.pxmid[0],M+=z*i.pxmid[1])}i.cxFinal=k,i.cyFinal=M;var S=v.hole;if(i.v===c.vTotal){var E=\"M\"+(k+i.px0[0])+\",\"+(M+i.px0[1])+b(i.px0,i.pxmid,!0,1)+b(i.pxmid,i.px0,!0,1)+\"Z\";S?L.attr(\"d\",\"M\"+(k+S*i.px0[0])+\",\"+(M+S*i.px0[1])+b(i.px0,i.pxmid,!1,S)+b(i.pxmid,i.px0,!1,S)+\"Z\"+E):L.attr(\"d\",E)}else{var C=b(i.px0,i.px1,!0,1);if(S){var O=1-S;L.attr(\"d\",\"M\"+(k+S*i.px1[0])+\",\"+(M+S*i.px1[1])+b(i.px1,i.px0,!1,S)+\"l\"+O*i.px0[0]+\",\"+O*i.px0[1]+C+\"Z\")}else L.attr(\"d\",\"M\"+k+\",\"+M+\"l\"+i.px0[0]+\",\"+i.px0[1]+C+\"Z\")}var P=Array.isArray(v.textposition)?v.textposition[i.i]:v.textposition,N=A.selectAll(\"g.slicetext\").data(i.text&&\"none\"!==P?[0]:[]);N.enter().append(\"g\").classed(\"slicetext\",!0),N.exit().remove(),N.each(function(){var t=u.select(this).selectAll(\"text\").data([0]);t.enter().append(\"text\").attr(\"data-notex\",1),t.exit().remove(),t.text(i.text).attr({\"class\":\"slicetext\",transform:\"\",\"data-bb\":\"\",\"text-anchor\":\"middle\",x:0,y:0}).call(h.font,\"outside\"===P?v.outsidetextfont:v.insidetextfont).call(p.convertToTspans),t.selectAll(\"tspan.line\").attr({x:0,y:0});var e,n=h.bBox(t.node());\"outside\"===P?e=o(n,i):(e=r(n,i,c),\"auto\"===P&&e.scale<1&&(t.call(h.font,v.outsidetextfont),v.outsidetextfont.family===v.insidetextfont.family&&v.outsidetextfont.size===v.insidetextfont.size||(t.attr({\"data-bb\":\"\"}),n=h.bBox(t.node())),e=o(n,i)));var a=k+i.pxmid[0]*e.rCenter+(e.x||0),l=M+i.pxmid[1]*e.rCenter+(e.y||0);e.outside&&(i.yLabelMin=l-n.height/2,i.yLabelMid=l,i.yLabelMax=l+n.height/2,i.labelExtraX=0,i.labelExtraY=0,m=!0),t.attr(\"transform\",\"translate(\"+a+\",\"+l+\")\"+(e.scale<1?\"scale(\"+e.scale+\")\":\"\")+(e.rotate?\"rotate(\"+e.rotate+\")\":\"\")+\"translate(\"+-(n.left+n.right)/2+\",\"+-(n.top+n.bottom)/2+\")\")})}),m&&i(s,v),l.each(function(t){if(t.labelExtraX||t.labelExtraY){var e=u.select(this),n=e.select(\"g.slicetext text\");n.attr(\"transform\",\"translate(\"+t.labelExtraX+\",\"+t.labelExtraY+\")\"+n.attr(\"transform\"));var r=t.cxFinal+t.pxmid[0],a=t.cyFinal+t.pxmid[1],o=\"M\"+r+\",\"+a,i=(t.yLabelMax-t.yLabelMin)*(t.pxmid[0]<0?-1:1)/4;if(t.labelExtraX){var l=t.labelExtraX*t.pxmid[1]/t.pxmid[0],s=t.yLabelMid+t.labelExtraY-(t.cyFinal+t.pxmid[1]);o+=Math.abs(l)>Math.abs(s)?\"l\"+s*t.pxmid[0]/t.pxmid[1]+\",\"+s+\"H\"+(r+t.labelExtraX+i):\"l\"+t.labelExtraX+\",\"+l+\"v\"+(s-l)+\"h\"+i}else o+=\"V\"+(t.yLabelMid+t.labelExtraY)+\"h\"+i;e.append(\"path\").classed(\"textline\",!0).call(d.stroke,v.outsidetextfont.color).attr({\"stroke-width\":Math.min(2,v.outsidetextfont.size/8),d:o,fill:\"none\"})}})})}),setTimeout(function(){c.selectAll(\"tspan\").each(function(){var t=u.select(this);t.attr(\"dy\")&&t.attr(\"dy\",t.attr(\"dy\"))})},0)}},{\"../../components/color\":18,\"../../components/drawing\":41,\"../../lib/svg_text_utils\":100,\"../../plots/cartesian/graph_interact\":117,\"./helpers"
+,
+"\":159,d3:9}],164:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"./style_one\");e.exports=function(t){t._fullLayout._pielayer.selectAll(\".trace\").each(function(t){var e=t[0],n=e.trace,o=r.select(this);o.style({opacity:n.opacity}),o.selectAll(\".top path.surface\").each(function(t){r.select(this).call(a,t,n)})})}},{\"./style_one\":165,d3:9}],165:[function(t,e,n){\"use strict\";var r=t(\"../../components/color\");e.exports=function(t,e,n){var a=n.marker.line.color;Array.isArray(a)&&(a=a[e.i]||r.defaultLine);var o=n.marker.line.width||0;Array.isArray(o)&&(o=o[e.i]||0),t.style({\"stroke-width\":o,fill:e.color}).call(r.stroke,a)}},{\"../../components/color\":18}],166:[function(t,e,n){\"use strict\";var r=t(\"../../lib\");e.exports=function(t){var e=t[0].trace,n=e.marker;if(r.mergeArray(e.text,t,\"tx\"),r.mergeArray(e.textposition,t,\"tp\"),e.textfont&&(r.mergeArray(e.textfont.size,t,\"ts\"),r.mergeArray(e.textfont.color,t,\"tc\"),r.mergeArray(e.textfont.family,t,\"tf\")),n&&n.line){var a=n.line;r.mergeArray(n.opacity,t,\"mo\"),r.mergeArray(n.symbol,t,\"mx\"),r.mergeArray(n.color,t,\"mc\"),r.mergeArray(a.color,t,\"mlc\"),r.mergeArray(a.width,t,\"mlw\")}}},{\"../../lib\":89}],167:[function(t,e,n){\"use strict\";var r=t(\"../../components/colorscale/color_attributes\"),a=t(\"../../components/drawing\"),o=(t(\"./constants\"),t(\"../../lib/extend\").extendFlat);e.exports={x:{valType:\"data_array\"},x0:{valType:\"any\",dflt:0},dx:{valType:\"number\",dflt:1},y:{valType:\"data_array\"},y0:{valType:\"any\",dflt:0},dy:{valType:\"number\",dflt:1},text:{valType:\"string\",dflt:\"\",arrayOk:!0},mode:{valType:\"flaglist\",flags:[\"lines\",\"markers\",\"text\"],extras:[\"none\"]},hoveron:{valType:\"flaglist\",flags:[\"points\",\"fills\"]},line:{color:{valType:\"color\"},width:{valType:\"number\",min:0,dflt:2},shape:{valType:\"enumerated\",values:[\"linear\",\"spline\",\"hv\",\"vh\",\"hvh\",\"vhv\"],dflt:\"linear\"},smoothing:{valType:\"number\",min:0,max:1.3,dflt:1},dash:{valType:\"string\",values:[\"solid\",\"dot\",\"dash\",\"longdash\",\"dashdot\",\"longdashdot\"],dflt:\"solid\"}},connectgaps:{valType:\"boolean\",dflt:!1},fill:{valType:\"enumerated\",values:[\"none\",\"tozeroy\",\"tozerox\",\"tonexty\",\"tonextx\",\"toself\",\"tonext\"],dflt:\"none\"},fillcolor:{valType:\"color\"},marker:o({},{symbol:{valType:\"enumerated\",values:a.symbolList,dflt:\"circle\",arrayOk:!0},opacity:{valType:\"number\",min:0,max:1,arrayOk:!0},size:{valType:\"number\",min:0,dflt:6,arrayOk:!0},maxdisplayed:{valType:\"number\",min:0,dflt:0},sizeref:{valType:\"number\",dflt:1},sizemin:{valType:\"number\",min:0,dflt:0},sizemode:{valType:\"enumerated\",values:[\"diameter\",\"area\"],dflt:\"diameter\"},showscale:{valType:\"boolean\",dflt:!1},line:o({},{width:{valType:\"number\",min:0,arrayOk:!0}},r(\"marker.line\"))},r(\"marker\")),textposition:{valType:\"enumerated\",values:[\"top left\",\"top center\",\"top right\",\"middle left\",\"middle center\",\"middle right\",\"bottom left\",\"bottom center\",\"bottom right\"],dflt:\"middle center\",arrayOk:!0},textfont:{family:{valType:\"string\",noBlank:!0,strict:!0,arrayOk:!0},size:{valType:\"number\",min:1,arrayOk:!0},color:{valType:\"color\",arrayOk:!0}},r:{valType:\"data_array\"},t:{valType:\"data_array\"},_nestedModules:{error_y:\"ErrorBars\",error_x:\"ErrorBars\",\"marker.colorbar\":\"Colorbar\"}}},{\"../../components/colorscale/color_attributes\":26,\"../../components/drawing\":41,\"../../lib/extend\":88,\"./constants\":172}],168:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\"),a=t(\"../../plots/cartesian/axes\"),o=t(\"../../lib\"),i=t(\"./subtypes\"),l=t(\"./colorscale_calc\");e.exports=function(t,e){var n,s,c,u=a.getFromId(t,e.xaxis||\"x\"),f=a.getFromId(t,e.yaxis||\"y\"),d=u.makeCalcdata(e,\"x\"),h=f.makeCalcdata(e,\"y\"),p=Math.min(d.length,h.length);u._minDtick=0,f._minDtick=0,d.length>p&&d.splice(p,d.length-p),h.length>p&&h.splice(p,h.length-p);var g={padded:!0},v={padded:!0};if(i.hasMarkers(e)){if(n=e.marker,s=n.size,Array.isArray(s)){var m={type:\"linear\"};a.setConvert(m),s=m.makeCalcdata(e.marker,\"size\"),s.length>p&&s.splice(p,s.length-p)}var y,x=1.6*(e.marker.sizeref||1);y=\"area\"===e.marker.sizemode?function(t){return Math.max(Math.sqrt((t||0)/x),3)}:function(t){return Math.max((t||0)/x,3)},g.ppad=v.ppad=Array.isArray(s)?s.map(y):y(s)}l(e),!(\"tozerox\"===e.fill||\"tonextx\"===e.fill&&t.firstscatter)||d[0]===d[p-1]&&h[0]===h[p-1]?e.error_y.visible||-1===[\"tonexty\",\"tozeroy\"].indexOf(e.fill)&&(i.hasMarkers(e)||i.hasText(e))||(g.padded=!1,g.ppad=0):g.tozero=!0,!(\"tozeroy\"===e.fill||\"tonexty\"===e.fill&&t.firstscatter)||d[0]===d[p-1]&&h[0]===h[p-1]?-1!==[\"tonextx\",\"tozerox\"].indexOf(e.fill)&&(v.padded=!1):v.tozero=!0,a.expand(u,d,g),a.expand(f,h,v);var b=new Array(p);for(c=0;p>c;c++)b[c]=r(d[c])&&r(h[c])?{x:d[c],y:h[c]}:{x:!1,y:!1};return void 0!==typeof s&&o.mergeArray(s,b,\"ms\"),t.firstscatter=!1,b}},{\"../../lib\":89,\"../../plots/cartesian/axes\":110,\"./colorscale_calc\":171,\"./subtypes\":186,\"fast-isnumeric\":11}],169:[function(t,e,n){\"use strict\";e.exports=function(t){var e,n,r,a,o;for(e=0;e<t.length;e++)if(n=t[e],r=n.fill,\"none\"!==r&&\"scatter\"===n.type&&(n.opacity=void 0,\"tonexty\"===r||\"tonextx\"===r))for(a=e-1;a>=0;a--)if(o=t[a],\"scatter\"===o.type&&o.xaxis===n.xaxis&&o.yaxis===n.yaxis){o.opacity=void 0;break}}},{}],170:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"fast-isnumeric\"),o=t(\"../../lib\"),i=t(\"../../plots/plots\"),l=t(\"../../components/colorscale/get_scale\"),s=t(\"../../components/colorbar/draw\");e.exports=function(t,e){var n=e[0].trace,c=n.marker,u=\"cb\"+n.uid;if(t._fullLayout._infolayer.selectAll(\".\"+u).remove(),void 0===c||!c.showscale)return void i.autoMargin(t,u);var f=l(c.colorscale),d=c.color,h=c.cmin,p=c.cmax;a(h)||(h=o.aggNums(Math.min,null,d)),a(p)||(p=o.aggNums(Math.max,null,d));var g=e[0].t.cb=s(t,u);g.fillcolor(r.scale.linear().domain(f.map(function(t){return h+t[0]*(p-h)})).range(f.map(function(t){return t[1]}))).filllevels({start:h,end:p,size:(p-h)/254}).options(c.colorbar)()}},{\"../../components/colorbar/draw\":21,\"../../components/colorscale/get_scale\":30,\"../../lib\":89,\"../../plots/plots\":130,d3:9,\"fast-isnumeric\":11}],171:[function(t,e,n){\"use strict\";var r=t(\"../../components/colorscale/has_colorscale\"),a=t(\"../../components/colorscale/calc\"),o=t(\"./subtypes\");e.exports=function(t){o.hasLines(t)&&r(t,\"line\")&&a(t,t.line.color,\"line\",\"c\"),o.hasMarkers(t)&&(r(t,\"marker\")&&a(t,t.marker.color,\"marker\",\"c\"),r(t,\"marker.line\")&&a(t,t.marker.line.color,\"marker.line\",\"c\"))}},{\"../../components/colorscale/calc\":25,\"../../components/colorscale/has_colorscale\":31,\"./subtypes\":186}],172:[function(t,e,n){\"use strict\";e.exports={PTS_LINESONLY:20}},{}],173:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"./attributes\"),o=t(\"./constants\"),i=t(\"./subtypes\"),l=t(\"./xy_defaults\"),s=t(\"./marker_defaults\"),c=t(\"./line_defaults\"),u=t(\"./line_shape_defaults\"),f=t(\"./text_defaults\"),d=t(\"./fillcolor_defaults\"),h=t(\"../../components/errorbars/defaults\");e.exports=function(t,e,n,p){function g(n,o){return r.coerce(t,e,a,n,o)}var v=l(t,e,g),m=v<o.PTS_LINESONLY?\"lines+markers\":\"lines\";if(!v)return void(e.visible=!1);g(\"text\"),g(\"mode\",m),i.hasLines(e)&&(c(t,e,n,p,g),u(t,e,g),g(\"connectgaps\")),i.hasMarkers(e)&&s(t,e,n,p,g),i.hasText(e)&&f(t,e,p,g);var y=[];(i.hasMarkers(e)||i.hasText(e))&&(g(\"marker.maxdisplayed\"),y.push(\"points\")),g(\"fill\"),\"none\"!==e.fill&&(d(t,e,n,g),i.hasLines(e)||u(t,e,g)),\"tonext\"!==e.fill&&\"toself\"!==e.fill||y.push(\"fills\"),g(\"hoveron\",y.join(\"+\")||\"points\"),h(t,e,n,{axis:\"y\"}),h(t,e,n,{axis:\"x\",inherit:\"y\"})}},{\"../../components/errorbars/defaults\":46,\"../../lib\":89,\"./attributes\":167,\"./constants\":172,\"./fillcolor_defaults\":174,\"./line_defaults\":178,\"./line_shape_defaults\":180,\"./marker_defaults\":182,\"./subtypes\":186,\"./text_defaults\":187,\"./xy_defaults\":188}],174:[function(t,e,n){\"use strict\";var r=t(\"../../components/color\");e.exports=function(t,e,n,a){var o=!1;if(e.marker){var i=e.marker.color,l=(e.marker.line||{}).color;i&&!Array.isArray(i)?o=i:l&&!Array.isArray(l)&&(o=l)}a(\"fillcolor\",r.addOpacity((e.line||{}).color||o||n,.5))}},{\"../../components/color\":18}],175:[function(t,e,n){\"use strict\";var r=t(\"../../components/color\"),a=t(\"./subtypes\");e.exports=function(t,e){var n,o;if(\"lines\"===t.mode)return n=t.line.color,n&&r.opacity(n)?n:t.fillcolor;if(\"none\"===t.mode)return t.fill?t.fillcolor:\"\";var i=e.mcc||(t.marker||{}).color,l=e.mlcc||((t.marker||{}).line||{}).color;return o=i&&r.opacity(i)?i:l&&r.opacity(l)&&(e.mlw||((t.marker||{}).line||{}).width)?l:\"\",o?r.opacity(o)<.3?r.addOpacity(o,.3):o:(n=(t.line||{}).color,n&&r.opacity(n)&&a.hasLines(t)&&t.line.width?n:t.fillcolor)}},{\"../../components/color\":18,\"./subtypes\":186}],176:[function(t,e,n){\"use strict\";var r=t(\"../../lib\"),a=t(\"../../plots/cartesian/graph_interact\"),o=t(\"../../plots/cartesian/constants\"),i=t(\"../../components/errorbars\"),l=t(\"./get_trace_color\"),s=t(\"../../components/color\");e.exports=function(t,e,n,c){var u=t.cd,f=u[0].trace,d=t.xa,h=t.ya,p=d.c2p(e),g=h.c2p(n),v=[p,g];if(-1!==f.hoveron.indexOf(\"points\")){var m=function(t){var e=Math.max(3,t.mrc||0);return Math.max(Math.abs(d.c2p(t.x)-p)-e,1-3/e)},y=function(t){var e=Math.max(3,t.mrc||0);return Math.max(Math.abs(h.c2p(t.y)-g)-e,1-3/e)},x=function(t){var e=Math.max(3,t.mrc||0),n=d.c2p(t.x)-p,r=h.c2p(t.y)-g;return Math.max(Math.sqrt(n*n+r*r)-e,1-3/e)},b=a.getDistanceFunction(c,m,y,x);if(a.getClosest(u,b,t),t.index!==!1){var _=u[t.index],w=d.c2p(_.x,!0),k=h.c2p(_.y,!0),M=_.mrc||1;return r.extendFlat(t,{color:l(f,_),x0:w-M,x1:w+M,xLabelVal:_.x,y0:k-M,y1:k+M,yLabelVal:_.y}),_.tx?t.text=_.tx:f.text&&(t.text=f.text),i.hoverInfo(_,f,t),[t]}}if(-1!==f.hoveron.indexOf(\"fills\")&&f._polygons){var A,L,T,z,S,E,C,O,P,N=f._polygons,D=[],I=!1,R=1/0,j=-(1/0),q=1/0,F=-(1/0);for(A=0;A<N.length;A++)T=N[A],T.contains(v)&&(I=!I,D.push(T),q=Math.min(q,T.ymin),F=Math.max(F,T.ymax));if(I){q=Math.max(q,0),F=Math.min(F,h._length);var B=(q+F)/2;for(A=0;A<D.length;A++)for(z=D[A].pts,L=1;L<z.length;L++)O=z[L-1][1],P=z[L][1],O>B!=P>=B&&(E=z[L-1][0],C=z[L][0],S=E+(C-E)*(B-O)/(P-O),R=Math.min(R,S),j=Math.max(j,S));R=Math.max(R,0),j=Math.min(j,d._length);var H=s.defaultLine;return s.opacity(f.fillcolor)?H"
+,
+"=f.fillcolor:s.opacity((f.line||{}).color)&&(H=f.line.color),r.extendFlat(t,{distance:o.MAXDIST+10,x0:R,x1:j,y0:B,y1:B,color:H}),delete t.index,f.text&&!Array.isArray(f.text)?t.text=String(f.text):t.text=f.name,[t]}}}},{\"../../components/color\":18,\"../../components/errorbars\":47,\"../../lib\":89,\"../../plots/cartesian/constants\":115,\"../../plots/cartesian/graph_interact\":117,\"./get_trace_color\":175}],177:[function(t,e,n){\"use strict\";var r={},a=t(\"./subtypes\");r.hasLines=a.hasLines,r.hasMarkers=a.hasMarkers,r.hasText=a.hasText,r.isBubble=a.isBubble,r.attributes=t(\"./attributes\"),r.supplyDefaults=t(\"./defaults\"),r.cleanData=t(\"./clean_data\"),r.calc=t(\"./calc\"),r.arraysToCalcdata=t(\"./arrays_to_calcdata\"),r.plot=t(\"./plot\"),r.colorbar=t(\"./colorbar\"),r.style=t(\"./style\"),r.hoverPoints=t(\"./hover\"),r.selectPoints=t(\"./select\"),r.moduleType=\"trace\",r.name=\"scatter\",r.basePlotModule=t(\"../../plots/cartesian\"),r.categories=[\"cartesian\",\"symbols\",\"markerColorscale\",\"errorBarsOK\",\"showLegend\"],r.meta={},e.exports=r},{\"../../plots/cartesian\":118,\"./arrays_to_calcdata\":166,\"./attributes\":167,\"./calc\":168,\"./clean_data\":169,\"./colorbar\":170,\"./defaults\":173,\"./hover\":176,\"./plot\":183,\"./select\":184,\"./style\":185,\"./subtypes\":186}],178:[function(t,e,n){\"use strict\";var r=t(\"../../components/colorscale/has_colorscale\"),a=t(\"../../components/colorscale/defaults\");e.exports=function(t,e,n,o,i){var l=(t.marker||{}).color;if(i(\"line.color\",n),r(t,\"line\"))a(t,e,o,i,{prefix:\"line.\",cLetter:\"c\"});else{var s=(Array.isArray(l)?!1:l)||n;i(\"line.color\",s)}i(\"line.width\"),i(\"line.dash\")}},{\"../../components/colorscale/defaults\":28,\"../../components/colorscale/has_colorscale\":31}],179:[function(t,e,n){\"use strict\";var r=t(\"../../plots/cartesian/axes\");e.exports=function(t,e){function n(e){var n=w.c2p(t[e].x),r=k.c2p(t[e].y);return n===z||r===z?!1:[n,r]}function a(t){var e=t[0]/w._length,n=t[1]/k._length;return(1+10*Math.max(0,-e,e-1,-n,n-1))*A}function o(t,e){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r);\n"
+"}var i,l,s,c,u,f,d,h,p,g,v,m,y,x,b,_,w=e.xaxis,k=e.yaxis,M=e.connectGaps,A=e.baseTolerance,L=e.linear,T=[],z=r.BADNUM,S=.2,E=new Array(t.length),C=0;for(i=0;i<t.length;i++)if(l=n(i)){for(C=0,E[C++]=l,i++;i<t.length;i++){if(c=n(i),!c){if(M)continue;break}if(L){if(d=o(c,l),!(d<a(c)*S)){for(p=[(c[0]-l[0])/d,(c[1]-l[1])/d],u=l,v=d,m=x=b=0,h=!1,s=c,i++;i<t.length;i++){if(f=n(i),!f){if(M)continue;break}if(g=[f[0]-l[0],f[1]-l[1]],_=g[0]*p[1]-g[1]*p[0],x=Math.min(x,_),b=Math.max(b,_),b-x>a(f))break;s=f,y=g[0]*p[0]+g[1]*p[1],y>v?(v=y,c=f,h=!1):m>y&&(m=y,u=f,h=!0)}if(h?(E[C++]=c,s!==u&&(E[C++]=u)):(u!==l&&(E[C++]=u),s!==c&&(E[C++]=c)),E[C++]=s,i>=t.length||!f)break;E[C++]=f,l=f}}else E[C++]=c}T.push(E.slice(0,C))}return T}},{\"../../plots/cartesian/axes\":110}],180:[function(t,e,n){\"use strict\";e.exports=function(t,e,n){var r=n(\"line.shape\");\"spline\"===r&&n(\"line.smoothing\")}},{}],181:[function(t,e,n){\"use strict\";var r=t(\"fast-isnumeric\");e.exports=function(t){var e=t.marker,n=e.sizeref||1,a=e.sizemin||0,o=\"area\"===e.sizemode?function(t){return Math.sqrt(t/n)}:function(t){return t/n};return function(t){var e=o(t/2);return r(e)&&e>0?Math.max(e,a):0}}},{\"fast-isnumeric\":11}],182:[function(t,e,n){\"use strict\";var r=t(\"../../components/color\"),a=t(\"../../components/colorscale/has_colorscale\"),o=t(\"../../components/colorscale/defaults\"),i=t(\"./subtypes\");e.exports=function(t,e,n,l,s){var c,u=i.isBubble(t),f=(t.line||{}).color;f&&(n=f),s(\"marker.symbol\"),s(\"marker.opacity\",u?.7:1),s(\"marker.size\"),s(\"marker.color\",n),a(t,\"marker\")&&o(t,e,l,s,{prefix:\"marker.\",cLetter:\"c\"}),c=f&&!Array.isArray(f)&&e.marker.color!==f?f:u?r.background:r.defaultLine,s(\"marker.line.color\",c),a(t,\"marker.line\")&&o(t,e,l,s,{prefix:\"marker.line.\",cLetter:\"c\"}),s(\"marker.line.width\",u?1:0),u&&(s(\"marker.sizeref\"),s(\"marker.sizemin\"),s(\"marker.sizemode\"))}},{\"../../components/color\":18,\"../../components/colorscale/defaults\":28,\"../../components/colorscale/has_colorscale\":31,\"./subtypes\":186}],183:[function(t,e,n){\"use strict\";function r(t,e,n){var r=e.x(),o=e.y(),i=a.extent(r.range.map(r.l2c)),l=a.extent(o.range.map(o.l2c));n.forEach(function(t,e){var r=t[0].trace;if(c.hasMarkers(r)){var a=r.marker.maxdisplayed;if(0!==a){var o=t.filter(function(t){return t.x>=i[0]&&t.x<=i[1]&&t.y>=l[0]&&t.y<=l[1]}),s=Math.ceil(o.length/a),u=0;n.forEach(function(t,n){var r=t[0].trace;c.hasMarkers(r)&&r.marker.maxdisplayed>0&&e>n&&u++});var f=Math.round(u*s/3+Math.floor(u/3)*s/7.1);t.forEach(function(t){delete t.vis}),o.forEach(function(t,e){0===Math.round((e+f)%s)&&(t.vis=!0)})}}})}var a=t(\"d3\"),o=t(\"../../lib\"),i=t(\"../../components/drawing\"),l=t(\"../../components/errorbars\"),s=t(\"../../lib/polygon\").tester,c=t(\"./subtypes\"),u=t(\"./arrays_to_calcdata\"),f=t(\"./line_points\");e.exports=function(t,e,n){function d(t){return t.filter(function(t){return t.vis})}r(t,e,n);var h=e.x(),p=e.y(),g=e.plot.select(\".scatterlayer\").selectAll(\"g.trace.scatter\").data(n);g.enter().append(\"g\").attr(\"class\",\"trace scatter\").style(\"stroke-miterlimit\",2),g.call(l.plot,e);var v,m,y,x,b=\"\",_=[];g.each(function(t){var e=t[0].trace,n=e.line,r=a.select(this);if(e.visible===!0&&(m=e.fill.charAt(e.fill.length-1),\"x\"!==m&&\"y\"!==m&&(m=\"\"),t[0].node3=r,u(t),c.hasLines(e)||\"none\"!==e.fill)){var o,l,d,g,w,k=\"\",M=\"\";v=\"tozero\"===e.fill.substr(0,6)||\"toself\"===e.fill||\"to\"===e.fill.substr(0,2)&&!b?r.append(\"path\").classed(\"js-fill\",!0):null,x&&(y=x.datum(t)),x=r.append(\"path\").classed(\"js-fill\",!0),-1!==[\"hv\",\"vh\",\"hvh\",\"vhv\"].indexOf(n.shape)?(d=i.steps(n.shape),g=i.steps(n.shape.split(\"\").reverse().join(\"\"))):d=g=\"spline\"===n.shape?function(t){var e=t[t.length-1];return t[0][0]===e[0]&&t[0][1]===e[1]?i.smoothclosed(t.slice(1),n.smoothing):i.smoothopen(t,n.smoothing)}:function(t){return\"M\"+t.join(\"L\")},w=function(t){return g(t.reverse())};var A,L=f(t,{xaxis:h,yaxis:p,connectGaps:e.connectgaps,baseTolerance:Math.max(n.width||1,3)/4,linear:\"linear\"===n.shape}),T=e._polygons=new Array(L.length);for(A=0;A<L.length;A++)e._polygons[A]=s(L[A]);if(L.length){var z=L[0][0],S=L[L.length-1],E=S[S.length-1];for(A=0;A<L.length;A++){var C=L[A];o=d(C),l=w(C),k?m?(k+=\"L\"+o.substr(1),M=l+(\"L\"+M.substr(1))):(k+=\"Z\"+o,M=l+\"Z\"+M):(k=o,M=l),c.hasLines(e)&&C.length>1&&r.append(\"path\").classed(\"js-line\",!0).style(\"vector-effect\",\"non-scaling-stroke\").attr(\"d\",o)}v?z&&E&&(m?(\"y\"===m?z[1]=E[1]=p.c2p(0,!0):\"x\"===m&&(z[0]=E[0]=h.c2p(0,!0)),v.attr(\"d\",k+\"L\"+E+\"L\"+z+\"Z\")):v.attr(\"d\",k+\"Z\")):\"tonext\"===e.fill.substr(0,6)&&k&&b&&(\"tonext\"===e.fill?y.attr(\"d\",k+\"Z\"+b+\"Z\"):y.attr(\"d\",k+\"L\"+b.substr(1)+\"Z\"),e._polygons=e._polygons.concat(_)),b=M,_=T}}}),g.selectAll(\"path:not([d])\").remove(),g.append(\"g\").attr(\"class\",\"points\").each(function(t){var e=t[0].trace,n=a.select(this),r=c.hasMarkers(e),l=c.hasText(e);!r&&!l||e.visible!==!0?n.remove():(r&&n.selectAll(\"path.point\").data(e.marker.maxdisplayed?d:o.identity).enter().append(\"path\").classed(\"point\",!0).call(i.translatePoints,h,p),l&&n.selectAll(\"g\").data(e.marker.maxdisplayed?d:o.identity).enter().append(\"g\").append(\"text\").call(i.translatePoints,h,p))})}},{\"../../components/drawing\":41,\"../../components/errorbars\":47,\"../../lib\":89,\"../../lib/polygon\":95,\"./arrays_to_calcdata\":166,\"./line_points\":179,\"./subtypes\":186,d3:9}],184:[function(t,e,n){\"use strict\";var r=t(\"./subtypes\"),a=.2;e.exports=function(t,e){var n,o,i,l,s=t.cd,c=t.xaxis,u=t.yaxis,f=[],d=s[0].trace,h=d.index,p=d.marker,g=!r.hasMarkers(d)&&!r.hasText(d);if(d.visible===!0&&!g){var v=Array.isArray(p.opacity)?1:p.opacity;if(e===!1)for(n=0;n<s.length;n++)s[n].dim=0;else for(n=0;n<s.length;n++)o=s[n],i=c.c2p(o.x),l=u.c2p(o.y),e.contains([i,l])?(f.push({curveNumber:h,pointNumber:n,x:o.x,y:o.y}),o.dim=0):o.dim=1;return s[0].node3.selectAll(\"path.point\").style(\"opacity\",function(t){return((t.mo+1||v+1)-1)*(t.dim?a:1)}),s[0].node3.selectAll(\"text\").style(\"opacity\",function(t){return t.dim?a:1}),f}}},{\"./subtypes\":186}],185:[function(t,e,n){\"use strict\";var r=t(\"d3\"),a=t(\"../../components/drawing\"),o=t(\"../../components/errorbars\");e.exports=function(t){var e=r.select(t).selectAll(\"g.trace.scatter\");e.style(\"opacity\",function(t){return t[0].trace.opacity}),e.selectAll(\"g.points\").each(function(t){r.select(this).selectAll(\"path.point\").call(a.pointStyle,t.trace||t[0].trace),r.select(this).selectAll(\"text\").call(a.textPointStyle,t.trace||t[0].trace)}),e.selectAll(\"g.trace path.js-line\").call(a.lineGroupStyle),e.selectAll(\"g.trace path.js-fill\").call(a.fillGroupStyle),e.call(o.style)}},{\"../../components/drawing\":41,\"../../components/errorbars\":47,d3:9}],186:[function(t,e,n){\"use strict\";e.exports={hasLines:function(t){return t.visible&&t.mode&&-1!==t.mode.indexOf(\"lines\")},hasMarkers:function(t){return t.visible&&t.mode&&-1!==t.mode.indexOf(\"markers\")},hasText:function(t){return t.visible&&t.mode&&-1!==t.mode.indexOf(\"text\")},isBubble:function(t){return\"object\"==typeof t.marker&&Array.isArray(t.marker.size)}}},{}],187:[function(t,e,n){\"use strict\";var r=t(\"../../lib\");e.exports=function(t,e,n,a){a(\"textposition\"),r.coerceFont(a,\"textfont\",n.font)}},{\"../../lib\":89}],188:[function(t,e,n){\"use strict\";e.exports=function(t,e,n){var r,a=n(\"x\"),o=n(\"y\");if(a)o?(r=Math.min(a.length,o.length),r<a.length&&(e.x=a.slice(0,r)),r<o.length&&(e.y=o.slice(0,r))):(r=a.length,n(\"y0\"),n(\"dy\"));else{if(!o)return 0;r=e.y.length,n(\"x0\"),n(\"dx\")}return r}},{}]},{},[5])(5)}); </script>\n"
+" </head>\n"
+" <body>\n"
+"   <div id=\"header\">\n"
+"     <label>plot:</label>\n"
+"     <div class=\"select\">\n"
+"       <span class=\"arr\"></span>\n"
+,
+"       <select id=\"chooser\">\n"
+"         <option value=\"summary\">summary</option>\n"
+"         {% for run in runs %}\n"
+"         <option value=\"{$loop.index0}\">samples\n"
+"           {% for param in run.params %}\n"
+"           | {$param.name}={$param.value}\n"
+"           {% endfor %}\n"
+"         </option>\n"
+"         {% endfor %}\n"
+"       </select>\n"
+"     </div>\n"
+"     <div class=\"is-sorted\">\n"
+"       <input id=\"is-sorted\" type=\"checkbox\"/>\n"
+"       <label for=\"is-sorted\">sorted</label>\n"
+"     </div>\n"
+"   </div>\n"
+"   <div id=\"plot\"></div>\n"
+"   <div id=\"footer\">Generated with <a href=\"http://flamingdangerzone.com/nonius\">nonius</a></div>\n"
+"   <script type=\"text/javascript\"> !function () {\n"
+"    var data = {\n"
+"        title: '{$title}',\n"
+"        units: '{$units}',\n"
+"        logarithmic: {$logarithmic},\n"
+"        param: '{$runparam}',\n"
+"        runs: [\n"
+"            {% for run in runs %}{\n"
+"                params: {\n"
+"                    {% for param in run.params %}'{$param.name}': '{$param.value}',\n"
+"                    {% endfor %}\n"
+"                },\n"
+"                benchmarks: [\n"
+"                    {% for benchmark in run.benchmarks %}{\n"
+"                        name: '{$benchmark.name}',\n"
+"                        {%if benchmark.data %}\n"
+"                        mean: {$benchmark.data.mean},\n"
+"                        stddev: {$benchmark.data.stddev},\n"
+"                        samples: [\n"
+"                            {% for sample in benchmark.data.samples %}{$sample}, {% endfor %}\n"
+"                        ],\n"
+"                        {% endif %}\n"
+"                    },{% endfor %}\n"
+"                ]\n"
+"            },{% endfor %}\n"
+"        ]\n"
+"    };\n"
+"    var origOrder = data.runs[0].benchmarks.map(function (_, i) { return i; })\n"
+"    var sortOrder = computeSortedOrder();\n"
+"    var plotdiv = document.getElementById(\"plot\");\n"
+"    window.addEventListener(\"resize\", function() {\n"
+"        Plotly.Plots.resize(plotdiv);\n"
+"    });\n"
+"\n"
+"    var chooser = document.getElementById(\"chooser\");\n"
+"    chooser.addEventListener(\"change\", choosePlot);\n"
+"    chooser.addEventListener(\"blur\", chooser.focus.bind(chooser));\n"
+"    chooser.focus();\n"
+"\n"
+"    var isSortedBox = document.getElementById(\"is-sorted\");\n"
+"    isSortedBox.addEventListener(\"change\", choosePlot);\n"
+"\n"
+"    var legendStyle = {\n"
+"        font: { family: 'monospace' },\n"
+"        borderwidth: 2,\n"
+"        bordercolor: 'black'\n"
+"    }\n"
+"\n"
+"    function zeroes(count) {\n"
+"        var arr = []\n"
+"        while (count --> 0) arr.push(0)\n"
+"        return arr\n"
+"    }\n"
+"\n"
+"    function computeSortedOrder() {\n"
+"        // We sort each run.  Then we compute the \"points\" of each\n"
+"        // benchmark as the sum of the positions of this benchmkark on\n"
+"        // each run.  This gives us a rough indication of which\n"
+"        // benchmark is best -- the lower the points, the better.\n"
+"        var runsOrder = data.runs.map(function (r) {\n"
+"            order = r.benchmarks.map(function (_, i) { return i; })\n"
+"            order.sort(function (a, b) {\n"
+"                return r.benchmarks[a].mean - r.benchmarks[b].mean\n"
+"            })\n"
+"            return order\n"
+"        })\n"
+"        var length = data.runs[0].benchmarks.length\n"
+"        var points = runsOrder.reduce(function (acc, r) {\n"
+"            r.forEach(function (elem, idx) {\n"
+"                acc[elem] += idx\n"
+"            })\n"
+"            return acc\n"
+"        }, zeroes(length))\n"
+"        var order = data.runs[0].benchmarks.map(function (_, i) { return i; })\n"
+"        order.sort(function (a, b) {\n"
+"            return points[a] - points[b]\n"
+"        })\n"
+"        return order\n"
+"    }\n"
+"\n"
+"    function choosePlot() {\n"
+"        var plot = chooser.options[chooser.selectedIndex].value\n"
+"        var order = isSortedBox.checked ? sortOrder : origOrder\n"
+"        if (plot == 'summary') {\n"
+"            if (data.runs.length > 1) {\n"
+"                plotSummary(order);\n"
+"            } else {\n"
+"                plotSingleSummary(order);\n"
+"            }\n"
+"        } else {\n"
+"            plotSamples(plot, order);\n"
+"        }\n"
+"    }\n"
+"\n"
+"    function plotSamples(plot, order) {\n"
+"        var run = data.runs[plot];\n"
+"        var traces = order.map(function (i) {\n"
+"            var b = run.benchmarks[i]\n"
+"            return {\n"
+"                name: b.name,\n"
+"                type: 'scatter',\n"
+"                mode: 'markers',\n"
+"                marker: { symbol: i },\n"
+"                y: b.samples,\n"
+"                x: b.samples && b.samples.map(function (_, i) { return i; })\n"
+"            }\n"
+"        });\n"
+"        var layout = {\n"
+"            title: data.title,\n"
+"            showLegend: true,\n"
+"            xaxis: { title: 'Measurement' },\n"
+"            yaxis: {\n"
+"                title: 'Time (' + data.units + ')',\n"
+"                rangemode: 'tozero',\n"
+"                zeroline: true\n"
+"            },\n"
+"            legend: legendStyle\n"
+"        };\n"
+"        Plotly.newPlot(plotdiv, traces, layout);\n"
+"    }\n"
+"\n"
+"    function plotSummary(order) {\n"
+"        var traces = order.map(function (i) {\n"
+"            return {\n"
+"                name: data.runs[0].benchmarks[i].name,\n"
+"                type: 'scatter',\n"
+"                marker: { symbol: i },\n"
+"                x: data.runs.map(function (r) { return r.params[data.param]; }),\n"
+"                y: data.runs.map(function (r) { return r.benchmarks[i].mean; }),\n"
+"                error_y: {\n"
+"                    type: 'data',\n"
+"                    array: data.runs.map(function (r) { return r.benchmarks[i].stddev; }),\n"
+"                    visible: true\n"
+"                }\n"
+"            }\n"
+"        });\n"
+"        var layout = {\n"
+"            title: data.title,\n"
+"            showLegend: true,\n"
+"            xaxis: {\n"
+"                title: data.param,\n"
+"                type: data.logarithmic ? 'log' : '',\n"
+"            },\n"
+"            yaxis: {\n"
+"                title: 'Time (' + data.units + ')',\n"
+"                rangemode: 'tozero',\n"
+"                zeroline: true,\n"
+"                type: data.logarithmic ? 'log' : '',\n"
+"            },\n"
+"            legend: legendStyle\n"
+"        };\n"
+"        Plotly.newPlot(plotdiv, traces, layout);\n"
+"    }\n"
+"\n"
+"    function plotSingleSummary(order) {\n"
+"        var traces = order.map(function (i) {\n"
+"            var b = data.runs[0].benchmarks[i]\n"
+"            return {\n"
+"                type: 'bar',\n"
+"                name: b.name,\n"
+"                x: [ data.title ],\n"
+"                y: [ b.mean ],\n"
+"                error_y: {\n"
+"                    type: 'data',\n"
+"                    array: [ b.stddev ],\n"
+"                    visible: true\n"
+"                }\n"
+"            }\n"
+"        });\n"
+"        var layout = {\n"
+"            title: data.title,\n"
+"            showLegend: true,\n"
+"            xaxis: {\n"
+"                title: '',\n"
+"                showticklabels: false,\n"
+"            },\n"
+"            yaxis: {\n"
+"                title: 'Time (' + data.units + ')',\n"
+"                rangemode: 'tozero',\n"
+"                zeroline: true\n"
+"            },\n"
+"            legend: legendStyle\n"
+"        };\n"
+"        Plotly.newPlot(plotdiv, traces, layout);\n"
+"    }\n"
+"\n"
+"    choosePlot();\n"
+"}();\n"
+" </script>\n"
+" </body>\n"
+"</html>\n"
+            };
+            static std::string const the_template = []() -> std::string {
+                std::string s;
+                for(auto part : template_parts) {
+                    s += part;
+                }
+                return s;
+            }();
+            return the_template;
+        }
+
+        std::string description() override {
+            return "outputs an HTML file with a single interactive chart of all benchmarks";
+        }
+
+        void do_configure(configuration& cfg) override {
+            cfg.no_analysis = false;
+            title = cfg.title;
+            n_samples = cfg.samples;
+            verbose = cfg.verbose;
+            logarithmic = cfg.params.run && cfg.params.run->op == "*";
+            run_param = cfg.params.run ? cfg.params.run->name : "";
+        }
+
+        void do_warmup_start() override {
+            if(verbose) progress_stream() << "warming up\n";
+        }
+        void do_estimate_clock_resolution_start() override {
+            if(verbose) progress_stream() << "estimating clock resolution\n";
+        }
+        void do_estimate_clock_cost_start() override {
+            if(verbose) progress_stream() << "estimating cost of a clock call\n";
+        }
+
+        void do_params_start(parameters const& params) override {
+            if(verbose) progress_stream() << "\n\nnew parameter round\n" << params;
+            runs.emplace_back();
+            runs.back().params = params;
+        }
+        void do_benchmark_start(std::string const& name) override {
+            if(verbose) progress_stream() << "\nbenchmarking " << name << "\n";
+            runs.back().data.push_back({name, {}, {}});
+        }
+
+        void do_measurement_start(execution_plan<fp_seconds> plan) override {
+            progress_stream() << std::setprecision(7);
+            progress_stream().unsetf(std::ios::floatfield);
+            if(verbose) progress_stream() << "collecting " << n_samples << " samples, " << plan.iterations_per_sample << " iterations each, in estimated " << detail::pretty_duration(plan.estimated_duration) << "\n";
+        }
+        void do_measurement_complete(std::vector<fp_seconds> const& samples) override {
+            runs.back().data.back().samples = samples;
+        }
+        void do_analysis_complete(sample_analysis<fp_seconds> const& analysis) override {
+            runs.back().data.back().analysis = analysis;
+        }
+        void do_benchmark_failure(std::exception_ptr) override {
+            error_stream() << runs.back().data.back().name << " failed to run successfully\n";
+        }
+
+        void do_suite_complete() override {
+            if(verbose) progress_stream() << "\ngenerating HTML report\n";
+
+            auto&& templ = template_string();
+            auto magnitude = ideal_magnitude();
+
+            cpptempl::data_map map;
+            map["title"] = escape(title);
+            map["units"] = detail::units_for_magnitude(magnitude);
+            map["logarithmic"] = logarithmic;
+            map["runparam"] = run_param;
+            for (auto&& r : runs) {
+                cpptempl::data_map run_item;
+                cpptempl::data_list params;
+                for (auto&& p : r.params) {
+                    cpptempl::data_map item;
+                    item["name"] = p.first;
+                    item["value"] = p.second;
+                    params.push_back(item);
+                }
+                run_item["params"] = cpptempl::make_data(params);
+                for(auto&& d : r.data) {
+                    cpptempl::data_map item;
+                    item["name"] = escape(d.name);
+                    cpptempl::data_map data;
+                    if (!d.samples.empty()) {
+                        data["mean"] = truncate(d.analysis.mean.point.count() * magnitude);
+                        data["stddev"] = truncate(d.analysis.standard_deviation.point.count() * magnitude);
+                        for(auto e : d.samples)
+                            data["samples"].push_back(truncate(e.count() * magnitude));
+                    }
+                    item["data"] = data;
+                    run_item["benchmarks"].push_back(item);
+                }
+                map["runs"].push_back(run_item);
+            }
+
+            cpptempl::parse(report_stream(), templ, map);
+            report_stream() << std::flush;
+            if(verbose) {
+                progress_stream() << "\n\nresult summary ("
+                                  << detail::units_for_magnitude(magnitude)
+                                  << ")\n";
+                for (auto&& r : runs) {
+                    for (auto&& p : r.params)
+                        progress_stream() << "\n  " << p.first << " = " << p.second;
+                    progress_stream() << "\n";
+                    for(auto&& d : r.data) {
+                        progress_stream() << "  " << d.name << "\t "
+                                  << truncate(d.analysis.mean.point.count() * magnitude) << "\t "
+                                  << truncate(d.analysis.standard_deviation.point.count() * magnitude)
+                                  << "\n";
+                    }
+                }
+                progress_stream() << "\ndone\n";
+                progress_stream() << std::flush;
+            }
+        }
+
+        static double truncate(double x) {
+            return std::trunc(x * 1000.) / 1000.;
+        }
+
+        double ideal_magnitude() const {
+            std::vector<fp_seconds> mins;
+            mins.reserve(runs.size() * runs.front().data.size());
+            for (auto&& r : runs) {
+                for(auto&& d : r.data) {
+                    if (d.samples.begin() != d.samples.end())
+                        mins.push_back(*std::min_element(d.samples.begin(), d.samples.end()));
+                }
+            }
+            auto min = *std::min_element(mins.begin(), mins.end());
+            return detail::get_magnitude(min);
+        }
+
+        static std::string escape(std::string const& source) {
+            static const std::unordered_map<char, std::string> escapes {
+                { '\'', "&apos;" },
+                { '"',  "&quot;" },
+                { '<',  "&lt;"   },
+                { '>',  "&gt;"   },
+                { '&',  "&amp;"  },
+                { '\\',  "\\\\"  },
+            };
+            return detail::escape(source, escapes);
+        }
+
+        struct result_t {
+            std::string name;
+            std::vector<fp_seconds> samples;
+            sample_analysis<fp_seconds> analysis;
+        };
+
+        struct run_t {
+            parameters params;
+            std::vector<result_t> data;
+        };
+
+        int n_samples;
+        bool verbose;
+        std::string title;
+        bool logarithmic;
+        std::string run_param;
+        std::vector<run_t> runs;
+    };
+
+    NONIUS_REPORTER("html", html_reporter);
+} // namespace nonius
+
+#endif // NONIUS_DISABLE_HTML_REPORTER
+#endif // NONIUS_DISABLE_EXTRA_REPORTERS
+
+#endif // NONIUS_HPP
+// #included from: main.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Executable building kit
+
+#define NONIUS_MAIN_HPP
+
+// #included from: detail/argparse.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// Command-line argument parsing
+
+#define NONIUS_ARGPARSE_HPP
+
+// #included from: detail/mismatch.h++
+// Nonius - C++ benchmarking tool
+//
+// Written in 2014- by the nonius contributors <nonius@rmf.io>
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related
+// and neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along with this software.
+// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>
+
+// mismatch algorithm
+
+#define NONIUS_DETAIL_MISMATCH_HPP
+
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename InputIt1, typename InputIt2, typename BinaryPredicate>
+        std::pair<InputIt1, InputIt2> mismatch(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) {
+            while(first1 != last1 && first2 != last2 && p(*first1, *first2)) {
+                ++first1, ++first2;
+            }
+            return std::make_pair(first1, first2);
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <algorithm>
+#include <ostream>
+#include <iomanip>
+#include <tuple>
+#include <functional>
+
+namespace nonius {
+    namespace detail {
+        struct option {
+            bool matches_short(std::string const& s) const {
+                return s == ("-" + short_form);
+            }
+            std::tuple<bool, std::string::const_iterator> long_separator(std::string const& s) const {
+                auto l = "--" + long_form;
+                auto its = detail::mismatch(s.begin(), s.end(), l.begin(), l.end(), [](char a, char b) { return a == b; });
+                return std::make_tuple(its.second == l.end(), its.first);
+            }
+            bool matches_long(std::string const& s) const {
+                return std::get<0>(long_separator(s));
+            }
+            bool matches_long(std::string const& s, std::string& argument) const {
+                bool match; std::string::const_iterator it;
+                std::tie(match, it) = long_separator(s);
+                if(match && it != s.end()) {
+                    if(*it == '=') argument.assign(it+1, s.end());
+                    else return false;
+                }
+                return match;
+            }
+
+            option(std::string long_form, std::string short_form, std::string description, std::string argument = std::string(), bool multiple = false)
+            : long_form(std::move(long_form)), short_form(std::move(short_form)), description(std::move(description)), argument(std::move(argument)), multiple(multiple) {}
+
+            std::string long_form;
+            std::string short_form;
+            std::string description;
+            std::string argument;
+            bool multiple;
+        };
+
+        using option_set = std::vector<option>;
+
+        struct help_text {
+            help_text(std::string name, option_set const& options) : name(std::move(name)), options(options) {}
+            std::string name;
+            option_set const& options;
+        };
+
+        template <typename Iterator, typename Projection>
+        int get_max_width(Iterator first, Iterator last, Projection proj) {
+            auto it = std::max_element(first, last, [&proj](option const& a, option const& b) { return proj(a) < proj(b); });
+            return static_cast<int>(proj(*it));
+        }
+
+        inline std::ostream& operator<<(std::ostream& os, help_text h) {
+            os << "Usage: " << h.name << " [OPTIONS]\n\n";
+
+            auto lwidth = 2 + get_max_width(h.options.begin(), h.options.end(), [](option const& o) { return 2 + o.long_form.size() + 1 + o.argument.size(); });
+            auto swidth = 2 + get_max_width(h.options.begin(), h.options.end(), [](option const& o) { return 1 + o.short_form.size() + 1 + o.argument.size(); });
+
+            os << std::left;
+            for(auto o : h.options) {
+                auto l = "--" + o.long_form;
+                auto s = "-" + o.short_form;
+                if(!o.argument.empty()) {
+                    l += "=" + o.argument;
+                    s += " " + o.argument;
+                }
+                os << std::setw(lwidth) << l
+                   << std::setw(swidth) << s
+                   << o.description
+                   << '\n';
+            }
+            return os;
+        }
+
+        using arguments = std::unordered_multimap<std::string, std::string>;
+
+        struct argument_error {
+            virtual ~argument_error() = default;
+        };
+
+        template <typename Iterator>
+        void parse_short(option const& o, arguments& args, Iterator& first, Iterator last) {
+            if(!o.argument.empty()) {
+                if(++first != last) {
+                    args.emplace(o.long_form, *first);
+                } else {
+                    throw argument_error();
+                }
+            } else {
+                args.emplace(o.long_form, "");
+            }
+        }
+        inline void parse_long(option const& o, arguments& args, std::string&& arg) {
+            args.emplace(o.long_form, std::move(arg));
+        }
+
+        template <typename Iterator>
+        arguments parse_arguments(option_set const& options, Iterator first, Iterator last) {
+            arguments args;
+            for(; first != last; ++first) {
+                bool parsed = false;
+                for(auto&& option : options) {
+                    if(option.matches_short(*first)) {
+                        parse_short(option, args, first, last);
+                        parsed = true;
+                    } else {
+                        std::string arg;
+                        if(option.matches_long(*first, arg)) {
+                            parse_long(option, args, std::move(arg));
+                            parsed = true;
+                        }
+                    }
+                }
+                if(!parsed) {
+                    throw argument_error();
+                }
+            }
+            return args;
+        }
+    } // namespace detail
+} // namespace nonius
+
+#include <boost/algorithm/string.hpp>
+
+#include <vector>
+#include <string>
+#include <stdexcept>
+#include <exception>
+#include <iostream>
+#include <iomanip>
+#include <utility>
+
+namespace nonius {
+    namespace detail {
+        template <typename T>
+        struct parser;
+        template <>
+        struct parser<int> {
+            static int parse(std::string const& s) { return std::stoi(s); }
+        };
+        template <>
+        struct parser<double> {
+            static double parse(std::string const& s) { return std::stod(s); }
+        };
+        template <>
+        struct parser<std::string> {
+            static std::string parse(std::string const& s) { return s; }
+        };
+        template <>
+        struct parser<bool> {
+            static bool parse(std::string const&) { return true; }
+        };
+        template <>
+        struct parser<param_configuration> {
+            static param_configuration parse(std::string const& param) {
+                auto v = std::vector<std::string>{};
+                boost::split(v, param, boost::is_any_of(":"));
+                try {
+                    if (v.size() > 0) {
+                        auto name = v[0];
+                        auto def  = global_param_registry().defaults().at(name);
+                        if (v.size() == 2)
+                            return {{{name, def.parse(v[1])}}, {}};
+                        else if (v.size() == 5) {
+                            auto oper  = v[1];
+                            auto init  = def.parse(v[2]);
+                            auto step  = def.parse(v[3]);
+                            auto count = boost::lexical_cast<std::size_t>(v[4]);
+                            return {{}, run_configuration{name, oper, init, step, count}};
+                        }
+                    }
+                }
+                catch (boost::bad_lexical_cast const&) {}
+                catch (std::out_of_range const&) {}
+                return {};
+            }
+        };
+        struct assign_fn {
+            template <typename T, typename U>
+            void operator() (T& dest, U&& src) const { dest = std::forward<U>(src); }
+        };
+        template <typename T, typename Predicate, typename Assignment=assign_fn>
+        void parse(T& variable, detail::arguments& args, std::string const& option, Predicate&& is_valid, Assignment&& assign={}) {
+            auto rng = args.equal_range(option);
+            for (auto it = rng.first; it != rng.second; ++it) {
+                if(it != args.end()) {
+                    auto value = parser<T>::parse(it->second);
+                    if(is_valid(value)) {
+                        std::forward<Assignment>(assign) (variable, std::move(value));
+                    } else {
+                        throw argument_error();
+                    }
+                }
+            }
+        }
+        template <typename T>
+        void parse(T& variable, detail::arguments& args, std::string const& option) {
+            return parse(variable, args, option, [](T const&) { return true; });
+        }
+
+        inline detail::option_set const& command_line_options() {
+            static detail::option_set the_options {
+                detail::option("help", "h", "show this help message"),
+                detail::option("samples", "s", "number of samples to collect (default: 100)", "SAMPLES"),
+                detail::option("resamples", "rs", "number of resamples for the bootstrap (default: 100000)", "RESAMPLES"),
+                detail::option("confidence-interval", "ci", "confidence interval for the bootstrap (between 0 and 1, default: 0.95)", "INTERVAL"),
+                detail::option("param", "p", "set a benchmark parameter", "PARAM"),
+                detail::option("output", "o", "output file (default: <stdout>)", "FILE"),
+                detail::option("reporter", "r", "reporter to use (default: standard)", "REPORTER"),
+                detail::option("title", "t", "set report title", "TITLE"),
+                detail::option("no-analysis", "A", "perform only measurements; do not perform any analysis"),
+                detail::option("filter", "f", "only run benchmarks whose name matches the regular expression pattern", "PATTERN"),
+                detail::option("list", "l", "list benchmarks"),
+                detail::option("list-params", "lp", "list available parameters"),
+                detail::option("list-reporters", "lr", "list available reporters"),
+                detail::option("verbose", "v", "show verbose output (mutually exclusive with -q)"),
+                detail::option("summary", "q", "show summary output (mutually exclusive with -v)")
+            };
+            return the_options;
+        }
+
+        template <typename Iterator>
+        configuration parse_args(std::string const& name, Iterator first, Iterator last) {
+            try {
+                auto args = detail::parse_arguments(command_line_options(), first, last);
+
+                configuration cfg;
+
+                auto is_positive = [](int x) { return x > 0; };
+                auto is_ci = [](double x) { return x > 0 && x < 1; };
+                auto is_reporter = [](std::string const x) { return global_reporter_registry().count(x) > 0; };
+                auto is_param = [](param_configuration const& x) {
+                    return !x.map.empty() || x.run;
+                };
+                auto merge_params = [](param_configuration& x, param_configuration&& y) {
+                    x.map = std::move(x.map).merged(std::move(y.map));
+                    if (y.run) x.run = y.run;
+                };
+
+                parse(cfg.help, args, "help");
+                parse(cfg.samples, args, "samples", is_positive);
+                parse(cfg.resamples, args, "resamples", is_positive);
+                parse(cfg.confidence_interval, args, "confidence-interval", is_ci);
+                parse(cfg.params, args, "param", is_param, merge_params);
+                parse(cfg.output_file, args, "output");
+                parse(cfg.reporter, args, "reporter", is_reporter);
+                parse(cfg.no_analysis, args, "no-analysis");
+                parse(cfg.filter_pattern, args, "filter");
+                parse(cfg.list_benchmarks, args, "list");
+                parse(cfg.list_params, args, "list-params");
+                parse(cfg.list_reporters, args, "list-reporters");
+                parse(cfg.verbose, args, "verbose");
+                parse(cfg.summary, args, "summary");
+                parse(cfg.title, args, "title");
+                if(cfg.verbose && cfg.summary) throw argument_error();
+
+                return cfg;
+            } catch(...) {
+                std::cout << help_text(name, command_line_options());
+                throw argument_error();
+            }
+        }
+    } // namespace detail
+
+    inline int print_help(std::string const& name) {
+        std::cout << detail::help_text(name, detail::command_line_options());
+        return 0;
+    }
+    inline int list_benchmarks() {
+        std::cout << "All available benchmarks:\n";
+        for(auto&& b : global_benchmark_registry()) {
+            std::cout << "  " << b.name << "\n";
+        }
+        std::cout << global_benchmark_registry().size() << " benchmarks\n\n";
+        return 0;
+    }
+    inline int list_params() {
+        std::cout << "Available parameters (= default):\n"
+                  << global_param_registry().defaults();
+        return 0;
+    }
+    inline int list_reporters() {
+        using reporter_entry_ref = decltype(*global_reporter_registry().begin());
+        auto cmp = [](reporter_entry_ref a, reporter_entry_ref b) { return a.first.size() < b.first.size(); };
+        auto width = 2 + std::max_element(global_reporter_registry().begin(), global_reporter_registry().end(), cmp)->first.size();
+
+        std::cout << "Available reporters:\n";
+        std::cout << std::left;
+        for(auto&& r : global_reporter_registry()) {
+            if(!r.first.empty()) {
+                std::cout << "  " << std::setw(width) << r.first << r.second->description() << "\n";
+            }
+        }
+        std::cout << '\n';
+        return 0;
+    }
+    inline int run_it(configuration cfg) {
+        try {
+            nonius::go(cfg);
+        } catch(...) {
+            std::cerr << "PANIC: clock is on fire\n";
+            try {
+                throw;
+            } catch(std::exception const& e) {
+                std::cerr << "  " << e.what() << "\n";
+            } catch(...) {}
+            return 23;
+        }
+        return 0;
+    }
+
+    template <typename Iterator>
+    int main(std::string const& name, Iterator first, Iterator last) {
+        configuration cfg;
+
+        try {
+            cfg = detail::parse_args(name, first, last);
+        } catch(detail::argument_error const&) {
+            return 17;
+        }
+
+        if(cfg.help) return print_help(name);
+        else if(cfg.list_benchmarks) return list_benchmarks();
+        else if(cfg.list_params) return list_params();
+        else if(cfg.list_reporters) return list_reporters();
+        else return run_it(cfg);
+    }
+    inline int main(int argc, char** argv) {
+        std::string name(argv[0]);
+        std::vector<std::string> args(argv+1, argv+argc);
+        return main(name, args.begin(), args.end());
+    }
+}
+
+#ifdef NONIUS_RUNNER
+int main(int argc, char** argv) {
+    return nonius::main(argc, argv);
+}
+#endif // NONIUS_RUNNER
+
+#endif // NONIUS_SINGLE_INCLUDE_HPP
diff --git a/third_party/immer/tools/include/prettyprint.hpp b/third_party/immer/tools/include/prettyprint.hpp
new file mode 100644
index 0000000000..f75683ccdd
--- /dev/null
+++ b/third_party/immer/tools/include/prettyprint.hpp
@@ -0,0 +1,445 @@
+//          Copyright Louis Delacroix 2010 - 2014.
+// Distributed under the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE_1_0.txt or copy at
+//          http://www.boost.org/LICENSE_1_0.txt)
+//
+// A pretty printing library for C++
+//
+// Usage:
+// Include this header, and operator<< will "just work".
+
+#ifndef H_PRETTY_PRINT
+#define H_PRETTY_PRINT
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <tuple>
+#include <type_traits>
+#include <unordered_set>
+#include <utility>
+#include <valarray>
+
+namespace pretty_print
+{
+    namespace detail
+    {
+        // SFINAE type trait to detect whether T::const_iterator exists.
+
+        struct sfinae_base
+        {
+            using yes = char;
+            using no  = yes[2];
+        };
+
+        template <typename T>
+        struct has_const_iterator : private sfinae_base
+        {
+        private:
+            template <typename C> static yes & test(typename C::const_iterator*);
+            template <typename C> static no  & test(...);
+        public:
+            static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
+            using type =  T;
+        };
+
+        template <typename T>
+        struct has_begin_end : private sfinae_base
+        {
+        private:
+            template <typename C>
+            static yes & f(typename std::enable_if<
+                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
+                             typename C::const_iterator(C::*)() const>::value>::type *);
+
+            template <typename C> static no & f(...);
+
+            template <typename C>
+            static yes & g(typename std::enable_if<
+                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
+                             typename C::const_iterator(C::*)() const>::value, void>::type*);
+
+            template <typename C> static no & g(...);
+
+        public:
+            static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
+            static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
+        };
+
+    }  // namespace detail
+
+
+    // Holds the delimiter values for a specific character type
+
+    template <typename TChar>
+    struct delimiters_values
+    {
+        using char_type = TChar;
+        const char_type * prefix;
+        const char_type * delimiter;
+        const char_type * postfix;
+    };
+
+
+    // Defines the delimiter values for a specific container and character type
+
+    template <typename T, typename TChar>
+    struct delimiters
+    {
+        using type = delimiters_values<TChar>;
+        static const type values; 
+    };
+
+
+    // Functor to print containers. You can use this directly if you want
+    // to specificy a non-default delimiters type. The printing logic can
+    // be customized by specializing the nested template.
+
+    template <typename T,
+              typename TChar = char,
+              typename TCharTraits = ::std::char_traits<TChar>,
+              typename TDelimiters = delimiters<T, TChar>>
+    struct print_container_helper
+    {
+        using delimiters_type = TDelimiters;
+        using ostream_type = std::basic_ostream<TChar, TCharTraits>;
+
+        template <typename U>
+        struct printer
+        {
+            static void print_body(const U & c, ostream_type & stream)
+            {
+                using std::begin;
+                using std::end;
+
+                auto it = begin(c);
+                const auto the_end = end(c);
+
+                if (it != the_end)
+                {
+                    for ( ; ; )
+                    {
+                        stream << *it;
+
+                    if (++it == the_end) break;
+
+                    if (delimiters_type::values.delimiter != NULL)
+                        stream << delimiters_type::values.delimiter;
+                    }
+                }
+            }
+        };
+
+        print_container_helper(const T & container)
+        : container_(container)
+        { }
+
+        inline void operator()(ostream_type & stream) const
+        {
+            if (delimiters_type::values.prefix != NULL)
+                stream << delimiters_type::values.prefix;
+
+            printer<T>::print_body(container_, stream);
+
+            if (delimiters_type::values.postfix != NULL)
+                stream << delimiters_type::values.postfix;
+        }
+
+    private:
+        const T & container_;
+    };
+
+    // Specialization for pairs
+
+    template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    template <typename T1, typename T2>
+    struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::pair<T1, T2>>
+    {
+        using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+
+        static void print_body(const std::pair<T1, T2> & c, ostream_type & stream)
+        {
+            stream << c.first;
+            if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+                stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+            stream << c.second;
+        }
+    };
+
+    // Specialization for tuples
+
+    template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    template <typename ...Args>
+    struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::tuple<Args...>>
+    {
+        using ostream_type = typename print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+        using element_type = std::tuple<Args...>;
+
+        template <std::size_t I> struct Int { };
+
+        static void print_body(const element_type & c, ostream_type & stream)
+        {
+            tuple_print(c, stream, Int<0>());
+        }
+
+        static void tuple_print(const element_type &, ostream_type &, Int<sizeof...(Args)>)
+        {
+        }
+
+        static void tuple_print(const element_type & c, ostream_type & stream,
+                                typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type)
+        {
+            stream << std::get<0>(c);
+            tuple_print(c, stream, Int<1>());
+        }
+
+        template <std::size_t N>
+        static void tuple_print(const element_type & c, ostream_type & stream, Int<N>)
+        {
+            if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+                stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+
+            stream << std::get<N>(c);
+
+            tuple_print(c, stream, Int<N + 1>());
+        }
+    };
+
+    // Prints a print_container_helper to the specified stream.
+
+    template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    inline std::basic_ostream<TChar, TCharTraits> & operator<<(
+        std::basic_ostream<TChar, TCharTraits> & stream,
+        const print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper)
+    {
+        helper(stream);
+        return stream;
+    }
+
+
+    // Basic is_container template; specialize to derive from std::true_type for all desired container types
+
+    template <typename T>
+    struct is_container : public std::integral_constant<bool,
+                                                        detail::has_const_iterator<T>::value &&
+                                                        detail::has_begin_end<T>::beg_value  &&
+                                                        detail::has_begin_end<T>::end_value> { };
+
+    template <typename T, std::size_t N>
+    struct is_container<T[N]> : std::true_type { };
+
+    template <std::size_t N>
+    struct is_container<char[N]> : std::false_type { };
+
+    template <typename T>
+    struct is_container<std::valarray<T>> : std::true_type { };
+
+    template <typename T1, typename T2>
+    struct is_container<std::pair<T1, T2>> : std::true_type { };
+
+    template <typename ...Args>
+    struct is_container<std::tuple<Args...>> : std::true_type { };
+
+
+    // Default delimiters
+
+    template <typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
+    template <typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" };
+    template <typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"[", L", ", L"]" };
+
+
+    // Delimiters for (multi)set and unordered_(multi)set
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::set<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::set<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::set<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::set<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::multiset<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::multiset<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+
+    // Delimiters for pair and tuple
+
+    template <typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
+    template <typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
+    template <typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename T1, typename T2> const delimiters_values<wchar_t> delimiters< ::std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };
+
+    template <typename ...Args> struct delimiters<std::tuple<Args...>, char> { static const delimiters_values<char> values; };
+    template <typename ...Args> const delimiters_values<char> delimiters<std::tuple<Args...>, char>::values = { "(", ", ", ")" };
+    template <typename ...Args> struct delimiters< ::std::tuple<Args...>, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename ...Args> const delimiters_values<wchar_t> delimiters< ::std::tuple<Args...>, wchar_t>::values = { L"(", L", ", L")" };
+
+
+    // Type-erasing helper class for easy use of custom delimiters.
+    // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar.
+    // Usage: "cout << pretty_print::custom_delims<MyDelims>(x)".
+
+    struct custom_delims_base
+    {
+        virtual ~custom_delims_base() { }
+        virtual std::ostream & stream(::std::ostream &) = 0;
+        virtual std::wostream & stream(::std::wostream &) = 0;
+    };
+
+    template <typename T, typename Delims>
+    struct custom_delims_wrapper : custom_delims_base
+    {
+        custom_delims_wrapper(const T & t_) : t(t_) { }
+
+        std::ostream & stream(std::ostream & s)
+        {
+            return s << print_container_helper<T, char, std::char_traits<char>, Delims>(t);
+        }
+
+        std::wostream & stream(std::wostream & s)
+        {
+            return s << print_container_helper<T, wchar_t, std::char_traits<wchar_t>, Delims>(t);
+        }
+
+    private:
+        const T & t;
+    };
+
+    template <typename Delims>
+    struct custom_delims
+    {
+        template <typename Container>
+        custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { }
+
+        std::unique_ptr<custom_delims_base> base;
+    };
+
+    template <typename TChar, typename TCharTraits, typename Delims>
+    inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & s, const custom_delims<Delims> & p)
+    {
+        return p.base->stream(s);
+    }
+
+
+    // A wrapper for a C-style array given as pointer-plus-size.
+    // Usage: std::cout << pretty_print_array(arr, n) << std::endl;
+
+    template<typename T>
+    struct array_wrapper_n
+    {
+        typedef const T * const_iterator;
+        typedef T value_type;
+
+        array_wrapper_n(const T * const a, size_t n) : _array(a), _n(n) { }
+        inline const_iterator begin() const { return _array; }
+        inline const_iterator end() const { return _array + _n; }
+
+    private:
+        const T * const _array;
+        size_t _n;
+    };
+
+
+    // A wrapper for hash-table based containers that offer local iterators to each bucket.
+    // Usage: std::cout << bucket_print(m, 4) << std::endl;  (Prints bucket 5 of container m.)
+
+    template <typename T>
+    struct bucket_print_wrapper
+    {
+        typedef typename T::const_local_iterator const_iterator;
+        typedef typename T::size_type size_type;
+
+        const_iterator begin() const
+        {
+            return m_map.cbegin(n);
+        }
+
+        const_iterator end() const
+        {
+            return m_map.cend(n);
+        }
+
+        bucket_print_wrapper(const T & m, size_type bucket) : m_map(m), n(bucket) { }
+
+    private:
+        const T & m_map;
+        const size_type n;
+    };
+
+}   // namespace pretty_print
+
+
+// Global accessor functions for the convenience wrappers
+
+template<typename T>
+inline pretty_print::array_wrapper_n<T> pretty_print_array(const T * const a, size_t n)
+{
+    return pretty_print::array_wrapper_n<T>(a, n);
+}
+
+template <typename T> pretty_print::bucket_print_wrapper<T>
+bucket_print(const T & m, typename T::size_type n)
+{
+    return pretty_print::bucket_print_wrapper<T>(m, n);
+}
+
+
+// Main magic entry point: An overload snuck into namespace std.
+// Can we do better?
+
+namespace std
+{
+    // Prints a container to the stream using default delimiters
+
+    template<typename T, typename TChar, typename TCharTraits>
+    inline typename enable_if< ::pretty_print::is_container<T>::value,
+                              basic_ostream<TChar, TCharTraits> &>::type
+    operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container)
+    {
+        return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container);
+    }
+}
+
+
+
+#endif  // H_PRETTY_PRINT
diff --git a/third_party/immer/tools/reproduce-paper-results.bash b/third_party/immer/tools/reproduce-paper-results.bash
new file mode 100755
index 0000000000..0ee2b98c34
--- /dev/null
+++ b/third_party/immer/tools/reproduce-paper-results.bash
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+test -d build || mkdir -p build
+cd build
+
+cmake .. \
+      -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -march=native" \
+      -DCHECK_BENCHMARKS=1 \
+      -DBENCHMARK_PARAM="N:1000" -DBENCHMARK_SAMPLES="100"
+make benchmarks -j1
+ctest -R "benchmark/vector-paper*"
+
+cmake ..\
+      -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -march=native" \
+      -DCHECK_BENCHMARKS=1 \
+      -DBENCHMARK_PARAM="N:100000" -DBENCHMARK_SAMPLES="20"
+make benchmarks -j1
+ctest -R "benchmark/vector-paper*"
+
+cmake .. \
+      -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -march=native" \
+      -DCHECK_BENCHMARKS=1 \
+      -DBENCHMARK_PARAM="N:10000000" -DBENCHMARK_SAMPLES="3"
+make benchmarks -j1
+ctest -R "benchmark/vector-paper*"
diff --git a/third_party/immer/tools/scala/README.md b/third_party/immer/tools/scala/README.md
new file mode 100644
index 0000000000..ce514e445e
--- /dev/null
+++ b/third_party/immer/tools/scala/README.md
@@ -0,0 +1,6 @@
+
+```
+# sbt
+> test
+> test-only org.immer.* -- -verbose
+```
diff --git a/third_party/immer/tools/scala/build.sbt b/third_party/immer/tools/scala/build.sbt
new file mode 100644
index 0000000000..f2d0bd2cf0
--- /dev/null
+++ b/third_party/immer/tools/scala/build.sbt
@@ -0,0 +1,27 @@
+/**  This is the simplest possible use of ScalaMeter.
+  *  It allows running ScalaMeter benchmarks as part of the test suite.
+  *  It means, that when the test command is run, ScalaMeter benchmarks are run along
+  *  the tests from other test frameworks, such as ScalaTest or ScalaCheck.
+  */
+lazy val basic = Project(
+  "basic",
+  file("."),
+  settings = Defaults.coreDefaultSettings ++ Seq(
+    name := "immer-bechmarks",
+    organization := "org.immer",
+    scalaVersion := "2.11.1",
+    scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature", "-Xlint"),
+    publishArtifact := false,
+    libraryDependencies ++= Seq(
+      "com.storm-enroute" %% "scalameter" % "0.8.2" % "test",
+      "io.github.nicolasstucki" %% "scala-rrb-vector" % "0.1.1"
+    ),
+    resolvers ++= Seq(
+      "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
+      "Sonatype OSS Releases" at "https://oss.sonatype.org/content/repositories/releases"
+    ),
+    testFrameworks += new TestFramework("org.scalameter.ScalaMeterFramework"),
+    parallelExecution in Test := false,
+    logBuffered := false
+  )
+)
diff --git a/third_party/immer/tools/scala/src/test/scala/org/immer/benchmarks.scala b/third_party/immer/tools/scala/src/test/scala/org/immer/benchmarks.scala
new file mode 100644
index 0000000000..98c40a45cd
--- /dev/null
+++ b/third_party/immer/tools/scala/src/test/scala/org/immer/benchmarks.scala
@@ -0,0 +1,168 @@
+package org.immer
+
+import org.scalameter.api._
+import scala.collection.immutable.Vector
+import scala.collection.immutable.rrbvector.RRBVector
+
+class BenchmarkBase extends Bench.ForkedTime {
+  val runs = 20
+  val size = 1000
+  val step = 10
+  val sizes = Gen.range("size")(size, size, 1)
+
+  //def relaxedV(v : RRBVector[Int], n : Int) : RRBVector[Int] = {
+  //  var r = v
+  //  for (i <- 0 to n) {
+  //    r = (RRBVector.empty[Int] :+ i) ++ r
+  //  }
+  //  r
+  //}
+
+  var vectors     = for { s <- sizes } yield Vector.empty[Int] ++ (0 until s)
+  var rrbvectors  = for { s <- sizes } yield RRBVector.empty[Int] ++ (0 until s)
+  //var rrbvectorsF = for { s <- sizes } yield relaxedV(RRBVector.empty[Int], s)
+  var rrbsteps    = for { s <- sizes } yield RRBVector.empty[Int] ++ (0 until s / step)
+}
+
+class PushBenchmark extends BenchmarkBase {
+  performance of "push" in {
+    measure method "Vector" in { using(sizes) config(exec.benchRuns -> runs) in { s => {
+      var v = Vector.empty[Int]
+      for (a <- 0 to s) {
+        v = v :+ a
+      }
+      v
+    }}}
+    measure method "RRBVector" in { using(sizes) config(exec.benchRuns -> runs) in { s => {
+      var v = RRBVector.empty[Int]
+      for (a <- 0 to s) {
+        v = v :+ a
+      }
+      v
+    }}}
+  }
+}
+
+class UpdateBenchmark extends BenchmarkBase {
+  performance of "update" in {
+    measure method "Vector" in { using(vectors) config(exec.benchRuns -> runs) in { v0 => {
+      var v = v0
+      for (a <- 0 to v.size - 1) {
+        v = v.updated(a, a + 1)
+      }
+      v
+    }}}
+    measure method "RRBVector" in { using(rrbvectors) config(exec.benchRuns -> runs) in { v0 => {
+      var v = v0
+      for (a <- 0 to v.size - 1) {
+        v = v.updated(a, a + 1)
+      }
+      v
+    }}}
+    //measure method "RRBVector/relaxed" in { using(rrbvectorsF) config(exec.benchRuns -> runs) in { v0 => {
+    //  var v = v0
+    //  for (a <- 0 to v.size - 1) {
+    //    v = v.updated(a, a + 1)
+    //  }
+    //  v
+    //}}}
+  }
+  performance of "update/random" in {
+    measure method "Vector" in { using(vectors) config(exec.benchRuns -> runs) in { v0 => {
+      var v = v0
+      var r = new scala.util.Random
+      for (a <- 0 to v.size - 1) {
+        v = v.updated(r.nextInt(v.size), a + 1)
+      }
+      v
+    }}}
+    measure method "RRBVector" in { using(rrbvectors) config(exec.benchRuns -> runs) in { v0 => {
+      var v = v0
+      var r = new scala.util.Random
+      for (a <- 0 to v.size - 1) {
+        v = v.updated(r.nextInt(v.size), a + 1)
+      }
+      v
+    }}}
+    //measure method "RRBVector/relaxed" in { using(rrbvectorsF) config(exec.benchRuns -> runs) in { v0 => {
+    //  var v = v0
+    //  var r = new scala.util.Random
+    //  for (a <- 0 to v.size - 1) {
+    //    v = v.updated(r.nextInt(v.size), a + 1)
+    //  }
+    //  v
+    //}}}
+  }
+}
+
+object IterBenchmark extends BenchmarkBase {
+  performance of "access/reduce" in {
+    measure method "Vector" in { using(vectors) config(exec.benchRuns -> runs) in { v => {
+      v.reduceLeft(_ + _)
+    }}}
+    measure method "RRBVector" in { using(rrbvectors) config(exec.benchRuns -> runs) in { v => {
+      v.reduceLeft(_ + _)
+    }}}
+    //measure method "RRBVector/relaxed" in { using(rrbvectorsF) config(exec.benchRuns -> runs) in { v => {
+    //  v.reduceLeft(_ + _)
+    //}}}
+  }
+  performance of "access/idx" in {
+    measure method "Vector" in { using(vectors) config(exec.benchRuns -> runs) in { v => {
+      var r = 0
+      for (a <- 0 to v.size - 1) {
+        r += v(a)
+      }
+      r
+    }}}
+    measure method "RRBVector" in { using(rrbvectors) config(exec.benchRuns -> runs) in { v => {
+      var r = 0
+      for (a <- 0 to v.size - 1) {
+        r += v(a)
+      }
+      r
+    }}}
+    //measure method "RRBVector/relaxed" in { using(rrbvectorsF) config(exec.benchRuns -> runs) in { v => {
+    //  var r = 0
+    //  for (a <- 0 to v.size - 1) {
+    //    r += v(a)
+    //  }
+    //  r
+    //}}}
+  }
+  performance of "access/iter" in {
+    measure method "Vector" in { using(vectors) config(exec.benchRuns -> runs) in { v => {
+      var r = 0
+      for (a <- v) {
+        r += a
+      }
+      r
+    }}}
+    measure method "RRBVector" in { using(rrbvectors) config(exec.benchRuns -> runs) in { v => {
+      var r = 0
+      for (a <- v) {
+        r += a
+      }
+      r
+    }}}
+    //measure method "RRBVector/relaxed" in { using(rrbvectorsF) config(exec.benchRuns -> runs) in { v => {
+    //  var r = 0
+    //  for (a <- v) {
+    //    r += a
+    //  }
+    //  r
+    //}}}
+  }
+}
+
+class ConcatBenchmark extends BenchmarkBase {
+  performance of "concat" in {
+    measure method "RRBVector ++" in { using(rrbsteps) config(exec.benchRuns -> runs) in { v => {
+      var r = RRBVector.empty[Int]
+      for (_ <- 0 to step) {
+        r = r ++ v
+      }
+      r
+    }}}
+  }
+}
diff --git a/third_party/immer/tools/scala/version.sbt b/third_party/immer/tools/scala/version.sbt
new file mode 100644
index 0000000000..3f0ac08901
--- /dev/null
+++ b/third_party/immer/tools/scala/version.sbt
@@ -0,0 +1 @@
+version in ThisBuild := "0.8.2"
diff --git a/third_party/immer/tools/sinusoidal-sphinx-theme b/third_party/immer/tools/sinusoidal-sphinx-theme
new file mode 160000
+Subproject b5adfa2a6def8aa55d95dedc4e1bfde214a5e36
diff --git a/third_party/immer/tools/travis/ssh-key.enc b/third_party/immer/tools/travis/ssh-key.enc
new file mode 100644
index 0000000000..9793aac2df
--- /dev/null
+++ b/third_party/immer/tools/travis/ssh-key.enc
Binary files differdiff --git a/third_party/immer/tools/travis/ssh-key.pub b/third_party/immer/tools/travis/ssh-key.pub
new file mode 100644
index 0000000000..9994a9772d
--- /dev/null
+++ b/third_party/immer/tools/travis/ssh-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBLgrMsYWwjiR/oe2bzg1kr1CGgLYiLrvQIeZw9mv10qF+MS+xpthy4tXxEHHeqkbQ1FsSoo3RaCKnya47LTysT1HE4Qf1TbxY5b1kU9SJ0jGNYejvkdmWzg/wspSCbFi3PGJhctxoks5sJCfVMc/oFwIlFAZALfL3JIRYk8EZv7bnrtA1Il1hPV2ug7ADBWJOXCc8pW4E7X3ZiDnvAZfqZ1QHrcQwy1aA8LVTv5azJg7Db8D90JjuRTlhO1XL7Cg/PGBkP1B3jOW5LRZK8AupnOPTw4kIsIc/i32O/poZPIRbHqYumYqEKBJQ/ggN4rKVXcp0lrMZ6/joslFLMvwr immer
diff --git a/third_party/immer/tools/with-tee.bash b/third_party/immer/tools/with-tee.bash
new file mode 100755
index 0000000000..60c331326d
--- /dev/null
+++ b/third_party/immer/tools/with-tee.bash
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+echo "${@:2} | tee $1"
+
+${@:2} | tee $1