summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnur Nigmetov <a.nigmetov@gmail.com>2018-01-20 19:11:29 +0100
committerArnur Nigmetov <a.nigmetov@gmail.com>2018-01-20 19:11:29 +0100
commit0cc35ad04f9c2997014d7cf62a12f697e79fb534 (patch)
tree744c07bc2c12fba193934ac98417c5063bead189
parent3552ce68bc7654df35da471bd937b09a9fde101f (diff)
Major rewrite, templatized version
-rw-r--r--geom_bottleneck/CMakeLists.txt40
-rw-r--r--geom_bottleneck/COPYING674
-rw-r--r--geom_bottleneck/COPYING.LESSER165
-rw-r--r--geom_bottleneck/README41
-rw-r--r--geom_bottleneck/bottleneck/CMakeLists.txt30
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/ANN.h917
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/ANNperf.h225
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/ANNx.h127
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/bd_tree.h105
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_fix_rad_search.h46
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_pr_search.h51
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_search.h50
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_split.h123
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_tree.h260
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/kd_util.h126
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/pr_queue.h127
-rw-r--r--geom_bottleneck/bottleneck/include/ANN/pr_queue_k.h120
-rw-r--r--geom_bottleneck/bottleneck/include/basic_defs_bt.h198
-rw-r--r--geom_bottleneck/bottleneck/include/bottleneck.h138
-rw-r--r--geom_bottleneck/bottleneck/include/bound_match.h80
-rw-r--r--geom_bottleneck/bottleneck/include/def_debug_bt.h34
-rw-r--r--geom_bottleneck/bottleneck/include/neighb_oracle.h91
-rw-r--r--geom_bottleneck/bottleneck/lib/dummy1
-rw-r--r--geom_bottleneck/bottleneck/src/ann/ANN.cpp239
-rw-r--r--geom_bottleneck/bottleneck/src/ann/bd_fix_rad_search.cpp64
-rw-r--r--geom_bottleneck/bottleneck/src/ann/bd_pr_search.cpp66
-rw-r--r--geom_bottleneck/bottleneck/src/ann/bd_search.cpp64
-rw-r--r--geom_bottleneck/bottleneck/src/ann/bd_tree.cpp422
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_dump.cpp458
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_fix_rad_search.cpp185
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_pr_search.cpp221
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_search.cpp298
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_split.cpp632
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_tree.cpp566
-rw-r--r--geom_bottleneck/bottleneck/src/ann/kd_util.cpp441
-rw-r--r--geom_bottleneck/bottleneck/src/basic_defs.cpp229
-rw-r--r--geom_bottleneck/bottleneck/src/brute.cpp110
-rw-r--r--geom_bottleneck/bottleneck/src/neighb_oracle.cpp284
-rw-r--r--geom_bottleneck/example/bottleneck_dist.cpp54
-rw-r--r--geom_bottleneck/include/basic_defs_bt.h475
-rw-r--r--geom_bottleneck/include/bottleneck.h116
-rw-r--r--geom_bottleneck/include/bottleneck_detail.h85
-rw-r--r--geom_bottleneck/include/bottleneck_detail.hpp (renamed from geom_bottleneck/bottleneck/src/bottleneck.cpp)259
-rw-r--r--geom_bottleneck/include/bound_match.h107
-rw-r--r--geom_bottleneck/include/bound_match.hpp (renamed from geom_bottleneck/bottleneck/src/bound_match.cpp)337
-rw-r--r--geom_bottleneck/include/def_debug_bt.h42
-rw-r--r--geom_bottleneck/include/diagram_reader.h139
-rw-r--r--geom_bottleneck/include/diagram_traits.h45
-rw-r--r--geom_bottleneck/include/dnn/geometry/euclidean-fixed.h162
-rw-r--r--geom_bottleneck/include/dnn/local/kd-tree.h106
-rw-r--r--geom_bottleneck/include/dnn/local/kd-tree.hpp296
-rw-r--r--geom_bottleneck/include/dnn/local/search-functors.h119
-rw-r--r--geom_bottleneck/include/dnn/parallel/tbb.h235
-rw-r--r--geom_bottleneck/include/dnn/parallel/utils.h100
-rw-r--r--geom_bottleneck/include/dnn/utils.h47
-rw-r--r--geom_bottleneck/include/neighb_oracle.h295
-rw-r--r--geom_bottleneck/license.txt22
-rw-r--r--geom_matching/wasserstein/CMakeLists.txt42
-rw-r--r--geom_matching/wasserstein/example/wasserstein_dist.cpp83
-rw-r--r--geom_matching/wasserstein/example/wasserstein_dist_point_cloud.cpp175
-rw-r--r--geom_matching/wasserstein/include/auction_oracle.h288
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_base.h85
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_base.hpp97
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.h97
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.hpp244
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.h122
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.hpp598
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.h219
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.hpp717
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_lazy_heap.h191
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_lazy_heap.hpp465
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.h114
-rw-r--r--geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.hpp568
-rw-r--r--geom_matching/wasserstein/include/auction_runner_fr.h289
-rw-r--r--geom_matching/wasserstein/include/auction_runner_fr.hpp1440
-rw-r--r--geom_matching/wasserstein/include/auction_runner_gs.h154
-rw-r--r--geom_matching/wasserstein/include/auction_runner_gs.hpp486
-rw-r--r--geom_matching/wasserstein/include/auction_runner_gs_single_diag.h149
-rw-r--r--geom_matching/wasserstein/include/auction_runner_gs_single_diag.hpp738
-rw-r--r--geom_matching/wasserstein/include/auction_runner_jac.h235
-rw-r--r--geom_matching/wasserstein/include/auction_runner_jac.hpp873
-rw-r--r--geom_matching/wasserstein/include/basic_defs_ws.h325
-rw-r--r--geom_matching/wasserstein/include/basic_defs_ws.hpp (renamed from geom_matching/wasserstein/src/basic_defs.cpp)157
-rw-r--r--geom_matching/wasserstein/include/catch/catch.hpp11545
-rw-r--r--geom_matching/wasserstein/include/def_debug_ws.h4
-rw-r--r--geom_matching/wasserstein/include/diagonal_heap.h149
-rw-r--r--geom_matching/wasserstein/include/diagram_reader.h369
-rw-r--r--geom_matching/wasserstein/include/dnn/geometry/euclidean-dynamic.h248
-rw-r--r--geom_matching/wasserstein/include/dnn/geometry/euclidean-fixed.h26
-rw-r--r--geom_matching/wasserstein/include/dnn/local/kd-tree.h15
-rw-r--r--geom_matching/wasserstein/include/dnn/local/kd-tree.hpp57
-rw-r--r--geom_matching/wasserstein/include/dnn/local/search-functors.h16
-rw-r--r--geom_matching/wasserstein/include/dnn/parallel/tbb.h29
-rw-r--r--geom_matching/wasserstein/include/dnn/parallel/utils.h20
-rw-r--r--geom_matching/wasserstein/include/dnn/utils.h12
-rw-r--r--geom_matching/wasserstein/include/spdlog/async_logger.h82
-rw-r--r--geom_matching/wasserstein/include/spdlog/common.h160
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/async_log_helper.h399
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/async_logger_impl.h105
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/file_helper.h117
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/log_msg.h50
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/logger_impl.h563
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/mpmc_bounded_q.h172
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/null_mutex.h45
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/os.h469
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/pattern_formatter_impl.h690
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/registry.h214
-rw-r--r--geom_matching/wasserstein/include/spdlog/details/spdlog_impl.h263
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/format.cc940
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/format.h4501
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.cc43
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.h126
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.cc238
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.h443
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/bundled/time.h58
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/fmt.h28
-rw-r--r--geom_matching/wasserstein/include/spdlog/fmt/ostr.h17
-rw-r--r--geom_matching/wasserstein/include/spdlog/formatter.h47
-rw-r--r--geom_matching/wasserstein/include/spdlog/logger.h132
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/android_sink.h90
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/ansicolor_sink.h133
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/base_sink.h50
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/dist_sink.h73
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/file_sinks.h242
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/msvc_sink.h51
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/null_sink.h34
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/ostream_sink.h47
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/sink.h53
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/stdout_sinks.h77
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/syslog_sink.h81
-rw-r--r--geom_matching/wasserstein/include/spdlog/sinks/wincolor_sink.h117
-rw-r--r--geom_matching/wasserstein/include/spdlog/spdlog.h187
-rw-r--r--geom_matching/wasserstein/include/spdlog/tweakme.h141
-rw-r--r--geom_matching/wasserstein/include/wasserstein.h355
-rw-r--r--geom_matching/wasserstein/include/wasserstein_pure_geom.hpp87
-rw-r--r--geom_matching/wasserstein/src/auction_oracle.cpp1289
-rw-r--r--geom_matching/wasserstein/src/auction_runner_gs.cpp391
-rw-r--r--geom_matching/wasserstein/src/auction_runner_jac.cpp388
-rw-r--r--geom_matching/wasserstein/src/wasserstein.cpp235
-rw-r--r--geom_matching/wasserstein/tests/data/test_100_A100
-rw-r--r--geom_matching/wasserstein/tests/data/test_100_B100
-rw-r--r--geom_matching/wasserstein/tests/data/test_200_A200
-rw-r--r--geom_matching/wasserstein/tests/data/test_200_B200
-rw-r--r--geom_matching/wasserstein/tests/data/test_5000_A5000
-rw-r--r--geom_matching/wasserstein/tests/data/test_5000_B5000
-rw-r--r--geom_matching/wasserstein/tests/data/test_5_A5
-rw-r--r--geom_matching/wasserstein/tests/data/test_5_B5
-rw-r--r--geom_matching/wasserstein/tests/data/test_list.txt18
-rw-r--r--geom_matching/wasserstein/tests/test_hera_wasserstein.cpp203
-rw-r--r--geom_matching/wasserstein/tests/tests_main.cpp3
150 files changed, 46041 insertions, 11346 deletions
diff --git a/geom_bottleneck/CMakeLists.txt b/geom_bottleneck/CMakeLists.txt
index 6ec3fc2..62dc2f2 100644
--- a/geom_bottleneck/CMakeLists.txt
+++ b/geom_bottleneck/CMakeLists.txt
@@ -1,5 +1,35 @@
-cmake_minimum_required(VERSION 2.8.9)
-project(geom_bottleneck)
-#add_subdirectory(ann)
-add_subdirectory(bottleneck)
-add_subdirectory(example)
+project (geom_bottleneck)
+cmake_minimum_required (VERSION 3.5.1)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# C++ 11 required
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+# Default to Release
+
+if (NOT CMAKE_BUILD_TYPE)
+ set (CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif (NOT CMAKE_BUILD_TYPE)
+
+if(NOT WIN32)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb -D_GLIBCXX_DEBUG")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 ")
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O2 -g -ggdb")
+endif(NOT WIN32)
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+file(GLOB WS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp)
+
+
+
+find_package (Threads)
+set (libraries ${libraries} ${CMAKE_THREAD_LIBS_INIT})
+
+add_executable(bottleneck_dist ${CMAKE_CURRENT_SOURCE_DIR}/example/bottleneck_dist.cpp ${WS_HEADERS})
+target_link_libraries(bottleneck_dist PUBLIC ${libraries})
diff --git a/geom_bottleneck/COPYING b/geom_bottleneck/COPYING
deleted file mode 100644
index 94a9ed0..0000000
--- a/geom_bottleneck/COPYING
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/geom_bottleneck/COPYING.LESSER b/geom_bottleneck/COPYING.LESSER
deleted file mode 100644
index 65c5ca8..0000000
--- a/geom_bottleneck/COPYING.LESSER
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/geom_bottleneck/README b/geom_bottleneck/README
index 839ffa5..8b368af 100644
--- a/geom_bottleneck/README
+++ b/geom_bottleneck/README
@@ -4,11 +4,6 @@ Bug reports can be sent to "nigmetov EMAIL SIGN tugraz DOT at".
# Dependencies
-The program uses the ANN library (http://www.cs.umd.edu/~mount/ANN/),
-modified to support deletion of points from k-d trees.
-The modified version is contained in "bottleneck/src/ann" and "bottleneck/iclude/ANN"
-directories, there is no need to build ANN separately or include ANN's headers.
-
Your compiler must support C++11.
# Usage:
@@ -38,8 +33,10 @@ x_n y_n
#include "bottleneck.h"
-// All classes and functions are in geom_bt namespace
-// (including modified ANN's classes).
+// the functions hera::bottleneckDistExact, hera::bottleneckDistApprox
+// return the exact and approximate bottleneck distance.
+
+// function hera::readDiagramPointSet reads diagram from a plain-text file.
std::vector<std::pair<double, double>> diagram1, diagram2;
// any container class that supports range-for loops will do.
@@ -50,25 +47,25 @@ std::vector<std::pair<double, double>> diagram1, diagram2;
// load your diagrams into diagram1, diagram2 (off-diagonal points).
// If you data is in plain text format, you can use readDiagramPointSet function:
-if (!geom_bt::readDiagramPointSet("diagram1.txt", diagram1)) {
+if (!hera::readDiagramPointSet("diagram1.txt", diagram1)) {
// something went wrong: function returns true if it successfully read the file
}
// OK: diagram1.txt was read.
// ...
// to get exact distance:
-double btDist = geom_bt::bottleneckDistExact(diagram1, diagram2);
+double btDist = hera::bottleneckDistExact(diagram1, diagram2);
// to get 1% approximation
-double btDistApprox = geom_bt::bottleneckDistApprox(diagram1, diagram2, 0.01);
+double btDistApprox = hera::bottleneckDistApprox(diagram1, diagram2, 0.01);
// ..............................................................................
// if diagrams will be used many times, you may want to avoid copying them
-// to DiagramPointSet (which is done internally in each call to
+// to hera::bt::DiagramPointSet (which is done internally in each call to
bottleneckDistExact/bottleneckDistApprox) and do it yourself once.
// Constructor takes two iterators:
-geom_bt::DiagramPointSet ds1(diagram1.begin(), diagram1.end());
-geom_bt::DiagramPointSet ds2(diagram2.begin(), diagram2.end());
-btDist = geom_bt::bottleneckDistExact(ds1, ds2);
-btDistApprox = geom_bt::bottleneckDistApprox(ds1, ds2, 0.01);
+hera::bt::DiagramPointSet ds1(diagram1.begin(), diagram1.end());
+hera::bt::DiagramPointSet ds2(diagram2.begin(), diagram2.end());
+btDist = hera::bt::bottleneckDistExact(ds1, ds2);
+btDistApprox = hera::bt::bottleneckDistApprox(ds1, ds2, 0.01);
Necessary projections (diagonal points) will be added in the bottleneckDistApprox
function.
@@ -79,20 +76,16 @@ See also code in example/bottleneck_dist.cpp.
1) If bottleneckDistApprox is called with epsilon = 0.0, it will never return.
2) Empty diagrams are not considered as error.
-3) Modifications made in the ANN code are only valid for 2-dimensional k-d trees.
-Do not use the modified ANN files from the project folder anywhere else.
-4) You can switch to non-geometric version by using another typedef in
-bottleneck/include/neigb_oracle.h.
# License
-The program is distributed under Lesser GPL license.
+See licence.txt
# Building
-CMakeLists.txt in the root directory can be used to make the library (contained
-in bottleneck/ directory) and the command-line utility (in example/ directory)
-to compute the distance between two diagrams in txt files.
+CMakeLists.txt in the root directory can be used to build
+the command-line utility (in example/ directory).
+The library itself is header-only and does not require separate compilation.
On Linux/Mac:
@@ -101,5 +94,5 @@ cd build
cmake ..
make
-On Windows (checked with Visual Studio 2015, Community version)
+On Windows:
use cmake-gui to create the solution in build directory and build it with VS.
diff --git a/geom_bottleneck/bottleneck/CMakeLists.txt b/geom_bottleneck/bottleneck/CMakeLists.txt
deleted file mode 100644
index 7f5cc6e..0000000
--- a/geom_bottleneck/bottleneck/CMakeLists.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-project(geom_bottleneck)
-cmake_minimum_required(VERSION 2.8.9)
-include (GenerateExportHeader)
-
-# Default to Release
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
-endif(NOT CMAKE_BUILD_TYPE)
-
-# Add path to ANN header files
-include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include"
- "${CMAKE_CURRENT_SOURCE_DIR}/include/ANN")
-
-set(CXX_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
-
-if(NOT WIN32)
- set(CMAKE_CXX_FLAGS "-O3 -DNDEBUG -DBOOST_DISABLE_ASSERTS")
- add_definitions(-std=c++11)
-endif(NOT WIN32)
-
-file(GLOB ANN_SRC_FILES src/ann/*.cpp)
-
-add_library(bottleneck src/bottleneck.cpp src/bound_match.cpp src/neighb_oracle.cpp src/basic_defs.cpp ${ANN_SRC_FILES})
-if (WIN32)
- GENERATE_EXPORT_HEADER(bottleneck
- BASE_NAME bottleneck
- EXPORT_MACRO_NAME bottleneck_EXPORT
- EXPORT_FILE_NAME bottleneck_export.h
- STATIC_DEFINE bottleneck_BUILT_AS_STATIC)
-endif(WIN32)
diff --git a/geom_bottleneck/bottleneck/include/ANN/ANN.h b/geom_bottleneck/bottleneck/include/ANN/ANN.h
deleted file mode 100644
index 004dfe2..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/ANN.h
+++ /dev/null
@@ -1,917 +0,0 @@
-//----------------------------------------------------------------------
-// File: ANN.h
-// Programmer: Sunil Arya and David Mount
-// Description: Basic include file for approximate nearest
-// neighbor searching.
-// Last modified: 01/27/10 (Version 1.1.2)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2010 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Added copyright and revision information
-// Added ANNcoordPrec for coordinate precision.
-// Added methods theDim, nPoints, maxPoints, thePoints to ANNpointSet.
-// Cleaned up C++ structure for modern compilers
-// Revision 1.1 05/03/05
-// Added fixed-radius k-NN searching
-// Revision 1.1.2 01/27/10
-// Fixed minor compilation bugs for new versions of gcc
-// --------------------------------------------------------------------
-// 2015 - modified by A. Nigmetov to support deletion of points
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// ANN - approximate nearest neighbor searching
-// ANN is a library for approximate nearest neighbor searching,
-// based on the use of standard and priority search in kd-trees
-// and balanced box-decomposition (bbd) trees. Here are some
-// references to the main algorithmic techniques used here:
-//
-// kd-trees:
-// Friedman, Bentley, and Finkel, ``An algorithm for finding
-// best matches in logarithmic expected time,'' ACM
-// Transactions on Mathematical Software, 3(3):209-226, 1977.
-//
-// Priority search in kd-trees:
-// Arya and Mount, ``Algorithms for fast vector quantization,''
-// Proc. of DCC '93: Data Compression Conference, eds. J. A.
-// Storer and M. Cohn, IEEE Press, 1993, 381-390.
-//
-// Approximate nearest neighbor search and bbd-trees:
-// Arya, Mount, Netanyahu, Silverman, and Wu, ``An optimal
-// algorithm for approximate nearest neighbor searching,''
-// 5th Ann. ACM-SIAM Symposium on Discrete Algorithms,
-// 1994, 573-582.
-//----------------------------------------------------------------------
-
-#ifndef ANN_H
-#define ANN_H
-
-// A. Nigmetov: ANN code is integrated into bottleneck library,
-// CMake will take care of correct __declspec, no need to define DLL_API
-#define DLL_API
-//#ifdef WIN32
- //----------------------------------------------------------------------
- // For Microsoft Visual C++, externally accessible symbols must be
- // explicitly indicated with DLL_API, which is somewhat like "extern."
- //
- // The following ifdef block is the standard way of creating macros
- // which make exporting from a DLL simpler. All files within this DLL
- // are compiled with the DLL_EXPORTS preprocessor symbol defined on the
- // command line. In contrast, projects that use (or import) the DLL
- // objects do not define the DLL_EXPORTS symbol. This way any other
- // project whose source files include this file see DLL_API functions as
- // being imported from a DLL, wheras this DLL sees symbols defined with
- // this macro as being exported.
- //----------------------------------------------------------------------
- //#ifdef DLL_EXPORTS
- // #define DLL_API __declspec(dllexport)
- //#else
- //#define DLL_API __declspec(dllimport)
- //#endif
- //----------------------------------------------------------------------
- // DLL_API is ignored for all other systems
- //----------------------------------------------------------------------
-//#else
- //#define DLL_API
-//#endif
-
-//----------------------------------------------------------------------
-// basic includes
-//----------------------------------------------------------------------
-
-#include <cstdlib> // standard lib includes
-#include <cmath> // math includes
-#include <cstring> // C-style strings
-#include <vector>
-#include <assert.h>
-
-//----------------------------------------------------------------------
-// Limits
-// There are a number of places where we use the maximum double value as
-// default initializers (and others may be used, depending on the
-// data/distance representation). These can usually be found in limits.h
-// (as LONG_MAX, INT_MAX) or in float.h (as DBL_MAX, FLT_MAX).
-//
-// Not all systems have these files. If you are using such a system,
-// you should set the preprocessor symbol ANN_NO_LIMITS_H when
-// compiling, and modify the statements below to generate the
-// appropriate value. For practical purposes, this does not need to be
-// the maximum double value. It is sufficient that it be at least as
-// large than the maximum squared distance between between any two
-// points.
-//----------------------------------------------------------------------
-#ifdef ANN_NO_LIMITS_H // limits.h unavailable
- #include <cvalues> // replacement for limits.h
- const double ANN_DBL_MAX = MAXDOUBLE; // insert maximum double
-#else
- #include <climits>
- #include <cfloat>
- const double ANN_DBL_MAX = DBL_MAX;
-#endif
-
-#define ANNversion "1.1.2" // ANN version and information
-#define ANNversionCmt ""
-#define ANNcopyright "David M. Mount and Sunil Arya"
-#define ANNlatestRev "Jan 27, 2010"
-
-#include "def_debug_bt.h"
-
-#ifndef FOR_R_TDA
-#include <iostream> // I/O streams
-#endif
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// ANNbool
-// This is a simple boolean type. Although ANSI C++ is supposed
-// to support the type bool, some compilers do not have it.
-//----------------------------------------------------------------------
-
-
-enum ANNbool {ANNfalse = 0, ANNtrue = 1}; // ANN boolean type (non ANSI C++)
-
-//----------------------------------------------------------------------
-// ANNcoord, ANNdist
-// ANNcoord and ANNdist are the types used for representing
-// point coordinates and distances. They can be modified by the
-// user, with some care. It is assumed that they are both numeric
-// types, and that ANNdist is generally of an equal or higher type
-// from ANNcoord. A variable of type ANNdist should be large
-// enough to store the sum of squared components of a variable
-// of type ANNcoord for the number of dimensions needed in the
-// application. For example, the following combinations are
-// legal:
-//
-// ANNcoord ANNdist
-// --------- -------------------------------
-// short short, int, long, float, double
-// int int, long, float, double
-// long long, float, double
-// float float, double
-// double double
-//
-// It is the user's responsibility to make sure that overflow does
-// not occur in distance calculation.
-//----------------------------------------------------------------------
-
-typedef double ANNcoord; // coordinate data type
-typedef double ANNdist; // distance data type
-
-//----------------------------------------------------------------------
-// ANNidx
-// ANNidx is a point index. When the data structure is built, the
-// points are given as an array. Nearest neighbor results are
-// returned as an integer index into this array. To make it
-// clearer when this is happening, we define the integer type
-// ANNidx. Indexing starts from 0.
-//
-// For fixed-radius near neighbor searching, it is possible that
-// there are not k nearest neighbors within the search radius. To
-// indicate this, the algorithm returns ANN_NULL_IDX as its result.
-// It should be distinguishable from any valid array index.
-//----------------------------------------------------------------------
-
-typedef int ANNidx; // point index
-const ANNidx ANN_NULL_IDX = -1; // a NULL point index
-
-//----------------------------------------------------------------------
-// Infinite distance:
-// The code assumes that there is an "infinite distance" which it
-// uses to initialize distances before performing nearest neighbor
-// searches. It should be as larger or larger than any legitimate
-// nearest neighbor distance.
-//
-// On most systems, these should be found in the standard include
-// file <limits.h> or possibly <float.h>. If you do not have these
-// file, some suggested values are listed below, assuming 64-bit
-// long, 32-bit int and 16-bit short.
-//
-// ANNdist ANN_DIST_INF Values (see <limits.h> or <float.h>)
-// ------- ------------ ------------------------------------
-// double DBL_MAX 1.79769313486231570e+308
-// float FLT_MAX 3.40282346638528860e+38
-// long LONG_MAX 0x7fffffffffffffff
-// int INT_MAX 0x7fffffff
-// short SHRT_MAX 0x7fff
-//----------------------------------------------------------------------
-
-const ANNdist ANN_DIST_INF = ANN_DBL_MAX;
-
-//----------------------------------------------------------------------
-// Significant digits for tree dumps:
-// When floating point coordinates are used, the routine that dumps
-// a tree needs to know roughly how many significant digits there
-// are in a ANNcoord, so it can output points to full precision.
-// This is defined to be ANNcoordPrec. On most systems these
-// values can be found in the standard include files <limits.h> or
-// <float.h>. For integer types, the value is essentially ignored.
-//
-// ANNcoord ANNcoordPrec Values (see <limits.h> or <float.h>)
-// -------- ------------ ------------------------------------
-// double DBL_DIG 15
-// float FLT_DIG 6
-// long doesn't matter 19
-// int doesn't matter 10
-// short doesn't matter 5
-//----------------------------------------------------------------------
-
-#ifdef DBL_DIG // number of sig. bits in ANNcoord
- const int ANNcoordPrec = DBL_DIG;
-#else
- const int ANNcoordPrec = 15; // default precision
-#endif
-
-//----------------------------------------------------------------------
-// Self match?
-// In some applications, the nearest neighbor of a point is not
-// allowed to be the point itself. This occurs, for example, when
-// computing all nearest neighbors in a set. By setting the
-// parameter ANN_ALLOW_SELF_MATCH to ANNfalse, the nearest neighbor
-// is the closest point whose distance from the query point is
-// strictly positive.
-//----------------------------------------------------------------------
-
-const ANNbool ANN_ALLOW_SELF_MATCH = ANNtrue;
-
-//----------------------------------------------------------------------
-// Norms and metrics:
-// ANN supports any Minkowski norm for defining distance. In
-// particular, for any p >= 1, the L_p Minkowski norm defines the
-// length of a d-vector (v0, v1, ..., v(d-1)) to be
-//
-// (|v0|^p + |v1|^p + ... + |v(d-1)|^p)^(1/p),
-//
-// (where ^ denotes exponentiation, and |.| denotes absolute
-// value). The distance between two points is defined to be the
-// norm of the vector joining them. Some common distance metrics
-// include
-//
-// Euclidean metric p = 2
-// Manhattan metric p = 1
-// Max metric p = infinity
-//
-// In the case of the max metric, the norm is computed by taking
-// the maxima of the absolute values of the components. ANN is
-// highly "coordinate-based" and does not support general distances
-// functions (e.g. those obeying just the triangle inequality). It
-// also does not support distance functions based on
-// inner-products.
-//
-// For the purpose of computing nearest neighbors, it is not
-// necessary to compute the final power (1/p). Thus the only
-// component that is used by the program is |v(i)|^p.
-//
-// ANN parameterizes the distance computation through the following
-// macros. (Macros are used rather than procedures for
-// efficiency.) Recall that the distance between two points is
-// given by the length of the vector joining them, and the length
-// or norm of a vector v is given by formula:
-//
-// |v| = ROOT(POW(v0) # POW(v1) # ... # POW(v(d-1)))
-//
-// where ROOT, POW are unary functions and # is an associative and
-// commutative binary operator mapping the following types:
-//
-// ** POW: ANNcoord --> ANNdist
-// ** #: ANNdist x ANNdist --> ANNdist
-// ** ROOT: ANNdist (>0) --> double
-//
-// For early termination in distance calculation (partial distance
-// calculation) we assume that POW and # together are monotonically
-// increasing on sequences of arguments, meaning that for all
-// v0..vk and y:
-//
-// POW(v0) #...# POW(vk) <= (POW(v0) #...# POW(vk)) # POW(y).
-//
-// Incremental Distance Calculation:
-// The program uses an optimized method of computing distances for
-// kd-trees and bd-trees, called incremental distance calculation.
-// It is used when distances are to be updated when only a single
-// coordinate of a point has been changed. In order to use this,
-// we assume that there is an incremental update function DIFF(x,y)
-// for #, such that if:
-//
-// s = x0 # ... # xi # ... # xk
-//
-// then if s' is equal to s but with xi replaced by y, that is,
-//
-// s' = x0 # ... # y # ... # xk
-//
-// then the length of s' can be computed by:
-//
-// |s'| = |s| # DIFF(xi,y).
-//
-// Thus, if # is + then DIFF(xi,y) is (yi-x). For the L_infinity
-// norm we make use of the fact that in the program this function
-// is only invoked when y > xi, and hence DIFF(xi,y)=y.
-//
-// Finally, for approximate nearest neighbor queries we assume
-// that POW and ROOT are related such that
-//
-// v*ROOT(x) = ROOT(POW(v)*x)
-//
-// Here are the values for the various Minkowski norms:
-//
-// L_p: p even: p odd:
-// ------------------------- ------------------------
-// POW(v) = v^p POW(v) = |v|^p
-// ROOT(x) = x^(1/p) ROOT(x) = x^(1/p)
-// # = + # = +
-// DIFF(x,y) = y - x DIFF(x,y) = y - x
-//
-// L_inf:
-// POW(v) = |v|
-// ROOT(x) = x
-// # = max
-// DIFF(x,y) = y
-//
-// By default the Euclidean norm is assumed. To change the norm,
-// uncomment the appropriate set of macros below.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// Use the following for the Euclidean norm
-//----------------------------------------------------------------------
-//#define ANN_POW(v) ((v)*(v))
-//#define ANN_ROOT(x) sqrt(x)
-//#define ANN_SUM(x,y) ((x) + (y))
-//#define ANN_DIFF(x,y) ((y) - (x))
-
-//----------------------------------------------------------------------
-// Use the following for the L_1 (Manhattan) norm
-//----------------------------------------------------------------------
-// #define ANN_POW(v) fabs(v)
-// #define ANN_ROOT(x) (x)
-// #define ANN_SUM(x,y) ((x) + (y))
-// #define ANN_DIFF(x,y) ((y) - (x))
-
-//----------------------------------------------------------------------
-// Use the following for a general L_p norm
-//----------------------------------------------------------------------
-// #define ANN_POW(v) pow(fabs(v),p)
-// #define ANN_ROOT(x) pow(fabs(x),1/p)
-// #define ANN_SUM(x,y) ((x) + (y))
-// #define ANN_DIFF(x,y) ((y) - (x))
-
-//----------------------------------------------------------------------
-// Use the following for the L_infinity (Max) norm
-//----------------------------------------------------------------------
-#define ANN_POW(v) fabs(v)
-#define ANN_ROOT(x) (x)
-#define ANN_SUM(x,y) ((x) > (y) ? (x) : (y))
-#define ANN_DIFF(x,y) (y)
-
-//----------------------------------------------------------------------
-// Array types
-// The following array types are of basic interest. A point is
-// just a dimensionless array of coordinates, a point array is a
-// dimensionless array of points. A distance array is a
-// dimensionless array of distances and an index array is a
-// dimensionless array of point indices. The latter two are used
-// when returning the results of k-nearest neighbor queries.
-//----------------------------------------------------------------------
-
-typedef ANNcoord* ANNpoint; // a point
-typedef ANNpoint* ANNpointArray; // an array of points
-typedef ANNdist* ANNdistArray; // an array of distances
-typedef ANNidx* ANNidxArray; // an array of point indices
-
-//----------------------------------------------------------------------
-// Basic point and array utilities:
-// The following procedures are useful supplements to ANN's nearest
-// neighbor capabilities.
-//
-// annDist():
-// Computes the (squared) distance between a pair of points.
-// Note that this routine is not used internally by ANN for
-// computing distance calculations. For reasons of efficiency
-// this is done using incremental distance calculation. Thus,
-// this routine cannot be modified as a method of changing the
-// metric.
-//
-// Because points (somewhat like strings in C) are stored as
-// pointers. Consequently, creating and destroying copies of
-// points may require storage allocation. These procedures do
-// this.
-//
-// annAllocPt() and annDeallocPt():
-// Allocate a deallocate storage for a single point, and
-// return a pointer to it. The argument to AllocPt() is
-// used to initialize all components.
-//
-// annAllocPts() and annDeallocPts():
-// Allocate and deallocate an array of points as well a
-// place to store their coordinates, and initializes the
-// points to point to their respective coordinates. It
-// allocates point storage in a contiguous block large
-// enough to store all the points. It performs no
-// initialization.
-//
-// annCopyPt():
-// Creates a copy of a given point, allocating space for
-// the new point. It returns a pointer to the newly
-// allocated copy.
-//----------------------------------------------------------------------
-
-DLL_API ANNdist annDist(
- int dim, // dimension of space
- ANNpoint p, // points
- ANNpoint q);
-
-DLL_API ANNpoint annAllocPt(
- int dim, // dimension
- ANNcoord c = 0); // coordinate value (all equal)
-
-DLL_API ANNpointArray annAllocPts(
- int n, // number of points
- int dim); // dimension
-
-DLL_API void annDeallocPt(
- ANNpoint &p); // deallocate 1 point
-
-DLL_API void annDeallocPts(
- ANNpointArray &pa); // point array
-
-DLL_API ANNpoint annCopyPt(
- int dim, // dimension
- ANNpoint source); // point to copy
-
-
-//----------------------------------------------------------------------
-// Orthogonal (axis aligned) rectangle
-// Orthogonal rectangles are represented by two points, one
-// for the lower left corner (min coordinates) and the other
-// for the upper right corner (max coordinates).
-//
-// The constructor initializes from either a pair of coordinates,
-// pair of points, or another rectangle. Note that all constructors
-// allocate new point storage. The destructor deallocates this
-// storage.
-//
-// BEWARE: Orthogonal rectangles should be passed ONLY BY REFERENCE.
-// (C++'s default copy constructor will not allocate new point
-// storage, then on return the destructor free's storage, and then
-// you get into big trouble in the calling procedure.)
-//----------------------------------------------------------------------
-
-class DLL_API ANNorthRect {
-public:
- ANNpoint lo; // rectangle lower bounds
- ANNpoint hi; // rectangle upper bounds
-//
- ANNorthRect( // basic constructor
- int dd, // dimension of space
- ANNcoord l=0, // default is empty
- ANNcoord h=0)
- { lo = annAllocPt(dd, l); hi = annAllocPt(dd, h); }
-
- ANNorthRect( // (almost a) copy constructor
- int dd, // dimension
- const ANNorthRect &r) // rectangle to copy
- { lo = annCopyPt(dd, r.lo); hi = annCopyPt(dd, r.hi); }
-
- ANNorthRect( // construct from points
- int dd, // dimension
- ANNpoint l, // low point
- ANNpoint h) // hight point
- { lo = annCopyPt(dd, l); hi = annCopyPt(dd, h); }
-
- ~ANNorthRect() // destructor
- { annDeallocPt(lo); annDeallocPt(hi); }
-
- ANNbool inside(const int dim, ANNpoint p) const;// is point p inside rectangle?
- bool contains(const int dim, const ANNorthRect& r) const;
- bool intersects(const int dim, const ANNorthRect& r) const;
-};
-
-
-//----------------------------------------------------------------------
-//Overall structure: ANN supports a number of different data structures
-//for approximate and exact nearest neighbor searching. These are:
-//
-// ANNbruteForce A simple brute-force search structure.
-// ANNkd_tree A kd-tree tree search structure. ANNbd_tree
-// A bd-tree tree search structure (a kd-tree with shrink
-// capabilities).
-//
-// At a minimum, each of these data structures support k-nearest
-// neighbor queries. The nearest neighbor query, annkSearch,
-// returns an integer identifier and the distance to the nearest
-// neighbor(s) and annRangeSearch returns the nearest points that
-// lie within a given query ball.
-//
-// Each structure is built by invoking the appropriate constructor
-// and passing it (at a minimum) the array of points, the total
-// number of points and the dimension of the space. Each structure
-// is also assumed to support a destructor and member functions
-// that return basic information about the point set.
-//
-// Note that the array of points is not copied by the data
-// structure (for reasons of space efficiency), and it is assumed
-// to be constant throughout the lifetime of the search structure.
-//
-// The search algorithm, annkSearch, is given the query point (q),
-// and the desired number of nearest neighbors to report (k), and
-// the error bound (eps) (whose default value is 0, implying exact
-// nearest neighbors). It returns two arrays which are assumed to
-// contain at least k elements: one (nn_idx) contains the indices
-// (within the point array) of the nearest neighbors and the other
-// (dd) contains the squared distances to these nearest neighbors.
-//
-// The search algorithm, annkFRSearch, is a fixed-radius kNN
-// search. In addition to a query point, it is given a (squared)
-// radius bound. (This is done for consistency, because the search
-// returns distances as squared quantities.) It does two things.
-// First, it computes the k nearest neighbors within the radius
-// bound, and second, it returns the total number of points lying
-// within the radius bound. It is permitted to set k = 0, in which
-// case it effectively answers a range counting query. If the
-// error bound epsilon is positive, then the search is approximate
-// in the sense that it is free to ignore any point that lies
-// outside a ball of radius r/(1+epsilon), where r is the given
-// (unsquared) radius bound.
-//
-// The generic object from which all the search structures are
-// dervied is given below. It is a virtual object, and is useless
-// by itself.
-//----------------------------------------------------------------------
-
-class DLL_API ANNpointSet {
-public:
- virtual ~ANNpointSet() {} // virtual distructor
-
- virtual void annkSearch( // approx k near neighbor search
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor array (modified)
- ANNdistArray dd, // dist to near neighbors (modified)
- double eps=0.0 // error bound
- ) = 0; // pure virtual (defined elsewhere)
-
- virtual int annkFRSearch( // approx fixed-radius kNN search
- ANNpoint q, // query point
- ANNdist sqRad, // squared radius
- int k = 0, // number of near neighbors to return
- ANNidxArray nn_idx = NULL, // nearest neighbor array (modified)
- ANNdistArray dd = NULL, // dist to near neighbors (modified)
- double eps=0.0 // error bound
- ) = 0; // pure virtual (defined elsewhere)
-
- virtual int theDim() = 0; // return dimension of space
- virtual int nPoints() = 0; // return number of points
- // return pointer to points
- virtual ANNpointArray thePoints() = 0;
-};
-
-//----------------------------------------------------------------------
-// Brute-force nearest neighbor search:
-// The brute-force search structure is very simple but inefficient.
-// It has been provided primarily for the sake of comparison with
-// and validation of the more complex search structures.
-//
-// Query processing is the same as described above, but the value
-// of epsilon is ignored, since all distance calculations are
-// performed exactly.
-//
-// WARNING: This data structure is very slow, and should not be
-// used unless the number of points is very small.
-//
-// Internal information:
-// ---------------------
-// This data structure bascially consists of the array of points
-// (each a pointer to an array of coordinates). The search is
-// performed by a simple linear scan of all the points.
-//----------------------------------------------------------------------
-
-class DLL_API ANNbruteForce: public ANNpointSet {
- int dim; // dimension
- int n_pts; // number of points
- ANNpointArray pts; // point array
-public:
- ANNbruteForce( // constructor from point array
- ANNpointArray pa, // point array
- int n, // number of points
- int dd); // dimension
-
- ~ANNbruteForce(); // destructor
-
- void annkSearch( // approx k near neighbor search
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor array (modified)
- ANNdistArray dd, // dist to near neighbors (modified)
- double eps=0.0); // error bound
-
- int annkFRSearch( // approx fixed-radius kNN search
- ANNpoint q, // query point
- ANNdist sqRad, // squared radius
- int k = 0, // number of near neighbors to return
- ANNidxArray nn_idx = NULL, // nearest neighbor array (modified)
- ANNdistArray dd = NULL, // dist to near neighbors (modified)
- double eps=0.0); // error bound
-
- int theDim() // return dimension of space
- { return dim; }
-
- int nPoints() // return number of points
- { return n_pts; }
-
- ANNpointArray thePoints() // return pointer to points
- { return pts; }
-};
-
-//----------------------------------------------------------------------
-// kd- and bd-tree splitting and shrinking rules
-// kd-trees supports a collection of different splitting rules.
-// In addition to the standard kd-tree splitting rule proposed
-// by Friedman, Bentley, and Finkel, we have introduced a
-// number of other splitting rules, which seem to perform
-// as well or better (for the distributions we have tested).
-//
-// The splitting methods given below allow the user to tailor
-// the data structure to the particular data set. They are
-// are described in greater details in the kd_split.cc source
-// file. The method ANN_KD_SUGGEST is the method chosen (rather
-// subjectively) by the implementors as the one giving the
-// fastest performance, and is the default splitting method.
-//
-// As with splitting rules, there are a number of different
-// shrinking rules. The shrinking rule ANN_BD_NONE does no
-// shrinking (and hence produces a kd-tree tree). The rule
-// ANN_BD_SUGGEST uses the implementors favorite rule.
-//----------------------------------------------------------------------
-
-enum ANNsplitRule {
- ANN_KD_STD = 0, // the optimized kd-splitting rule
- ANN_KD_MIDPT = 1, // midpoint split
- ANN_KD_FAIR = 2, // fair split
- ANN_KD_SL_MIDPT = 3, // sliding midpoint splitting method
- ANN_KD_SL_FAIR = 4, // sliding fair split method
- ANN_KD_SUGGEST = 5, // the authors' suggestion for best
- // for kd-trees with deletion
- //ANN_KD_STD_WD = 6,
- //ANN_KD_MIDPT_WD = 7,
- //ANN_KD_SL_MIDPT_WD = 8
- };
-const int ANN_N_SPLIT_RULES = 6; // number of split rules
-//const int ANN_N_SPLIT_RULES = 9; // number of split rules
-
-enum ANNshrinkRule {
- ANN_BD_NONE = 0, // no shrinking at all (just kd-tree)
- ANN_BD_SIMPLE = 1, // simple splitting
- ANN_BD_CENTROID = 2, // centroid splitting
- ANN_BD_SUGGEST = 3}; // the authors' suggested choice
-const int ANN_N_SHRINK_RULES = 4; // number of shrink rules
-
-//----------------------------------------------------------------------
-// kd-tree:
-// The main search data structure supported by ANN is a kd-tree.
-// The main constructor is given a set of points and a choice of
-// splitting method to use in building the tree.
-//
-// Construction:
-// -------------
-// The constructor is given the point array, number of points,
-// dimension, bucket size (default = 1), and the splitting rule
-// (default = ANN_KD_SUGGEST). The point array is not copied, and
-// is assumed to be kept constant throughout the lifetime of the
-// search structure. There is also a "load" constructor that
-// builds a tree from a file description that was created by the
-// Dump operation.
-//
-// Search:
-// -------
-// There are two search methods:
-//
-// Standard search (annkSearch()):
-// Searches nodes in tree-traversal order, always visiting
-// the closer child first.
-// Priority search (annkPriSearch()):
-// Searches nodes in order of increasing distance of the
-// associated cell from the query point. For many
-// distributions the standard search seems to work just
-// fine, but priority search is safer for worst-case
-// performance.
-//
-// Printing:
-// ---------
-// There are two methods provided for printing the tree. Print()
-// is used to produce a "human-readable" display of the tree, with
-// indenation, which is handy for debugging. Dump() produces a
-// format that is suitable reading by another program. There is a
-// "load" constructor, which constructs a tree which is assumed to
-// have been saved by the Dump() procedure.
-//
-// Performance and Structure Statistics:
-// -------------------------------------
-// The procedure getStats() collects statistics information on the
-// tree (its size, height, etc.) See ANNperf.h for information on
-// the stats structure it returns.
-//
-// Internal information:
-// ---------------------
-// The data structure consists of three major chunks of storage.
-// The first (implicit) storage are the points themselves (pts),
-// which have been provided by the users as an argument to the
-// constructor, or are allocated dynamically if the tree is built
-// using the load constructor). These should not be changed during
-// the lifetime of the search structure. It is the user's
-// responsibility to delete these after the tree is destroyed.
-//
-// The second is the tree itself (which is dynamically allocated in
-// the constructor) and is given as a pointer to its root node
-// (root). These nodes are automatically deallocated when the tree
-// is deleted. See the file src/kd_tree.h for further information
-// on the structure of the tree nodes.
-//
-// Each leaf of the tree does not contain a pointer directly to a
-// point, but rather contains a pointer to a "bucket", which is an
-// array consisting of point indices. The third major chunk of
-// storage is an array (pidx), which is a large array in which all
-// these bucket subarrays reside. (The reason for storing them
-// separately is the buckets are typically small, but of varying
-// sizes. This was done to avoid fragmentation.) This array is
-// also deallocated when the tree is deleted.
-//
-// In addition to this, the tree consists of a number of other
-// pieces of information which are used in searching and for
-// subsequent tree operations. These consist of the following:
-//
-// dim Dimension of space
-// n_pts Number of points currently in the tree
-// n_max Maximum number of points that are allowed
-// in the tree
-// bkt_size Maximum bucket size (no. of points per leaf)
-// bnd_box_lo Bounding box low point
-// bnd_box_hi Bounding box high point
-// splitRule Splitting method used
-//
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// Some types and objects used by kd-tree functions
-// See src/kd_tree.h and src/kd_tree.cpp for definitions
-//----------------------------------------------------------------------
-class ANNkdStats; // stats on kd-tree
-class ANNkd_node; // generic node in a kd-tree
-typedef ANNkd_node* ANNkd_ptr; // pointer to a kd-tree node
-class ANNkd_leaf;
-
-class DLL_API ANNkd_tree: public ANNpointSet {
-protected:
- int dim; // dimension of space
- int n_pts; // number of points in tree
- int bkt_size; // bucket size
- ANNpointArray pts; // the points
- ANNidxArray pidx; // point indices (to pts array)
- ANNkd_ptr root; // root of kd-tree
- ANNpoint bnd_box_lo; // bounding box low point
- ANNpoint bnd_box_hi; // bounding box high point
-
- void SkeletonTree( // construct skeleton tree
- int n, // number of points
- int dd, // dimension
- int bs, // bucket size
- ANNpointArray pa = NULL, // point array (optional)
- ANNidxArray pi = NULL); // point indices (optional)
-
-public:
- ANNkd_tree( // build skeleton tree
- int n = 0, // number of points
- int dd = 0, // dimension
- int bs = 1); // bucket size
-
- ANNkd_tree( // build from point array
- ANNpointArray pa, // point array
- int n, // number of points
- int dd, // dimension
- int bs = 1, // bucket size
- ANNsplitRule split = ANN_KD_SUGGEST); // splitting method
-
-#ifndef FOR_R_TDA
- ANNkd_tree( // build from dump file
- std::istream& in); // input stream for dump file
-#endif
-
- ~ANNkd_tree(); // tree destructor
-
- void annkSearch( // approx k near neighbor search
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor array (modified)
- ANNdistArray dd, // dist to near neighbors (modified)
- double eps=0.0); // error bound
-
- void annkPriSearch( // priority k near neighbor search
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor array (modified)
- ANNdistArray dd, // dist to near neighbors (modified)
- double eps=0.0); // error bound
-
- int annkFRSearch( // approx fixed-radius kNN search
- ANNpoint q, // the query point
- ANNdist sqRad, // squared radius of query ball
- int k, // number of neighbors to return
- ANNidxArray nn_idx = NULL, // nearest neighbor array (modified)
- ANNdistArray dd = NULL, // dist to near neighbors (modified)
- double eps=0.0); // error bound
-
- int theDim() // return dimension of space
- { return dim; }
-
- int nPoints() // return number of points
- { return n_pts; }
-
- ANNpointArray thePoints() // return pointer to points
- { return pts; }
-
-#ifndef FOR_R_TDA
- virtual void Print( // print the tree (for debugging)
- ANNbool with_pts, // print points as well?
- std::ostream& out); // output stream
-
- virtual void Dump( // dump entire tree
- ANNbool with_pts, // print points as well?
- std::ostream& out); // output stream
-#endif
-
- virtual void getStats( // compute tree statistics
- ANNkdStats& st); // the statistics (modified)
-
- ///////////////////////////////////////////////////////////////
- // for deletion
- std::vector<ANNkd_leaf*> pointToLeafVec;
- std::vector<bool> isDeleted; // will be used to check implementation;
- //TODO remove after testing
- void delete_point(const int point_idx);
- int actual_num_points;
- int getActualNumPoints(void) const { return actual_num_points; }
- void range_search(const ANNorthRect& region, std::vector<size_t>& pointIdices);
-};
-
-//----------------------------------------------------------------------
-// Box decomposition tree (bd-tree)
-// The bd-tree is inherited from a kd-tree. The main difference
-// in the bd-tree and the kd-tree is a new type of internal node
-// called a shrinking node (in the kd-tree there is only one type
-// of internal node, a splitting node). The shrinking node
-// makes it possible to generate balanced trees in which the
-// cells have bounded aspect ratio, by allowing the decomposition
-// to zoom in on regions of dense point concentration. Although
-// this is a nice idea in theory, few point distributions are so
-// densely clustered that this is really needed.
-//----------------------------------------------------------------------
-
-class DLL_API ANNbd_tree: public ANNkd_tree {
-public:
- ANNbd_tree( // build skeleton tree
- int n, // number of points
- int dd, // dimension
- int bs = 1) // bucket size
- : ANNkd_tree(n, dd, bs) {} // build base kd-tree
-
- ANNbd_tree( // build from point array
- ANNpointArray pa, // point array
- int n, // number of points
- int dd, // dimension
- int bs = 1, // bucket size
- ANNsplitRule split = ANN_KD_SUGGEST, // splitting rule
- ANNshrinkRule shrink = ANN_BD_SUGGEST); // shrinking rule
-
-#ifndef FOR_R_TDA
- ANNbd_tree( // build from dump file
- std::istream& in); // input stream for dump file
-#endif
-};
-
-//----------------------------------------------------------------------
-// Other functions
-// annMaxPtsVisit Sets a limit on the maximum number of points
-// to visit in the search.
-// annClose Can be called when all use of ANN is finished.
-// It clears up a minor memory leak.
-//----------------------------------------------------------------------
-
-DLL_API void annMaxPtsVisit( // max. pts to visit in search
- int maxPts); // the limit
-
-DLL_API void annClose(); // called to end use of ANN
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/ANNperf.h b/geom_bottleneck/bottleneck/include/ANN/ANNperf.h
deleted file mode 100644
index d242266..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/ANNperf.h
+++ /dev/null
@@ -1,225 +0,0 @@
-//----------------------------------------------------------------------
-// File: ANNperf.h
-// Programmer: Sunil Arya and David Mount
-// Last modified: 03/04/98 (Release 0.1)
-// Description: Include file for ANN performance stats
-//
-// Some of the code for statistics gathering has been adapted
-// from the SmplStat.h package in the g++ library.
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Added ANN_ prefix to avoid name conflicts.
-//----------------------------------------------------------------------
-
-#ifndef ANNperf_H
-#define ANNperf_H
-
-//----------------------------------------------------------------------
-// basic includes
-//----------------------------------------------------------------------
-
-#include <ANN/ANN.h> // basic ANN includes
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// kd-tree stats object
-// This object is used for collecting information about a kd-tree
-// or bd-tree.
-//----------------------------------------------------------------------
-
-class ANNkdStats { // stats on kd-tree
-public:
- int dim; // dimension of space
- int n_pts; // no. of points
- int bkt_size; // bucket size
- int n_lf; // no. of leaves (including trivial)
- int n_tl; // no. of trivial leaves (no points)
- int n_spl; // no. of splitting nodes
- int n_shr; // no. of shrinking nodes (for bd-trees)
- int depth; // depth of tree
- float sum_ar; // sum of leaf aspect ratios
- float avg_ar; // average leaf aspect ratio
- //
- // reset stats
- void reset(int d=0, int n=0, int bs=0)
- {
- dim = d; n_pts = n; bkt_size = bs;
- n_lf = n_tl = n_spl = n_shr = depth = 0;
- sum_ar = avg_ar = 0.0;
- }
-
- ANNkdStats() // basic constructor
- { reset(); }
-
- void merge(const ANNkdStats &st); // merge stats from child
-};
-
-//----------------------------------------------------------------------
-// ANNsampStat
-// A sample stat collects numeric (double) samples and returns some
-// simple statistics. Its main functions are:
-//
-// reset() Reset to no samples.
-// += x Include sample x.
-// samples() Return number of samples.
-// mean() Return mean of samples.
-// stdDev() Return standard deviation
-// min() Return minimum of samples.
-// max() Return maximum of samples.
-//----------------------------------------------------------------------
-class DLL_API ANNsampStat {
- int n; // number of samples
- double sum; // sum
- double sum2; // sum of squares
- double minVal, maxVal; // min and max
-public :
- void reset() // reset everything
- {
- n = 0;
- sum = sum2 = 0;
- minVal = ANN_DBL_MAX;
- maxVal = -ANN_DBL_MAX;
- }
-
- ANNsampStat() { reset(); } // constructor
-
- void operator+=(double x) // add sample
- {
- n++; sum += x; sum2 += x*x;
- if (x < minVal) minVal = x;
- if (x > maxVal) maxVal = x;
- }
-
- int samples() { return n; } // number of samples
-
- double mean() { return sum/n; } // mean
-
- // standard deviation
- double stdDev() { return sqrt((sum2 - (sum*sum)/n)/(n-1));}
-
- double min() { return minVal; } // minimum
- double max() { return maxVal; } // maximum
-};
-
-//----------------------------------------------------------------------
-// Operation count updates
-//----------------------------------------------------------------------
-
-#ifdef ANN_PERF
- #define ANN_FLOP(n) {ann_Nfloat_ops += (n);}
- #define ANN_LEAF(n) {ann_Nvisit_lfs += (n);}
- #define ANN_SPL(n) {ann_Nvisit_spl += (n);}
- #define ANN_SHR(n) {ann_Nvisit_shr += (n);}
- #define ANN_PTS(n) {ann_Nvisit_pts += (n);}
- #define ANN_COORD(n) {ann_Ncoord_hts += (n);}
-#else
- #define ANN_FLOP(n)
- #define ANN_LEAF(n)
- #define ANN_SPL(n)
- #define ANN_SHR(n)
- #define ANN_PTS(n)
- #define ANN_COORD(n)
-#endif
-
-//----------------------------------------------------------------------
-// Performance statistics
-// The following data and routines are used for computing performance
-// statistics for nearest neighbor searching. Because these routines
-// can slow the code down, they can be activated and deactiviated by
-// defining the ANN_PERF variable, by compiling with the option:
-// -DANN_PERF
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// Global counters for performance measurement
-//
-// visit_lfs The number of leaf nodes visited in the
-// tree.
-//
-// visit_spl The number of splitting nodes visited in the
-// tree.
-//
-// visit_shr The number of shrinking nodes visited in the
-// tree.
-//
-// visit_pts The number of points visited in all the
-// leaf nodes visited. Equivalently, this
-// is the number of points for which distance
-// calculations are performed.
-//
-// coord_hts The number of times a coordinate of a
-// data point is accessed. This is generally
-// less than visit_pts*d if partial distance
-// calculation is used. This count is low
-// in the sense that if a coordinate is hit
-// many times in the same routine we may
-// count it only once.
-//
-// float_ops The number of floating point operations.
-// This includes all operations in the heap
-// as well as distance calculations to boxes.
-//
-// average_err The average error of each query (the
-// error of the reported point to the true
-// nearest neighbor). For k nearest neighbors
-// the error is computed k times.
-//
-// rank_err The rank error of each query (the difference
-// in the rank of the reported point and its
-// true rank).
-//
-// data_pts The number of data points. This is not
-// a counter, but used in stats computation.
-//----------------------------------------------------------------------
-
-extern int ann_Ndata_pts; // number of data points
-extern int ann_Nvisit_lfs; // number of leaf nodes visited
-extern int ann_Nvisit_spl; // number of splitting nodes visited
-extern int ann_Nvisit_shr; // number of shrinking nodes visited
-extern int ann_Nvisit_pts; // visited points for one query
-extern int ann_Ncoord_hts; // coordinate hits for one query
-extern int ann_Nfloat_ops; // floating ops for one query
-extern ANNsampStat ann_visit_lfs; // stats on leaf nodes visits
-extern ANNsampStat ann_visit_spl; // stats on splitting nodes visits
-extern ANNsampStat ann_visit_shr; // stats on shrinking nodes visits
-extern ANNsampStat ann_visit_nds; // stats on total nodes visits
-extern ANNsampStat ann_visit_pts; // stats on points visited
-extern ANNsampStat ann_coord_hts; // stats on coordinate hits
-extern ANNsampStat ann_float_ops; // stats on floating ops
-//----------------------------------------------------------------------
-// The following need to be part of the public interface, because
-// they are accessed outside the DLL in ann_test.cpp.
-//----------------------------------------------------------------------
-DLL_API extern ANNsampStat ann_average_err; // average error
-DLL_API extern ANNsampStat ann_rank_err; // rank error
-
-//----------------------------------------------------------------------
-// Declaration of externally accessible routines for statistics
-//----------------------------------------------------------------------
-
-DLL_API void annResetStats(int data_size); // reset stats for a set of queries
-
-DLL_API void annResetCounts(); // reset counts for one queries
-
-DLL_API void annUpdateStats(); // update stats with current counts
-
-DLL_API void annPrintStats(ANNbool validate); // print statistics for a run
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/ANNx.h b/geom_bottleneck/bottleneck/include/ANN/ANNx.h
deleted file mode 100644
index 0c9e190..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/ANNx.h
+++ /dev/null
@@ -1,127 +0,0 @@
-//----------------------------------------------------------------------
-// File: ANNx.h
-// Programmer: Sunil Arya and David Mount
-// Description: Internal include file for ANN
-// Last modified: 01/27/10 (Version 1.1.2)
-//
-// These declarations are of use in manipulating some of
-// the internal data objects appearing in ANN, but are not
-// needed for applications just using the nearest neighbor
-// search.
-//
-// Typical users of ANN should not need to access this file.
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2010 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Changed LO, HI, IN, OUT to ANN_LO, ANN_HI, etc.
-// Revision 1.1.2 01/27/10
-// Fixed minor compilation bugs for new versions of gcc
-//----------------------------------------------------------------------
-
-#ifndef ANNx_H
-#define ANNx_H
-
-#include <iomanip> // I/O manipulators
-#include <ANN/ANN.h> // ANN includes
-
-namespace geom_bt {
-
-//----------------------------------------------------------------------
-// Global constants and types
-//----------------------------------------------------------------------
-enum {ANN_LO=0, ANN_HI=1}; // splitting indices
-enum {ANN_IN=0, ANN_OUT=1}; // shrinking indices
- // what to do in case of error
-enum ANNerr {ANNwarn = 0, ANNabort = 1};
-
-//----------------------------------------------------------------------
-// Maximum number of points to visit
-// We have an option for terminating the search early if the
-// number of points visited exceeds some threshold. If the
-// threshold is 0 (its default) this means there is no limit
-// and the algorithm applies its normal termination condition.
-//----------------------------------------------------------------------
-
-extern int ANNmaxPtsVisited; // maximum number of pts visited
-extern int ANNptsVisited; // number of pts visited in search
-
-//----------------------------------------------------------------------
-// Global function declarations
-//----------------------------------------------------------------------
-
-void annError( // ANN error routine
- const char* msg, // error message
- ANNerr level); // level of error
-
-void annPrintPt( // print a point
- ANNpoint pt, // the point
- int dim, // the dimension
- std::ostream &out); // output stream
-
-void annAssignRect( // assign one rect to another
- int dim, // dimension (both must be same)
- ANNorthRect &dest, // destination (modified)
- const ANNorthRect &source); // source
-
-//----------------------------------------------------------------------
-// Orthogonal (axis aligned) halfspace
-// An orthogonal halfspace is represented by an integer cutting
-// dimension cd, coordinate cutting value, cv, and side, sd, which is
-// either +1 or -1. Our convention is that point q lies in the (closed)
-// halfspace if (q[cd] - cv)*sd >= 0.
-//----------------------------------------------------------------------
-
-class ANNorthHalfSpace {
-public:
- int cd; // cutting dimension
- ANNcoord cv; // cutting value
- int sd; // which side
-//
- ANNorthHalfSpace() // default constructor
- { cd = 0; cv = 0; sd = 0; }
-
- ANNorthHalfSpace( // basic constructor
- int cdd, // dimension of space
- ANNcoord cvv, // cutting value
- int sdd) // side
- { cd = cdd; cv = cvv; sd = sdd; }
-
- ANNbool in(ANNpoint q) const // is q inside halfspace?
- { return (ANNbool) ((q[cd] - cv)*sd >= 0); }
-
- ANNbool out(ANNpoint q) const // is q outside halfspace?
- { return (ANNbool) ((q[cd] - cv)*sd < 0); }
-
- ANNdist dist(ANNpoint q) const // (squared) distance from q
- { return (ANNdist) ANN_POW(q[cd] - cv); }
-
- void setLowerBound(int d, ANNpoint p)// set to lower bound at p[i]
- { cd = d; cv = p[d]; sd = +1; }
-
- void setUpperBound(int d, ANNpoint p)// set to upper bound at p[i]
- { cd = d; cv = p[d]; sd = -1; }
-
- void project(ANNpoint &q) // project q (modified) onto halfspace
- { if (out(q)) q[cd] = cv; }
-};
-
- // array of halfspaces
-typedef ANNorthHalfSpace *ANNorthHSArray;
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/bd_tree.h b/geom_bottleneck/bottleneck/include/ANN/bd_tree.h
deleted file mode 100644
index 38cecb7..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/bd_tree.h
+++ /dev/null
@@ -1,105 +0,0 @@
-//----------------------------------------------------------------------
-// File: bd_tree.h
-// Programmer: David Mount
-// Description: Declarations for standard bd-tree routines
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Changed IN, OUT to ANN_IN, ANN_OUT
-//----------------------------------------------------------------------
-
-#ifndef ANN_bd_tree_H
-#define ANN_bd_tree_H
-
-#include <ANN/ANNx.h> // all ANN includes
-#include "kd_tree.h" // kd-tree includes
-#include "def_debug_bt.h"
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// bd-tree shrinking node.
-// The main addition in the bd-tree is the shrinking node, which
-// is declared here.
-//
-// Shrinking nodes are defined by list of orthogonal halfspaces.
-// These halfspaces define a (possibly unbounded) orthogonal
-// rectangle. There are two children, in and out. Points that
-// lie within this rectangle are stored in the in-child, and the
-// other points are stored in the out-child.
-//
-// We use a list of orthogonal halfspaces rather than an
-// orthogonal rectangle object because typically the number of
-// sides of the shrinking box will be much smaller than the
-// worst case bound of 2*dim.
-//
-// BEWARE: Note that constructor just copies the pointer to the
-// bounding array, but the destructor deallocates it. This is
-// rather poor practice, but happens to be convenient. The list
-// is allocated in the bd-tree building procedure rbd_tree() just
-// prior to construction, and is used for no other purposes.
-//
-// WARNING: In the near neighbor searching code it is assumed that
-// the list of bounding halfspaces is irredundant, meaning that there
-// are no two distinct halfspaces in the list with the same outward
-// pointing normals.
-//----------------------------------------------------------------------
-
-class ANNbd_shrink : public ANNkd_node // splitting node of a kd-tree
-{
- int n_bnds; // number of bounding halfspaces
- ANNorthHSArray bnds; // list of bounding halfspaces
- ANNkd_ptr child[2]; // in and out children
-public:
- ANNbd_shrink( // constructor
- int nb, // number of bounding halfspaces
- ANNorthHSArray bds, // list of bounding halfspaces
- ANNkd_ptr ic=NULL, ANNkd_ptr oc=NULL) // children
- {
- n_bnds = nb; // cutting dimension
- bnds = bds; // assign bounds
- child[ANN_IN] = ic; // set children
- child[ANN_OUT] = oc;
- }
-
- ~ANNbd_shrink() // destructor
- {
- if (child[ANN_IN]!= NULL && child[ANN_IN]!= KD_TRIVIAL)
- delete child[ANN_IN];
- if (child[ANN_OUT]!= NULL&& child[ANN_OUT]!= KD_TRIVIAL)
- delete child[ANN_OUT];
- if (bnds != NULL)
- delete [] bnds; // delete bounds
- }
-
- virtual void getStats( // get tree statistics
- int dim, // dimension of space
- ANNkdStats &st, // statistics
- ANNorthRect &bnd_box); // bounding box
- virtual void print(int level, ostream &out);// print node
-#ifndef FOR_R_TDA
- virtual void dump(ostream &out); // dump node
-#endif
-
- virtual void ann_search(ANNdist); // standard search
- virtual void ann_pri_search(ANNdist); // priority search
- virtual void ann_FR_search(ANNdist); // fixed-radius search
-};
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_fix_rad_search.h b/geom_bottleneck/bottleneck/include/ANN/kd_fix_rad_search.h
deleted file mode 100644
index 36f9528..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_fix_rad_search.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_fix_rad_search.h
-// Programmer: Sunil Arya and David Mount
-// Description: Standard kd-tree fixed-radius kNN search
-// Last modified: 05/03/05 (Version 1.1)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 1.1 05/03/05
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef ANN_kd_fix_rad_search_H
-#define ANN_kd_fix_rad_search_H
-
-#include "kd_tree.h" // kd-tree declarations
-#include "kd_util.h" // kd-tree utilities
-#include "pr_queue_k.h" // k-element priority queue
-
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Global variables
-// These are active for the life of each call to
-// annRangeSearch(). They are set to save the number of
-// variables that need to be passed among the various search
-// procedures.
-//----------------------------------------------------------------------
-
-extern ANNpoint ANNkdFRQ; // query point (static copy)
-
-}
-#endif \ No newline at end of file
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_pr_search.h b/geom_bottleneck/bottleneck/include/ANN/kd_pr_search.h
deleted file mode 100644
index 1f4c4fc..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_pr_search.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_pr_search.h
-// Programmer: Sunil Arya and David Mount
-// Description: Priority kd-tree search
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef ANN_kd_pr_search_H
-#define ANN_kd_pr_search_H
-
-#include "kd_tree.h" // kd-tree declarations
-#include "kd_util.h" // kd-tree utilities
-#include "pr_queue.h" // priority queue declarations
-#include "pr_queue_k.h" // k-element priority queue
-
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Global variables
-// Active for the life of each call to Appx_Near_Neigh() or
-// Appx_k_Near_Neigh().
-//----------------------------------------------------------------------
-
-extern double ANNprEps; // the error bound
-extern int ANNprDim; // dimension of space
-extern ANNpoint ANNprQ; // query point
-extern double ANNprMaxErr; // max tolerable squared error
-extern ANNpointArray ANNprPts; // the points
-extern ANNpr_queue *ANNprBoxPQ; // priority queue for boxes
-extern ANNmin_k *ANNprPointMK; // set of k closest points
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_search.h b/geom_bottleneck/bottleneck/include/ANN/kd_search.h
deleted file mode 100644
index 7491779..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_search.h
+++ /dev/null
@@ -1,50 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_search.h
-// Programmer: Sunil Arya and David Mount
-// Description: Standard kd-tree search
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef ANN_kd_search_H
-#define ANN_kd_search_H
-
-#include "kd_tree.h" // kd-tree declarations
-#include "kd_util.h" // kd-tree utilities
-#include "pr_queue_k.h" // k-element priority queue
-
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// More global variables
-// These are active for the life of each call to annkSearch(). They
-// are set to save the number of variables that need to be passed
-// among the various search procedures.
-//----------------------------------------------------------------------
-
-extern int ANNkdDim; // dimension of space (static copy)
-extern ANNpoint ANNkdQ; // query point (static copy)
-extern double ANNkdMaxErr; // max tolerable squared error
-extern ANNpointArray ANNkdPts; // the points (static copy)
-extern ANNmin_k *ANNkdPointMK; // set of k closest points
-extern int ANNptsVisited; // number of points visited
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_split.h b/geom_bottleneck/bottleneck/include/ANN/kd_split.h
deleted file mode 100644
index 62533a1..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_split.h
+++ /dev/null
@@ -1,123 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_split.h
-// Programmer: Sunil Arya and David Mount
-// Description: Methods for splitting kd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef ANN_KD_SPLIT_H
-#define ANN_KD_SPLIT_H
-
-#include "kd_tree.h" // kd-tree definitions
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// External entry points
-// These are all splitting procedures for kd-trees.
-//----------------------------------------------------------------------
-
-void kd_split( // standard (optimized) kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-void midpt_split( // midpoint kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-void sl_midpt_split( // sliding midpoint kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-void fair_split( // fair-split kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-void sl_fair_split( // sliding fair-split kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-////////////////////////////////////////////////////////////////////////////////
-//
-void kd_split_wd( // standard (optimized) kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx); // index of cutting point (returned)
-
-void midpt_split_wd( // midpoint kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx); // index of cutting point (returned)
-
-void sl_midpt_split_wd( // sliding midpoint kd-splitter
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx); // index of cutting point (returned)
-
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_tree.h b/geom_bottleneck/bottleneck/include/ANN/kd_tree.h
deleted file mode 100644
index a1e53e5..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_tree.h
+++ /dev/null
@@ -1,260 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_tree.h
-// Programmer: Sunil Arya and David Mount
-// Description: Declarations for standard kd-tree routines
-// Last modified: 05/03/05 (Version 1.1)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.1 05/03/05
-// Added fixed radius kNN search
-// --------------------------------------------------------------------
-// 2015 - modified by A. Nigmetov to support deletion of points
-//----------------------------------------------------------------------
-
-#ifndef ANN_kd_tree_H
-#define ANN_kd_tree_H
-
-#include <utility> // for std::pair
-#include <ANN/ANNx.h> // all ANN includes
-#include "def_debug_bt.h"
-
-using namespace std; // make std:: available
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Generic kd-tree node
-//
-// Nodes in kd-trees are of two types, splitting nodes which contain
-// splitting information (a splitting hyperplane orthogonal to one
-// of the coordinate axes) and leaf nodes which contain point
-// information (an array of points stored in a bucket). This is
-// handled by making a generic class kd_node, which is essentially an
-// empty shell, and then deriving the leaf and splitting nodes from
-// this.
-//----------------------------------------------------------------------
-//class ANNkd_node;
-class ANNkd_split;
-
-//typedef std::pair<ANNidx, ANNkd_node*> ANNreplaceSearchRes;
-
-class ANNkd_node{ // generic kd-tree node (empty shell)
-protected:
- int actual_num_points; //
- ANNkd_split* parent;
-public:
- ANNkd_split* getParent() const { return parent; }
- void setParent(ANNkd_split* par) { parent = par; }
- int getNumPoints() const { return actual_num_points; }
- void setNumPoints(int n) { assert(n >=0 ); actual_num_points = n; }
- void decNumPoints() { assert(actual_num_points > 0); actual_num_points--; }
- virtual ~ANNkd_node() {} // virtual distroyer
-
- virtual void ann_search(ANNdist) = 0; // tree search
- virtual void ann_pri_search(ANNdist) = 0; // priority search
- virtual void ann_FR_search(ANNdist) = 0; // fixed-radius search
-
- virtual void getStats( // get tree statistics
- int dim, // dimension of space
- ANNkdStats &st, // statistics
- ANNorthRect &bnd_box) = 0; // bounding box
- // print node
- virtual void print(int level, ostream &out) = 0;
-#ifndef FOR_R_TDA
- virtual void dump(ostream &out) = 0; // dump node
-#endif
-
- friend class ANNkd_tree; // allow kd-tree to access us
-
- ////////////////////////////////////////////////////////////////////////
- // deletion
- virtual void delete_point(const int point_idx) {}
- // range search
- virtual void range_search(const ANNorthRect& region, // query region
- int ANNkdDim, // dimension of points,
- ANNpointArray ANNkdPts, // array of points
- ANNorthRect& bnd_box, // bounding box of the current node,
- // comes precomputed from the caller
- std::vector<size_t>& pointIdices) {} // indices of points are returned in this vector
- virtual void range_search_add(std::vector<size_t>& pointIdices) {} // add all points to pointIdices
-};
-
-
-
-//----------------------------------------------------------------------
-// kd-splitting function:
-// kd_splitter is a pointer to a splitting routine for preprocessing.
-// Different splitting procedures result in different strategies
-// for building the tree.
-//----------------------------------------------------------------------
-typedef void (*ANNkd_splitter)( // splitting routine for kd-trees
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo); // num of points on low side (returned)
-
-//----------------------------------------------------------------------
-// Leaf kd-tree node
-// Leaf nodes of the kd-tree store the set of points associated
-// with this bucket, stored as an array of point indices. These
-// are indices in the array points, which resides with the
-// root of the kd-tree. We also store the number of points
-// that reside in this bucket.
-//----------------------------------------------------------------------
-
-class ANNkd_leaf: public ANNkd_node // leaf node for kd-tree
-{
- int n_pts;
- ANNidxArray bkt; // bucket of points
-public:
- ANNkd_leaf( // constructor
- int n, // number of points
- ANNidxArray b) : // bucket
- n_pts(n),
- bkt(b)
- {
- setNumPoints(n);
- parent = NULL;
- }
-
- ~ANNkd_leaf() { } // destructor (none)
-
- virtual void getStats( // get tree statistics
- int dim, // dimension of space
- ANNkdStats &st, // statistics
- ANNorthRect &bnd_box); // bounding box
- virtual void print(int level, ostream &out);// print node
-#ifndef FOR_R_TDA
- virtual void dump(ostream &out); // dump node
-#endif
-
- virtual void ann_search(ANNdist); // standard search
- virtual void ann_pri_search(ANNdist); // priority search
- virtual void ann_FR_search(ANNdist); // fixed-radius search
- // deletion
- void delete_point(const int point_idx, const bool killYourself);
- // range search
- virtual void range_search(const ANNorthRect& region, // query region
- int ANNkdDim, // dimension of points,
- ANNpointArray ANNkdPts, // array of points
- ANNorthRect& bnd_box, // bounding box of the current node,
- // comes precomputed from the caller
- std::vector<size_t>& pointIdices); // indices of points are returned in this vector
- virtual void range_search_add(std::vector<size_t>& pointIdices); // add all points to pointIdices
-};
-
-//----------------------------------------------------------------------
-// KD_TRIVIAL is a special pointer to an empty leaf node. Since
-// some splitting rules generate many (more than 50%) trivial
-// leaves, we use this one shared node to save space.
-//
-// The pointer is initialized to NULL, but whenever a kd-tree is
-// created, we allocate this node, if it has not already been
-// allocated. This node is *never* deallocated, so it produces
-// a small memory leak.
-//----------------------------------------------------------------------
-
-extern ANNkd_leaf *KD_TRIVIAL; // trivial (empty) leaf node
-
-//----------------------------------------------------------------------
-// kd-tree splitting node.
-// Splitting nodes contain a cutting dimension and a cutting value.
-// These indicate the axis-parellel plane which subdivide the
-// box for this node. The extent of the bounding box along the
-// cutting dimension is maintained (this is used to speed up point
-// to box distance calculations) [we do not store the entire bounding
-// box since this may be wasteful of space in high dimensions].
-// We also store pointers to the 2 children.
-//----------------------------------------------------------------------
-
-class ANNkd_split : public ANNkd_node // splitting node of a kd-tree
-{
- int cut_dim; // dim orthogonal to cutting plane
- ANNcoord cut_val; // location of cutting plane
- ANNcoord cd_bnds[2]; // lower and upper bounds of
- // rectangle along cut_dim
- ANNkd_ptr child[2]; // left and right children
-public:
- ANNkd_split( // constructor
- int cd, // cutting dimension
- ANNcoord cv, // cutting value
- ANNcoord lv, ANNcoord hv, // low and high values
- ANNkd_ptr lc=NULL, ANNkd_ptr hc=NULL) // children
- {
- cut_dim = cd; // cutting dimension
- cut_val = cv; // cutting value
- cd_bnds[ANN_LO] = lv; // lower bound for rectangle
- cd_bnds[ANN_HI] = hv; // upper bound for rectangle
- child[ANN_LO] = lc; // left child
- child[ANN_HI] = hc; // right child
- parent = NULL;
- }
-
-
- ~ANNkd_split() // destructor
- {
- if (child[ANN_LO]!= NULL && child[ANN_LO]!= KD_TRIVIAL)
- delete child[ANN_LO];
- if (child[ANN_HI]!= NULL && child[ANN_HI]!= KD_TRIVIAL)
- delete child[ANN_HI];
- }
-
- virtual void getStats( // get tree statistics
- int dim, // dimension of space
- ANNkdStats &st, // statistics
- ANNorthRect &bnd_box); // bounding box
- virtual void print(int level, ostream &out);// print node
-#ifndef FOR_R_TDA
- virtual void dump(ostream &out); // dump node
-#endif
-
- virtual void ann_search(ANNdist); // standard search
- virtual void ann_pri_search(ANNdist); // priority search
- virtual void ann_FR_search(ANNdist); // fixed-radius search
-
- ///////
- void delete_leaf(ANNkd_leaf* childToDelete); // set the leaf to KD_TRIVIAL
- // range search
- virtual void range_search(const ANNorthRect& region, // query region
- int ANNkdDim, // dimension of points,
- ANNpointArray ANNkdPts, // array of points
- ANNorthRect& bnd_box, // bounding box of the current node,
- // comes precomputed from the caller
- std::vector<size_t>& pointIdices); // indices of points are returned in this vector
- virtual void range_search_add(std::vector<size_t>& pointIdices); // add all points to pointIdices
-};
-
-//----------------------------------------------------------------------
-// External entry points
-//----------------------------------------------------------------------
-
-ANNkd_ptr rkd_tree( // recursive construction of kd-tree
- ANNpointArray pa, // point array (unaltered)
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- int bsp, // bucket space
- ANNorthRect &bnd_box, // bounding box for current node
- ANNkd_splitter splitter, // splitting routine
- vector<ANNkd_leaf*>* ppointToLeafVec);
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/kd_util.h b/geom_bottleneck/bottleneck/include/ANN/kd_util.h
deleted file mode 100644
index fa9f554..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/kd_util.h
+++ /dev/null
@@ -1,126 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_util.h
-// Programmer: Sunil Arya and David Mount
-// Description: Common utilities for kd- trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef ANN_kd_util_H
-#define ANN_kd_util_H
-
-#include "kd_tree.h" // kd-tree declarations
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// externally accessible functions
-//----------------------------------------------------------------------
-
-double annAspectRatio( // compute aspect ratio of box
- int dim, // dimension
- const ANNorthRect &bnd_box); // bounding cube
-
-void annEnclRect( // compute smallest enclosing rectangle
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension
- ANNorthRect &bnds); // bounding cube (returned)
-
-void annEnclCube( // compute smallest enclosing cube
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension
- ANNorthRect &bnds); // bounding cube (returned)
-
-ANNdist annBoxDistance( // compute distance from point to box
- const ANNpoint q, // the point
- const ANNpoint lo, // low point of box
- const ANNpoint hi, // high point of box
- int dim); // dimension of space
-
-ANNcoord annSpread( // compute point spread along dimension
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d); // dimension to check
-
-void annMinMax( // compute min and max coordinates along dim
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension to check
- ANNcoord& min, // minimum value (returned)
- ANNcoord& max); // maximum value (returned)
-
-int annMaxSpread( // compute dimension of max spread
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim); // dimension of space
-
-void annMedianSplit( // split points along median value
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord &cv, // cutting value
- int n_lo); // split into n_lo and n-n_lo
-
-void annPlaneSplit( // split points by a plane
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord cv, // cutting value
- int &br1, // first break (values < cv)
- int &br2); // second break (values == cv)
-
-void annBoxSplit( // split points by a box
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension of space
- ANNorthRect &box, // the box
- int &n_in); // number of points inside (returned)
-
-int annSplitBalance( // determine balance factor of a split
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord cv); // cutting value
-
-void annBox2Bnds( // convert inner box to bounds
- const ANNorthRect &inner_box, // inner box
- const ANNorthRect &bnd_box, // enclosing box
- int dim, // dimension of space
- int &n_bnds, // number of bounds (returned)
- ANNorthHSArray &bnds); // bounds array (returned)
-
-void annBnds2Box( // convert bounds to inner box
- const ANNorthRect &bnd_box, // enclosing box
- int dim, // dimension of space
- int n_bnds, // number of bounds
- ANNorthHSArray bnds, // bounds array
- ANNorthRect &inner_box); // inner box (returned)
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/ANN/pr_queue.h b/geom_bottleneck/bottleneck/include/ANN/pr_queue.h
deleted file mode 100644
index f938a73..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/pr_queue.h
+++ /dev/null
@@ -1,127 +0,0 @@
-//----------------------------------------------------------------------
-// File: pr_queue.h
-// Programmer: Sunil Arya and David Mount
-// Description: Include file for priority queue and related
-// structures.
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef PR_QUEUE_H
-#define PR_QUEUE_H
-
-#include <ANN/ANNx.h> // all ANN includes
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Basic types.
-//----------------------------------------------------------------------
-typedef void *PQinfo; // info field is generic pointer
-typedef ANNdist PQkey; // key field is distance
-
-//----------------------------------------------------------------------
-// Priority queue
-// A priority queue is a list of items, along with associated
-// priorities. The basic operations are insert and extract_minimum.
-//
-// The priority queue is maintained using a standard binary heap.
-// (Implementation note: Indexing is performed from [1..max] rather
-// than the C standard of [0..max-1]. This simplifies parent/child
-// computations.) User information consists of a void pointer,
-// and the user is responsible for casting this quantity into whatever
-// useful form is desired.
-//
-// Because the priority queue is so central to the efficiency of
-// query processing, all the code is inline.
-//----------------------------------------------------------------------
-
-class ANNpr_queue {
-
- struct pq_node { // node in priority queue
- PQkey key; // key value
- PQinfo info; // info field
- };
- int n; // number of items in queue
- int max_size; // maximum queue size
- pq_node *pq; // the priority queue (array of nodes)
-
-public:
- ANNpr_queue(int max) // constructor (given max size)
- {
- n = 0; // initially empty
- max_size = max; // maximum number of items
- pq = new pq_node[max+1]; // queue is array [1..max] of nodes
- }
-
- ~ANNpr_queue() // destructor
- { delete [] pq; }
-
- ANNbool empty() // is queue empty?
- { if (n==0) return ANNtrue; else return ANNfalse; }
-
- ANNbool non_empty() // is queue nonempty?
- { if (n==0) return ANNfalse; else return ANNtrue; }
-
- void reset() // make existing queue empty
- { n = 0; }
-
- inline void insert( // insert item (inlined for speed)
- PQkey kv, // key value
- PQinfo inf) // item info
- {
- if (++n > max_size) annError("Priority queue overflow.", ANNabort);
- register int r = n;
- while (r > 1) { // sift up new item
- register int p = r/2;
- ANN_FLOP(1) // increment floating ops
- if (pq[p].key <= kv) // in proper order
- break;
- pq[r] = pq[p]; // else swap with parent
- r = p;
- }
- pq[r].key = kv; // insert new item at final location
- pq[r].info = inf;
- }
-
- inline void extr_min( // extract minimum (inlined for speed)
- PQkey &kv, // key (returned)
- PQinfo &inf) // item info (returned)
- {
- kv = pq[1].key; // key of min item
- inf = pq[1].info; // information of min item
- register PQkey kn = pq[n--].key;// last item in queue
- register int p = 1; // p points to item out of position
- register int r = p<<1; // left child of p
- while (r <= n) { // while r is still within the heap
- ANN_FLOP(2) // increment floating ops
- // set r to smaller child of p
- if (r < n && pq[r].key > pq[r+1].key) r++;
- if (kn <= pq[r].key) // in proper order
- break;
- pq[p] = pq[r]; // else swap with child
- p = r; // advance pointers
- r = p<<1;
- }
- pq[p] = pq[n+1]; // insert last item in proper place
- }
-};
-
-}
-#endif \ No newline at end of file
diff --git a/geom_bottleneck/bottleneck/include/ANN/pr_queue_k.h b/geom_bottleneck/bottleneck/include/ANN/pr_queue_k.h
deleted file mode 100644
index 133a766..0000000
--- a/geom_bottleneck/bottleneck/include/ANN/pr_queue_k.h
+++ /dev/null
@@ -1,120 +0,0 @@
-//----------------------------------------------------------------------
-// File: pr_queue_k.h
-// Programmer: Sunil Arya and David Mount
-// Description: Include file for priority queue with k items.
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#ifndef PR_QUEUE_K_H
-#define PR_QUEUE_K_H
-
-#include <ANN/ANNx.h> // all ANN includes
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Basic types
-//----------------------------------------------------------------------
-typedef ANNdist PQKkey; // key field is distance
-typedef int PQKinfo; // info field is int
-
-//----------------------------------------------------------------------
-// Constants
-// The NULL key value is used to initialize the priority queue, and
-// so it should be larger than any valid distance, so that it will
-// be replaced as legal distance values are inserted. The NULL
-// info value must be a nonvalid array index, we use ANN_NULL_IDX,
-// which is guaranteed to be negative.
-//----------------------------------------------------------------------
-
-const PQKkey PQ_NULL_KEY = ANN_DIST_INF; // nonexistent key value
-const PQKinfo PQ_NULL_INFO = ANN_NULL_IDX; // nonexistent info value
-
-//----------------------------------------------------------------------
-// ANNmin_k
-// An ANNmin_k structure is one which maintains the smallest
-// k values (of type PQKkey) and associated information (of type
-// PQKinfo). The special info and key values PQ_NULL_INFO and
-// PQ_NULL_KEY means that thise entry is empty.
-//
-// It is currently implemented using an array with k items.
-// Items are stored in increasing sorted order, and insertions
-// are made through standard insertion sort. (This is quite
-// inefficient, but current applications call for small values
-// of k and relatively few insertions.)
-//
-// Note that the list contains k+1 entries, but the last entry
-// is used as a simple placeholder and is otherwise ignored.
-//----------------------------------------------------------------------
-
-class ANNmin_k {
- struct mk_node { // node in min_k structure
- PQKkey key; // key value
- PQKinfo info; // info field (user defined)
- };
-
- int k; // max number of keys to store
- int n; // number of keys currently active
- mk_node *mk; // the list itself
-
-public:
- ANNmin_k(int max) // constructor (given max size)
- {
- n = 0; // initially no items
- k = max; // maximum number of items
- mk = new mk_node[max+1]; // sorted array of keys
- }
-
- ~ANNmin_k() // destructor
- { delete [] mk; }
-
- PQKkey ANNmin_key() // return minimum key
- { return (n > 0 ? mk[0].key : PQ_NULL_KEY); }
-
- PQKkey max_key() // return maximum key
- { return (n == k ? mk[k-1].key : PQ_NULL_KEY); }
-
- PQKkey ith_smallest_key(int i) // ith smallest key (i in [0..n-1])
- { return (i < n ? mk[i].key : PQ_NULL_KEY); }
-
- PQKinfo ith_smallest_info(int i) // info for ith smallest (i in [0..n-1])
- { return (i < n ? mk[i].info : PQ_NULL_INFO); }
-
- inline void insert( // insert item (inlined for speed)
- PQKkey kv, // key value
- PQKinfo inf) // item info
- {
- register int i;
- // slide larger values up
- for (i = n; i > 0; i--) {
- if (mk[i-1].key > kv)
- mk[i] = mk[i-1];
- else
- break;
- }
- mk[i].key = kv; // store element here
- mk[i].info = inf;
- if (n < k) n++; // increment number of items
- ANN_FLOP(k-i+1) // increment floating ops
- }
-};
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/basic_defs_bt.h b/geom_bottleneck/bottleneck/include/basic_defs_bt.h
deleted file mode 100644
index 5d6d264..0000000
--- a/geom_bottleneck/bottleneck/include/basic_defs_bt.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#ifndef BASIC_DEFS_BT_H
-#define BASIC_DEFS_BT_H
-
-#ifdef _WIN32
-#include <ciso646>
-#endif
-
-#include <vector>
-#include <stdexcept>
-#include <cmath>
-#include <cstddef>
-#include <unordered_map>
-#include <unordered_set>
-#include <string>
-#include <cassert>
-
-#include "def_debug_bt.h"
-
-#ifndef FOR_R_TDA
-#include <iostream>
-#endif
-
-
-namespace geom_bt {
-
-typedef double CoordinateType;
-typedef int IdType;
-constexpr IdType MinValidId = 10;
-
-struct Point {
- CoordinateType x, y;
- bool operator==(const Point& other) const;
- bool operator!=(const Point& other) const;
- Point(CoordinateType ax, CoordinateType ay) : x(ax), y(ay) {}
- Point() : x(0.0), y(0.0) {}
-#ifndef FOR_R_TDA
- friend std::ostream& operator<<(std::ostream& output, const Point p);
-#endif
-};
-
-struct DiagramPoint
-{
- // Points above the diagonal have type NORMAL
- // Projections onto the diagonal have type DIAG
- // for DIAG points only x-coordinate is relevant
- // to-do: add getters/setters, checks in constructors, etc
- enum Type { NORMAL, DIAG};
- // data members
-private:
- CoordinateType x, y;
-public:
- Type type;
- IdType id;
- // operators, constructors
- bool operator==(const DiagramPoint& other) const;
- bool operator!=(const DiagramPoint& other) const;
- DiagramPoint(CoordinateType xx, CoordinateType yy, Type ttype, IdType uid);
- bool isDiagonal(void) const { return type == DIAG; }
- bool isNormal(void) const { return type == NORMAL; }
- CoordinateType inline getRealX() const // return the x-coord
- {
- return x;
- //if (DiagramPoint::NORMAL == type)
- //return x;
- //else
- //return 0.5 * ( x + y);
- }
-
- CoordinateType inline getRealY() const // return the y-coord
- {
- return y;
- //if (DiagramPoint::NORMAL == type)
- //return y;
- //else
- //return 0.5 * ( x + y);
- }
-
-#ifndef FOR_R_TDA
- friend std::ostream& operator<<(std::ostream& output, const DiagramPoint p);
-#endif
-};
-
-struct PointHash {
- size_t operator()(const Point& p) const{
- return std::hash<CoordinateType>()(p.x)^std::hash<CoordinateType>()(p.y);
- }
-};
-
-struct DiagramPointHash {
- size_t operator()(const DiagramPoint& p) const{
- //return std::hash<CoordinateType>()(p.x)^std::hash<CoordinateType>()(p.y)^std::hash<bool>()(p.type == DiagramPoint::NORMAL);
- assert(p.id >= MinValidId);
- return std::hash<int>()(p.id);
- }
-};
-
-CoordinateType sqrDist(const Point& a, const Point& b);
-CoordinateType dist(const Point& a, const Point& b);
-CoordinateType distLInf(const DiagramPoint& a, const DiagramPoint& b);
-
-typedef std::unordered_set<Point, PointHash> PointSet;
-
-class DiagramPointSet {
-public:
- void insert(const DiagramPoint p);
- void erase(const DiagramPoint& p, bool doCheck = true); // if doCheck, erasing non-existing elements causes assert
- void erase(const std::unordered_set<DiagramPoint, DiagramPointHash>::const_iterator it);
- void removeDiagonalPoints();
- size_t size() const;
- void reserve(const size_t newSize);
- void clear();
- bool empty() const;
- bool hasElement(const DiagramPoint& p) const;
- std::unordered_set<DiagramPoint, DiagramPointHash>::iterator find(const DiagramPoint& p) { return points.find(p); };
- std::unordered_set<DiagramPoint, DiagramPointHash>::const_iterator find(const DiagramPoint& p) const { return points.find(p); };
- std::unordered_set<DiagramPoint, DiagramPointHash>::iterator begin() { return points.begin(); };
- std::unordered_set<DiagramPoint, DiagramPointHash>::iterator end() { return points.end(); }
- std::unordered_set<DiagramPoint, DiagramPointHash>::const_iterator cbegin() const { return points.cbegin(); }
- std::unordered_set<DiagramPoint, DiagramPointHash>::const_iterator cend() const { return points.cend(); }
-#ifndef FOR_R_TDA
- friend std::ostream& operator<<(std::ostream& output, const DiagramPointSet& ps);
-#endif
- friend void addProjections(DiagramPointSet& A, DiagramPointSet& B);
- template<class PairIterator> DiagramPointSet(PairIterator first, PairIterator last);
- template<class PairIterator> void fillIn(PairIterator first, PairIterator last);
- // default ctor, empty diagram
- DiagramPointSet(IdType minId = MinValidId + 1) : maxId(minId + 1) {};
- IdType nextId() { return maxId + 1; }
-private:
- bool isLinked { false };
- IdType maxId {MinValidId + 1};
- std::unordered_set<DiagramPoint, DiagramPointHash> points;
-};
-
-template<typename DiagPointContainer>
-CoordinateType getFurthestDistance3Approx(DiagPointContainer& A, DiagPointContainer& B)
-{
- CoordinateType result { 0.0 };
- DiagramPoint begA = *(A.begin());
- DiagramPoint optB = *(B.begin());
- for(const auto& pointB : B) {
- if (distLInf(begA, pointB) > result) {
- result = distLInf(begA, pointB);
- optB = pointB;
- }
- }
- for(const auto& pointA : A) {
- if (distLInf(pointA, optB) > result) {
- result = distLInf(pointA, optB);
- }
- }
- return result;
-}
-
-template<class PairIterator>
-void DiagramPointSet::fillIn(PairIterator start, PairIterator end)
-{
- isLinked = false;
- clear();
- IdType uniqueId = MinValidId + 1;
- for(auto iter = start; iter != end; ++iter) {
- insert(DiagramPoint(iter->first, iter->second, DiagramPoint::NORMAL, uniqueId++));
- }
-}
-
-template<class PairIterator>
-DiagramPointSet::DiagramPointSet(PairIterator start, PairIterator end)
-{
- fillIn(start, end);
-}
-
-// preprocess diagrams A and B by adding projections onto diagonal of points of
-// A to B and vice versa. NB: ids of points will be changed!
-void addProjections(DiagramPointSet& A, DiagramPointSet& B);
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/bottleneck.h b/geom_bottleneck/bottleneck/include/bottleneck.h
deleted file mode 100644
index 75c902f..0000000
--- a/geom_bottleneck/bottleneck/include/bottleneck.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef BOTTLENECK_H
-#define BOTTLENECK_H
-
-
-//#include <iostream>
-#include <fstream>
-#include <vector>
-#include <algorithm>
-#include <limits>
-#include <random>
-
-#include "basic_defs_bt.h"
-#include "bound_match.h"
-//#include "test_neighb_oracle.h"
-//#include "test_dist_calc.h"
-
-namespace geom_bt {
-typedef std::pair<double, std::pair<size_t, size_t>> DistVerticesPair;
-
-// functions taking DiagramPointSet as input.
-// ATTENTION: parameters A and B (diagrams) will be changed after the call
-// (projections added).
-
-// return the interval (distMin, distMax) such that:
-// a) actual bottleneck distance between A and B is contained in the interval
-// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-std::pair<double, double> bottleneckDistApproxInterval(DiagramPointSet& A, DiagramPointSet& B, const double epsilon);
-
-
-// heuristic (sample diagram to estimate the distance)
-std::pair<double, double> bottleneckDistApproxIntervalHeur(DiagramPointSet& A, DiagramPointSet& B, const double epsilon);
-
-// get approximate distance,
-// see bottleneckDistApproxInterval
-double bottleneckDistApprox(DiagramPointSet& A, DiagramPointSet& B, const double epsilon);
-
-// get exact bottleneck distance,
-double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int decPrecision);
-
-// get exact bottleneck distance,
-double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B);
-
-
-// functions taking containers as input
-// template parameter PairContainer must be a container of pairs of real
-// numbers (pair.first = x-coordinate, pair.second = y-coordinate)
-// PairContainer class must support iteration of the form
-// for(it = pairContainer.begin(); it != pairContainer.end(); ++it)
-
-// return the interval (distMin, distMax) such that:
-// a) actual bottleneck distance between A and B is contained in the interval
-// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-template<class PairContainer>
-std::pair<double, double> bottleneckDistApproxInterval(PairContainer& A, PairContainer& B, const double epsilon)
-{
- DiagramPointSet a(A.begin(), A.end());
- DiagramPointSet b(B.begin(), B.end());
- return bottleneckDistApproxInterval(a, b, epsilon);
-}
-
-
-template<class PairContainer>
-double bottleneckDistApproxHeur(PairContainer& A, PairContainer& B, const double epsilon)
-{
- DiagramPointSet a(A.begin(), A.end());
- DiagramPointSet b(B.begin(), B.end());
- std::pair<double, double> resPair = bottleneckDistApproxIntervalHeur(a, b, epsilon);
- return resPair.second;
-}
-
-
-// get approximate distance,
-// see bottleneckDistApproxInterval
-template<class PairContainer>
-double bottleneckDistApprox(PairContainer& A, PairContainer& B, const double epsilon)
-{
- DiagramPointSet a(A.begin(), A.end());
- DiagramPointSet b(B.begin(), B.end());
- return bottleneckDistApprox(a, b, epsilon);
-}
-
-// get exact bottleneck distance,
-template<class PairContainer>
-double bottleneckDistExact(PairContainer& A, PairContainer& B)
-{
- DiagramPointSet a(A.begin(), A.end());
- DiagramPointSet b(B.begin(), B.end());
- return bottleneckDistExact(a, b, 14);
-}
-
-
-// get exact bottleneck distance,
-template<class PairContainer>
-double bottleneckDistExact(PairContainer& A, PairContainer& B, const int decPrecision)
-{
- DiagramPointSet a(A.begin(), A.end());
- DiagramPointSet b(B.begin(), B.end());
- return bottleneckDistExact(a, b, decPrecision);
-}
-
-// fill in result with points from file fname
-// return false if file can't be opened
-// or error occurred while reading
-// decPrecision is the maximal decimal precision in the input,
-// it is zero if all coordinates in the input are integers
-bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double>>& result, int& decPrecision);
-// wrapper for standard string
-bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<double, double>>& result, int& decPrecision);
-
-
-// these two functions are now just wrappers for the previous ones,
-// in case someone needs them; decPrecision is ignored
-bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double>>& result);
-// wrapper for standard string
-bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<double, double>>& result);
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/bound_match.h b/geom_bottleneck/bottleneck/include/bound_match.h
deleted file mode 100644
index 2e2d369..0000000
--- a/geom_bottleneck/bottleneck/include/bound_match.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef BOUND_MATCH_H
-#define BOUND_MATCH_H
-
-#include <unordered_map>
-
-#include "basic_defs_bt.h"
-#include "neighb_oracle.h"
-
-
-namespace geom_bt {
-typedef std::vector<DiagramPoint> Path;
-
-class Matching {
-public:
- Matching(const DiagramPointSet& AA, const DiagramPointSet& BB) : A(AA), B(BB) {};
- DiagramPointSet getExposedVertices(bool forA = true) const ;
- bool isExposed(const DiagramPoint& p) const;
- void getAllAdjacentVertices(const DiagramPointSet& setIn, DiagramPointSet& setOut, bool forA = true) const;
- void increase(const Path& augmentingPath);
- void checkAugPath(const Path& augPath) const;
- bool getMatchedVertex(const DiagramPoint& p, DiagramPoint& result) const;
- bool isPerfect() const;
- void trimMatching(const double newThreshold);
- friend std::ostream& operator<<(std::ostream& output, const Matching& m);
-private:
- DiagramPointSet A;
- DiagramPointSet B;
- std::unordered_map<DiagramPoint, DiagramPoint, DiagramPointHash> AToB, BToA;
- void matchVertices(const DiagramPoint& pA, const DiagramPoint& pB);
- void sanityCheck() const;
-};
-
-
-
-class BoundMatchOracle {
-public:
- BoundMatchOracle(DiagramPointSet psA, DiagramPointSet psB, double dEps, bool useRS = true);
- bool isMatchLess(double r);
- void setInnerOracle(NeighbOracleAbstract* innerOracle) { neighbOracle = innerOracle; }
- bool buildMatchingForThreshold(const double r);
- ~BoundMatchOracle();
-private:
- DiagramPointSet A, B;
- Matching M;
- void printLayerGraph(void);
- void buildLayerGraph(double r);
- void buildLayerOracles(double r);
- bool buildAugmentingPath(const DiagramPoint startVertex, Path& result);
- void removeFromLayer(const DiagramPoint& p, const int layerIdx);
- NeighbOracleAbstract* neighbOracle;
- bool augPathExist;
- std::vector<DiagramPointSet> layerGraph;
- std::vector<NeighbOracle*> layerOracles;
- double distEpsilon;
- bool useRangeSearch;
- double prevQueryValue;
-};
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/include/def_debug_bt.h b/geom_bottleneck/bottleneck/include/def_debug_bt.h
deleted file mode 100644
index 888ded6..0000000
--- a/geom_bottleneck/bottleneck/include/def_debug_bt.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DEF_DEBUG_BT_H
-#define DEF_DEBUG_BT_H
-
-//#define DEBUG_BOUND_MATCH
-//#define DEBUG_NEIGHBOUR_ORACLE
-//#define DEBUG_MATCHING
-//#define DEBUG_AUCTION
-// This symbol should be defined only in the version
-// for R package TDA, to comply with some CRAN rules
-// like no usage of cout, cerr, cin, exit, etc.
-//#define FOR_R_TDA
-//#define VERBOSE_BOTTLENECK
-
-#endif
diff --git a/geom_bottleneck/bottleneck/include/neighb_oracle.h b/geom_bottleneck/bottleneck/include/neighb_oracle.h
deleted file mode 100644
index f6f78b1..0000000
--- a/geom_bottleneck/bottleneck/include/neighb_oracle.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef NEIGHB_ORACLE_H
-#define NEIGHB_ORACLE_H
-
-#include <unordered_map>
-#include "basic_defs_bt.h"
-#include <ANN/ANN.h>
-
-namespace geom_bt {
-class NeighbOracleAbstract{
-public:
- virtual void deletePoint(const DiagramPoint& p) = 0;
- virtual void rebuild(const DiagramPointSet& S, double rr) = 0;
- // return true, if r-neighbour of q exists in pointSet,
- // false otherwise.
- // the neighbour is returned in result
- virtual bool getNeighbour(const DiagramPoint& q, DiagramPoint& result) const = 0;
- virtual void getAllNeighbours(const DiagramPoint& q, std::vector<DiagramPoint>& result) = 0;
- virtual ~NeighbOracleAbstract() {};
-protected:
- double r;
- double distEpsilon;
-};
-
-class NeighbOracleSimple : public NeighbOracleAbstract
-{
-public:
- NeighbOracleSimple();
- NeighbOracleSimple(const DiagramPointSet& S, const double rr, const double dEps);
- void deletePoint(const DiagramPoint& p);
- void rebuild(const DiagramPointSet& S, const double rr);
- bool getNeighbour(const DiagramPoint& q, DiagramPoint& result) const;
- void getAllNeighbours(const DiagramPoint& q, std::vector<DiagramPoint>& result);
- ~NeighbOracleSimple() {};
-private:
- DiagramPointSet pointSet;
-};
-
-class NeighbOracleAnn : public NeighbOracleAbstract
-{
-public:
- NeighbOracleAnn(const DiagramPointSet& S, const double rr, const double dEps);
- void deletePoint(const DiagramPoint& p);
- void rebuild(const DiagramPointSet& S, const double rr);
- bool getNeighbour(const DiagramPoint& q, DiagramPoint& result) const;
- void getAllNeighbours(const DiagramPoint& q, std::vector<DiagramPoint>& result);
- ~NeighbOracleAnn();
-//private:
- //DiagramPointSet originalPointSet;
- std::vector<DiagramPoint> allPoints;
- DiagramPointSet diagonalPoints;
- std::unordered_map<DiagramPoint, size_t, DiagramPointHash> pointIdxLookup;
- // ann-stuff
- static constexpr double annEpsilon {0};
- static const int annK {1};
- static const int annDim{2};
- ANNpointArray annPoints;
- ANNkd_tree* kdTree;
- ANNidxArray annNeigbIndices;
- ANNpoint annQueryPoint;
- // to use in getAllNeighbours
- ANNpoint lo;
- ANNpoint hi;
- ANNidxArray annIndices;
- ANNdistArray annDistances;
-};
-
-//typedef NeighbOracleSimple NeighbOracle;
-typedef NeighbOracleAnn NeighbOracle;
-
-}
-#endif
diff --git a/geom_bottleneck/bottleneck/lib/dummy b/geom_bottleneck/bottleneck/lib/dummy
deleted file mode 100644
index 8b13789..0000000
--- a/geom_bottleneck/bottleneck/lib/dummy
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/geom_bottleneck/bottleneck/src/ann/ANN.cpp b/geom_bottleneck/bottleneck/src/ann/ANN.cpp
deleted file mode 100644
index 83c7ef6..0000000
--- a/geom_bottleneck/bottleneck/src/ann/ANN.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-//----------------------------------------------------------------------
-// File: ANN.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Methods for ANN.h and ANNx.h
-// Last modified: 01/27/10 (Version 1.1.2)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2010 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Added performance counting to annDist()
-// Revision 1.1.2 01/27/10
-// Fixed minor compilation bugs for new versions of gcc
-//----------------------------------------------------------------------
-
-#ifdef _WIN32
-#include <ciso646> // make VS more conformal
-#endif
-
-#include <stdexcept>
-#include <cstdlib> // C standard lib defs
-#include <ANN/ANNx.h> // all ANN includes
-#include <ANN/ANNperf.h> // ANN performance
-#include "def_debug_bt.h"
-
-
-
-using namespace std; // make std:: accessible
-
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Point methods
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// Distance utility.
-// (Note: In the nearest neighbor search, most distances are
-// computed using partial distance calculations, not this
-// procedure.)
-//----------------------------------------------------------------------
-
-ANNdist annDist( // interpoint squared distance
- int dim,
- ANNpoint p,
- ANNpoint q)
-{
- register int d;
- register ANNcoord diff;
- register ANNcoord dist;
-
- dist = 0;
- for (d = 0; d < dim; d++) {
- diff = p[d] - q[d];
- dist = ANN_SUM(dist, ANN_POW(diff));
- }
- ANN_FLOP(3*dim) // performance counts
- ANN_PTS(1)
- ANN_COORD(dim)
- return dist;
-}
-
-//----------------------------------------------------------------------
-// annPrintPoint() prints a point to a given output stream.
-//----------------------------------------------------------------------
-
-void annPrintPt( // print a point
- ANNpoint pt, // the point
- int dim, // the dimension
- std::ostream &out) // output stream
-{
-#ifndef FOR_R_TDA
- for (int j = 0; j < dim; j++) {
- out << pt[j];
- if (j < dim-1) out << " ";
- }
-#endif
-}
-
-//----------------------------------------------------------------------
-// Point allocation/deallocation:
-//
-// Because points (somewhat like strings in C) are stored
-// as pointers. Consequently, creating and destroying
-// copies of points may require storage allocation. These
-// procedures do this.
-//
-// annAllocPt() and annDeallocPt() allocate a deallocate
-// storage for a single point, and return a pointer to it.
-//
-// annAllocPts() allocates an array of points as well a place
-// to store their coordinates, and initializes the points to
-// point to their respective coordinates. It allocates point
-// storage in a contiguous block large enough to store all the
-// points. It performs no initialization.
-//
-// annDeallocPts() should only be used on point arrays allocated
-// by annAllocPts since it assumes that points are allocated in
-// a block.
-//
-// annCopyPt() copies a point taking care to allocate storage
-// for the new point.
-//
-// annAssignRect() assigns the coordinates of one rectangle to
-// another. The two rectangles must have the same dimension
-// (and it is not possible to test this here).
-//----------------------------------------------------------------------
-
-ANNpoint annAllocPt(int dim, ANNcoord c) // allocate 1 point
-{
- ANNpoint p = new ANNcoord[dim];
- for (int i = 0; i < dim; i++) p[i] = c;
- return p;
-}
-
-ANNpointArray annAllocPts(int n, int dim) // allocate n pts in dim
-{
- ANNpointArray pa = new ANNpoint[n]; // allocate points
- ANNpoint p = new ANNcoord[n*dim]; // allocate space for coords
- for (int i = 0; i < n; i++) {
- pa[i] = &(p[i*dim]);
- }
- return pa;
-}
-
-void annDeallocPt(ANNpoint &p) // deallocate 1 point
-{
- delete [] p;
- p = NULL;
-}
-
-void annDeallocPts(ANNpointArray &pa) // deallocate points
-{
- delete [] pa[0]; // dealloc coordinate storage
- delete [] pa; // dealloc points
- pa = NULL;
-}
-
-ANNpoint annCopyPt(int dim, ANNpoint source) // copy point
-{
- ANNpoint p = new ANNcoord[dim];
- for (int i = 0; i < dim; i++) p[i] = source[i];
- return p;
-}
-
- // assign one rect to another
-void annAssignRect(int dim, ANNorthRect &dest, const ANNorthRect &source)
-{
- for (int i = 0; i < dim; i++) {
- dest.lo[i] = source.lo[i];
- dest.hi[i] = source.hi[i];
- }
-}
-
- // is point inside rectangle?
-ANNbool ANNorthRect::inside(const int dim, ANNpoint p) const
-{
- for (int i = 0; i < dim; i++) {
- if (p[i] < lo[i] || p[i] > hi[i]) return ANNfalse;
- }
- return ANNtrue;
-}
-
-bool ANNorthRect::contains(const int dim, const ANNorthRect& r) const
-{
- return this->inside(dim, r.hi) and this->inside(dim, r.lo);
-}
-
-bool ANNorthRect::intersects(const int dim, const ANNorthRect& r) const
-{
- assert(dim == 2); // works for plane only
- const ANNpoint otherLo = r.lo;
- const ANNpoint otherHi = r.hi;
- if ( otherLo[0] > hi[0] or
- otherLo[1] > hi[1] or
- otherHi[0] < lo[0] or
- otherHi[1] < lo[1]) {
- return false;
- } else {
- return true;
- }
-}
-
-//----------------------------------------------------------------------
-// Error handler
-//----------------------------------------------------------------------
-
-void annError(const char* msg, ANNerr level)
-{
- if (level == ANNabort) {
-#ifndef FOR_R_TDA
- cerr << "ANN: ERROR------->" << msg << "<-------------ERROR\n";
-#endif
- throw std::runtime_error(std::string("ANN: Error: ") + std::string(msg));
- //exit(1);
- }
- else {
-#ifndef FOR_R_TDA
- cerr << "ANN: WARNING----->" << msg << "<-------------WARNING\n";
-#endif
- }
-}
-
-//----------------------------------------------------------------------
-// Limit on number of points visited
-// We have an option for terminating the search early if the
-// number of points visited exceeds some threshold. If the
-// threshold is 0 (its default) this means there is no limit
-// and the algorithm applies its normal termination condition.
-// This is for applications where there are real time constraints
-// on the running time of the algorithm.
-//----------------------------------------------------------------------
-
-int ANNmaxPtsVisited = 0; // maximum number of pts visited
-int ANNptsVisited; // number of pts visited in search
-
-//----------------------------------------------------------------------
-// Global function declarations
-//----------------------------------------------------------------------
-
-void annMaxPtsVisit( // set limit on max. pts to visit in search
- int maxPts) // the limit
-{
- ANNmaxPtsVisited = maxPts;
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/bd_fix_rad_search.cpp b/geom_bottleneck/bottleneck/src/ann/bd_fix_rad_search.cpp
deleted file mode 100644
index fe8ab78..0000000
--- a/geom_bottleneck/bottleneck/src/ann/bd_fix_rad_search.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//----------------------------------------------------------------------
-// File: bd_fix_rad_search.cpp
-// Programmer: David Mount
-// Description: Standard bd-tree search
-// Last modified: 05/03/05 (Version 1.1)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 1.1 05/03/05
-// Initial release
-//----------------------------------------------------------------------
-
-#include "bd_tree.h" // bd-tree declarations
-#include "kd_fix_rad_search.h" // kd-tree FR search declarations
-
-namespace geom_bt {
-
-//----------------------------------------------------------------------
-// Approximate searching for bd-trees.
-// See the file kd_FR_search.cpp for general information on the
-// approximate nearest neighbor search algorithm. Here we
-// include the extensions for shrinking nodes.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// bd_shrink::ann_FR_search - search a shrinking node
-//----------------------------------------------------------------------
-
-void ANNbd_shrink::ann_FR_search(ANNdist box_dist)
-{
- // check dist calc term cond.
- if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
-
- ANNdist inner_dist = 0; // distance to inner box
- for (int i = 0; i < n_bnds; i++) { // is query point in the box?
- if (bnds[i].out(ANNkdFRQ)) { // outside this bounding side?
- // add to inner distance
- inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNkdFRQ));
- }
- }
- if (inner_dist <= box_dist) { // if inner box is closer
- child[ANN_IN]->ann_FR_search(inner_dist);// search inner child first
- child[ANN_OUT]->ann_FR_search(box_dist);// ...then outer child
- }
- else { // if outer box is closer
- child[ANN_OUT]->ann_FR_search(box_dist);// search outer child first
- child[ANN_IN]->ann_FR_search(inner_dist);// ...then outer child
- }
- ANN_FLOP(3*n_bnds) // increment floating ops
- ANN_SHR(1) // one more shrinking node
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/bd_pr_search.cpp b/geom_bottleneck/bottleneck/src/ann/bd_pr_search.cpp
deleted file mode 100644
index fb9dea6..0000000
--- a/geom_bottleneck/bottleneck/src/ann/bd_pr_search.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-//----------------------------------------------------------------------
-// File: bd_pr_search.cpp
-// Programmer: David Mount
-// Description: Priority search for bd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-//History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#include "bd_tree.h" // bd-tree declarations
-#include "kd_pr_search.h" // kd priority search declarations
-
-
-namespace geom_bt {
-
-//----------------------------------------------------------------------
-// Approximate priority searching for bd-trees.
-// See the file kd_pr_search.cc for general information on the
-// approximate nearest neighbor priority search algorithm. Here
-// we include the extensions for shrinking nodes.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// bd_shrink::ann_search - search a shrinking node
-//----------------------------------------------------------------------
-
-void ANNbd_shrink::ann_pri_search(ANNdist box_dist)
-{
- ANNdist inner_dist = 0; // distance to inner box
- for (int i = 0; i < n_bnds; i++) { // is query point in the box?
- if (bnds[i].out(ANNprQ)) { // outside this bounding side?
- // add to inner distance
- inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNprQ));
- }
- }
- if (inner_dist <= box_dist) { // if inner box is closer
- if (child[ANN_OUT] != KD_TRIVIAL) // enqueue outer if not trivial
- ANNprBoxPQ->insert(box_dist,child[ANN_OUT]);
- // continue with inner child
- child[ANN_IN]->ann_pri_search(inner_dist);
- }
- else { // if outer box is closer
- if (child[ANN_IN] != KD_TRIVIAL) // enqueue inner if not trivial
- ANNprBoxPQ->insert(inner_dist,child[ANN_IN]);
- // continue with outer child
- child[ANN_OUT]->ann_pri_search(box_dist);
- }
- ANN_FLOP(3*n_bnds) // increment floating ops
- ANN_SHR(1) // one more shrinking node
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/bd_search.cpp b/geom_bottleneck/bottleneck/src/ann/bd_search.cpp
deleted file mode 100644
index 2935bcb..0000000
--- a/geom_bottleneck/bottleneck/src/ann/bd_search.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-//----------------------------------------------------------------------
-// File: bd_search.cpp
-// Programmer: David Mount
-// Description: Standard bd-tree search
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#include "bd_tree.h" // bd-tree declarations
-#include "kd_search.h" // kd-tree search declarations
-
-namespace geom_bt {
-
-//----------------------------------------------------------------------
-// Approximate searching for bd-trees.
-// See the file kd_search.cpp for general information on the
-// approximate nearest neighbor search algorithm. Here we
-// include the extensions for shrinking nodes.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// bd_shrink::ann_search - search a shrinking node
-//----------------------------------------------------------------------
-
-void ANNbd_shrink::ann_search(ANNdist box_dist)
-{
- // check dist calc term cond.
- if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
-
- ANNdist inner_dist = 0; // distance to inner box
- for (int i = 0; i < n_bnds; i++) { // is query point in the box?
- if (bnds[i].out(ANNkdQ)) { // outside this bounding side?
- // add to inner distance
- inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNkdQ));
- }
- }
- if (inner_dist <= box_dist) { // if inner box is closer
- child[ANN_IN]->ann_search(inner_dist); // search inner child first
- child[ANN_OUT]->ann_search(box_dist); // ...then outer child
- }
- else { // if outer box is closer
- child[ANN_OUT]->ann_search(box_dist); // search outer child first
- child[ANN_IN]->ann_search(inner_dist); // ...then outer child
- }
- ANN_FLOP(3*n_bnds) // increment floating ops
- ANN_SHR(1) // one more shrinking node
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/bd_tree.cpp b/geom_bottleneck/bottleneck/src/ann/bd_tree.cpp
deleted file mode 100644
index a5dd69c..0000000
--- a/geom_bottleneck/bottleneck/src/ann/bd_tree.cpp
+++ /dev/null
@@ -1,422 +0,0 @@
-//----------------------------------------------------------------------
-// File: bd_tree.cpp
-// Programmer: David Mount
-// Description: Basic methods for bd-trees.
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision l.0 04/01/05
-// Fixed centroid shrink threshold condition to depend on the
-// dimension.
-// Moved dump routine to kd_dump.cpp.
-//----------------------------------------------------------------------
-
-#include "bd_tree.h" // bd-tree declarations
-#include "kd_util.h" // kd-tree utilities
-#include "kd_split.h" // kd-tree splitting rules
-
-#include <ANN/ANNperf.h> // performance evaluation
-#include "def_debug_bt.h"
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Printing a bd-tree
-// These routines print a bd-tree. See the analogous procedure
-// in kd_tree.cpp for more information.
-//----------------------------------------------------------------------
-
-void ANNbd_shrink::print( // print shrinking node
- int level, // depth of node in tree
- ostream &out) // output stream
-{
-#ifndef FOR_R_TDA
- child[ANN_OUT]->print(level+1, out); // print out-child
-
- out << " ";
- for (int i = 0; i < level; i++) // print indentation
- out << "..";
- out << "Shrink";
- for (int j = 0; j < n_bnds; j++) { // print sides, 2 per line
- if (j % 2 == 0) {
- out << "\n"; // newline and indentation
- for (int i = 0; i < level+2; i++) out << " ";
- }
- out << " ([" << bnds[j].cd << "]"
- << (bnds[j].sd > 0 ? ">=" : "< ")
- << bnds[j].cv << ")";
- }
- out << "\n";
-
- child[ANN_IN]->print(level+1, out); // print in-child
-#endif
-}
-
-//----------------------------------------------------------------------
-// kd_tree statistics utility (for performance evaluation)
-// This routine computes various statistics information for
-// shrinking nodes. See file kd_tree.cpp for more information.
-//----------------------------------------------------------------------
-
-void ANNbd_shrink::getStats( // get subtree statistics
- int dim, // dimension of space
- ANNkdStats &st, // stats (modified)
- ANNorthRect &bnd_box) // bounding box
-{
- ANNkdStats ch_stats; // stats for children
- ANNorthRect inner_box(dim); // inner box of shrink
-
- annBnds2Box(bnd_box, // enclosing box
- dim, // dimension
- n_bnds, // number of bounds
- bnds, // bounds array
- inner_box); // inner box (modified)
- // get stats for inner child
- ch_stats.reset(); // reset
- child[ANN_IN]->getStats(dim, ch_stats, inner_box);
- st.merge(ch_stats); // merge them
- // get stats for outer child
- ch_stats.reset(); // reset
- child[ANN_OUT]->getStats(dim, ch_stats, bnd_box);
- st.merge(ch_stats); // merge them
-
- st.depth++; // increment depth
- st.n_shr++; // increment number of shrinks
-}
-
-//----------------------------------------------------------------------
-// bd-tree constructor
-// This is the main constructor for bd-trees given a set of points.
-// It first builds a skeleton kd-tree as a basis, then computes the
-// bounding box of the data points, and then invokes rbd_tree() to
-// actually build the tree, passing it the appropriate splitting
-// and shrinking information.
-//----------------------------------------------------------------------
-
-ANNkd_ptr rbd_tree( // recursive construction of bd-tree
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- int bsp, // bucket space
- ANNorthRect &bnd_box, // bounding box for current node
- ANNkd_splitter splitter, // splitting routine
- ANNshrinkRule shrink); // shrinking rule
-
-ANNbd_tree::ANNbd_tree( // construct from point array
- ANNpointArray pa, // point array (with at least n pts)
- int n, // number of points
- int dd, // dimension
- int bs, // bucket size
- ANNsplitRule split, // splitting rule
- ANNshrinkRule shrink) // shrinking rule
- : ANNkd_tree(n, dd, bs) // build skeleton base tree
-{
- pts = pa; // where the points are
- if (n == 0) return; // no points--no sweat
-
- ANNorthRect bnd_box(dd); // bounding box for points
- // construct bounding rectangle
- annEnclRect(pa, pidx, n, dd, bnd_box);
- // copy to tree structure
- bnd_box_lo = annCopyPt(dd, bnd_box.lo);
- bnd_box_hi = annCopyPt(dd, bnd_box.hi);
-
- switch (split) { // build by rule
- case ANN_KD_STD: // standard kd-splitting rule
- root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, kd_split, shrink);
- break;
- case ANN_KD_MIDPT: // midpoint split
- root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, midpt_split, shrink);
- break;
- case ANN_KD_SUGGEST: // best (in our opinion)
- case ANN_KD_SL_MIDPT: // sliding midpoint split
- root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, sl_midpt_split, shrink);
- break;
- case ANN_KD_FAIR: // fair split
- root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, fair_split, shrink);
- break;
- case ANN_KD_SL_FAIR: // sliding fair split
- root = rbd_tree(pa, pidx, n, dd, bs,
- bnd_box, sl_fair_split, shrink);
- break;
- default:
- annError("Illegal splitting method", ANNabort);
- }
-}
-
-//----------------------------------------------------------------------
-// Shrinking rules
-//----------------------------------------------------------------------
-
-enum ANNdecomp {SPLIT, SHRINK}; // decomposition methods
-
-//----------------------------------------------------------------------
-// trySimpleShrink - Attempt a simple shrink
-//
-// We compute the tight bounding box of the points, and compute
-// the 2*dim ``gaps'' between the sides of the tight box and the
-// bounding box. If any of the gaps is large enough relative to
-// the longest side of the tight bounding box, then we shrink
-// all sides whose gaps are large enough. (The reason for
-// comparing against the tight bounding box, is that after
-// shrinking the longest box size will decrease, and if we use
-// the standard bounding box, we may decide to shrink twice in
-// a row. Since the tight box is fixed, we cannot shrink twice
-// consecutively.)
-//----------------------------------------------------------------------
-const float BD_GAP_THRESH = 0.5; // gap threshold (must be < 1)
-const int BD_CT_THRESH = 2; // min number of shrink sides
-
-ANNdecomp trySimpleShrink( // try a simple shrink
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- const ANNorthRect &bnd_box, // current bounding box
- ANNorthRect &inner_box) // inner box if shrinking (returned)
-{
- int i;
- // compute tight bounding box
- annEnclRect(pa, pidx, n, dim, inner_box);
-
- ANNcoord max_length = 0; // find longest box side
- for (i = 0; i < dim; i++) {
- ANNcoord length = inner_box.hi[i] - inner_box.lo[i];
- if (length > max_length) {
- max_length = length;
- }
- }
-
- int shrink_ct = 0; // number of sides we shrunk
- for (i = 0; i < dim; i++) { // select which sides to shrink
- // gap between boxes
- ANNcoord gap_hi = bnd_box.hi[i] - inner_box.hi[i];
- // big enough gap to shrink?
- if (gap_hi < max_length*BD_GAP_THRESH)
- inner_box.hi[i] = bnd_box.hi[i]; // no - expand
- else shrink_ct++; // yes - shrink this side
-
- // repeat for high side
- ANNcoord gap_lo = inner_box.lo[i] - bnd_box.lo[i];
- if (gap_lo < max_length*BD_GAP_THRESH)
- inner_box.lo[i] = bnd_box.lo[i]; // no - expand
- else shrink_ct++; // yes - shrink this side
- }
-
- if (shrink_ct >= BD_CT_THRESH) // did we shrink enough sides?
- return SHRINK;
- else return SPLIT;
-}
-
-//----------------------------------------------------------------------
-// tryCentroidShrink - Attempt a centroid shrink
-//
-// We repeatedly apply the splitting rule, always to the larger subset
-// of points, until the number of points decreases by the constant
-// fraction BD_FRACTION. If this takes more than dim*BD_MAX_SPLIT_FAC
-// splits for this to happen, then we shrink to the final inner box
-// Otherwise we split.
-//----------------------------------------------------------------------
-
-const float BD_MAX_SPLIT_FAC = 0.5; // maximum number of splits allowed
-const float BD_FRACTION = 0.5; // ...to reduce points by this fraction
- // ...This must be < 1.
-
-ANNdecomp tryCentroidShrink( // try a centroid shrink
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- const ANNorthRect &bnd_box, // current bounding box
- ANNkd_splitter splitter, // splitting procedure
- ANNorthRect &inner_box) // inner box if shrinking (returned)
-{
- int n_sub = n; // number of points in subset
- int n_goal = (int) (n*BD_FRACTION); // number of point in goal
- int n_splits = 0; // number of splits needed
- // initialize inner box to bounding box
- annAssignRect(dim, inner_box, bnd_box);
-
- while (n_sub > n_goal) { // keep splitting until goal reached
- int cd; // cut dim from splitter (ignored)
- ANNcoord cv; // cut value from splitter (ignored)
- int n_lo; // number of points on low side
- // invoke splitting procedure
- (*splitter)(pa, pidx, inner_box, n_sub, dim, cd, cv, n_lo);
- n_splits++; // increment split count
-
- if (n_lo >= n_sub/2) { // most points on low side
- inner_box.hi[cd] = cv; // collapse high side
- n_sub = n_lo; // recurse on lower points
- }
- else { // most points on high side
- inner_box.lo[cd] = cv; // collapse low side
- pidx += n_lo; // recurse on higher points
- n_sub -= n_lo;
- }
- }
- if (n_splits > dim*BD_MAX_SPLIT_FAC)// took too many splits
- return SHRINK; // shrink to final subset
- else
- return SPLIT;
-}
-
-//----------------------------------------------------------------------
-// selectDecomp - select which decomposition to use
-//----------------------------------------------------------------------
-
-ANNdecomp selectDecomp( // select decomposition method
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- const ANNorthRect &bnd_box, // current bounding box
- ANNkd_splitter splitter, // splitting procedure
- ANNshrinkRule shrink, // shrinking rule
- ANNorthRect &inner_box) // inner box if shrinking (returned)
-{
- ANNdecomp decomp = SPLIT; // decomposition
-
- switch (shrink) { // check shrinking rule
- case ANN_BD_NONE: // no shrinking allowed
- decomp = SPLIT;
- break;
- case ANN_BD_SUGGEST: // author's suggestion
- case ANN_BD_SIMPLE: // simple shrink
- decomp = trySimpleShrink(
- pa, pidx, // points and indices
- n, dim, // number of points and dimension
- bnd_box, // current bounding box
- inner_box); // inner box if shrinking (returned)
- break;
- case ANN_BD_CENTROID: // centroid shrink
- decomp = tryCentroidShrink(
- pa, pidx, // points and indices
- n, dim, // number of points and dimension
- bnd_box, // current bounding box
- splitter, // splitting procedure
- inner_box); // inner box if shrinking (returned)
- break;
- default:
- annError("Illegal shrinking rule", ANNabort);
- }
- return decomp;
-}
-
-//----------------------------------------------------------------------
-// rbd_tree - recursive procedure to build a bd-tree
-//
-// This is analogous to rkd_tree, but for bd-trees. See the
-// procedure rkd_tree() in kd_split.cpp for more information.
-//
-// If the number of points falls below the bucket size, then a
-// leaf node is created for the points. Otherwise we invoke the
-// procedure selectDecomp() which determines whether we are to
-// split or shrink. If splitting is chosen, then we essentially
-// do exactly as rkd_tree() would, and invoke the specified
-// splitting procedure to the points. Otherwise, the selection
-// procedure returns a bounding box, from which we extract the
-// appropriate shrinking bounds, and create a shrinking node.
-// Finally the points are subdivided, and the procedure is
-// invoked recursively on the two subsets to form the children.
-//----------------------------------------------------------------------
-
-ANNkd_ptr rbd_tree( // recursive construction of bd-tree
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- int bsp, // bucket space
- ANNorthRect &bnd_box, // bounding box for current node
- ANNkd_splitter splitter, // splitting routine
- ANNshrinkRule shrink) // shrinking rule
-{
- ANNdecomp decomp; // decomposition method
-
- ANNorthRect inner_box(dim); // inner box (if shrinking)
-
- if (n <= bsp) { // n small, make a leaf node
- if (n == 0) // empty leaf node
- return KD_TRIVIAL; // return (canonical) empty leaf
- else // construct the node and return
- return new ANNkd_leaf(n, pidx);
- }
-
- decomp = selectDecomp( // select decomposition method
- pa, pidx, // points and indices
- n, dim, // number of points and dimension
- bnd_box, // current bounding box
- splitter, shrink, // splitting/shrinking methods
- inner_box); // inner box if shrinking (returned)
-
- if (decomp == SPLIT) { // split selected
- int cd; // cutting dimension
- ANNcoord cv; // cutting value
- int n_lo; // number on low side of cut
- // invoke splitting procedure
- (*splitter)(pa, pidx, bnd_box, n, dim, cd, cv, n_lo);
-
- ANNcoord lv = bnd_box.lo[cd]; // save bounds for cutting dimension
- ANNcoord hv = bnd_box.hi[cd];
-
- bnd_box.hi[cd] = cv; // modify bounds for left subtree
- ANNkd_ptr lo = rbd_tree( // build left subtree
- pa, pidx, n_lo, // ...from pidx[0..n_lo-1]
- dim, bsp, bnd_box, splitter, shrink);
- bnd_box.hi[cd] = hv; // restore bounds
-
- bnd_box.lo[cd] = cv; // modify bounds for right subtree
- ANNkd_ptr hi = rbd_tree( // build right subtree
- pa, pidx + n_lo, n-n_lo,// ...from pidx[n_lo..n-1]
- dim, bsp, bnd_box, splitter, shrink);
- bnd_box.lo[cd] = lv; // restore bounds
- // create the splitting node
- return new ANNkd_split(cd, cv, lv, hv, lo, hi);
- }
- else { // shrink selected
- int n_in; // number of points in box
- int n_bnds; // number of bounding sides
-
- annBoxSplit( // split points around inner box
- pa, // points to split
- pidx, // point indices
- n, // number of points
- dim, // dimension
- inner_box, // inner box
- n_in); // number of points inside (returned)
-
- ANNkd_ptr in = rbd_tree( // build inner subtree pidx[0..n_in-1]
- pa, pidx, n_in, dim, bsp, inner_box, splitter, shrink);
- ANNkd_ptr out = rbd_tree( // build outer subtree pidx[n_in..n]
- pa, pidx+n_in, n - n_in, dim, bsp, bnd_box, splitter, shrink);
-
- ANNorthHSArray bnds = NULL; // bounds (alloc in Box2Bnds and
- // ...freed in bd_shrink destroyer)
-
- annBox2Bnds( // convert inner box to bounds
- inner_box, // inner box
- bnd_box, // enclosing box
- dim, // dimension
- n_bnds, // number of bounds (returned)
- bnds); // bounds array (modified)
-
- // return shrinking node
- return new ANNbd_shrink(n_bnds, bnds, in, out);
- }
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_dump.cpp b/geom_bottleneck/bottleneck/src/ann/kd_dump.cpp
deleted file mode 100644
index ecaf7ea..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_dump.cpp
+++ /dev/null
@@ -1,458 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_dump.cc
-// Programmer: David Mount
-// Description: Dump and Load for kd- and bd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Moved dump out of kd_tree.cc into this file.
-// Added kd-tree load constructor.
-//----------------------------------------------------------------------
-// This file contains routines for dumping kd-trees and bd-trees and
-// reloading them. (It is an abuse of policy to include both kd- and
-// bd-tree routines in the same file, sorry. There should be no problem
-// in deleting the bd- versions of the routines if they are not
-// desired.)
-//----------------------------------------------------------------------
-
-#include "kd_tree.h" // kd-tree declarations
-#include "bd_tree.h" // bd-tree declarations
-#include "def_debug_bt.h"
-
-using namespace std; // make std:: available
-
-namespace geom_bt {
-
- //----------------------------------------------------------------------
- // Constants
- //----------------------------------------------------------------------
-
- const int STRING_LEN = 500; // maximum string length
- const double EPSILON = 1E-5; // small number for float comparison
-
- enum ANNtreeType { KD_TREE, BD_TREE }; // tree types (used in loading)
-
- //----------------------------------------------------------------------
- // Procedure declarations
- //----------------------------------------------------------------------
-
- static ANNkd_ptr annReadDump( // read dump file
- istream &in, // input stream
- ANNtreeType tree_type, // type of tree expected
- ANNpointArray &the_pts, // new points (if applic)
- ANNidxArray &the_pidx, // point indices (returned)
- int &the_dim, // dimension (returned)
- int &the_n_pts, // number of points (returned)
- int &the_bkt_size, // bucket size (returned)
- ANNpoint &the_bnd_box_lo, // low bounding point
- ANNpoint &the_bnd_box_hi); // high bounding point
-
- static ANNkd_ptr annReadTree( // read tree-part of dump file
- istream &in, // input stream
- ANNtreeType tree_type, // type of tree expected
- ANNidxArray the_pidx, // point indices (modified)
- int &next_idx); // next index (modified)
-
- //----------------------------------------------------------------------
- // ANN kd- and bd-tree Dump Format
- // The dump file begins with a header containing the version of
- // ANN, an optional section containing the points, followed by
- // a description of the tree. The tree is printed in preorder.
- //
- // Format:
- // #ANN <version number> <comments> [END_OF_LINE]
- // points <dim> <n_pts> (point coordinates: this is optional)
- // 0 <xxx> <xxx> ... <xxx> (point indices and coordinates)
- // 1 <xxx> <xxx> ... <xxx>
- // ...
- // tree <dim> <n_pts> <bkt_size>
- // <xxx> <xxx> ... <xxx> (lower end of bounding box)
- // <xxx> <xxx> ... <xxx> (upper end of bounding box)
- // If the tree is null, then a single line "null" is
- // output. Otherwise the nodes of the tree are printed
- // one per line in preorder. Leaves and splitting nodes
- // have the following formats:
- // Leaf node:
- // leaf <n_pts> <bkt[0]> <bkt[1]> ... <bkt[n-1]>
- // Splitting nodes:
- // split <cut_dim> <cut_val> <lo_bound> <hi_bound>
- //
- // For bd-trees:
- //
- // Shrinking nodes:
- // shrink <n_bnds>
- // <cut_dim> <cut_val> <side>
- // <cut_dim> <cut_val> <side>
- // ... (repeated n_bnds times)
- //----------------------------------------------------------------------
-
-#ifndef FOR_R_TDA
- void ANNkd_tree::Dump( // dump entire tree
- ANNbool with_pts, // print points as well?
- ostream &out) // output stream
- {
- out << "#ANN " << ANNversion << "\n";
- out.precision(ANNcoordPrec); // use full precision in dumping
- if (with_pts) { // print point coordinates
- out << "points " << dim << " " << n_pts << "\n";
- for (int i = 0; i < n_pts; i++) {
- out << i << " ";
- annPrintPt(pts[i], dim, out);
- out << "\n";
- }
- }
- out << "tree " // print tree elements
- << dim << " "
- << n_pts << " "
- << bkt_size << "\n";
-
- annPrintPt(bnd_box_lo, dim, out); // print lower bound
- out << "\n";
- annPrintPt(bnd_box_hi, dim, out); // print upper bound
- out << "\n";
-
- if (root == NULL) // empty tree?
- out << "null\n";
- else {
- root->dump(out); // invoke printing at root
- }
- out.precision(0); // restore default precision
- }
-#endif
-
- void ANNkd_split::dump( // dump a splitting node
- ostream &out) // output stream
- {
-#ifndef FOR_R_TDA
- out << "split " << cut_dim << " " << cut_val << " ";
- out << cd_bnds[ANN_LO] << " " << cd_bnds[ANN_HI] << "\n";
-
- child[ANN_LO]->dump(out); // print low child
- child[ANN_HI]->dump(out); // print high child
-#endif
- }
-
- void ANNkd_leaf::dump( // dump a leaf node
- ostream &out) // output stream
- {
-#ifndef FOR_R_TDA
- if (this == KD_TRIVIAL) { // canonical trivial leaf node
- out << "leaf 0\n"; // leaf no points
- }
- else {
- out << "leaf " << n_pts;
- for (int j = 0; j < n_pts; j++) {
- out << " " << bkt[j];
- }
- out << "\n";
- }
-#endif
- }
-
- void ANNbd_shrink::dump( // dump a shrinking node
- ostream &out) // output stream
- {
-#ifndef FOR_R_TDA
- out << "shrink " << n_bnds << "\n";
- for (int j = 0; j < n_bnds; j++) {
- out << bnds[j].cd << " " << bnds[j].cv << " " << bnds[j].sd << "\n";
- }
- child[ANN_IN]->dump(out); // print in-child
- child[ANN_OUT]->dump(out); // print out-child
-#endif
- }
-
- //----------------------------------------------------------------------
- // Load kd-tree from dump file
- // This rebuilds a kd-tree which was dumped to a file. The dump
- // file contains all the basic tree information according to a
- // preorder traversal. We assume that the dump file also contains
- // point data. (This is to guarantee the consistency of the tree.)
- // If not, then an error is generated.
- //
- // Indirectly, this procedure allocates space for points, point
- // indices, all nodes in the tree, and the bounding box for the
- // tree. When the tree is destroyed, all but the points are
- // deallocated.
- //
- // This routine calls annReadDump to do all the work.
- //----------------------------------------------------------------------
-
- ANNkd_tree::ANNkd_tree( // build from dump file
- istream &in) // input stream for dump file
- {
- int the_dim; // local dimension
- int the_n_pts; // local number of points
- int the_bkt_size; // local number of points
- ANNpoint the_bnd_box_lo; // low bounding point
- ANNpoint the_bnd_box_hi; // high bounding point
- ANNpointArray the_pts; // point storage
- ANNidxArray the_pidx; // point index storage
- ANNkd_ptr the_root; // root of the tree
-
- the_root = annReadDump( // read the dump file
- in, // input stream
- KD_TREE, // expecting a kd-tree
- the_pts, // point array (returned)
- the_pidx, // point indices (returned)
- the_dim, the_n_pts, the_bkt_size, // basic tree info (returned)
- the_bnd_box_lo, the_bnd_box_hi); // bounding box info (returned)
-
- // create a skeletal tree
- SkeletonTree(the_n_pts, the_dim, the_bkt_size, the_pts, the_pidx);
-
- bnd_box_lo = the_bnd_box_lo;
- bnd_box_hi = the_bnd_box_hi;
-
- root = the_root; // set the root
- }
-
- ANNbd_tree::ANNbd_tree( // build bd-tree from dump file
- istream &in) : ANNkd_tree() // input stream for dump file
- {
- int the_dim; // local dimension
- int the_n_pts; // local number of points
- int the_bkt_size; // local number of points
- ANNpoint the_bnd_box_lo; // low bounding point
- ANNpoint the_bnd_box_hi; // high bounding point
- ANNpointArray the_pts; // point storage
- ANNidxArray the_pidx; // point index storage
- ANNkd_ptr the_root; // root of the tree
-
- the_root = annReadDump( // read the dump file
- in, // input stream
- BD_TREE, // expecting a bd-tree
- the_pts, // point array (returned)
- the_pidx, // point indices (returned)
- the_dim, the_n_pts, the_bkt_size, // basic tree info (returned)
- the_bnd_box_lo, the_bnd_box_hi); // bounding box info (returned)
-
- // create a skeletal tree
- SkeletonTree(the_n_pts, the_dim, the_bkt_size, the_pts, the_pidx);
- bnd_box_lo = the_bnd_box_lo;
- bnd_box_hi = the_bnd_box_hi;
-
- root = the_root; // set the root
- }
-
- //----------------------------------------------------------------------
- // annReadDump - read a dump file
- //
- // This procedure reads a dump file, constructs a kd-tree
- // and returns all the essential information needed to actually
- // construct the tree. Because this procedure is used for
- // constructing both kd-trees and bd-trees, the second argument
- // is used to indicate which type of tree we are expecting.
- //----------------------------------------------------------------------
-
- static ANNkd_ptr annReadDump(
- istream &in, // input stream
- ANNtreeType tree_type, // type of tree expected
- ANNpointArray &the_pts, // new points (returned)
- ANNidxArray &the_pidx, // point indices (returned)
- int &the_dim, // dimension (returned)
- int &the_n_pts, // number of points (returned)
- int &the_bkt_size, // bucket size (returned)
- ANNpoint &the_bnd_box_lo, // low bounding point (ret'd)
- ANNpoint &the_bnd_box_hi) // high bounding point (ret'd)
- {
- int j;
- char str[STRING_LEN]; // storage for string
- char version[STRING_LEN]; // ANN version number
- ANNkd_ptr the_root = NULL;
-
- //------------------------------------------------------------------
- // Input file header
- //------------------------------------------------------------------
- in >> str; // input header
- if (strcmp(str, "#ANN") != 0) { // incorrect header
- annError("Incorrect header for dump file", ANNabort);
- }
- in.getline(version, STRING_LEN); // get version (ignore)
-
- //------------------------------------------------------------------
- // Input the points
- // An array the_pts is allocated and points are read from
- // the dump file.
- //------------------------------------------------------------------
- in >> str; // get major heading
- if (strcmp(str, "points") == 0) { // points section
- in >> the_dim; // input dimension
- in >> the_n_pts; // number of points
- // allocate point storage
- the_pts = annAllocPts(the_n_pts, the_dim);
- for (int i = 0; i < the_n_pts; i++) { // input point coordinates
- ANNidx idx; // point index
- in >> idx; // input point index
- if (idx < 0 || idx >= the_n_pts) {
- annError("Point index is out of range", ANNabort);
- }
- for (j = 0; j < the_dim; j++) {
- in >> the_pts[idx][j]; // read point coordinates
- }
- }
- in >> str; // get next major heading
- }
- else { // no points were input
- annError("Points must be supplied in the dump file", ANNabort);
- }
-
- //------------------------------------------------------------------
- // Input the tree
- // After the basic header information, we invoke annReadTree
- // to do all the heavy work. We create our own array of
- // point indices (so we can pass them to annReadTree())
- // but we do not deallocate them. They will be deallocated
- // when the tree is destroyed.
- //------------------------------------------------------------------
- if (strcmp(str, "tree") == 0) { // tree section
- in >> the_dim; // read dimension
- in >> the_n_pts; // number of points
- in >> the_bkt_size; // bucket size
- the_bnd_box_lo = annAllocPt(the_dim); // allocate bounding box pts
- the_bnd_box_hi = annAllocPt(the_dim);
-
- for (j = 0; j < the_dim; j++) { // read bounding box low
- in >> the_bnd_box_lo[j];
- }
- for (j = 0; j < the_dim; j++) { // read bounding box low
- in >> the_bnd_box_hi[j];
- }
- the_pidx = new ANNidx[the_n_pts]; // allocate point index array
- int next_idx = 0; // number of indices filled
- // read the tree and indices
- the_root = annReadTree(in, tree_type, the_pidx, next_idx);
- if (next_idx != the_n_pts) { // didn't see all the points?
- annError("Didn't see as many points as expected", ANNwarn);
- }
- }
- else {
- annError("Illegal dump format. Expecting section heading", ANNabort);
- }
- return the_root;
- }
-
- //----------------------------------------------------------------------
- // annReadTree - input tree and return pointer
- //
- // annReadTree reads in a node of the tree, makes any recursive
- // calls as needed to input the children of this node (if internal).
- // It returns a pointer to the node that was created. An array
- // of point indices is given along with a pointer to the next
- // available location in the array. As leaves are read, their
- // point indices are stored here, and the point buckets point
- // to the first entry in the array.
- //
- // Recall that these are the formats. The tree is given in
- // preorder.
- //
- // Leaf node:
- // leaf <n_pts> <bkt[0]> <bkt[1]> ... <bkt[n-1]>
- // Splitting nodes:
- // split <cut_dim> <cut_val> <lo_bound> <hi_bound>
- //
- // For bd-trees:
- //
- // Shrinking nodes:
- // shrink <n_bnds>
- // <cut_dim> <cut_val> <side>
- // <cut_dim> <cut_val> <side>
- // ... (repeated n_bnds times)
- //----------------------------------------------------------------------
-
- static ANNkd_ptr annReadTree(
- istream &in, // input stream
- ANNtreeType tree_type, // type of tree expected
- ANNidxArray the_pidx, // point indices (modified)
- int &next_idx) // next index (modified)
- {
- char tag[STRING_LEN]; // tag (leaf, split, shrink)
- int n_pts; // number of points in leaf
- int cd; // cut dimension
- ANNcoord cv; // cut value
- ANNcoord lb; // low bound
- ANNcoord hb; // high bound
- int n_bnds; // number of bounding sides
- int sd; // which side
-
- in >> tag; // input node tag
-
- if (strcmp(tag, "null") == 0) { // null tree
- return NULL;
- }
- //------------------------------------------------------------------
- // Read a leaf
- //------------------------------------------------------------------
- if (strcmp(tag, "leaf") == 0) { // leaf node
-
- in >> n_pts; // input number of points
- int old_idx = next_idx; // save next_idx
- if (n_pts == 0) { // trivial leaf
- return KD_TRIVIAL;
- }
- else {
- for (int i = 0; i < n_pts; i++) { // input point indices
- in >> the_pidx[next_idx++]; // store in array of indices
- }
- }
- return new ANNkd_leaf(n_pts, &the_pidx[old_idx]);
- }
- //------------------------------------------------------------------
- // Read a splitting node
- //------------------------------------------------------------------
- else if (strcmp(tag, "split") == 0) { // splitting node
-
- in >> cd >> cv >> lb >> hb;
-
- // read low and high subtrees
- ANNkd_ptr lc = annReadTree(in, tree_type, the_pidx, next_idx);
- ANNkd_ptr hc = annReadTree(in, tree_type, the_pidx, next_idx);
- // create new node and return
- return new ANNkd_split(cd, cv, lb, hb, lc, hc);
- }
- //------------------------------------------------------------------
- // Read a shrinking node (bd-tree only)
- //------------------------------------------------------------------
- else if (strcmp(tag, "shrink") == 0) { // shrinking node
- if (tree_type != BD_TREE) {
- annError("Shrinking node not allowed in kd-tree", ANNabort);
- }
-
- in >> n_bnds; // number of bounding sides
- // allocate bounds array
- ANNorthHSArray bds = new ANNorthHalfSpace[n_bnds];
- for (int i = 0; i < n_bnds; i++) {
- in >> cd >> cv >> sd; // input bounding halfspace
- // copy to array
- bds[i] = ANNorthHalfSpace(cd, cv, sd);
- }
- // read inner and outer subtrees
- ANNkd_ptr ic = annReadTree(in, tree_type, the_pidx, next_idx);
- ANNkd_ptr oc = annReadTree(in, tree_type, the_pidx, next_idx);
- // create new node and return
- return new ANNbd_shrink(n_bnds, bds, ic, oc);
- }
- else {
- annError("Illegal node type in dump file", ANNabort);
-#ifndef FOR_R_TDA
- exit(0); // to keep the compiler happy
-#endif
- }
- }
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_fix_rad_search.cpp b/geom_bottleneck/bottleneck/src/ann/kd_fix_rad_search.cpp
deleted file mode 100644
index 1a4749e..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_fix_rad_search.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_fix_rad_search.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Standard kd-tree fixed-radius kNN search
-// Last modified: 05/03/05 (Version 1.1)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 1.1 05/03/05
-// Initial release
-//----------------------------------------------------------------------
-
-#include "kd_fix_rad_search.h" // kd fixed-radius search decls
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Approximate fixed-radius k nearest neighbor search
-// The squared radius is provided, and this procedure finds the
-// k nearest neighbors within the radius, and returns the total
-// number of points lying within the radius.
-//
-// The method used for searching the kd-tree is a variation of the
-// nearest neighbor search used in kd_search.cpp, except that the
-// radius of the search ball is known. We refer the reader to that
-// file for the explanation of the recursive search procedure.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// To keep argument lists short, a number of global variables
-// are maintained which are common to all the recursive calls.
-// These are given below.
-//----------------------------------------------------------------------
-
-int ANNkdFRDim; // dimension of space
-ANNpoint ANNkdFRQ; // query point
-ANNdist ANNkdFRSqRad; // squared radius search bound
-double ANNkdFRMaxErr; // max tolerable squared error
-ANNpointArray ANNkdFRPts; // the points
-ANNmin_k* ANNkdFRPointMK; // set of k closest points
-int ANNkdFRPtsVisited; // total points visited
-int ANNkdFRPtsInRange; // number of points in the range
-
-//----------------------------------------------------------------------
-// annkFRSearch - fixed radius search for k nearest neighbors
-//----------------------------------------------------------------------
-
-int ANNkd_tree::annkFRSearch(
- ANNpoint q, // the query point
- ANNdist sqRad, // squared radius search bound
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor indices (returned)
- ANNdistArray dd, // the approximate nearest neighbor
- double eps) // the error bound
-{
- ANNkdFRDim = dim; // copy arguments to static equivs
- ANNkdFRQ = q;
- ANNkdFRSqRad = sqRad;
- ANNkdFRPts = pts;
- ANNkdFRPtsVisited = 0; // initialize count of points visited
- ANNkdFRPtsInRange = 0; // ...and points in the range
-
- ANNkdFRMaxErr = ANN_POW(1.0 + eps);
- ANN_FLOP(2) // increment floating op count
-
- ANNkdFRPointMK = new ANNmin_k(k); // create set for closest k points
- // search starting at the root
- root->ann_FR_search(annBoxDistance(q, bnd_box_lo, bnd_box_hi, dim));
-
- for (int i = 0; i < k; i++) { // extract the k-th closest points
- if (dd != NULL)
- dd[i] = ANNkdFRPointMK->ith_smallest_key(i);
- if (nn_idx != NULL)
- nn_idx[i] = ANNkdFRPointMK->ith_smallest_info(i);
- }
-
- delete ANNkdFRPointMK; // deallocate closest point set
- return ANNkdFRPtsInRange; // return final point count
-}
-
-//----------------------------------------------------------------------
-// kd_split::ann_FR_search - search a splitting node
-// Note: This routine is similar in structure to the standard kNN
-// search. It visits the subtree that is closer to the query point
-// first. For fixed-radius search, there is no benefit in visiting
-// one subtree before the other, but we maintain the same basic
-// code structure for the sake of uniformity.
-//----------------------------------------------------------------------
-
-void ANNkd_split::ann_FR_search(ANNdist box_dist)
-{
- // check dist calc term condition
- if (ANNmaxPtsVisited != 0 && ANNkdFRPtsVisited > ANNmaxPtsVisited) return;
-
- // distance to cutting plane
- ANNcoord cut_diff = ANNkdFRQ[cut_dim] - cut_val;
-
- if (cut_diff < 0) { // left of cutting plane
- child[ANN_LO]->ann_FR_search(box_dist);// visit closer child first
-
- ANNcoord box_diff = cd_bnds[ANN_LO] - ANNkdFRQ[cut_dim];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- box_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- // visit further child if in range
- if (box_dist * ANNkdFRMaxErr <= ANNkdFRSqRad)
- child[ANN_HI]->ann_FR_search(box_dist);
-
- }
- else { // right of cutting plane
- child[ANN_HI]->ann_FR_search(box_dist);// visit closer child first
-
- ANNcoord box_diff = ANNkdFRQ[cut_dim] - cd_bnds[ANN_HI];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- box_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- // visit further child if close enough
- if (box_dist * ANNkdFRMaxErr <= ANNkdFRSqRad)
- child[ANN_LO]->ann_FR_search(box_dist);
-
- }
- ANN_FLOP(13) // increment floating ops
- ANN_SPL(1) // one more splitting node visited
-}
-
-//----------------------------------------------------------------------
-// kd_leaf::ann_FR_search - search points in a leaf node
-// Note: The unreadability of this code is the result of
-// some fine tuning to replace indexing by pointer operations.
-//----------------------------------------------------------------------
-
-void ANNkd_leaf::ann_FR_search(ANNdist box_dist)
-{
- register ANNdist dist; // distance to data point
- register ANNcoord* pp; // data coordinate pointer
- register ANNcoord* qq; // query coordinate pointer
- register ANNcoord t;
- register int d;
-
- for (int i = 0; i < n_pts; i++) { // check points in bucket
-
- pp = ANNkdFRPts[bkt[i]]; // first coord of next data point
- qq = ANNkdFRQ; // first coord of query point
- dist = 0;
-
- for(d = 0; d < ANNkdFRDim; d++) {
- ANN_COORD(1) // one more coordinate hit
- ANN_FLOP(5) // increment floating ops
-
- t = *(qq++) - *(pp++); // compute length and adv coordinate
- // exceeds dist to k-th smallest?
- if( (dist = ANN_SUM(dist, ANN_POW(t))) > ANNkdFRSqRad) {
- break;
- }
- }
-
- if (d >= ANNkdFRDim && // among the k best?
- (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
- // add it to the list
- ANNkdFRPointMK->insert(dist, bkt[i]);
- ANNkdFRPtsInRange++; // increment point count
- }
- }
- ANN_LEAF(1) // one more leaf node visited
- ANN_PTS(n_pts) // increment points visited
- ANNkdFRPtsVisited += n_pts; // increment number of points visited
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_pr_search.cpp b/geom_bottleneck/bottleneck/src/ann/kd_pr_search.cpp
deleted file mode 100644
index 73d643f..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_pr_search.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_pr_search.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Priority search for kd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#include "kd_pr_search.h" // kd priority search declarations
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Approximate nearest neighbor searching by priority search.
-// The kd-tree is searched for an approximate nearest neighbor.
-// The point is returned through one of the arguments, and the
-// distance returned is the SQUARED distance to this point.
-//
-// The method used for searching the kd-tree is called priority
-// search. (It is described in Arya and Mount, ``Algorithms for
-// fast vector quantization,'' Proc. of DCC '93: Data Compression
-// Conference}, eds. J. A. Storer and M. Cohn, IEEE Press, 1993,
-// 381--390.)
-//
-// The cell of the kd-tree containing the query point is located,
-// and cells are visited in increasing order of distance from the
-// query point. This is done by placing each subtree which has
-// NOT been visited in a priority queue, according to the closest
-// distance of the corresponding enclosing rectangle from the
-// query point. The search stops when the distance to the nearest
-// remaining rectangle exceeds the distance to the nearest point
-// seen by a factor of more than 1/(1+eps). (Implying that any
-// point found subsequently in the search cannot be closer by more
-// than this factor.)
-//
-// The main entry point is annkPriSearch() which sets things up and
-// then call the recursive routine ann_pri_search(). This is a
-// recursive routine which performs the processing for one node in
-// the kd-tree. There are two versions of this virtual procedure,
-// one for splitting nodes and one for leaves. When a splitting node
-// is visited, we determine which child to continue the search on
-// (the closer one), and insert the other child into the priority
-// queue. When a leaf is visited, we compute the distances to the
-// points in the buckets, and update information on the closest
-// points.
-//
-// Some trickery is used to incrementally update the distance from
-// a kd-tree rectangle to the query point. This comes about from
-// the fact that which each successive split, only one component
-// (along the dimension that is split) of the squared distance to
-// the child rectangle is different from the squared distance to
-// the parent rectangle.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// To keep argument lists short, a number of global variables
-// are maintained which are common to all the recursive calls.
-// These are given below.
-//----------------------------------------------------------------------
-
-double ANNprEps; // the error bound
-int ANNprDim; // dimension of space
-ANNpoint ANNprQ; // query point
-double ANNprMaxErr; // max tolerable squared error
-ANNpointArray ANNprPts; // the points
-ANNpr_queue *ANNprBoxPQ; // priority queue for boxes
-ANNmin_k *ANNprPointMK; // set of k closest points
-
-//----------------------------------------------------------------------
-// annkPriSearch - priority search for k nearest neighbors
-//----------------------------------------------------------------------
-
-void ANNkd_tree::annkPriSearch(
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor indices (returned)
- ANNdistArray dd, // dist to near neighbors (returned)
- double eps) // error bound (ignored)
-{
- // max tolerable squared error
- ANNprMaxErr = ANN_POW(1.0 + eps);
- ANN_FLOP(2) // increment floating ops
-
- ANNprDim = dim; // copy arguments to static equivs
- ANNprQ = q;
- ANNprPts = pts;
- ANNptsVisited = 0; // initialize count of points visited
-
- ANNprPointMK = new ANNmin_k(k); // create set for closest k points
-
- // distance to root box
- ANNdist box_dist = annBoxDistance(q,
- bnd_box_lo, bnd_box_hi, dim);
-
- ANNprBoxPQ = new ANNpr_queue(n_pts);// create priority queue for boxes
- ANNprBoxPQ->insert(box_dist, root); // insert root in priority queue
-
- while (ANNprBoxPQ->non_empty() &&
- (!(ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited))) {
- ANNkd_ptr np; // next box from prior queue
-
- // extract closest box from queue
- ANNprBoxPQ->extr_min(box_dist, (void *&) np);
-
- ANN_FLOP(2) // increment floating ops
- if (box_dist*ANNprMaxErr >= ANNprPointMK->max_key())
- break;
-
- np->ann_pri_search(box_dist); // search this subtree.
- }
-
- for (int i = 0; i < k; i++) { // extract the k-th closest points
- dd[i] = ANNprPointMK->ith_smallest_key(i);
- nn_idx[i] = ANNprPointMK->ith_smallest_info(i);
- }
-
- delete ANNprPointMK; // deallocate closest point set
- delete ANNprBoxPQ; // deallocate priority queue
-}
-
-//----------------------------------------------------------------------
-// kd_split::ann_pri_search - search a splitting node
-//----------------------------------------------------------------------
-
-void ANNkd_split::ann_pri_search(ANNdist box_dist)
-{
- ANNdist new_dist; // distance to child visited later
- // distance to cutting plane
- ANNcoord cut_diff = ANNprQ[cut_dim] - cut_val;
-
- if (cut_diff < 0) { // left of cutting plane
- ANNcoord box_diff = cd_bnds[ANN_LO] - ANNprQ[cut_dim];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- new_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- if (child[ANN_HI] != KD_TRIVIAL)// enqueue if not trivial
- ANNprBoxPQ->insert(new_dist, child[ANN_HI]);
- // continue with closer child
- child[ANN_LO]->ann_pri_search(box_dist);
- }
- else { // right of cutting plane
- ANNcoord box_diff = ANNprQ[cut_dim] - cd_bnds[ANN_HI];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- new_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- if (child[ANN_LO] != KD_TRIVIAL)// enqueue if not trivial
- ANNprBoxPQ->insert(new_dist, child[ANN_LO]);
- // continue with closer child
- child[ANN_HI]->ann_pri_search(box_dist);
- }
- ANN_SPL(1) // one more splitting node visited
- ANN_FLOP(8) // increment floating ops
-}
-
-//----------------------------------------------------------------------
-// kd_leaf::ann_pri_search - search points in a leaf node
-//
-// This is virtually identical to the ann_search for standard search.
-//----------------------------------------------------------------------
-
-void ANNkd_leaf::ann_pri_search(ANNdist box_dist)
-{
- register ANNdist dist; // distance to data point
- register ANNcoord* pp; // data coordinate pointer
- register ANNcoord* qq; // query coordinate pointer
- register ANNdist min_dist; // distance to k-th closest point
- register ANNcoord t;
- register int d;
-
- min_dist = ANNprPointMK->max_key(); // k-th smallest distance so far
-
- for (int i = 0; i < n_pts; i++) { // check points in bucket
-
- pp = ANNprPts[bkt[i]]; // first coord of next data point
- qq = ANNprQ; // first coord of query point
- dist = 0;
-
- for(d = 0; d < ANNprDim; d++) {
- ANN_COORD(1) // one more coordinate hit
- ANN_FLOP(4) // increment floating ops
-
- t = *(qq++) - *(pp++); // compute length and adv coordinate
- // exceeds dist to k-th smallest?
- if( (dist = ANN_SUM(dist, ANN_POW(t))) > min_dist) {
- break;
- }
- }
-
- if (d >= ANNprDim && // among the k best?
- (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
- // add it to the list
- ANNprPointMK->insert(dist, bkt[i]);
- min_dist = ANNprPointMK->max_key();
- }
- }
- ANN_LEAF(1) // one more leaf node visited
- ANN_PTS(n_pts) // increment points visited
- ANNptsVisited += n_pts; // increment number of points visited
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_search.cpp b/geom_bottleneck/bottleneck/src/ann/kd_search.cpp
deleted file mode 100644
index f559eb9..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_search.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_search.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Standard kd-tree search
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Changed names LO, HI to ANN_LO, ANN_HI
-// --------------------------------------------------------------------
-// 2015 - modified by A. Nigmetov to support deletion of points
-//----------------------------------------------------------------------
-
-#include "kd_search.h" // kd-search declarations
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Approximate nearest neighbor searching by kd-tree search
-// The kd-tree is searched for an approximate nearest neighbor.
-// The point is returned through one of the arguments, and the
-// distance returned is the squared distance to this point.
-//
-// The method used for searching the kd-tree is an approximate
-// adaptation of the search algorithm described by Friedman,
-// Bentley, and Finkel, ``An algorithm for finding best matches
-// in logarithmic expected time,'' ACM Transactions on Mathematical
-// Software, 3(3):209-226, 1977).
-//
-// The algorithm operates recursively. When first encountering a
-// node of the kd-tree we first visit the child which is closest to
-// the query point. On return, we decide whether we want to visit
-// the other child. If the box containing the other child exceeds
-// 1/(1+eps) times the current best distance, then we skip it (since
-// any point found in this child cannot be closer to the query point
-// by more than this factor.) Otherwise, we visit it recursively.
-// The distance between a box and the query point is computed exactly
-// (not approximated as is often done in kd-tree), using incremental
-// distance updates, as described by Arya and Mount in ``Algorithms
-// for fast vector quantization,'' Proc. of DCC '93: Data Compression
-// Conference, eds. J. A. Storer and M. Cohn, IEEE Press, 1993,
-// 381-390.
-//
-// The main entry points is annkSearch() which sets things up and
-// then call the recursive routine ann_search(). This is a recursive
-// routine which performs the processing for one node in the kd-tree.
-// There are two versions of this virtual procedure, one for splitting
-// nodes and one for leaves. When a splitting node is visited, we
-// determine which child to visit first (the closer one), and visit
-// the other child on return. When a leaf is visited, we compute
-// the distances to the points in the buckets, and update information
-// on the closest points.
-//
-// Some trickery is used to incrementally update the distance from
-// a kd-tree rectangle to the query point. This comes about from
-// the fact that which each successive split, only one component
-// (along the dimension that is split) of the squared distance to
-// the child rectangle is different from the squared distance to
-// the parent rectangle.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// To keep argument lists short, a number of global variables
-// are maintained which are common to all the recursive calls.
-// These are given below.
-//----------------------------------------------------------------------
-
-int ANNkdDim; // dimension of space
-ANNpoint ANNkdQ; // query point
-double ANNkdMaxErr; // max tolerable squared error
-ANNpointArray ANNkdPts; // the points
-ANNmin_k *ANNkdPointMK; // set of k closest points
-
-//----------------------------------------------------------------------
-// annkSearch - search for the k nearest neighbors
-//----------------------------------------------------------------------
-
-void ANNkd_tree::annkSearch(
- ANNpoint q, // the query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor indices (returned)
- ANNdistArray dd, // the approximate nearest neighbor
- double eps) // the error bound
-{
-
- ANNkdDim = dim; // copy arguments to static equivs
- ANNkdQ = q;
- ANNkdPts = pts;
- ANNptsVisited = 0; // initialize count of points visited
-
- if (k > actual_num_points) { // too many near neighbors?
- annError("Requesting more near neighbors than data points", ANNabort);
- }
-
- ANNkdMaxErr = ANN_POW(1.0 + eps);
- ANN_FLOP(2) // increment floating op count
-
- ANNkdPointMK = new ANNmin_k(k); // create set for closest k points
- // search starting at the root
- root->ann_search(annBoxDistance(q, bnd_box_lo, bnd_box_hi, dim));
-
- for (int i = 0; i < k; i++) { // extract the k-th closest points
- dd[i] = ANNkdPointMK->ith_smallest_key(i);
- nn_idx[i] = ANNkdPointMK->ith_smallest_info(i);
- }
- delete ANNkdPointMK; // deallocate closest point set
-}
-
-//----------------------------------------------------------------------
-// kd_split::ann_search - search a splitting node
-//----------------------------------------------------------------------
-
-void ANNkd_split::ann_search(ANNdist box_dist)
-{
- // check if the subtree is empty
- if (0 == actual_num_points) return;
- // check dist calc term condition
- if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
-
- // distance to cutting plane
- ANNcoord cut_diff = ANNkdQ[cut_dim] - cut_val;
-
- if (cut_diff < 0) { // left of cutting plane
- child[ANN_LO]->ann_search(box_dist);// visit closer child first
-
- ANNcoord box_diff = cd_bnds[ANN_LO] - ANNkdQ[cut_dim];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- box_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- // visit further child if close enough
- if (box_dist * ANNkdMaxErr < ANNkdPointMK->max_key())
- child[ANN_HI]->ann_search(box_dist);
-
- }
- else { // right of cutting plane
- child[ANN_HI]->ann_search(box_dist);// visit closer child first
-
- ANNcoord box_diff = ANNkdQ[cut_dim] - cd_bnds[ANN_HI];
- if (box_diff < 0) // within bounds - ignore
- box_diff = 0;
- // distance to further box
- box_dist = (ANNdist) ANN_SUM(box_dist,
- ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
-
- // visit further child if close enough
- if (box_dist * ANNkdMaxErr < ANNkdPointMK->max_key())
- child[ANN_LO]->ann_search(box_dist);
-
- }
- ANN_FLOP(10) // increment floating ops
- ANN_SPL(1) // one more splitting node visited
-}
-
-//----------------------------------------------------------------------
-// kd_leaf::ann_search - search points in a leaf node
-// Note: The unreadability of this code is the result of
-// some fine tuning to replace indexing by pointer operations.
-//----------------------------------------------------------------------
-
-void ANNkd_leaf::ann_search(ANNdist box_dist)
-{
- register ANNdist dist; // distance to data point
- register ANNcoord* pp; // data coordinate pointer
- register ANNcoord* qq; // query coordinate pointer
- register ANNdist min_dist; // distance to k-th closest point
- register ANNcoord t;
- register int d;
-
- min_dist = ANNkdPointMK->max_key(); // k-th smallest distance so far
-
- for (int i = 0; i < n_pts; i++) { // check points in bucket
-
- pp = ANNkdPts[bkt[i]]; // first coord of next data point
- qq = ANNkdQ; // first coord of query point
- dist = 0;
-
- for(d = 0; d < ANNkdDim; d++) {
- ANN_COORD(1) // one more coordinate hit
- ANN_FLOP(4) // increment floating ops
-
- t = *(qq++) - *(pp++); // compute length and adv coordinate
- // exceeds dist to k-th smallest?
- if( (dist = ANN_SUM(dist, ANN_POW(t))) > min_dist) {
- break;
- }
- }
-
- if (d >= ANNkdDim && // among the k best?
- (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
- // add it to the list
- ANNkdPointMK->insert(dist, bkt[i]);
- min_dist = ANNkdPointMK->max_key();
- }
- }
- ANN_LEAF(1) // one more leaf node visited
- ANN_PTS(n_pts) // increment points visited
- ANNptsVisited += n_pts; // increment number of points visited
-}
-
-
-
-////////////////////////////////////////////////
-// range search
-// ////////////////////////////////////////////
-
-void ANNkd_tree::range_search(const ANNorthRect& region,
- std::vector<size_t>& point_indices)
-{
-
- // get bounding box of the root
- ANNorthRect bnd_box = ANNorthRect(dim, bnd_box_lo, bnd_box_hi);
- root->range_search(region, dim, pts, bnd_box, point_indices);
-}
-
-void ANNkd_split::range_search(const ANNorthRect& region,
- int ANNkdDim,
- ANNpointArray ANNkdPts,
- ANNorthRect& bnd_box,
- std::vector<size_t>& point_indices)
-{
- // check if the subtree is empty
- if (0 == actual_num_points) return;
-
- // process left child
- ANNcoord old_bnd_box_val = bnd_box.hi[cut_dim];
- bnd_box.hi[cut_dim] = cut_val;
- if (region.contains(ANNkdDim, bnd_box)) {
- child[ANN_LO]->range_search_add(point_indices);
- } else if (region.intersects(ANNkdDim, bnd_box)) {
- child[ANN_LO]->range_search(region, ANNkdDim, ANNkdPts, bnd_box, point_indices);
- }
- // restore bounding box
- bnd_box.hi[cut_dim] = old_bnd_box_val;
- // process right child
- old_bnd_box_val = bnd_box.lo[cut_dim];
- bnd_box.lo[cut_dim] = cut_val;
- if (region.contains(ANNkdDim, bnd_box)) {
- child[ANN_HI]->range_search_add(point_indices);
- } else if (region.intersects(ANNkdDim, bnd_box)) {
- child[ANN_HI]->range_search(region, ANNkdDim, ANNkdPts, bnd_box, point_indices);
- }
- // restore bounding box
- bnd_box.lo[cut_dim] = old_bnd_box_val;
-}
-
-void ANNkd_leaf::range_search(const ANNorthRect& region,
- int ANNkdDim,
- ANNpointArray ANNkdPts,
- ANNorthRect&, // nameless parameter to suppress
- // warnings and allow recursion
- // in splitting node
- std::vector<size_t>& point_indices)
-{
- for (int i = 0; i < n_pts; i++) { // check points in bucket
- if (region.inside(ANNkdDim, ANNkdPts[bkt[i]]) == ANNtrue) {
- //std::cout << "adding point, i = " << i;
- //std::cout << ", x = " << ANNkdPts[bkt[i]][0];
- //std::cout << ", y = " << ANNkdPts[bkt[i]][1] << std::endl;
- point_indices.push_back(bkt[i]);
- }
- }
-}
-
-void ANNkd_split::range_search_add(std::vector<size_t>& point_indices)
-{
- if ( 0 == actual_num_points )
- return;
- child[ANN_LO]->range_search_add(point_indices);
- child[ANN_HI]->range_search_add(point_indices);
-}
-
-void ANNkd_leaf::range_search_add(std::vector<size_t>& point_indices)
-{
- if ( 0 == actual_num_points )
- return;
- for (int i = 0; i < n_pts; i++) { // add all points in a bucket
- //std::cout << "adding point without checking, i = " << i <<", bkt[i] = " << bkt[i] << std::endl;
- point_indices.push_back(bkt[i]);
- }
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_split.cpp b/geom_bottleneck/bottleneck/src/ann/kd_split.cpp
deleted file mode 100644
index 7979aaa..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_split.cpp
+++ /dev/null
@@ -1,632 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_split.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Methods for splitting kd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-//----------------------------------------------------------------------
-
-#include "kd_tree.h" // kd-tree definitions
-#include "kd_util.h" // kd-tree utilities
-#include "kd_split.h" // splitting functions
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Constants
-//----------------------------------------------------------------------
-
-const double ERR = 0.001; // a small value
-const double FS_ASPECT_RATIO = 3.0; // maximum allowed aspect ratio
- // in fair split. Must be >= 2.
-
-//----------------------------------------------------------------------
-// NOTE: Virtually all point indexing is done through an index (i.e.
-// permutation) array pidx. Consequently, a reference to the d-th
-// coordinate of the i-th point is pa[pidx[i]][d]. The macro PA(i,d)
-// is a shorthand for this.
-//----------------------------------------------------------------------
- // standard 2-d indirect indexing
-#define PA(i,d) (pa[pidx[(i)]][(d)])
- // accessing a single point
-#define PP(i) (pa[pidx[(i)]])
-
-
-//----------------------------------------------------------------------
-// kd_split - Bentley's standard splitting routine for kd-trees
-// Find the dimension of the greatest spread, and split
-// just before the median point along this dimension.
-//----------------------------------------------------------------------
-
-void kd_split(
- ANNpointArray pa, // point array (permuted on return)
- ANNidxArray pidx, // point indices
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo) // num of points on low side (returned)
-{
- // find dimension of maximum spread
- cut_dim = annMaxSpread(pa, pidx, n, dim);
- n_lo = n/2; // median rank
- // split about median
- annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
-}
-
-//----------------------------------------------------------------------
-// midpt_split - midpoint splitting rule for box-decomposition trees
-//
-// This is the simplest splitting rule that guarantees boxes
-// of bounded aspect ratio. It simply cuts the box with the
-// longest side through its midpoint. If there are ties, it
-// selects the dimension with the maximum point spread.
-//
-// WARNING: This routine (while simple) doesn't seem to work
-// well in practice in high dimensions, because it tends to
-// generate a large number of trivial and/or unbalanced splits.
-// Either kd_split(), sl_midpt_split(), or fair_split() are
-// recommended, instead.
-//----------------------------------------------------------------------
-
-void midpt_split(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo) // num of points on low side (returned)
-{
- int d;
-
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- }
- }
- ANNcoord max_spread = -1; // find long side with most spread
- for (d = 0; d < dim; d++) {
- // is it among longest?
- if (double(bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
- // compute its spread
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // is it max so far?
- max_spread = spr;
- cut_dim = d;
- }
- }
- }
- // split along cut_dim at midpoint
- cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim]) / 2;
- // permute points accordingly
- int br1, br2;
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- //------------------------------------------------------------------
- // On return: pa[0..br1-1] < cut_val
- // pa[br1..br2-1] == cut_val
- // pa[br2..n-1] > cut_val
- //
- // We can set n_lo to any value in the range [br1..br2].
- // We choose split so that points are most evenly divided.
- //------------------------------------------------------------------
- if (br1 > n/2) n_lo = br1;
- else if (br2 < n/2) n_lo = br2;
- else n_lo = n/2;
-}
-
-//----------------------------------------------------------------------
-// sl_midpt_split - sliding midpoint splitting rule
-//
-// This is a modification of midpt_split, which has the nonsensical
-// name "sliding midpoint". The idea is that we try to use the
-// midpoint rule, by bisecting the longest side. If there are
-// ties, the dimension with the maximum spread is selected. If,
-// however, the midpoint split produces a trivial split (no points
-// on one side of the splitting plane) then we slide the splitting
-// (maintaining its orientation) until it produces a nontrivial
-// split. For example, if the splitting plane is along the x-axis,
-// and all the data points have x-coordinate less than the x-bisector,
-// then the split is taken along the maximum x-coordinate of the
-// data points.
-//
-// Intuitively, this rule cannot generate trivial splits, and
-// hence avoids midpt_split's tendency to produce trees with
-// a very large number of nodes.
-//
-//----------------------------------------------------------------------
-
-void sl_midpt_split(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo) // num of points on low side (returned)
-{
- int d;
-
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- }
- }
- ANNcoord max_spread = -1; // find long side with most spread
- for (d = 0; d < dim; d++) {
- // is it among longest?
- if ((bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
- // compute its spread
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // is it max so far?
- max_spread = spr;
- cut_dim = d;
- }
- }
- }
- // ideal split at midpoint
- ANNcoord ideal_cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim])/2;
-
- ANNcoord min, max;
- annMinMax(pa, pidx, n, cut_dim, min, max); // find min/max coordinates
-
- if (ideal_cut_val < min) // slide to min or max as needed
- cut_val = min;
- else if (ideal_cut_val > max)
- cut_val = max;
- else
- cut_val = ideal_cut_val;
-
- // permute points accordingly
- int br1, br2;
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- //------------------------------------------------------------------
- // On return: pa[0..br1-1] < cut_val
- // pa[br1..br2-1] == cut_val
- // pa[br2..n-1] > cut_val
- //
- // We can set n_lo to any value in the range [br1..br2] to satisfy
- // the exit conditions of the procedure.
- //
- // if ideal_cut_val < min (implying br2 >= 1),
- // then we select n_lo = 1 (so there is one point on left) and
- // if ideal_cut_val > max (implying br1 <= n-1),
- // then we select n_lo = n-1 (so there is one point on right).
- // Otherwise, we select n_lo as close to n/2 as possible within
- // [br1..br2].
- //------------------------------------------------------------------
- if (ideal_cut_val < min) n_lo = 1;
- else if (ideal_cut_val > max) n_lo = n-1;
- else if (br1 > n/2) n_lo = br1;
- else if (br2 < n/2) n_lo = br2;
- else n_lo = n/2;
-}
-
-//----------------------------------------------------------------------
-// fair_split - fair-split splitting rule
-//
-// This is a compromise between the kd-tree splitting rule (which
-// always splits data points at their median) and the midpoint
-// splitting rule (which always splits a box through its center.
-// The goal of this procedure is to achieve both nicely balanced
-// splits, and boxes of bounded aspect ratio.
-//
-// A constant FS_ASPECT_RATIO is defined. Given a box, those sides
-// which can be split so that the ratio of the longest to shortest
-// side does not exceed ASPECT_RATIO are identified. Among these
-// sides, we select the one in which the points have the largest
-// spread. We then split the points in a manner which most evenly
-// distributes the points on either side of the splitting plane,
-// subject to maintaining the bound on the ratio of long to short
-// sides. To determine that the aspect ratio will be preserved,
-// we determine the longest side (other than this side), and
-// determine how narrowly we can cut this side, without causing the
-// aspect ratio bound to be exceeded (small_piece).
-//
-// This procedure is more robust than either kd_split or midpt_split,
-// but is more complicated as well. When point distribution is
-// extremely skewed, this degenerates to midpt_split (actually
-// 1/3 point split), and when the points are most evenly distributed,
-// this degenerates to kd-split.
-//----------------------------------------------------------------------
-
-void fair_split(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo) // num of points on low side (returned)
-{
- int d;
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- cut_dim = 0;
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- cut_dim = d;
- }
- }
-
- ANNcoord max_spread = 0; // find legal cut with max spread
- cut_dim = 0;
- for (d = 0; d < dim; d++) {
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- // is this side midpoint splitable
- // without violating aspect ratio?
- if (((double) max_length)*2.0/((double) length) <= FS_ASPECT_RATIO) {
- // compute spread along this dim
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // best spread so far
- max_spread = spr;
- cut_dim = d; // this is dimension to cut
- }
- }
- }
-
- max_length = 0; // find longest side other than cut_dim
- for (d = 0; d < dim; d++) {
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (d != cut_dim && length > max_length)
- max_length = length;
- }
- // consider most extreme splits
- ANNcoord small_piece = max_length / FS_ASPECT_RATIO;
- ANNcoord lo_cut = bnds.lo[cut_dim] + small_piece;// lowest legal cut
- ANNcoord hi_cut = bnds.hi[cut_dim] - small_piece;// highest legal cut
-
- int br1, br2;
- // is median below lo_cut ?
- if (annSplitBalance(pa, pidx, n, cut_dim, lo_cut) >= 0) {
- cut_val = lo_cut; // cut at lo_cut
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = br1;
- }
- // is median above hi_cut?
- else if (annSplitBalance(pa, pidx, n, cut_dim, hi_cut) <= 0) {
- cut_val = hi_cut; // cut at hi_cut
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = br2;
- }
- else { // median cut preserves asp ratio
- n_lo = n/2; // split about median
- annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
- }
-}
-
-//----------------------------------------------------------------------
-// sl_fair_split - sliding fair split splitting rule
-//
-// Sliding fair split is a splitting rule that combines the
-// strengths of both fair split with sliding midpoint split.
-// Fair split tends to produce balanced splits when the points
-// are roughly uniformly distributed, but it can produce many
-// trivial splits when points are highly clustered. Sliding
-// midpoint never produces trivial splits, and shrinks boxes
-// nicely if points are highly clustered, but it may produce
-// rather unbalanced splits when points are unclustered but not
-// quite uniform.
-//
-// Sliding fair split is based on the theory that there are two
-// types of splits that are "good": balanced splits that produce
-// fat boxes, and unbalanced splits provided the cell with fewer
-// points is fat.
-//
-// This splitting rule operates by first computing the longest
-// side of the current bounding box. Then it asks which sides
-// could be split (at the midpoint) and still satisfy the aspect
-// ratio bound with respect to this side. Among these, it selects
-// the side with the largest spread (as fair split would). It
-// then considers the most extreme cuts that would be allowed by
-// the aspect ratio bound. This is done by dividing the longest
-// side of the box by the aspect ratio bound. If the median cut
-// lies between these extreme cuts, then we use the median cut.
-// If not, then consider the extreme cut that is closer to the
-// median. If all the points lie to one side of this cut, then
-// we slide the cut until it hits the first point. This may
-// violate the aspect ratio bound, but will never generate empty
-// cells. However the sibling of every such skinny cell is fat,
-// and hence packing arguments still apply.
-//
-//----------------------------------------------------------------------
-
-void sl_fair_split(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo) // num of points on low side (returned)
-{
- int d;
- ANNcoord min, max; // min/max coordinates
- int br1, br2; // split break points
-
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- cut_dim = 0;
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- cut_dim = d;
- }
- }
-
- ANNcoord max_spread = 0; // find legal cut with max spread
- cut_dim = 0;
- for (d = 0; d < dim; d++) {
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- // is this side midpoint splitable
- // without violating aspect ratio?
- if (((double) max_length)*2.0/((double) length) <= FS_ASPECT_RATIO) {
- // compute spread along this dim
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // best spread so far
- max_spread = spr;
- cut_dim = d; // this is dimension to cut
- }
- }
- }
-
- max_length = 0; // find longest side other than cut_dim
- for (d = 0; d < dim; d++) {
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (d != cut_dim && length > max_length)
- max_length = length;
- }
- // consider most extreme splits
- ANNcoord small_piece = max_length / FS_ASPECT_RATIO;
- ANNcoord lo_cut = bnds.lo[cut_dim] + small_piece;// lowest legal cut
- ANNcoord hi_cut = bnds.hi[cut_dim] - small_piece;// highest legal cut
- // find min and max along cut_dim
- annMinMax(pa, pidx, n, cut_dim, min, max);
- // is median below lo_cut?
- if (annSplitBalance(pa, pidx, n, cut_dim, lo_cut) >= 0) {
- if (max > lo_cut) { // are any points above lo_cut?
- cut_val = lo_cut; // cut at lo_cut
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = br1; // balance if there are ties
- }
- else { // all points below lo_cut
- cut_val = max; // cut at max value
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = n-1;
- }
- }
- // is median above hi_cut?
- else if (annSplitBalance(pa, pidx, n, cut_dim, hi_cut) <= 0) {
- if (min < hi_cut) { // are any points below hi_cut?
- cut_val = hi_cut; // cut at hi_cut
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = br2; // balance if there are ties
- }
- else { // all points above hi_cut
- cut_val = min; // cut at min value
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- n_lo = 1;
- }
- }
- else { // median cut is good enough
- n_lo = n/2; // split about median
- annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
- }
-}
-
-
-/////////////////////////////////////////////////////////////////////////////////
-// for kd-trees with deletion
-//
-//----------------------------------------------------------------------
-// kd_split - Bentley's standard splitting routine for kd-trees
-// Find the dimension of the greatest spread, and split
-// just before the median point along this dimension.
-//----------------------------------------------------------------------
-
-void kd_split_wd(
- ANNpointArray pa, // point array (permuted on return)
- ANNidxArray pidx, // point indices
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx) // index of cutting point (returned)
-{
- // find dimension of maximum spread
- cut_dim = annMaxSpread(pa, pidx, n, dim);
- n_lo = n/2; // median rank
- // split about median
- annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
- cut_pt_idx = n_lo;
- cut_val = PA(cut_pt_idx, cut_dim);
-}
-
-//----------------------------------------------------------------------
-// midpt_split - midpoint splitting rule for box-decomposition trees
-//
-// This is the simplest splitting rule that guarantees boxes
-// of bounded aspect ratio. It simply cuts the box with the
-// longest side through its midpoint. If there are ties, it
-// selects the dimension with the maximum point spread.
-//
-// WARNING: This routine (while simple) doesn't seem to work
-// well in practice in high dimensions, because it tends to
-// generate a large number of trivial and/or unbalanced splits.
-// Either kd_split(), sl_midpt_split(), or fair_split() are
-// recommended, instead.
-//----------------------------------------------------------------------
-
-void midpt_split_wd(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx) // index of cutting point (returned)
-{
- int d;
-
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- }
- }
- ANNcoord max_spread = -1; // find long side with most spread
- for (d = 0; d < dim; d++) {
- // is it among longest?
- if (double(bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
- // compute its spread
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // is it max so far?
- max_spread = spr;
- cut_dim = d;
- }
- }
- }
- // split along cut_dim at midpoint
- cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim]) / 2;
- // permute points accordingly
- int br1, br2;
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- //------------------------------------------------------------------
- // On return: pa[0..br1-1] < cut_val
- // pa[br1..br2-1] == cut_val
- // pa[br2..n-1] > cut_val
- //
- // We can set n_lo to any value in the range [br1..br2].
- // We choose split so that points are most evenly divided.
- //------------------------------------------------------------------
- if (br1 > n/2) n_lo = br1;
- else if (br2 < n/2) n_lo = br2;
- else n_lo = n/2;
-
- cut_pt_idx = n_lo;
- cut_val = PA(cut_pt_idx, cut_dim);
-
-}
-
-//----------------------------------------------------------------------
-// sl_midpt_split - sliding midpoint splitting rule
-//
-// This is a modification of midpt_split, which has the nonsensical
-// name "sliding midpoint". The idea is that we try to use the
-// midpoint rule, by bisecting the longest side. If there are
-// ties, the dimension with the maximum spread is selected. If,
-// however, the midpoint split produces a trivial split (no points
-// on one side of the splitting plane) then we slide the splitting
-// (maintaining its orientation) until it produces a nontrivial
-// split. For example, if the splitting plane is along the x-axis,
-// and all the data points have x-coordinate less than the x-bisector,
-// then the split is taken along the maximum x-coordinate of the
-// data points.
-//
-// Intuitively, this rule cannot generate trivial splits, and
-// hence avoids midpt_split's tendency to produce trees with
-// a very large number of nodes.
-//
-//----------------------------------------------------------------------
-
-void sl_midpt_split_wd(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices (permuted on return)
- const ANNorthRect &bnds, // bounding rectangle for cell
- int n, // number of points
- int dim, // dimension of space
- int &cut_dim, // cutting dimension (returned)
- ANNcoord &cut_val, // cutting value (returned)
- int &n_lo, // num of points on low side (returned)
- int &cut_pt_idx) // index of cutting point (returned)
-{
- int d;
-
- ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
- for (d = 1; d < dim; d++) { // find length of longest box side
- ANNcoord length = bnds.hi[d] - bnds.lo[d];
- if (length > max_length) {
- max_length = length;
- }
- }
- ANNcoord max_spread = -1; // find long side with most spread
- for (d = 0; d < dim; d++) {
- // is it among longest?
- if ((bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
- // compute its spread
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spread) { // is it max so far?
- max_spread = spr;
- cut_dim = d;
- }
- }
- }
- // ideal split at midpoint
- ANNcoord ideal_cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim])/2;
-
- ANNcoord min, max;
- annMinMax(pa, pidx, n, cut_dim, min, max); // find min/max coordinates
-
- if (ideal_cut_val < min) // slide to min or max as needed
- cut_val = min;
- else if (ideal_cut_val > max)
- cut_val = max;
- else
- cut_val = ideal_cut_val;
-
- // permute points accordingly
- int br1, br2;
- annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
- //------------------------------------------------------------------
- // On return: pa[0..br1-1] < cut_val
- // pa[br1..br2-1] == cut_val
- // pa[br2..n-1] > cut_val
- //
- // We can set n_lo to any value in the range [br1..br2] to satisfy
- // the exit conditions of the procedure.
- //
- // if ideal_cut_val < min (implying br2 >= 1),
- // then we select n_lo = 1 (so there is one point on left) and
- // if ideal_cut_val > max (implying br1 <= n-1),
- // then we select n_lo = n-1 (so there is one point on right).
- // Otherwise, we select n_lo as close to n/2 as possible within
- // [br1..br2].
- //------------------------------------------------------------------
- if (ideal_cut_val < min) n_lo = 1;
- else if (ideal_cut_val > max) n_lo = n-1;
- else if (br1 > n/2) n_lo = br1;
- else if (br2 < n/2) n_lo = br2;
- else n_lo = n/2;
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_tree.cpp b/geom_bottleneck/bottleneck/src/ann/kd_tree.cpp
deleted file mode 100644
index e8f7f63..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_tree.cpp
+++ /dev/null
@@ -1,566 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_tree.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Basic methods for kd-trees.
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.0 04/01/05
-// Increased aspect ratio bound (ANN_AR_TOOBIG) from 100 to 1000.
-// Fixed leaf counts to count trivial leaves.
-// Added optional pa, pi arguments to Skeleton kd_tree constructor
-// for use in load constructor.
-// Added annClose() to eliminate KD_TRIVIAL memory leak.
-// --------------------------------------------------------------------
-// 2015 - modified by A. Nigmetov to support deletion of points
-//----------------------------------------------------------------------
-
-#ifdef _WIN32
-#include <ciso646> // make VS more conformal
-#endif
-
-#include "kd_tree.h" // kd-tree declarations
-#include "kd_split.h" // kd-tree splitting rules
-#include "kd_util.h" // kd-tree utilities
-#include <ANN/ANNperf.h> // performance evaluation
-#include "def_debug_bt.h"
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// Global data
-//
-// For some splitting rules, especially with small bucket sizes,
-// it is possible to generate a large number of empty leaf nodes.
-// To save storage we allocate a single trivial leaf node which
-// contains no points. For messy coding reasons it is convenient
-// to have it reference a trivial point index.
-//
-// KD_TRIVIAL is allocated when the first kd-tree is created. It
-// must *never* deallocated (since it may be shared by more than
-// one tree).
-//----------------------------------------------------------------------
-static int IDX_TRIVIAL[] = {0}; // trivial point index
-ANNkd_leaf *KD_TRIVIAL = NULL; // trivial leaf node
-
-//----------------------------------------------------------------------
-// Printing the kd-tree
-// These routines print a kd-tree in reverse inorder (high then
-// root then low). (This is so that if you look at the output
-// from the right side it appear from left to right in standard
-// inorder.) When outputting leaves we output only the point
-// indices rather than the point coordinates. There is an option
-// to print the point coordinates separately.
-//
-// The tree printing routine calls the printing routines on the
-// individual nodes of the tree, passing in the level or depth
-// in the tree. The level in the tree is used to print indentation
-// for readability.
-//----------------------------------------------------------------------
-
-void ANNkd_split::print( // print splitting node
- int level, // depth of node in tree
- ostream &out) // output stream
-{
-#ifndef FOR_R_TDA
- child[ANN_HI]->print(level+1, out); // print high child
- out << " ";
- for (int i = 0; i < level; i++) // print indentation
- out << "..";
- out << "Split cd=" << cut_dim << " cv=" << cut_val;
- out << " lbnd=" << cd_bnds[ANN_LO];
- out << " hbnd=" << cd_bnds[ANN_HI];
- out << " np=" << actual_num_points;
- out << "\n";
- child[ANN_LO]->print(level+1, out); // print low child
-#endif
-}
-
-void ANNkd_leaf::print( // print leaf node
- int level, // depth of node in tree
- ostream &out) // output stream
-{
-#ifndef FOR_R_TDA
- out << " ";
- for (int i = 0; i < level; i++) // print indentation
- out << "..";
-
- if (this == KD_TRIVIAL) { // canonical trivial leaf node
- out << "Leaf (trivial)\n";
- }
- else{
- out << "Leaf n=" << n_pts << " <";
- for (int j = 0; j < n_pts; j++) {
- out << bkt[j];
- if (j < n_pts-1) out << ",";
- }
- out << ">\n";
- }
-#endif
-}
-
-#ifndef FOR_R_TDA
-void ANNkd_tree::Print( // print entire tree
- ANNbool with_pts, // print points as well?
- ostream &out) // output stream
-{
- out << "ANN Version " << ANNversion << "\n";
- if (with_pts) { // print point coordinates
- out << " Points:\n";
- for (int i = 0; i < n_pts; i++) {
- out << "\t" << i << ": ";
- annPrintPt(pts[i], dim, out);
- out << "\n";
- }
- }
- if (root == NULL) // empty tree?
- out << " Null tree.\n";
- else {
- root->print(0, out); // invoke printing at root
- }
-}
-#endif
-
-//----------------------------------------------------------------------
-// kd_tree statistics (for performance evaluation)
-// This routine compute various statistics information for
-// a kd-tree. It is used by the implementors for performance
-// evaluation of the data structure.
-//----------------------------------------------------------------------
-
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-
-void ANNkdStats::merge(const ANNkdStats &st) // merge stats from child
-{
- n_lf += st.n_lf; n_tl += st.n_tl;
- n_spl += st.n_spl; n_shr += st.n_shr;
- depth = MAX(depth, st.depth);
- sum_ar += st.sum_ar;
-}
-
-//----------------------------------------------------------------------
-// Update statistics for nodes
-//----------------------------------------------------------------------
-
-const double ANN_AR_TOOBIG = 1000; // too big an aspect ratio
-
-void ANNkd_leaf::getStats( // get subtree statistics
- int dim, // dimension of space
- ANNkdStats &st, // stats (modified)
- ANNorthRect &bnd_box) // bounding box
-{
- st.reset();
- st.n_lf = 1; // count this leaf
- if (this == KD_TRIVIAL) st.n_tl = 1; // count trivial leaf
- double ar = annAspectRatio(dim, bnd_box); // aspect ratio of leaf
- // incr sum (ignore outliers)
- st.sum_ar += float(ar < ANN_AR_TOOBIG ? ar : ANN_AR_TOOBIG);
-}
-
-void ANNkd_split::getStats( // get subtree statistics
- int dim, // dimension of space
- ANNkdStats &st, // stats (modified)
- ANNorthRect &bnd_box) // bounding box
-{
- ANNkdStats ch_stats; // stats for children
- // get stats for low child
- ANNcoord hv = bnd_box.hi[cut_dim]; // save box bounds
- bnd_box.hi[cut_dim] = cut_val; // upper bound for low child
- ch_stats.reset(); // reset
- child[ANN_LO]->getStats(dim, ch_stats, bnd_box);
- st.merge(ch_stats); // merge them
- bnd_box.hi[cut_dim] = hv; // restore bound
- // get stats for high child
- ANNcoord lv = bnd_box.lo[cut_dim]; // save box bounds
- bnd_box.lo[cut_dim] = cut_val; // lower bound for high child
- ch_stats.reset(); // reset
- child[ANN_HI]->getStats(dim, ch_stats, bnd_box);
- st.merge(ch_stats); // merge them
- bnd_box.lo[cut_dim] = lv; // restore bound
-
- st.depth++; // increment depth
- st.n_spl++; // increment number of splits
-}
-
-//----------------------------------------------------------------------
-// getStats
-// Collects a number of statistics related to kd_tree or
-// bd_tree.
-//----------------------------------------------------------------------
-
-void ANNkd_tree::getStats( // get tree statistics
- ANNkdStats &st) // stats (modified)
-{
- st.reset(dim, n_pts, bkt_size); // reset stats
- // create bounding box
- ANNorthRect bnd_box(dim, bnd_box_lo, bnd_box_hi);
- if (root != NULL) { // if nonempty tree
- root->getStats(dim, st, bnd_box); // get statistics
- st.avg_ar = st.sum_ar / st.n_lf; // average leaf asp ratio
- }
-}
-
-//----------------------------------------------------------------------
-// kd_tree destructor
-// The destructor just frees the various elements that were
-// allocated in the construction process.
-//----------------------------------------------------------------------
-
-ANNkd_tree::~ANNkd_tree() // tree destructor
-{
- if (root != NULL and root != KD_TRIVIAL) delete root;
- if (pidx != NULL) delete [] pidx;
- if (bnd_box_lo != NULL) annDeallocPt(bnd_box_lo);
- if (bnd_box_hi != NULL) annDeallocPt(bnd_box_hi);
-}
-
-//----------------------------------------------------------------------
-// This is called with all use of ANN is finished. It eliminates the
-// minor memory leak caused by the allocation of KD_TRIVIAL.
-//----------------------------------------------------------------------
-void annClose() // close use of ANN
-{
- if (KD_TRIVIAL != NULL) {
- delete KD_TRIVIAL;
- KD_TRIVIAL = NULL;
- }
-}
-
-//----------------------------------------------------------------------
-// kd_tree constructors
-// There is a skeleton kd-tree constructor which sets up a
-// trivial empty tree. The last optional argument allows
-// the routine to be passed a point index array which is
-// assumed to be of the proper size (n). Otherwise, one is
-// allocated and initialized to the identity. Warning: In
-// either case the destructor will deallocate this array.
-//
-// As a kludge, we need to allocate KD_TRIVIAL if one has not
-// already been allocated. (This is because I'm too dumb to
-// figure out how to cause a pointer to be allocated at load
-// time.)
-//----------------------------------------------------------------------
-
-void ANNkd_tree::SkeletonTree( // construct skeleton tree
- int n, // number of points
- int dd, // dimension
- int bs, // bucket size
- ANNpointArray pa, // point array
- ANNidxArray pi) // point indices
-{
- dim = dd; // initialize basic elements
- n_pts = n;
- bkt_size = bs;
- pts = pa; // initialize points array
-
- root = NULL; // no associated tree yet
-
- if (pi == NULL) { // point indices provided?
- pidx = new ANNidx[n]; // no, allocate space for point indices
- for (int i = 0; i < n; i++) {
- pidx[i] = i; // initially identity
- }
- }
- else {
- pidx = pi; // yes, use them
- }
-
- bnd_box_lo = bnd_box_hi = NULL; // bounding box is nonexistent
- if (KD_TRIVIAL == NULL) // no trivial leaf node yet?
- KD_TRIVIAL = new ANNkd_leaf(0, IDX_TRIVIAL); // allocate it
-
- // for deletion
- pointToLeafVec.clear();
- pointToLeafVec.reserve(n_pts);
- for(int k = 0; k < n_pts; ++k) {
- pointToLeafVec.push_back(NULL);
- }
-}
-
-ANNkd_tree::ANNkd_tree( // basic constructor
- int n, // number of points
- int dd, // dimension
- int bs) // bucket size
-{ SkeletonTree(n, dd, bs); } // construct skeleton tree
-
-
-
-//----------------------------------------------------------------------
-// rkd_tree - recursive procedure to build a kd-tree
-//
-// Builds a kd-tree for points in pa as indexed through the
-// array pidx[0..n-1] (typically a subarray of the array used in
-// the top-level call). This routine permutes the array pidx,
-// but does not alter pa[].
-//
-// The construction is based on a standard algorithm for constructing
-// the kd-tree (see Friedman, Bentley, and Finkel, ``An algorithm for
-// finding best matches in logarithmic expected time,'' ACM Transactions
-// on Mathematical Software, 3(3):209-226, 1977). The procedure
-// operates by a simple divide-and-conquer strategy, which determines
-// an appropriate orthogonal cutting plane (see below), and splits
-// the points. When the number of points falls below the bucket size,
-// we simply store the points in a leaf node's bucket.
-//
-// One of the arguments is a pointer to a splitting routine,
-// whose prototype is:
-//
-// void split(
-// ANNpointArray pa, // complete point array
-// ANNidxArray pidx, // point array (permuted on return)
-// ANNorthRect &bnds, // bounds of current cell
-// int n, // number of points
-// int dim, // dimension of space
-// int &cut_dim, // cutting dimension
-// ANNcoord &cut_val, // cutting value
-// int &n_lo) // no. of points on low side of cut
-//
-// This procedure selects a cutting dimension and cutting value,
-// partitions pa about these values, and returns the number of
-// points on the low side of the cut.
-//----------------------------------------------------------------------
-
-ANNkd_ptr rkd_tree( // recursive construction of kd-tree
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- int bsp, // bucket space
- ANNorthRect &bnd_box, // bounding box for current node
- ANNkd_splitter splitter, // splitting routine
- vector<ANNkd_leaf*>* ppointToLeafVec)
-{
- if (n <= bsp) { // n small, make a leaf node
- if (n == 0) // empty leaf node
- return KD_TRIVIAL; // return (canonical) empty leaf
- else { // construct the node and return
- ANNkd_leaf* res = new ANNkd_leaf(n, pidx);
- if ( 1 == bsp) {
- (*ppointToLeafVec)[*pidx] = res;
- }
- return res;
- }
- }
- else { // n large, make a splitting node
- int cd; // cutting dimension
- ANNcoord cv; // cutting value
- int n_lo; // number on low side of cut
- ANNkd_node *lo, *hi; // low and high children
-
- // invoke splitting procedure
- (*splitter)(pa, pidx, bnd_box, n, dim, cd, cv, n_lo);
-
- ANNcoord lv = bnd_box.lo[cd]; // save bounds for cutting dimension
- ANNcoord hv = bnd_box.hi[cd];
-
- bnd_box.hi[cd] = cv; // modify bounds for left subtree
- lo = rkd_tree( // build left subtree
- pa, pidx, n_lo, // ...from pidx[0..n_lo-1]
- dim, bsp, bnd_box, splitter, ppointToLeafVec);
- bnd_box.hi[cd] = hv; // restore bounds
-
- bnd_box.lo[cd] = cv; // modify bounds for right subtree
- hi = rkd_tree( // build right subtree
- pa, pidx + n_lo, n-n_lo,// ...from pidx[n_lo..n-1]
- dim, bsp, bnd_box, splitter, ppointToLeafVec);
- bnd_box.lo[cd] = lv; // restore bounds
-
- // create the splitting node
- ANNkd_split *ptr = new ANNkd_split(cd, cv, lv, hv, lo, hi);
- if (lo != KD_TRIVIAL)
- lo->setParent(ptr);
- if (hi != KD_TRIVIAL)
- hi->setParent(ptr);
- ptr->setNumPoints(lo->getNumPoints() + hi->getNumPoints());
-
- return ptr; // return pointer to this node
- }
-}
-
-// for kd-trees with deletion
-/*
-ANNkd_ptr rkd_tree_wd( // recursive construction of kd-tree
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices to store in subtree
- int n, // number of points
- int dim, // dimension of space
- int bsp, // bucket space
- ANNorthRect &bnd_box, // bounding box for current node
- ANNkd_splitter_wd splitter) // splitting routine
-{
- ANNidx cut_pt_idx;
- if (n <= bsp) { // n small, make a leaf node
- if (n == 0) // empty leaf node
- return KD_TRIVIAL; // return (canonical) empty leaf
- else // construct the node and return
- return new ANNkd_leaf(n, pidx);
- }
- else { // n large, make a splitting node
- int cd; // cutting dimension
- ANNcoord cv; // cutting value
- int n_lo; // number on low side of cut
- ANNkd_node *lo, *hi; // low and high children
-
- // invoke splitting procedure
- (*splitter)(pa, pidx, bnd_box, n, dim, cd, cv, n_lo, cut_pt_idx);
-
- ANNcoord lv = bnd_box.lo[cd]; // save bounds for cutting dimension
- ANNcoord hv = bnd_box.hi[cd];
-
- bnd_box.hi[cd] = cv; // modify bounds for left subtree
- lo = rkd_tree_wd( // build left subtree
- pa, pidx, n_lo, // ...from pidx[0..n_lo-1]
- dim, bsp, bnd_box, splitter);
- bnd_box.hi[cd] = hv; // restore bounds
-
- bnd_box.lo[cd] = cv; // modify bounds for right subtree
- hi = rkd_tree_wd( // build right subtree
- pa, pidx + n_lo, n-n_lo,// ...from pidx[n_lo..n-1]
- dim, bsp, bnd_box, splitter);
- bnd_box.lo[cd] = lv; // restore bounds
-
- // create the splitting node
- ANNkd_split *ptr = new ANNkd_split(cd, cv, lv, hv, lo, hi, cut_pt_idx);
-
- return ptr; // return pointer to this node
- }
-}
-*/
-
-//----------------------------------------------------------------------
-// kd-tree constructor
-// This is the main constructor for kd-trees given a set of points.
-// It first builds a skeleton tree, then computes the bounding box
-// of the data points, and then invokes rkd_tree() to actually
-// build the tree, passing it the appropriate splitting routine.
-//----------------------------------------------------------------------
-
-ANNkd_tree::ANNkd_tree( // construct from point array
- ANNpointArray pa, // point array (with at least n pts)
- int n, // number of points
- int dd, // dimension
- int bs, // bucket size
- ANNsplitRule split) // splitting method
-{
- SkeletonTree(n, dd, bs); // set up the basic stuff
- pts = pa; // where the points are
- actual_num_points = n;
- if (n == 0) return; // no points--no sweat
-
- ANNorthRect bnd_box(dd); // bounding box for points
- annEnclRect(pa, pidx, n, dd, bnd_box);// construct bounding rectangle
- // copy to tree structure
- bnd_box_lo = annCopyPt(dd, bnd_box.lo);
- bnd_box_hi = annCopyPt(dd, bnd_box.hi);
-
- switch (split) { // build by rule
- case ANN_KD_STD: // standard kd-splitting rule
- root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, kd_split, &pointToLeafVec);
- break;
- case ANN_KD_MIDPT: // midpoint split
- root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, midpt_split, &pointToLeafVec);
- break;
- case ANN_KD_FAIR: // fair split
- root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, fair_split, &pointToLeafVec);
- break;
- case ANN_KD_SUGGEST: // best (in our opinion)
- case ANN_KD_SL_MIDPT: // sliding midpoint split
- root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, sl_midpt_split, &pointToLeafVec);
- break;
- case ANN_KD_SL_FAIR: // sliding fair split
- root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, sl_fair_split, &pointToLeafVec);
- break;
- // for kd-trees with deletion
- /*
- //case ANN_KD_SUGGEST:
- case ANN_KD_STD_WD:
- root = rkd_tree_wd(pa, pidx, n, dd, bs, bnd_box, kd_split_wd);
- break;
- case ANN_KD_MIDPT_WD:
- root = rkd_tree_wd(pa, pidx, n, dd, bs, bnd_box, kd_split_wd);
- break;
- case ANN_KD_SL_MIDPT_WD:
- root = rkd_tree_wd(pa, pidx, n, dd, bs, bnd_box, kd_split_wd);
- break;
- */
- default:
- annError("Illegal splitting method", ANNabort);
- }
-}
-
-
-// deletion code
-//
-//
-//
-//
-//
-void ANNkd_tree::delete_point(const int point_idx)
-{
- // range check
- assert(0 <= point_idx and point_idx < n_pts);
- assert(actual_num_points > 0);
- // if this is the first deletion,
- // initialize isDeleted vector
- if (isDeleted.empty()) {
- isDeleted.reserve(n_pts);
- for(size_t k = 0; k < n_pts; ++k) {
- isDeleted.push_back(false);
- }
- }
- // points shouldn't be deleted twice
- assert(!isDeleted[point_idx]);
- assert(root != NULL);
- ANNkd_leaf* leafWithPoint = pointToLeafVec.at(point_idx);
- assert(leafWithPoint != NULL);
- // if leafWithPoint != root,
- // its parent will delete the leaf
- pointToLeafVec.at(point_idx)->delete_point(point_idx, leafWithPoint != root);
- if (leafWithPoint == root) {
- // we had only one point,
- // so the tree must delete it
- root = KD_TRIVIAL;
- delete leafWithPoint;
- }
- isDeleted[point_idx] = true;
- actual_num_points--;
-}
-
-void ANNkd_leaf::delete_point(const int point_idx, const bool killYourself)
-{
- assert(n_pts == 1);
- assert(bkt[0] == point_idx);
- ANNkd_split* myPar = parent;
- while(myPar != NULL) {
- myPar->decNumPoints();
- myPar = myPar->getParent();
- }
- if (parent != NULL)
- parent->delete_leaf(this);
- if (killYourself)
- delete this;
-}
-
-void ANNkd_split::delete_leaf(ANNkd_leaf* childToDelete)
-{
- assert(child[ANN_LO] == childToDelete or child[ANN_HI] == childToDelete);
- if (child[ANN_LO] == childToDelete)
- child[ANN_LO] = KD_TRIVIAL;
- else
- child[ANN_HI] = KD_TRIVIAL;
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/ann/kd_util.cpp b/geom_bottleneck/bottleneck/src/ann/kd_util.cpp
deleted file mode 100644
index 02b35c4..0000000
--- a/geom_bottleneck/bottleneck/src/ann/kd_util.cpp
+++ /dev/null
@@ -1,441 +0,0 @@
-//----------------------------------------------------------------------
-// File: kd_util.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Common utilities for kd-trees
-// Last modified: 01/04/05 (Version 1.0)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-//----------------------------------------------------------------------
-
-#include "kd_util.h" // kd-utility declarations
-
-#include <ANN/ANNperf.h> // performance evaluation
-
-namespace geom_bt {
-//----------------------------------------------------------------------
-// The following routines are utility functions for manipulating
-// points sets, used in determining splitting planes for kd-tree
-// construction.
-//----------------------------------------------------------------------
-
-//----------------------------------------------------------------------
-// NOTE: Virtually all point indexing is done through an index (i.e.
-// permutation) array pidx. Consequently, a reference to the d-th
-// coordinate of the i-th point is pa[pidx[i]][d]. The macro PA(i,d)
-// is a shorthand for this.
-//----------------------------------------------------------------------
- // standard 2-d indirect indexing
-#define PA(i,d) (pa[pidx[(i)]][(d)])
- // accessing a single point
-#define PP(i) (pa[pidx[(i)]])
-
-//----------------------------------------------------------------------
-// annAspectRatio
-// Compute the aspect ratio (ratio of longest to shortest side)
-// of a rectangle.
-//----------------------------------------------------------------------
-
-double annAspectRatio(
- int dim, // dimension
- const ANNorthRect &bnd_box) // bounding cube
-{
- ANNcoord length = bnd_box.hi[0] - bnd_box.lo[0];
- ANNcoord min_length = length; // min side length
- ANNcoord max_length = length; // max side length
- for (int d = 0; d < dim; d++) {
- length = bnd_box.hi[d] - bnd_box.lo[d];
- if (length < min_length) min_length = length;
- if (length > max_length) max_length = length;
- }
- return max_length/min_length;
-}
-
-//----------------------------------------------------------------------
-// annEnclRect, annEnclCube
-// These utilities compute the smallest rectangle and cube enclosing
-// a set of points, respectively.
-//----------------------------------------------------------------------
-
-void annEnclRect(
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension
- ANNorthRect &bnds) // bounding cube (returned)
-{
- for (int d = 0; d < dim; d++) { // find smallest enclosing rectangle
- ANNcoord lo_bnd = PA(0,d); // lower bound on dimension d
- ANNcoord hi_bnd = PA(0,d); // upper bound on dimension d
- for (int i = 0; i < n; i++) {
- if (PA(i,d) < lo_bnd) lo_bnd = PA(i,d);
- else if (PA(i,d) > hi_bnd) hi_bnd = PA(i,d);
- }
- bnds.lo[d] = lo_bnd;
- bnds.hi[d] = hi_bnd;
- }
-}
-
-void annEnclCube( // compute smallest enclosing cube
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension
- ANNorthRect &bnds) // bounding cube (returned)
-{
- int d;
- // compute smallest enclosing rect
- annEnclRect(pa, pidx, n, dim, bnds);
-
- ANNcoord max_len = 0; // max length of any side
- for (d = 0; d < dim; d++) { // determine max side length
- ANNcoord len = bnds.hi[d] - bnds.lo[d];
- if (len > max_len) { // update max_len if longest
- max_len = len;
- }
- }
- for (d = 0; d < dim; d++) { // grow sides to match max
- ANNcoord len = bnds.hi[d] - bnds.lo[d];
- ANNcoord half_diff = (max_len - len) / 2;
- bnds.lo[d] -= half_diff;
- bnds.hi[d] += half_diff;
- }
-}
-
-//----------------------------------------------------------------------
-// annBoxDistance - utility routine which computes distance from point to
-// box (Note: most distances to boxes are computed using incremental
-// distance updates, not this function.)
-//----------------------------------------------------------------------
-
-ANNdist annBoxDistance( // compute distance from point to box
- const ANNpoint q, // the point
- const ANNpoint lo, // low point of box
- const ANNpoint hi, // high point of box
- int dim) // dimension of space
-{
- register ANNdist dist = 0.0; // sum of squared distances
- register ANNdist t;
-
- for (register int d = 0; d < dim; d++) {
- if (q[d] < lo[d]) { // q is left of box
- t = ANNdist(lo[d]) - ANNdist(q[d]);
- dist = ANN_SUM(dist, ANN_POW(t));
- }
- else if (q[d] > hi[d]) { // q is right of box
- t = ANNdist(q[d]) - ANNdist(hi[d]);
- dist = ANN_SUM(dist, ANN_POW(t));
- }
- }
- ANN_FLOP(4*dim) // increment floating op count
-
- return dist;
-}
-
-//----------------------------------------------------------------------
-// annSpread - find spread along given dimension
-// annMinMax - find min and max coordinates along given dimension
-// annMaxSpread - find dimension of max spread
-//----------------------------------------------------------------------
-
-ANNcoord annSpread( // compute point spread along dimension
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d) // dimension to check
-{
- ANNcoord min = PA(0,d); // compute max and min coords
- ANNcoord max = PA(0,d);
- for (int i = 1; i < n; i++) {
- ANNcoord c = PA(i,d);
- if (c < min) min = c;
- else if (c > max) max = c;
- }
- return (max - min); // total spread is difference
-}
-
-void annMinMax( // compute min and max coordinates along dim
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension to check
- ANNcoord &min, // minimum value (returned)
- ANNcoord &max) // maximum value (returned)
-{
- min = PA(0,d); // compute max and min coords
- max = PA(0,d);
- for (int i = 1; i < n; i++) {
- ANNcoord c = PA(i,d);
- if (c < min) min = c;
- else if (c > max) max = c;
- }
-}
-
-int annMaxSpread( // compute dimension of max spread
- ANNpointArray pa, // point array
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim) // dimension of space
-{
- int max_dim = 0; // dimension of max spread
- ANNcoord max_spr = 0; // amount of max spread
-
- if (n == 0) return max_dim; // no points, who cares?
-
- for (int d = 0; d < dim; d++) { // compute spread along each dim
- ANNcoord spr = annSpread(pa, pidx, n, d);
- if (spr > max_spr) { // bigger than current max
- max_spr = spr;
- max_dim = d;
- }
- }
- return max_dim;
-}
-
-//----------------------------------------------------------------------
-// annMedianSplit - split point array about its median
-// Splits a subarray of points pa[0..n] about an element of given
-// rank (median: n_lo = n/2) with respect to dimension d. It places
-// the element of rank n_lo-1 correctly (because our splitting rule
-// takes the mean of these two). On exit, the array is permuted so
-// that:
-//
-// pa[0..n_lo-2][d] <= pa[n_lo-1][d] <= pa[n_lo][d] <= pa[n_lo+1..n-1][d].
-//
-// The mean of pa[n_lo-1][d] and pa[n_lo][d] is returned as the
-// splitting value.
-//
-// All indexing is done indirectly through the index array pidx.
-//
-// This function uses the well known selection algorithm due to
-// C.A.R. Hoare.
-//----------------------------------------------------------------------
-
- // swap two points in pa array
-#define PASWAP(a,b) { int tmp = pidx[a]; pidx[a] = pidx[b]; pidx[b] = tmp; }
-
-void annMedianSplit(
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord &cv, // cutting value
- int n_lo) // split into n_lo and n-n_lo
-{
- int l = 0; // left end of current subarray
- int r = n-1; // right end of current subarray
- while (l < r) {
- register int i = (r+l)/2; // select middle as pivot
- register int k;
-
- if (PA(i,d) > PA(r,d)) // make sure last > pivot
- PASWAP(i,r)
- PASWAP(l,i); // move pivot to first position
-
- ANNcoord c = PA(l,d); // pivot value
- i = l;
- k = r;
- for(;;) { // pivot about c
- while (PA(++i,d) < c) ;
- while (PA(--k,d) > c) ;
- if (i < k) PASWAP(i,k) else break;
- }
- PASWAP(l,k); // pivot winds up in location k
-
- if (k > n_lo) r = k-1; // recurse on proper subarray
- else if (k < n_lo) l = k+1;
- else break; // got the median exactly
- }
- if (n_lo > 0) { // search for next smaller item
- ANNcoord c = PA(0,d); // candidate for max
- int k = 0; // candidate's index
- for (int i = 1; i < n_lo; i++) {
- if (PA(i,d) > c) {
- c = PA(i,d);
- k = i;
- }
- }
- PASWAP(n_lo-1, k); // max among pa[0..n_lo-1] to pa[n_lo-1]
- }
- // cut value is midpoint value
- cv = (PA(n_lo-1,d) + PA(n_lo,d))/2.0;
-}
-
-//----------------------------------------------------------------------
-// annPlaneSplit - split point array about a cutting plane
-// Split the points in an array about a given plane along a
-// given cutting dimension. On exit, br1 and br2 are set so
-// that:
-//
-// pa[ 0 ..br1-1] < cv
-// pa[br1..br2-1] == cv
-// pa[br2.. n -1] > cv
-//
-// All indexing is done indirectly through the index array pidx.
-//
-//----------------------------------------------------------------------
-
-void annPlaneSplit( // split points by a plane
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord cv, // cutting value
- int &br1, // first break (values < cv)
- int &br2) // second break (values == cv)
-{
- int l = 0;
- int r = n-1;
- for(;;) { // partition pa[0..n-1] about cv
- while (l < n && PA(l,d) < cv) l++;
- while (r >= 0 && PA(r,d) >= cv) r--;
- if (l > r) break;
- PASWAP(l,r);
- l++; r--;
- }
- br1 = l; // now: pa[0..br1-1] < cv <= pa[br1..n-1]
- r = n-1;
- for(;;) { // partition pa[br1..n-1] about cv
- while (l < n && PA(l,d) <= cv) l++;
- while (r >= br1 && PA(r,d) > cv) r--;
- if (l > r) break;
- PASWAP(l,r);
- l++; r--;
- }
- br2 = l; // now: pa[br1..br2-1] == cv < pa[br2..n-1]
-}
-
-
-//----------------------------------------------------------------------
-// annBoxSplit - split point array about a orthogonal rectangle
-// Split the points in an array about a given orthogonal
-// rectangle. On exit, n_in is set to the number of points
-// that are inside (or on the boundary of) the rectangle.
-//
-// All indexing is done indirectly through the index array pidx.
-//
-//----------------------------------------------------------------------
-
-void annBoxSplit( // split points by a box
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int dim, // dimension of space
- ANNorthRect &box, // the box
- int &n_in) // number of points inside (returned)
-{
- int l = 0;
- int r = n-1;
- for(;;) { // partition pa[0..n-1] about box
- while (l < n && box.inside(dim, PP(l))) l++;
- while (r >= 0 && !box.inside(dim, PP(r))) r--;
- if (l > r) break;
- PASWAP(l,r);
- l++; r--;
- }
- n_in = l; // now: pa[0..n_in-1] inside and rest outside
-}
-
-//----------------------------------------------------------------------
-// annSplitBalance - compute balance factor for a given plane split
-// Balance factor is defined as the number of points lying
-// below the splitting value minus n/2 (median). Thus, a
-// median split has balance 0, left of this is negative and
-// right of this is positive. (The points are unchanged.)
-//----------------------------------------------------------------------
-
-int annSplitBalance( // determine balance factor of a split
- ANNpointArray pa, // points to split
- ANNidxArray pidx, // point indices
- int n, // number of points
- int d, // dimension along which to split
- ANNcoord cv) // cutting value
-{
- int n_lo = 0;
- for(int i = 0; i < n; i++) { // count number less than cv
- if (PA(i,d) < cv) n_lo++;
- }
- return n_lo - n/2;
-}
-
-//----------------------------------------------------------------------
-// annBox2Bnds - convert bounding box to list of bounds
-// Given two boxes, an inner box enclosed within a bounding
-// box, this routine determines all the sides for which the
-// inner box is strictly contained with the bounding box,
-// and adds an appropriate entry to a list of bounds. Then
-// we allocate storage for the final list of bounds, and return
-// the resulting list and its size.
-//----------------------------------------------------------------------
-
-void annBox2Bnds( // convert inner box to bounds
- const ANNorthRect &inner_box, // inner box
- const ANNorthRect &bnd_box, // enclosing box
- int dim, // dimension of space
- int &n_bnds, // number of bounds (returned)
- ANNorthHSArray &bnds) // bounds array (returned)
-{
- int i;
- n_bnds = 0; // count number of bounds
- for (i = 0; i < dim; i++) {
- if (inner_box.lo[i] > bnd_box.lo[i]) // low bound is inside
- n_bnds++;
- if (inner_box.hi[i] < bnd_box.hi[i]) // high bound is inside
- n_bnds++;
- }
-
- bnds = new ANNorthHalfSpace[n_bnds]; // allocate appropriate size
-
- int j = 0;
- for (i = 0; i < dim; i++) { // fill the array
- if (inner_box.lo[i] > bnd_box.lo[i]) {
- bnds[j].cd = i;
- bnds[j].cv = inner_box.lo[i];
- bnds[j].sd = +1;
- j++;
- }
- if (inner_box.hi[i] < bnd_box.hi[i]) {
- bnds[j].cd = i;
- bnds[j].cv = inner_box.hi[i];
- bnds[j].sd = -1;
- j++;
- }
- }
-}
-
-//----------------------------------------------------------------------
-// annBnds2Box - convert list of bounds to bounding box
-// Given an enclosing box and a list of bounds, this routine
-// computes the corresponding inner box. It is assumed that
-// the box points have been allocated already.
-//----------------------------------------------------------------------
-
-void annBnds2Box(
- const ANNorthRect &bnd_box, // enclosing box
- int dim, // dimension of space
- int n_bnds, // number of bounds
- ANNorthHSArray bnds, // bounds array
- ANNorthRect &inner_box) // inner box (returned)
-{
- annAssignRect(dim, inner_box, bnd_box); // copy bounding box to inner
-
- for (int i = 0; i < n_bnds; i++) {
- bnds[i].project(inner_box.lo); // project each endpoint
- bnds[i].project(inner_box.hi);
- }
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/basic_defs.cpp b/geom_bottleneck/bottleneck/src/basic_defs.cpp
deleted file mode 100644
index 76e6cc5..0000000
--- a/geom_bottleneck/bottleneck/src/basic_defs.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include <algorithm>
-#include <cfloat>
-#include "def_debug_bt.h"
-#include "basic_defs_bt.h"
-
-namespace geom_bt {
-
-// Point
-
-bool Point::operator==(const Point& other) const
-{
- return ((this->x == other.x) and (this->y == other.y));
-}
-
-bool Point::operator!=(const Point& other) const
-{
- return !(*this == other);
-}
-
-#ifndef FOR_R_TDA
-std::ostream& operator<<(std::ostream& output, const Point p)
-{
- output << "(" << p.x << ", " << p.y << ")";
- return output;
-}
-
-std::ostream& operator<<(std::ostream& output, const PointSet& ps)
-{
- output << "{ ";
- for(auto& p : ps) {
- output << p << ", ";
- }
- output << "\b\b }";
- return output;
-}
-#endif
-
-double sqrDist(const Point& a, const Point& b)
-{
- return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
-}
-
-double dist(const Point& a, const Point& b)
-{
- return sqrt(sqrDist(a, b));
-}
-
-// DiagramPoint
-
-// compute l-inf distance between two diagram points
-double distLInf(const DiagramPoint& a, const DiagramPoint& b)
-{
- if ( DiagramPoint::DIAG == a.type &&
- DiagramPoint::DIAG == b.type ) {
- // distance between points on the diagonal is 0
- return 0.0;
- }
- // otherwise distance is a usual l-inf distance
- return std::max(fabs(a.getRealX() - b.getRealX()), fabs(a.getRealY() - b.getRealY()));
-}
-
-bool DiagramPoint::operator==(const DiagramPoint& other) const
-{
- assert(this->id >= MinValidId);
- assert(other.id >= MinValidId);
- bool areEqual{ this->id == other.id };
- assert(!areEqual or ((this->x == other.x) and (this->y == other.y) and (this->type == other.type)));
- return areEqual;
-}
-
-bool DiagramPoint::operator!=(const DiagramPoint& other) const
-{
- return !(*this == other);
-}
-
-#ifndef FOR_R_TDA
-std::ostream& operator<<(std::ostream& output, const DiagramPoint p)
-{
- if ( p.type == DiagramPoint::DIAG ) {
- output << "(" << p.x << ", " << p.y << ", " << 0.5 * (p.x + p.y) << ", " << p.id << " DIAG )";
- } else {
- output << "(" << p.x << ", " << p.y << ", " << p.id << " NORMAL)";
- }
- return output;
-}
-
-std::ostream& operator<<(std::ostream& output, const DiagramPointSet& ps)
-{
- output << "{ ";
- for(auto pit = ps.cbegin(); pit != ps.cend(); ++pit) {
- output << *pit << ", ";
- }
- output << "\b\b }";
- return output;
-}
-#endif
-
-DiagramPoint::DiagramPoint(double xx, double yy, Type ttype, IdType uid) :
- x(xx),
- y(yy),
- type(ttype),
- id(uid)
-{
- if ( yy == xx and ttype != DiagramPoint::DIAG)
- throw std::runtime_error("Point on the main diagonal must have DIAG type");
-
-}
-
-void DiagramPointSet::insert(const DiagramPoint p)
-{
- points.insert(p);
- if (p.id > maxId) {
- maxId = p.id + 1;
- }
-}
-
-// erase should be called only for the element of the set
-void DiagramPointSet::erase(const DiagramPoint& p, bool doCheck)
-{
- auto it = points.find(p);
- if (it != points.end()) {
- points.erase(it);
- } else {
- assert(!doCheck);
- }
-}
-
-void DiagramPointSet::reserve(const size_t newSize)
-{
- points.reserve(newSize);
-}
-
-
-void DiagramPointSet::erase(const std::unordered_set<DiagramPoint, DiagramPointHash>::const_iterator it)
-{
- points.erase(it);
-}
-
-void DiagramPointSet::clear()
-{
- points.clear();
-}
-
-size_t DiagramPointSet::size() const
-{
- return points.size();
-}
-
-bool DiagramPointSet::empty() const
-{
- return points.empty();
-}
-
-bool DiagramPointSet::hasElement(const DiagramPoint& p) const
-{
- return points.find(p) != points.end();
-}
-
-
-void DiagramPointSet::removeDiagonalPoints()
-{
- if (isLinked) {
- auto ptIter = points.begin();
- while(ptIter != points.end()) {
- if (ptIter->isDiagonal()) {
- ptIter = points.erase(ptIter);
- } else {
- ptIter++;
- }
- }
- isLinked = false;
- }
-}
-
-
-// preprocess diagrams A and B by adding projections onto diagonal of points of
-// A to B and vice versa. NB: ids of points will be changed!
-void addProjections(DiagramPointSet& A, DiagramPointSet& B)
-{
-
- IdType uniqueId {MinValidId + 1};
- DiagramPointSet newA, newB;
-
- // copy normal points from A to newA
- // add projections to newB
- for(auto& pA : A) {
- if (pA.isNormal()) {
- DiagramPoint dpA {pA.getRealX(), pA.getRealY(), DiagramPoint::NORMAL, uniqueId++};
- DiagramPoint dpB {0.5*(pA.getRealX() +pA.getRealY()), 0.5 *(pA.getRealX() +pA.getRealY()), DiagramPoint::DIAG, uniqueId++};
- newA.insert(dpA);
- newB.insert(dpB);
- }
- }
-
- for(auto& pB : B) {
- if (pB.isNormal()) {
- DiagramPoint dpB {pB.getRealX(), pB.getRealY(), DiagramPoint::NORMAL, uniqueId++};
- DiagramPoint dpA {0.5*(pB.getRealX() +pB.getRealY()), 0.5 *(pB.getRealX() +pB.getRealY()), DiagramPoint::DIAG, uniqueId++};
- newB.insert(dpB);
- newA.insert(dpA);
- }
- }
-
- A = newA;
- B = newB;
- A.isLinked = true;
- B.isLinked = true;
-}
-}
diff --git a/geom_bottleneck/bottleneck/src/brute.cpp b/geom_bottleneck/bottleneck/src/brute.cpp
deleted file mode 100644
index 200bc35..0000000
--- a/geom_bottleneck/bottleneck/src/brute.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-//----------------------------------------------------------------------
-// File: brute.cpp
-// Programmer: Sunil Arya and David Mount
-// Description: Brute-force nearest neighbors
-// Last modified: 05/03/05 (Version 1.1)
-//----------------------------------------------------------------------
-// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
-// David Mount. All Rights Reserved.
-//
-// This software and related documentation is part of the Approximate
-// Nearest Neighbor Library (ANN). This software is provided under
-// the provisions of the Lesser GNU Public License (LGPL). See the
-// file ../ReadMe.txt for further information.
-//
-// The University of Maryland (U.M.) and the authors make no
-// representations about the suitability or fitness of this software for
-// any purpose. It is provided "as is" without express or implied
-// warranty.
-//----------------------------------------------------------------------
-// History:
-// Revision 0.1 03/04/98
-// Initial release
-// Revision 1.1 05/03/05
-// Added fixed-radius kNN search
-//----------------------------------------------------------------------
-
-#include <ANN/ANNx.h> // all ANN includes
-#include "pr_queue_k.h" // k element priority queue
-
-//----------------------------------------------------------------------
-// Brute-force search simply stores a pointer to the list of
-// data points and searches linearly for the nearest neighbor.
-// The k nearest neighbors are stored in a k-element priority
-// queue (which is implemented in a pretty dumb way as well).
-//
-// If ANN_ALLOW_SELF_MATCH is ANNfalse then data points at distance
-// zero are not considered.
-//
-// Note that the error bound eps is passed in, but it is ignored.
-// These routines compute exact nearest neighbors (which is needed
-// for validation purposes in ann_test.cpp).
-//----------------------------------------------------------------------
-
-ANNbruteForce::ANNbruteForce( // constructor from point array
- ANNpointArray pa, // point array
- int n, // number of points
- int dd) // dimension
-{
- dim = dd; n_pts = n; pts = pa;
-}
-
-ANNbruteForce::~ANNbruteForce() { } // destructor (empty)
-
-void ANNbruteForce::annkSearch( // approx k near neighbor search
- ANNpoint q, // query point
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor indices (returned)
- ANNdistArray dd, // dist to near neighbors (returned)
- double eps) // error bound (ignored)
-{
- ANNmin_k mk(k); // construct a k-limited priority queue
- int i;
-
- if (k > n_pts) { // too many near neighbors?
- annError("Requesting more near neighbors than data points", ANNabort);
- }
- // run every point through queue
- for (i = 0; i < n_pts; i++) {
- // compute distance to point
- ANNdist sqDist = annDist(dim, pts[i], q);
- if (ANN_ALLOW_SELF_MATCH || sqDist != 0)
- mk.insert(sqDist, i);
- }
- for (i = 0; i < k; i++) { // extract the k closest points
- dd[i] = mk.ith_smallest_key(i);
- nn_idx[i] = mk.ith_smallest_info(i);
- }
-}
-
-int ANNbruteForce::annkFRSearch( // approx fixed-radius kNN search
- ANNpoint q, // query point
- ANNdist sqRad, // squared radius
- int k, // number of near neighbors to return
- ANNidxArray nn_idx, // nearest neighbor array (returned)
- ANNdistArray dd, // dist to near neighbors (returned)
- double eps) // error bound
-{
- ANNmin_k mk(k); // construct a k-limited priority queue
- int i;
- int pts_in_range = 0; // number of points in query range
- // run every point through queue
- for (i = 0; i < n_pts; i++) {
- // compute distance to point
- ANNdist sqDist = annDist(dim, pts[i], q);
- if (sqDist <= sqRad && // within radius bound
- (ANN_ALLOW_SELF_MATCH || sqDist != 0)) { // ...and no self match
- mk.insert(sqDist, i);
- pts_in_range++;
- }
- }
- for (i = 0; i < k; i++) { // extract the k closest points
- if (dd != NULL)
- dd[i] = mk.ith_smallest_key(i);
- if (nn_idx != NULL)
- nn_idx[i] = mk.ith_smallest_info(i);
- }
-
- return pts_in_range;
-}
--\n}\n
diff --git a/geom_bottleneck/bottleneck/src/neighb_oracle.cpp b/geom_bottleneck/bottleneck/src/neighb_oracle.cpp
deleted file mode 100644
index 7195ef0..0000000
--- a/geom_bottleneck/bottleneck/src/neighb_oracle.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-
- This file is part of GeomBottleneck.
-
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
-
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include <algorithm>
-#include "neighb_oracle.h"
-#include "def_debug_bt.h"
-
-namespace geom_bt {
-/*static void printDebug(//bool isDebug, std::string s)*/
-//{
-//#ifdef DEBUG_NEIGHBOUR_ORACLE
- //if (isDebug) {
- //std::cout << s << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const DiagramPoint& p)
-//{
-//#ifdef DEBUG_NEIGHBOUR_ORACLE
- //if (isDebug) {
- //std::cout << s << p << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const double r)
-//{
-//#ifdef DEBUG_NEIGHBOUR_ORACLE
- //if (isDebug) {
- //std::cout << s << r << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const DiagramPointSet& dpSet)
-//{
-//#ifdef DEBUG_NEIGHBOUR_ORACLE
- //if (isDebug) {
- //std::cout << s << dpSet << std::endl;
- //}
-//#endif
-//}
-
-
-
-// simple oracle
-NeighbOracleSimple::NeighbOracleSimple()
-{
- r = 0.0;
-}
-
-NeighbOracleSimple::NeighbOracleSimple(const DiagramPointSet& S, const double rr, const double dEps)
-{
- r = rr;
- distEpsilon = dEps;
- pointSet = S;
-}
-
-void NeighbOracleSimple::rebuild(const DiagramPointSet& S, const double rr)
-{
- pointSet = S;
- r = rr;
-}
-
-void NeighbOracleSimple::deletePoint(const DiagramPoint& p)
-{
- pointSet.erase(p);
-}
-
-bool NeighbOracleSimple::getNeighbour(const DiagramPoint& q, DiagramPoint& result) const
-{
- for(auto pit = pointSet.cbegin(); pit != pointSet.cend(); ++pit) {
- if ( distLInf(*pit, q) <= r) {
- result = *pit;
- return true;
- }
- }
- return false;
-}
-
-void NeighbOracleSimple::getAllNeighbours(const DiagramPoint& q, std::vector<DiagramPoint>& result)
-{
- result.clear();
- for(const auto& point : pointSet) {
- if ( distLInf(point, q) <= r) {
- result.push_back(point);
- }
- }
- for(auto& pt : result) {
- deletePoint(pt);
- }
-}
-
-// ANN oracle
-//
-
-NeighbOracleAnn::NeighbOracleAnn(const DiagramPointSet& S, const double rr, const double dEps)
-{
- assert(dEps >= 0);
- distEpsilon = dEps;
- // allocate space for query point
- // and the output of nearest neighbour search
- // this memory will be used in getNeighbour and freed in destructor
- annQueryPoint = annAllocPt(annDim);
- annIndices = new ANNidx[annK];
- annDistances = new ANNdist[annK];
- annPoints = nullptr;
- lo = annAllocPt(annDim);
- hi = annAllocPt(annDim);
- // create kd tree
- kdTree = nullptr;
- rebuild(S, rr);
-}
-
-void NeighbOracleAnn::rebuild(const DiagramPointSet& S, double rr)
-{
-#ifdef VERBOSE_BOTTLENECK
- std::cout << "Entered rebuild, r = " << rr << ", size = " << S.size() << std::endl;
-#endif
- r = rr;
- size_t annNumPoints = S.size();
- //printDebug(isDebug, "S = ", S);
- if (annNumPoints > 0) {
- //originalPointSet = S;
- pointIdxLookup.clear();
- pointIdxLookup.reserve(S.size());
- allPoints.clear();
- allPoints.reserve(S.size());
- diagonalPoints.clear();
- diagonalPoints.reserve(S.size() / 2);
- for(auto pit = S.cbegin(); pit != S.cend(); ++pit) {
- allPoints.push_back(*pit);
- if (pit->type == DiagramPoint::DIAG) {
- diagonalPoints.insert(*pit);
- }
- }
- if (annPoints) {
- annDeallocPts(annPoints);
- }
- annPoints = annAllocPts(annNumPoints, annDim);
- auto annPointsPtr = *annPoints;
- size_t pointIdx = 0;
- for(auto& dataPoint : allPoints) {
- pointIdxLookup.insert( { dataPoint, pointIdx++ } );
- *annPointsPtr++ = dataPoint.getRealX();
- *annPointsPtr++ = dataPoint.getRealY();
- }
- delete kdTree;
- kdTree = new ANNkd_tree(annPoints,
- annNumPoints,
- annDim,
- 1, // bucket size
- ANN_KD_STD);
- }
-#ifdef VERBOSE_BOTTLENECK
- std::cout << "Exit rebuild" << std::endl;
-#endif
-}
-
-void NeighbOracleAnn::deletePoint(const DiagramPoint& p)
-{
- //bool isDebug { true };
- auto findRes = pointIdxLookup.find(p);
- assert(findRes != pointIdxLookup.end());
- //printDebug(isDebug, "Deleting point ", p);
- size_t pointIdx { (*findRes).second };
- //printDebug(isDebug, "pointIdx = ", pointIdx);
- //originalPointSet.erase(p);
- diagonalPoints.erase(p, false);
- kdTree->delete_point(pointIdx);
-#ifdef DEBUG_NEIGHBOUR_ORACLE
-#ifndef FOR_R_TDA
- kdTree->Print(ANNtrue, std::cout);
-#endif
-#endif
-}
-
-bool NeighbOracleAnn::getNeighbour(const DiagramPoint& q, DiagramPoint& result) const
-{
- //bool isDebug { false };
- //printDebug(isDebug, "getNeighbour for q = ", q);
- if (0 == kdTree->getActualNumPoints() ) {
- //printDebug(isDebug, "annNumPoints = 0, not found ");
- return false;
- }
- // distance between two diagonal points
- // is 0
- if (DiagramPoint::DIAG == q.type) {
- if (!diagonalPoints.empty()) {
- result = *diagonalPoints.cbegin();
- //printDebug(isDebug, "Neighbour found in diagonal points, res = ", result);
- return true;
- }
- }
- // if no neighbour found among diagonal points,
- // search in ANN kd_tree
- annQueryPoint[0] = q.getRealX();
- annQueryPoint[1] = q.getRealY();
- //annIndices[0] = ANN_NULL_IDX;
- kdTree->annkSearch(annQueryPoint, annK, annIndices, annDistances, annEpsilon);
- //kdTree->annkFRSearch(annQueryPoint, r, annK, annIndices, annDistances, annEpsilon);
- //std::cout << distEpsilon << " = distEpsilon " << std::endl;
- if (annDistances[0] <= r + distEpsilon) {
- //if (annIndices[0] != ANN_NULL_IDX) {
- result = allPoints[annIndices[0]];
- //printDebug(isDebug, "Neighbour found with kd-tree, index = ", annIndices[0]);
- //printDebug(isDebug, "result = ", result);
- return true;
- }
- //printDebug(isDebug, "No neighbour found for r = ", r);
- return false;
-}
-
-void NeighbOracleAnn::getAllNeighbours(const DiagramPoint& q, std::vector<DiagramPoint>& result)
-{
- //bool isDebug { true };
- //printDebug(isDebug, "Entered getAllNeighbours for q = ", q);
- result.clear();
- // add diagonal points, if necessary
- if ( DiagramPoint::DIAG == q.type) {
- for( auto& diagPt : diagonalPoints ) {
- result.push_back(diagPt);
- }
- }
- // delete diagonal points we found
- // to prevent finding them again
- for(auto& pt : result) {
- //printDebug(isDebug, "deleting DIAG point pt = ", pt);
- deletePoint(pt);
- }
- size_t diagOffset = result.size();
- // create the query rectangle
- // centered at q of radius r
- lo[0] = q.getRealX() - r;
- lo[1] = q.getRealY() - r;
- hi[0] = q.getRealX() + r;
- hi[1] = q.getRealY() + r;
- ANNorthRect annRect { annDim, lo, hi };
- std::vector<size_t> pointIndicesOut;
- // perorm range search on kd-tree
- kdTree->range_search(annRect, pointIndicesOut);
- // get actual points in result
- for(auto& ptIdx : pointIndicesOut) {
- result.push_back(allPoints[ptIdx]);
- }
- // delete all points we found
- for(auto ptIt = result.begin() + diagOffset; ptIt != result.end(); ++ptIt) {
- //printDebug(isDebug, "deleting point pt = ", *ptIt);
- deletePoint(*ptIt);
- }
-}
-
-NeighbOracleAnn::~NeighbOracleAnn()
-{
- delete [] annIndices;
- delete [] annDistances;
- delete kdTree;
- annDeallocPt(annQueryPoint);
- annDeallocPt(lo);
- annDeallocPt(hi);
- if (annPoints) {
- annDeallocPts(annPoints);
- }
-}
-}
diff --git a/geom_bottleneck/example/bottleneck_dist.cpp b/geom_bottleneck/example/bottleneck_dist.cpp
index 81d8605..b66e7bc 100644
--- a/geom_bottleneck/example/bottleneck_dist.cpp
+++ b/geom_bottleneck/example/bottleneck_dist.cpp
@@ -1,3 +1,31 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
#include <iomanip>
#include "bottleneck.h"
@@ -20,11 +48,11 @@ int main(int argc, char* argv[])
PairVector diagramA, diagramB;
int decPrecision { 0 };
- if (!geom_bt::readDiagramPointSet(argv[1], diagramA, decPrecision)) {
+ if (!hera::bt::readDiagramPointSet(argv[1], diagramA, decPrecision)) {
std::exit(1);
}
- if (!geom_bt::readDiagramPointSet(argv[2], diagramB, decPrecision)) {
+ if (!hera::bt::readDiagramPointSet(argv[2], diagramB, decPrecision)) {
std::exit(1);
}
@@ -32,24 +60,24 @@ int main(int argc, char* argv[])
if (argc >= 4) {
// the third parameter is epsilon,
// return approximate distance (faster)
- double approxEpsilon = atof(argv[3]);
- if (approxEpsilon > 0.0) {
+ double delta = atof(argv[3]);
+ if (delta > 0.0) {
if (useSamplingHeur && diagramA.size() > heurThreshold && diagramB.size() > heurThreshold) {
#ifdef VERBOSE_BOTTLENECK
std::cout << "using sampling heuristic" << std::endl;
#endif
- res = geom_bt::bottleneckDistApproxHeur(diagramA, diagramB, approxEpsilon);
+ res = hera::bottleneckDistApproxHeur(diagramA, diagramB, delta);
} else {
#ifdef VERBOSE_BOTTLENECK
std::cout << "NOT using sampling heuristic" << std::endl;
#endif
- res = geom_bt::bottleneckDistApprox(diagramA, diagramB, approxEpsilon);
+ res = hera::bottleneckDistApprox(diagramA, diagramB, delta);
}
- } else if (approxEpsilon == 0.0) {
+ } else if (delta == 0.0) {
#ifdef VERBOSE_BOTTLENECK
std::cout << "NOT using sampling heuristic, computing EXACT answer" << std::endl;
#endif
- res = geom_bt::bottleneckDistExact(diagramA, diagramB, decPrecision);
+ res = hera::bottleneckDistExact(diagramA, diagramB, decPrecision);
} else {
std::cerr << "The third parameter (relative error) must be positive!" << std::endl;
std::exit(1);
@@ -59,7 +87,7 @@ int main(int argc, char* argv[])
#ifdef VERBOSE_BOTTLENECK
std::cout << "NOT using sampling heuristic, computing EXACT answer" << std::endl;
#endif
- res = geom_bt::bottleneckDistExact(diagramA, diagramB, decPrecision);
+ res = hera::bottleneckDistExact(diagramA, diagramB, decPrecision);
}
std::cout << std::setprecision(15) << res << std::endl;
@@ -67,10 +95,10 @@ int main(int argc, char* argv[])
// using the constructor with iterators.
// May be useful if the same diagram is used multiple times
// to avoid copying data from user's container each time.
-
- //geom_bt::DiagramPointSet dA(diagramA.begin(), diagramA.end());
- //geom_bt::DiagramPointSet dB(diagramB.begin(), diagramB.end());
- //double result1 = geom_bt::bottleneckDistExact(dA, dB);
+
+ //hera::bt::DiagramPointSet dA(diagramA);
+ //hera::bt::DiagramPointSet dB(diagramB);
+ //double result1 = hera::bt::bottleneckDistExact(dA, dB);
//std::cout << std::setprecision(15) << result1 << std::endl;
return 0;
diff --git a/geom_bottleneck/include/basic_defs_bt.h b/geom_bottleneck/include/basic_defs_bt.h
new file mode 100644
index 0000000..ad09986
--- /dev/null
+++ b/geom_bottleneck/include/basic_defs_bt.h
@@ -0,0 +1,475 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_BASIC_DEFS_BT_H
+#define HERA_BASIC_DEFS_BT_H
+
+#ifdef _WIN32
+#include <ciso646>
+#endif
+
+#include <vector>
+#include <stdexcept>
+#include <math.h>
+#include <cstddef>
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+#include <assert.h>
+
+#include "def_debug_bt.h"
+
+#ifndef FOR_R_TDA
+#include <iostream>
+#endif
+
+namespace hera {
+namespace bt {
+
+typedef int IdType;
+constexpr IdType MinValidId = 10;
+
+template<class Real = double>
+struct Point {
+ Real x, y;
+
+ bool operator==(const Point<Real> &other) const
+ {
+ return ((this->x == other.x) and (this->y == other.y));
+ }
+
+ bool operator!=(const Point<Real> &other) const
+ {
+ return !(*this == other);
+ }
+
+ Point(Real ax, Real ay) : x(ax), y(ay) {}
+
+ Point() : x(0.0), y(0.0) {}
+
+#ifndef FOR_R_TDA
+
+ template<class R>
+ friend std::ostream& operator<<(std::ostream& output, const Point<R>& p)
+ {
+ output << "(" << p.x << ", " << p.y << ")";
+ return output;
+ }
+
+#endif
+};
+
+template<class Real = double>
+struct DiagramPoint {
+ // Points above the diagonal have type NORMAL
+ // Projections onto the diagonal have type DIAG
+ // for DIAG points only x-coordinate is relevant
+ // to-do: add getters/setters, checks in constructors, etc
+ enum Type {
+ NORMAL, DIAG
+ };
+ // data members
+private:
+ Real x, y;
+public:
+ Type type;
+ IdType id;
+
+ // operators, constructors
+ bool operator==(const DiagramPoint<Real> &other) const
+ {
+ // compare by id only
+ assert(this->id >= MinValidId);
+ assert(other.id >= MinValidId);
+ bool areEqual{ this->id == other.id };
+ assert(!areEqual or ((this->x == other.x) and (this->y == other.y) and (this->type == other.type)));
+ return areEqual;
+ }
+
+ bool operator!=(const DiagramPoint &other) const
+ {
+ return !(*this == other);
+ }
+
+ DiagramPoint(Real _x, Real _y, Type _type, IdType _id) :
+ x(_x),
+ y(_y),
+ type(_type),
+ id(_id)
+ {
+ if ( _y == _x and _type != DIAG)
+ throw std::runtime_error("Point on the main diagonal must have DIAG type");
+
+ }
+
+
+ bool isDiagonal(void) const { return type == DIAG; }
+
+ bool isNormal(void) const { return type == NORMAL; }
+
+ Real inline getRealX() const // return the x-coord
+ {
+ return x;
+ }
+
+ Real inline getRealY() const // return the y-coord
+ {
+ return y;
+ }
+
+#ifndef FOR_R_TDA
+ template<class R>
+ friend std::ostream& operator<<(std::ostream& output, const DiagramPoint<R>& p)
+ {
+ if ( p.isDiagonal() ) {
+ output << "(" << p.x << ", " << p.y << ", " << 0.5 * (p.x + p.y) << ", " << p.id << " DIAG )";
+ } else {
+ output << "(" << p.x << ", " << p.y << ", " << p.id << " NORMAL)";
+ }
+ return output;
+ }
+#endif
+
+};
+
+// compute l-inf distance between two diagram points
+template<class Real>
+Real distLInf(const DiagramPoint<Real>& a, const DiagramPoint<Real>& b)
+{
+ if ( a.isDiagonal() and b.isDiagonal() ) {
+ // distance between points on the diagonal is 0
+ return 0.0;
+ }
+ // otherwise distance is a usual l-inf distance
+ return std::max(fabs(a.getRealX() - b.getRealX()), fabs(a.getRealY() - b.getRealY()));
+}
+
+
+template <class T>
+inline void hash_combine(std::size_t & seed, const T & v)
+{
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+}
+
+//template<class Real = double>
+//struct PointHash {
+// size_t operator()(const Point<Real> &p) const
+// {
+// size_t seed = 0;
+// hash_combine(seed, p.x);
+// hash_combine(seed, p.y);
+// return seed;
+// }
+//};
+
+template<class Real = double>
+struct DiagramPointHash {
+ size_t operator()(const DiagramPoint<Real> &p) const
+ {
+ assert(p.id >= MinValidId);
+ return std::hash<int>()(p.id);
+ }
+};
+
+template<class Real = double>
+Real distLInf(const DiagramPoint<Real> &a, const DiagramPoint<Real> &b);
+
+//template<class Real = double>
+//typedef std::unordered_set<Point, PointHash> PointSet;
+template<class Real_ = double>
+class DiagramPointSet;
+
+template<class Real>
+void addProjections(DiagramPointSet<Real> &A, DiagramPointSet<Real> &B);
+
+template<class Real_>
+class DiagramPointSet {
+public:
+
+ using Real = Real_;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointHash = DiagramPointHash<Real>;
+ using const_iterator = typename std::unordered_set<DgmPoint, DgmPointHash>::const_iterator;
+ using iterator = typename std::unordered_set<DgmPoint, DgmPointHash>::iterator;
+
+private:
+
+ bool isLinked { false };
+ IdType maxId { MinValidId + 1 };
+ std::unordered_set<DgmPoint, DgmPointHash> points;
+
+public:
+
+ void insert(const DgmPoint& p)
+ {
+ points.insert(p);
+ if (p.id > maxId) {
+ maxId = p.id + 1;
+ }
+ }
+
+ void erase(const DgmPoint& p, bool doCheck = true)
+ {
+ // if doCheck, erasing non-existing elements causes assert
+ auto it = points.find(p);
+ if (it != points.end()) {
+ points.erase(it);
+ } else {
+ assert(!doCheck);
+ }
+ }
+
+
+ void erase(const const_iterator it)
+ {
+ points.erase(it);
+ }
+
+ void removeDiagonalPoints()
+ {
+ if (isLinked) {
+ auto ptIter = points.begin();
+ while(ptIter != points.end()) {
+ if (ptIter->isDiagonal()) {
+ ptIter = points.erase(ptIter);
+ } else {
+ ptIter++;
+ }
+ }
+ isLinked = false;
+ }
+ }
+
+ size_t size() const
+ {
+ return points.size();
+ }
+
+ void reserve(const size_t newSize)
+ {
+ points.reserve(newSize);
+ }
+
+ void clear()
+ {
+ points.clear();
+ }
+
+ bool empty() const
+ {
+ return points.empty();
+ }
+
+ bool hasElement(const DgmPoint &p) const
+ {
+ return points.find(p) != points.end();
+ }
+
+ iterator find(const DgmPoint &p)
+ {
+ return points.find(p);
+ }
+
+ iterator begin()
+ {
+ return points.begin();
+ }
+
+ iterator end()
+ {
+ return points.end();
+ }
+
+ const_iterator find(const DgmPoint &p) const
+ {
+ return points.find(p);
+ }
+
+ const_iterator cbegin() const
+ {
+ return points.cbegin();
+ }
+
+ const_iterator cend() const
+ {
+ return points.cend();
+ }
+
+#ifndef FOR_R_TDA
+ template<class R>
+ friend std::ostream& operator<<(std::ostream& output, const DiagramPointSet<R>& ps)
+ {
+ output << "{ ";
+ for(auto pit = ps.cbegin(); pit != ps.cend(); ++pit) {
+ output << *pit << ", ";
+ }
+ output << "\b\b }";
+ return output;
+ }
+#endif
+
+ friend void addProjections<Real_>(DiagramPointSet<Real_>& A, DiagramPointSet<Real_>& B);
+
+ template<class PairIterator>
+ void fillIn(PairIterator begin_iter, PairIterator end_iter)
+ {
+ isLinked = false;
+ clear();
+ IdType uniqueId = MinValidId + 1;
+ for (auto iter = begin_iter; iter != end_iter; ++iter) {
+ insert(DgmPoint(iter->first, iter->second, DgmPoint::NORMAL, uniqueId++));
+ }
+ }
+
+ template<class PointContainer>
+ void fillIn(const PointContainer& dgm_cont)
+ {
+ using Traits = DiagramTraits<PointContainer>;
+ isLinked = false;
+ clear();
+ IdType uniqueId = MinValidId + 1;
+ for (const auto& pt : dgm_cont) {
+ Real x = Traits::get_x(pt);
+ Real y = Traits::get_y(pt);
+ insert(DgmPoint(x, y, DgmPoint::NORMAL, uniqueId++));
+ }
+ }
+
+
+ // ctor from range
+ template<class PairIterator>
+ DiagramPointSet(PairIterator begin_iter, PairIterator end_iter)
+ {
+ fillIn(begin_iter, end_iter);
+ }
+
+ // ctor from container, uses DiagramTraits
+ template<class PointContainer>
+ DiagramPointSet(const PointContainer& dgm)
+ {
+ fillIn(dgm);
+ }
+
+
+ // default ctor, empty diagram
+ DiagramPointSet(IdType minId = MinValidId + 1) : maxId(minId + 1) {};
+
+ IdType nextId() { return maxId + 1; }
+
+}; // DiagramPointSet
+
+
+template<class Real, class DiagPointContainer>
+Real getFurthestDistance3Approx(DiagPointContainer& A, DiagPointContainer& B) {
+ Real result{0.0};
+ DiagramPoint<Real> begA = *(A.begin());
+ DiagramPoint<Real> optB = *(B.begin());
+ for (const auto &pointB : B) {
+ if (distLInf(begA, pointB) > result) {
+ result = distLInf(begA, pointB);
+ optB = pointB;
+ }
+ }
+ for (const auto &pointA : A) {
+ if (distLInf(pointA, optB) > result) {
+ result = distLInf(pointA, optB);
+ }
+ }
+ return result;
+}
+
+// preprocess diagrams A and B by adding projections onto diagonal of points of
+// A to B and vice versa. NB: ids of points will be changed!
+template<class Real_>
+void addProjections(DiagramPointSet<Real_>& A, DiagramPointSet<Real_>& B)
+{
+
+ using Real = Real_;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointSet = DiagramPointSet<Real>;
+
+ IdType uniqueId {MinValidId + 1};
+ DgmPointSet newA, newB;
+
+ // copy normal points from A to newA
+ // add projections to newB
+ for(auto& pA : A) {
+ if (pA.isNormal()) {
+ DgmPoint dpA {pA.getRealX(), pA.getRealY(), DgmPoint::NORMAL, uniqueId++};
+ DgmPoint dpB {(pA.getRealX() +pA.getRealY())/2, (pA.getRealX() +pA.getRealY())/2, DgmPoint::DIAG, uniqueId++};
+ newA.insert(dpA);
+ newB.insert(dpB);
+ }
+ }
+
+ for(auto& pB : B) {
+ if (pB.isNormal()) {
+ DgmPoint dpB {pB.getRealX(), pB.getRealY(), DgmPoint::NORMAL, uniqueId++};
+ DgmPoint dpA {(pB.getRealX() +pB.getRealY())/2, (pB.getRealX() +pB.getRealY())/2, DgmPoint::DIAG, uniqueId++};
+ newB.insert(dpB);
+ newA.insert(dpA);
+ }
+ }
+
+ A = newA;
+ B = newB;
+ A.isLinked = true;
+ B.isLinked = true;
+}
+
+
+//#ifndef FOR_R_TDA
+
+//template<class Real>
+//std::ostream& operator<<(std::ostream& output, const DiagramPoint<Real>& p)
+//{
+// if ( p.isDiagonal() ) {
+// output << "(" << p.x << ", " << p.y << ", " << 0.5 * (p.x + p.y) << ", " << p.id << " DIAG )";
+// } else {
+// output << "(" << p.x << ", " << p.y << ", " << p.id << " NORMAL)";
+// }
+// return output;
+//}
+
+//template<class Real>
+//std::ostream& operator<<(std::ostream& output, const DiagramPointSet<Real>& ps)
+//{
+// output << "{ ";
+// for(auto pit = ps.cbegin(); pit != ps.cend(); ++pit) {
+// output << *pit << ", ";
+// }
+// output << "\b\b }";
+// return output;
+//}
+//#endif // FOR_R_TDA
+
+
+} // end namespace bt
+} // end namespace hera
+#endif // HERA_BASIC_DEFS_BT_H
diff --git a/geom_bottleneck/include/bottleneck.h b/geom_bottleneck/include/bottleneck.h
new file mode 100644
index 0000000..d0d82b6
--- /dev/null
+++ b/geom_bottleneck/include/bottleneck.h
@@ -0,0 +1,116 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_BOTTLENECK_H
+#define HERA_BOTTLENECK_H
+
+
+#include <fstream>
+#include <vector>
+#include <algorithm>
+#include <limits>
+#include <random>
+
+#include "diagram_traits.h"
+#include "diagram_reader.h"
+#include "bottleneck_detail.h"
+#include "basic_defs_bt.h"
+#include "bound_match.h"
+
+namespace hera {
+
+// functions taking containers as input
+// template parameter PairContainer must be a container of pairs of real
+// numbers (pair.first = x-coordinate, pair.second = y-coordinate)
+// PairContainer class must support iteration of the form
+// for(it = pairContainer.begin(); it != pairContainer.end(); ++it)
+
+
+// get exact bottleneck distance,
+template<class PairContainer>
+typename DiagramTraits<PairContainer>::RealType
+bottleneckDistExact(PairContainer& dgm_A, PairContainer& dgm_B)
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ hera::bt::DiagramPointSet<Real> a(dgm_A);
+ hera::bt::DiagramPointSet<Real> b(dgm_B);
+ return hera::bt::bottleneckDistExact<Real>(a, b, 14);
+}
+
+// get exact bottleneck distance,
+template<class PairContainer>
+typename DiagramTraits<PairContainer>::RealType
+bottleneckDistExact(PairContainer& dgm_A, PairContainer& dgm_B, const int decPrecision)
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ hera::bt::DiagramPointSet<Real> a(dgm_A);
+ hera::bt::DiagramPointSet<Real> b(dgm_B);
+ return hera::bt::bottleneckDistExact(a, b, decPrecision);
+}
+
+// return the interval (distMin, distMax) such that:
+// a) actual bottleneck distance between A and B is contained in the interval
+// b) if the interval is not (0,0), then (distMax - distMin) / distMin < delta
+template<class PairContainer>
+std::pair<typename DiagramTraits<PairContainer>::RealType, typename DiagramTraits<PairContainer>::RealType>
+bottleneckDistApproxInterval(PairContainer& dgm_A, PairContainer& dgm_B, const typename DiagramTraits<PairContainer>::RealType delta)
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ hera::bt::DiagramPointSet<Real> a(dgm_A);
+ hera::bt::DiagramPointSet<Real> b(dgm_B);
+ return hera::bt::bottleneckDistApproxInterval(a, b, delta);
+}
+
+
+template<class PairContainer>
+typename DiagramTraits<PairContainer>::RealType
+bottleneckDistApproxHeur(PairContainer& dgm_A, PairContainer& dgm_B, const typename DiagramTraits<PairContainer>::RealType delta)
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ hera::bt::DiagramPointSet<Real> a(dgm_A);
+ hera::bt::DiagramPointSet<Real> b(dgm_B);
+ std::pair<Real, Real> resPair = hera::bt::bottleneckDistApproxIntervalHeur(a, b, delta);
+ return resPair.second;
+}
+
+
+// get approximate distance,
+// see bottleneckDistApproxInterval
+template<class PairContainer>
+typename DiagramTraits<PairContainer>::RealType
+bottleneckDistApprox(PairContainer& A, PairContainer& B, const typename DiagramTraits<PairContainer>::RealType delta)
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ hera::bt::DiagramPointSet<Real> a(A.begin(), A.end());
+ hera::bt::DiagramPointSet<Real> b(B.begin(), B.end());
+ return hera::bt::bottleneckDistApprox(a, b, delta);
+}
+
+} // end namespace hera
+
+#endif
diff --git a/geom_bottleneck/include/bottleneck_detail.h b/geom_bottleneck/include/bottleneck_detail.h
new file mode 100644
index 0000000..27c3c5d
--- /dev/null
+++ b/geom_bottleneck/include/bottleneck_detail.h
@@ -0,0 +1,85 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_BOTTLENECK_DETAIL_H
+#define HERA_BOTTLENECK_DETAIL_H
+
+
+#include <fstream>
+#include <vector>
+#include <algorithm>
+#include <limits>
+#include <random>
+
+#include "diagram_traits.h"
+#include "basic_defs_bt.h"
+#include "bound_match.h"
+
+namespace hera {
+
+
+namespace bt {
+
+
+
+// functions taking DiagramPointSet as input.
+// ATTENTION: parameters A and B (diagrams) will be changed after the call
+// (projections added).
+
+// return the interval (distMin, distMax) such that:
+// a) actual bottleneck distance between A and B is contained in the interval
+// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
+template<class Real>
+std::pair<Real, Real> bottleneckDistApproxInterval(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon);
+
+
+// heuristic (sample diagram to estimate the distance)
+template<class Real>
+std::pair<Real, Real> bottleneckDistApproxIntervalHeur(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon);
+
+// get approximate distance,
+// see bottleneckDistApproxInterval
+template<class Real>
+Real bottleneckDistApprox(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon);
+
+// get exact bottleneck distance,
+template<class Real>
+Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const int decPrecision);
+
+// get exact bottleneck distance,
+template<class Real>
+Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B);
+
+} // end namespace bt
+
+
+} // end namespace hera
+
+#include "bottleneck_detail.hpp"
+
+#endif // HERA_BOTTLENECK_DETAIL_H
diff --git a/geom_bottleneck/bottleneck/src/bottleneck.cpp b/geom_bottleneck/include/bottleneck_detail.hpp
index 05e0e27..64c6696 100644
--- a/geom_bottleneck/bottleneck/src/bottleneck.cpp
+++ b/geom_bottleneck/include/bottleneck_detail.hpp
@@ -1,38 +1,56 @@
/*
- Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
- This file is part of GeomBottleneck.
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
- GeomBottleneck is free software: you can redistribute it and/or modify
- it under the terms of the Lesser GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- GeomBottleneck is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- Lesser GNU General Public License for more details.
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- You should have received a copy of the Lesser GNU General Public License
- along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
*/
+#ifndef HERA_BOTTLENECK_HPP
+#define HERA_BOTTLENECK_HPP
+
+#ifdef FOR_R_TDA
+#undef DEBUG_BOUND_MATCH
+#undef DEBUG_MATCHING
+#undef VERBOSE_BOTTLENECK
+#endif
+
#include <iomanip>
#include <sstream>
#include <string>
#include <cctype>
-#include "bottleneck.h"
-//#include "test_dist_calc.h"
+#include "bottleneck_detail.h"
-namespace geom_bt {
+namespace hera {
+namespace bt {
-// return the interval (distMin, distMax) such that:
+// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
-// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-std::pair<double, double> bottleneckDistApproxInterval(DiagramPointSet& A, DiagramPointSet& B, const double epsilon)
+// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
+template<class Real>
+std::pair<Real, Real> bottleneckDistApproxInterval(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
{
// empty diagrams are not considered as error
if (A.empty() and B.empty())
@@ -44,24 +62,24 @@ std::pair<double, double> bottleneckDistApproxInterval(DiagramPointSet& A, Diagr
// TODO: think about that!
// we need one threshold for checking if the distance is 0,
// another one for the oracle!
- constexpr double epsThreshold { 1.0e-10 };
- std::pair<double, double> result { 0.0, 0.0 };
+ constexpr Real epsThreshold { 1.0e-10 };
+ std::pair<Real, Real> result { 0.0, 0.0 };
bool useRangeSearch { true };
// construct an oracle
- BoundMatchOracle oracle(A, B, epsThreshold, useRangeSearch);
+ BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
// check for distance = 0
if (oracle.isMatchLess(2*epsThreshold)) {
return result;
}
// get a 3-approximation of maximal distance between A and B
// as a starting value for probe distance
- double distProbe { getFurthestDistance3Approx(A, B) };
+ Real distProbe { getFurthestDistance3Approx<Real, DiagramPointSet<Real>>(A, B) };
// aliases for result components
- double& distMin {result.first};
- double& distMax {result.second};
+ Real& distMin {result.first};
+ Real& distMax {result.second};
if ( oracle.isMatchLess(distProbe) ) {
- // distProbe is an upper bound,
+ // distProbe is an upper bound,
// find lower bound with binary search
do {
distMax = distProbe;
@@ -91,18 +109,19 @@ std::pair<double, double> bottleneckDistApproxInterval(DiagramPointSet& A, Diagr
return result;
}
-void sampleDiagramForHeur(const DiagramPointSet& dgmIn, DiagramPointSet& dgmOut)
+template<class Real>
+void sampleDiagramForHeur(const DiagramPointSet<Real>& dgmIn, DiagramPointSet<Real>& dgmOut)
{
#ifdef VERBOSE_BOTTLENECK
std::cout << "Entered sampleDiagramForHeur, dgmIn.size = " << dgmIn.size() << std::endl;
#endif
struct pair_hash {
- std::size_t operator()(const std::pair<double, double> p) const
+ std::size_t operator()(const std::pair<Real, Real> p) const
{
- return std::hash<double>()(p.first) ^ std::hash<double>()(p.second);
+ return std::hash<Real>()(p.first) ^ std::hash<Real>()(p.second);
}
};
- std::unordered_map<std::pair<double, double>, int, pair_hash> m;
+ std::unordered_map<std::pair<Real, Real>, int, pair_hash> m;
for(auto ptIter = dgmIn.cbegin(); ptIter != dgmIn.cend(); ++ptIter) {
if (ptIter->isNormal()) {
m[std::make_pair(ptIter->getRealX(), ptIter->getRealY())]++;
@@ -128,7 +147,7 @@ void sampleDiagramForHeur(const DiagramPointSet& dgmIn, DiagramPointSet& dgmOut)
#endif
int maxLeap = v[1] - v[0];
int cutVal = v[0];
- for(int i = 1; i < v.size() - 1; ++i) {
+ for(int i = 1; i < static_cast<int>(v.size())- 1; ++i) {
int currLeap = v[i+1] - v[i];
if (currLeap > maxLeap) {
maxLeap = currLeap;
@@ -138,7 +157,7 @@ void sampleDiagramForHeur(const DiagramPointSet& dgmIn, DiagramPointSet& dgmOut)
#ifdef VERBOSE_BOTTLENECK
std::cout << "cutVal found, cutVal = " << cutVal << std::endl;
#endif
- std::vector<std::pair<double, double>> vv;
+ std::vector<std::pair<Real, Real>> vv;
// keep points whose multiplicites are at most cutVal
// quick-and-dirty: fill in vv with copies of each point
// to construct DiagramPointSet from it later
@@ -153,17 +172,18 @@ void sampleDiagramForHeur(const DiagramPointSet& dgmIn, DiagramPointSet& dgmOut)
std::cout << "vv filled in, vv.size = " << v.size() << std::endl;
#endif
dgmOut.clear();
- dgmOut = DiagramPointSet(vv.begin(), vv.end());
+ dgmOut = DiagramPointSet<Real>(vv.begin(), vv.end());
#ifdef VERBOSE_BOTTLENECK
std::cout << "dgmOut filled in, dgmOut.size = " << dgmOut.size() << std::endl;
#endif
}
-// return the interval (distMin, distMax) such that:
+// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
-// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-std::pair<double, double> bottleneckDistApproxIntervalWithInitial(DiagramPointSet& A, DiagramPointSet& B, const double epsilon, const std::pair<double, double> initialGuess)
+// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
+template<class Real>
+std::pair<Real, Real> bottleneckDistApproxIntervalWithInitial(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon, const std::pair<Real, Real> initialGuess)
{
// empty diagrams are not considered as error
if (A.empty() and B.empty())
@@ -172,13 +192,13 @@ std::pair<double, double> bottleneckDistApproxIntervalWithInitial(DiagramPointSe
// link diagrams A and B by adding projections
addProjections(A, B);
- constexpr double epsThreshold { 1.0e-10 };
- std::pair<double, double> result { 0.0, 0.0 };
+ constexpr Real epsThreshold { 1.0e-10 };
+ std::pair<Real, Real> result { 0.0, 0.0 };
bool useRangeSearch { true };
// construct an oracle
- BoundMatchOracle oracle(A, B, epsThreshold, useRangeSearch);
- double& distMin {result.first};
- double& distMax {result.second};
+ BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
+ Real& distMin {result.first};
+ Real& distMax {result.second};
// initialize search interval from initialGuess
distMin = initialGuess.first;
@@ -204,7 +224,7 @@ std::pair<double, double> bottleneckDistApproxIntervalWithInitial(DiagramPointSe
// bounds are found, perform binary search
//std::cout << "Bounds found, distMin = " << distMin << ", distMax = " << distMax << ", ratio = " << ( distMax - distMin ) / distMin << std::endl ;
- double distProbe = ( distMin + distMax ) / 2.0;
+ Real distProbe = ( distMin + distMax ) / 2.0;
while ( ( distMax - distMin ) / distMin >= epsilon ) {
if (oracle.isMatchLess(distProbe)) {
distMax = distProbe;
@@ -216,54 +236,57 @@ std::pair<double, double> bottleneckDistApproxIntervalWithInitial(DiagramPointSe
return result;
}
-// return the interval (distMin, distMax) such that:
+// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
-// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
+// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
// use heuristic: initial estimate on sampled diagrams
-std::pair<double, double> bottleneckDistApproxIntervalHeur(DiagramPointSet& A, DiagramPointSet& B, const double epsilon)
+template<class Real>
+std::pair<Real, Real> bottleneckDistApproxIntervalHeur(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
{
// empty diagrams are not considered as error
if (A.empty() and B.empty())
return std::make_pair(0.0, 0.0);
- DiagramPointSet sampledA, sampledB;
+ DiagramPointSet<Real> sampledA, sampledB;
sampleDiagramForHeur(A, sampledA);
sampleDiagramForHeur(B, sampledB);
#ifdef VERBOSE_BOTTLENECK
std::cout << "A : " << A.size() << ", sampled: " << sampledA.size() << std::endl;
std::cout << "B : " << B.size() << ", sampled: " << sampledB.size() << std::endl;
#endif
- std::pair<double, double> initGuess = bottleneckDistApproxInterval(sampledA, sampledB, epsilon);
+ std::pair<Real, Real> initGuess = bottleneckDistApproxInterval(sampledA, sampledB, epsilon);
#ifdef VERBOSE_BOTTLENECK
std::cout << "initial guess with sampling: " << initGuess.first << ", " << initGuess.second << std::endl;
std::cout << "running on the original diagrams" << std::endl;
-#endif
- return bottleneckDistApproxIntervalWithInitial(A, B, epsilon, initGuess);
+#endif
+ return bottleneckDistApproxIntervalWithInitial<Real>(A, B, epsilon, initGuess);
}
// get approximate distance,
// see bottleneckDistApproxInterval
-double bottleneckDistApprox(DiagramPointSet& A, DiagramPointSet& B, const double epsilon)
+template<class Real>
+Real bottleneckDistApprox(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
{
- auto interval = bottleneckDistApproxInterval(A, B, epsilon);
+ auto interval = bottleneckDistApproxInterval<Real>(A, B, epsilon);
return interval.second;
}
-double bottleneckDistExactFromSortedPwDist(DiagramPointSet&A, DiagramPointSet& B, std::vector<double>& pairwiseDist, const int decPrecision)
+template<class Real>
+Real bottleneckDistExactFromSortedPwDist(DiagramPointSet<Real>&A, DiagramPointSet<Real>& B, std::vector<Real>& pairwiseDist, const int decPrecision)
{
//for(size_t k = 0; k < pairwiseDist.size(); ++k) {
//std::cout << "pairwiseDist[" << k << "] = " << std::setprecision(15) << pairwiseDist[k] << std::endl;
//}
// trivial case: we have only one candidate
- if (pairwiseDist.size() == 1)
+ if (pairwiseDist.size() == 1)
return pairwiseDist[0];
bool useRangeSearch = true;
- double distEpsilon = std::numeric_limits<double>::max();
- double diffThreshold = 0.1;
+ Real distEpsilon = std::numeric_limits<Real>::max();
+ Real diffThreshold = 0.1;
for(int k = 0; k < decPrecision; ++k) {
diffThreshold /= 10.0;
}
@@ -277,7 +300,7 @@ double bottleneckDistExactFromSortedPwDist(DiagramPointSet&A, DiagramPointSet& B
distEpsilon /= 3.0;
//std::cout << "decPrecision = " << decPrecision << ", distEpsilon = " << distEpsilon << std::endl;
- BoundMatchOracle oracle(A, B, distEpsilon, useRangeSearch);
+ BoundMatchOracle<Real> oracle(A, B, distEpsilon, useRangeSearch);
// binary search
size_t iterNum {0};
size_t idxMin {0}, idxMax {pairwiseDist.size() - 1};
@@ -301,32 +324,36 @@ double bottleneckDistExactFromSortedPwDist(DiagramPointSet&A, DiagramPointSet& B
}
-double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B)
+template<class Real>
+Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B)
{
return bottleneckDistExact(A, B, 14);
}
-double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int decPrecision)
+template<class Real>
+Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const int decPrecision)
{
- constexpr double epsilon = 0.001;
+ using DgmPoint = DiagramPoint<Real>;
+
+ constexpr Real epsilon = 0.001;
auto interval = bottleneckDistApproxInterval(A, B, epsilon);
- const double delta = 0.50001 * (interval.second - interval.first);
- const double approxDist = 0.5 * ( interval.first + interval.second);
- const double minDist = interval.first;
- const double maxDist = interval.second;
+ const Real delta = 0.50001 * (interval.second - interval.first);
+ const Real approxDist = 0.5 * ( interval.first + interval.second);
+ const Real minDist = interval.first;
+ const Real maxDist = interval.second;
//std::cout << std::setprecision(15) << "minDist = " << minDist << ", maxDist = " << maxDist << std::endl;
if ( delta == 0 ) {
return interval.first;
}
// copy points from A to a vector
// todo: get rid of this?
- std::vector<DiagramPoint> pointsA;
+ std::vector<DgmPoint> pointsA;
pointsA.reserve(A.size());
for(const auto& ptA : A) {
pointsA.push_back(ptA);
}
- //std::vector<double> killdist;
+ //std::vector<Real> killdist;
//for(auto pta : a) {
//for(auto ptb : b) {
//if ( distlinf(pta, ptb) > mindist and distlinf(pta, ptb) < maxdist) {
@@ -342,21 +369,21 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
//std::cout << "*************" << std::endl;
// in this vector we store the distances between the points
- // that are candidates to realize
- std::vector<double> pairwiseDist;
+ // that are candidates to realize
+ std::vector<Real> pairwiseDist;
{
// vector to store centers of vertical stripes
// two for each point in A and the id of the corresponding point
- std::vector<std::pair<double, DiagramPoint>> xCentersVec;
+ std::vector<std::pair<Real, DgmPoint>> xCentersVec;
xCentersVec.reserve(2 * pointsA.size());
for(auto ptA : pointsA) {
xCentersVec.push_back(std::make_pair(ptA.getRealX() - approxDist, ptA));
xCentersVec.push_back(std::make_pair(ptA.getRealX() + approxDist, ptA));
}
// lambda to compare pairs <coordinate, id> w.r.t coordinate
- auto compLambda = [](std::pair<double, DiagramPoint> a, std::pair<double, DiagramPoint> b)
+ auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b)
{ return a.first < b.first; };
-
+
std::sort(xCentersVec.begin(), xCentersVec.end(), compLambda);
//std::cout << "xCentersVec.size = " << xCentersVec.size() << std::endl;
//for(auto p = xCentersVec.begin(); p!= xCentersVec.end(); ++p) {
@@ -364,13 +391,13 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
//std::cout << "index of 200: " << p - xCentersVec.begin() << std::endl;
//}
//}
- //std::vector<DiagramPoint>
+ //std::vector<DgmPoint>
// todo: sort points in B, reduce search range in lower and upper bounds
for(auto ptB : B) {
// iterator to the first stripe such that ptB lies to the left
// from its right boundary (x_B <= x_j + \delta iff x_j >= x_B - \delta
auto itStart = std::lower_bound(xCentersVec.begin(),
- xCentersVec.end(),
+ xCentersVec.end(),
std::make_pair(ptB.getRealX() - delta, ptB),
compLambda);
//if (ptB.id == 236) {
@@ -392,7 +419,7 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
}
// we're here => ptB lies in vertical stripe,
// check if distance fits into the interval we've found
- double pwDist = distLInf(iterA->second, ptB);
+ Real pwDist = distLInf(iterA->second, ptB);
//if (ptB.id == 236) {
//std::cout << pwDist << std::endl;
//}
@@ -408,16 +435,16 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
// for y
// vector to store centers of vertical stripes
// two for each point in A and the id of the corresponding point
- std::vector<std::pair<double, DiagramPoint>> yCentersVec;
+ std::vector<std::pair<Real, DgmPoint>> yCentersVec;
yCentersVec.reserve(2 * pointsA.size());
for(auto ptA : pointsA) {
yCentersVec.push_back(std::make_pair(ptA.getRealY() - approxDist, ptA));
yCentersVec.push_back(std::make_pair(ptA.getRealY() + approxDist, ptA));
}
// lambda to compare pairs <coordinate, id> w.r.t coordinate
- auto compLambda = [](std::pair<double, DiagramPoint> a, std::pair<double, DiagramPoint> b)
+ auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b)
{ return a.first < b.first; };
-
+
std::sort(yCentersVec.begin(), yCentersVec.end(), compLambda);
// std::cout << "Sorted vector of y-centers:" << std::endl;
@@ -426,11 +453,11 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
//}
/*std::cout << "End of sorted vector of y-centers:" << std::endl;*/
- //std::vector<DiagramPoint>
+ //std::vector<DgmPoint>
// todo: sort points in B, reduce search range in lower and upper bounds
for(auto ptB : B) {
auto itStart = std::lower_bound(yCentersVec.begin(),
- yCentersVec.end(),
+ yCentersVec.end(),
std::make_pair(ptB.getRealY() - delta, ptB),
compLambda);
@@ -439,7 +466,7 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
if ( ptB.getRealY() < iterA->first - delta) {
break;
}
- double pwDist = distLInf(iterA->second, ptB);
+ Real pwDist = distLInf(iterA->second, ptB);
//std::cout << 1000*minDist << " <= " << 1000*pwDist << " <= " << 1000*maxDist << std::endl;
if (pwDist >= minDist and pwDist <= maxDist) {
pairwiseDist.push_back(pwDist);
@@ -457,8 +484,11 @@ double bottleneckDistExact(DiagramPointSet& A, DiagramPointSet& B, const int dec
return bottleneckDistExactFromSortedPwDist(A, B, pairwiseDist, decPrecision);
}
-double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
+template<class Real>
+Real bottleneckDistSlow(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B)
{
+ using DistVerticesPair = std::pair<Real, std::pair<size_t, size_t>>;
+
// use range search when building the layer graph
bool useRangeSearch { true };
// find maximum of min. distances for each point,
@@ -471,33 +501,34 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
// use successive multiplication of idxMin with 2 to get idxMax
bool goUpToFindIdxMax { false };
- //
- goUpToFindIdxMax = goUpToFindIdxMax and !useHeurGreedyMatching;
+ //
+ goUpToFindIdxMax = goUpToFindIdxMax and !useHeurGreedyMatching;
if (!useHeurGreedyMatching) {
long int N = 3 * (A.size() / 2 ) * (B.size() / 2);
- std::vector<double> pairwiseDist;
+ std::vector<Real> pairwiseDist;
pairwiseDist.reserve(N);
- double maxMinDist {0.0};
+ Real maxMinDist {0.0};
for(auto& p_A : A) {
- double minDist { std::numeric_limits<double>::max() };
+ Real minDist { std::numeric_limits<Real>::max() };
for(auto& p_B : B) {
- if (p_A.type != DiagramPoint::DIAG or p_B.type != DiagramPoint::DIAG) {
- double d = distLInf(p_A, p_B);
+ if (p_A.isNormal() or p_B.isNormal()) {
+ Real d = distLInf(p_A, p_B);
pairwiseDist.push_back(d);
- if (useHeurMinIdx and p_A.type != DiagramPoint::DIAG) {
+ if (useHeurMinIdx and p_A.isNormal()) {
if (d < minDist)
minDist = d;
}
}
}
- if (useHeurMinIdx and DiagramPoint::DIAG != p_A.type and minDist > maxMinDist) {
+ if (useHeurMinIdx and p_A.isNormal() and minDist > maxMinDist) {
maxMinDist = minDist;
}
}
+
std::sort(pairwiseDist.begin(), pairwiseDist.end());
- double distEpsilon = std::numeric_limits<double>::max();
+ Real distEpsilon = std::numeric_limits<Real>::max();
for(size_t k = 0; k < pairwiseDist.size() - 2; ++k) {
auto diff = pairwiseDist[k+1]- pairwiseDist[k];
if ( diff > 1.0e-10 and diff < distEpsilon ) {
@@ -506,7 +537,7 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
}
distEpsilon /= 3.0;
- BoundMatchOracle oracle(A, B, distEpsilon, useRangeSearch);
+ BoundMatchOracle<Real> oracle(A, B, distEpsilon, useRangeSearch);
// binary search
size_t iterNum {0};
size_t idxMin {0}, idxMax {pairwiseDist.size() - 1};
@@ -555,34 +586,34 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
long int N = A.size() * B.size();
std::vector<DistVerticesPair> pairwiseDist;
pairwiseDist.reserve(N);
- double maxMinDist {0.0};
+ Real maxMinDist {0.0};
size_t idxA{0}, idxB{0};
for(auto p_A : A) {
- double minDist { std::numeric_limits<double>::max() };
+ Real minDist { std::numeric_limits<Real>::max() };
idxB = 0;
for(auto p_B : B) {
- double d = distLInf(p_A, p_B);
+ Real d = distLInf(p_A, p_B);
pairwiseDist.push_back( std::make_pair(d, std::make_pair(idxA, idxB) ) );
- if (useHeurMinIdx and p_A.type != DiagramPoint::DIAG) {
+ if (useHeurMinIdx and p_A.isNormal()) {
if (d < minDist)
minDist = d;
}
idxB++;
}
- if (useHeurMinIdx and DiagramPoint::DIAG != p_A.type and minDist > maxMinDist) {
+ if (useHeurMinIdx and p_A.isNormal() and minDist > maxMinDist) {
maxMinDist = minDist;
}
idxA++;
}
- auto compLambda = [](DistVerticesPair a, DistVerticesPair b)
+ auto compLambda = [](DistVerticesPair a, DistVerticesPair b)
{ return a.first < b.first;};
- std::sort(pairwiseDist.begin(),
- pairwiseDist.end(),
+ std::sort(pairwiseDist.begin(),
+ pairwiseDist.end(),
compLambda);
- double distEpsilon = std::numeric_limits<double>::max();
+ Real distEpsilon = std::numeric_limits<Real>::max();
for(size_t k = 0; k < pairwiseDist.size() - 2; ++k) {
auto diff = pairwiseDist[k+1].first - pairwiseDist[k].first;
if ( diff > 1.0e-10 and diff < distEpsilon ) {
@@ -591,7 +622,7 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
}
distEpsilon /= 3.0;
- BoundMatchOracle oracle(A, B, distEpsilon, useRangeSearch);
+ BoundMatchOracle<Real> oracle(A, B, distEpsilon, useRangeSearch);
// construct greedy matching
size_t numVert { A.size() };
@@ -618,7 +649,7 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
size_t idxMin {0};
if (useHeurMinIdx) {
auto maxMinIter = std::equal_range(pairwiseDist.begin(),
- pairwiseDist.end(),
+ pairwiseDist.end(),
std::make_pair(maxMinDist, std::make_pair(0,0)),
compLambda);
assert(maxMinIter.first != pairwiseDist.end());
@@ -654,30 +685,34 @@ double bottleneckDistSlow(DiagramPointSet& A, DiagramPointSet& B)
std::cout << "\t" << iterNum;
std::cout << "\t" << A.size() + B.size();
std::cout << "\t" << edgeNumber << "\t";
- std::cout << (double)(edgeNumber) / (double)(A.size() + B.size()) << std::endl;
+ std::cout << (Real)(edgeNumber) / (Real)(A.size() + B.size()) << std::endl;
*/
}
// wrappers
-bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<double, double>>& result)
+template<class Real>
+bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<Real, Real>>& result)
{
int decPrecision;
return readDiagramPointSet(fname.c_str(), result, decPrecision);
}
-bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double>>& result)
+template<class Real>
+bool readDiagramPointSet(const char* fname, std::vector<std::pair<Real, Real>>& result)
{
int decPrecision;
return readDiagramPointSet(fname, result, decPrecision);
}
-bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<double, double>>& result, int& decPrecision)
+template<class Real>
+bool readDiagramPointSet(const std::string& fname, std::vector<std::pair<Real, Real>>& result, int& decPrecision)
{
return readDiagramPointSet(fname.c_str(), result, decPrecision);
}
// reading function
-bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double>>& result, int& decPrecision)
+template<class Real>
+bool readDiagramPointSet(const char* fname, std::vector<std::pair<Real, Real>>& result, int& decPrecision)
{
size_t lineNumber { 0 };
result.clear();
@@ -699,7 +734,7 @@ bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double
if (line.empty()) {
continue;
}
- // trim whitespaces
+ // trim whitespaces
auto whiteSpaceFront = std::find_if_not(line.begin(),line.end(),isspace);
auto whiteSpaceBack = std::find_if_not(line.rbegin(),line.rend(),isspace).base();
if (whiteSpaceBack <= whiteSpaceFront) {
@@ -721,9 +756,9 @@ bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double
decPrecision = currDecPrecision;
currDecPrecision = 0;
}
- }
+ }
}
- double x, y;
+ Real x, y;
std::istringstream iss(line);
if (not(iss >> x >> y)) {
#ifndef FOR_R_TDA
@@ -745,6 +780,6 @@ bool readDiagramPointSet(const char* fname, std::vector<std::pair<double, double
return true;
}
-
-
-}
+} // end namespace bt
+} // end namespace hera
+#endif // HERA_BOTTLENECK_HPP
diff --git a/geom_bottleneck/include/bound_match.h b/geom_bottleneck/include/bound_match.h
new file mode 100644
index 0000000..770c7df
--- /dev/null
+++ b/geom_bottleneck/include/bound_match.h
@@ -0,0 +1,107 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_BOUND_MATCH_H
+#define HERA_BOUND_MATCH_H
+
+#include <unordered_map>
+#include <memory>
+
+#include "basic_defs_bt.h"
+#include "neighb_oracle.h"
+
+
+namespace hera {
+namespace bt {
+
+template<class Real = double>
+class Matching {
+public:
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointSet = DiagramPointSet<Real>;
+ using DgmPointHash = DiagramPointHash<Real>;
+ using Path = std::vector<DgmPoint>;
+
+ Matching(const DgmPointSet& AA, const DgmPointSet& BB) : A(AA), B(BB) {};
+ DgmPointSet getExposedVertices(bool forA = true) const ;
+ bool isExposed(const DgmPoint& p) const;
+ void getAllAdjacentVertices(const DgmPointSet& setIn, DgmPointSet& setOut, bool forA = true) const;
+ void increase(const Path& augmentingPath);
+ void checkAugPath(const Path& augPath) const;
+ bool getMatchedVertex(const DgmPoint& p, DgmPoint& result) const;
+ bool isPerfect() const;
+ void trimMatching(const Real newThreshold);
+#ifndef FOR_R_TDA
+ template<class R>
+ friend std::ostream& operator<<(std::ostream& output, const Matching<R>& m);
+#endif
+private:
+ DgmPointSet A;
+ DgmPointSet B;
+ std::unordered_map<DgmPoint, DgmPoint, DgmPointHash> AToB, BToA;
+ void matchVertices(const DgmPoint& pA, const DgmPoint& pB);
+ void sanityCheck() const;
+};
+
+
+
+template<class Real_ = double, class NeighbOracle_ = NeighbOracleDnn<Real_>>
+class BoundMatchOracle {
+public:
+ using Real = Real_;
+ using NeighbOracle = NeighbOracle_;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointSet = DiagramPointSet<Real>;
+ using Path = std::vector<DgmPoint>;
+
+ BoundMatchOracle(DgmPointSet psA, DgmPointSet psB, Real dEps, bool useRS = true);
+ bool isMatchLess(Real r);
+ bool buildMatchingForThreshold(const Real r);
+private:
+ DgmPointSet A, B;
+ Matching<Real> M;
+ void printLayerGraph();
+ void buildLayerGraph(Real r);
+ void buildLayerOracles(Real r);
+ bool buildAugmentingPath(const DgmPoint startVertex, Path& result);
+ void removeFromLayer(const DgmPoint& p, const int layerIdx);
+ std::unique_ptr<NeighbOracle> neighbOracle;
+ bool augPathExist;
+ std::vector<DgmPointSet> layerGraph;
+ std::vector<std::unique_ptr<NeighbOracle>> layerOracles;
+ Real distEpsilon;
+ bool useRangeSearch;
+ Real prevQueryValue;
+};
+
+} // end namespace bt
+} // end namespace hera
+
+#include "bound_match.hpp"
+
+#endif // HERA_BOUND_MATCH_H
diff --git a/geom_bottleneck/bottleneck/src/bound_match.cpp b/geom_bottleneck/include/bound_match.hpp
index 210bd81..221bd0f 100644
--- a/geom_bottleneck/bottleneck/src/bound_match.cpp
+++ b/geom_bottleneck/include/bound_match.hpp
@@ -1,23 +1,41 @@
/*
-Copyrigth 2015, D. Morozov, M. Kerber, A. Nigmetov
-This file is part of GeomBottleneck.
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
-GeomBottleneck is free software: you can redistribute it and/or modify
-it under the terms of the Lesser GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-GeomBottleneck is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-Lesser GNU General Public License for more details.
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-You should have received a copy of the Lesser GNU General Public License
-along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
*/
+#ifndef HERA_BOUND_MATCH_HPP
+#define HERA_BOUND_MATCH_HPP
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_BOUND_MATCH
+#undef DEBUG_MATCHING
+#undef VERBOSE_BOTTLENECK
+#endif
+
#include <assert.h>
#include "def_debug_bt.h"
#include "bound_match.h"
@@ -30,68 +48,12 @@ along with GeomBottleneck. If not, see <http://www.gnu.org/licenses/>.
#include <iostream>
#endif
-namespace geom_bt {
-/*static void printDebug(//bool isDebug, std::string s)*/
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const Matching& m)
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s << std::endl;
- //std::cout << m << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const DiagramPoint& p)
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s << p << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const double r)
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s << r << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const Path p)
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s;
- //for(auto pt : p) {
- //std::cout << pt << "; ";
- //}
- //std::cout << std::endl;
- //}
-//#endif
-//}
-
-//static void printDebug(//bool isDebug, std::string s, const DiagramPointSet& dpSet)
-//{
-//#ifdef DEBUG_BOUND_MATCH
- //if (isDebug) {
- //std::cout << s << dpSet << std::endl;
- //}
-//#endif
-/*}*/
+namespace hera{
+namespace bt {
#ifndef FOR_R_TDA
-std::ostream& operator<<(std::ostream& output, const Matching& m)
+template<class Real>
+std::ostream& operator<<(std::ostream& output, const Matching<Real>& m)
{
output << "Matching: " << m.AToB.size() << " pairs (";
if (!m.isPerfect()) {
@@ -105,32 +67,27 @@ std::ostream& operator<<(std::ostream& output, const Matching& m)
}
#endif
-void Matching::sanityCheck() const
+template<class R>
+void Matching<R>::sanityCheck() const
{
#ifdef DEBUG_MATCHING
assert( AToB.size() == BToA.size() );
for(auto aToBPair : AToB) {
auto bToAPair = BToA.find(aToBPair.second);
assert(bToAPair != BToA.end());
- if (aToBPair.first != bToAPair->second) {
-#ifndef FOR_R_TDA
- std::cerr << "failed assertion, in aToB " << aToBPair.first;
- std::cerr << ", in bToA " << bToAPair->second << std::endl;
-#endif
- assert(false);
- }
assert( aToBPair.first == bToAPair->second);
}
-#endif
+#endif
}
-bool Matching::isPerfect() const
+template<class R>
+bool Matching<R>::isPerfect() const
{
- //sanityCheck();
return AToB.size() == A.size();
}
-void Matching::matchVertices(const DiagramPoint& pA, const DiagramPoint& pB)
+template<class R>
+void Matching<R>::matchVertices(const DgmPoint& pA, const DgmPoint& pB)
{
assert(A.hasElement(pA));
assert(B.hasElement(pB));
@@ -140,17 +97,18 @@ void Matching::matchVertices(const DiagramPoint& pA, const DiagramPoint& pB)
BToA.insert( {{ pB, pA }} );
}
-bool Matching::getMatchedVertex(const DiagramPoint& p, DiagramPoint& result) const
+template<class R>
+bool Matching<R>::getMatchedVertex(const DgmPoint& p, DgmPoint& result) const
{
sanityCheck();
auto inA = AToB.find(p);
if (inA != AToB.end()) {
- result = (*inA).second;
+ result = inA->second;
return true;
} else {
auto inB = BToA.find(p);
if (inB != BToA.end()) {
- result = (*inB).second;
+ result = inB->second;
return true;
}
}
@@ -158,7 +116,8 @@ bool Matching::getMatchedVertex(const DiagramPoint& p, DiagramPoint& result) con
}
-void Matching::checkAugPath(const Path& augPath) const
+template<class R>
+void Matching<R>::checkAugPath(const Path& augPath) const
{
assert(augPath.size() % 2 == 0);
for(size_t idx = 0; idx < augPath.size(); ++idx) {
@@ -169,16 +128,16 @@ void Matching::checkAugPath(const Path& augPath) const
#endif
}
assert( isExposed(augPath[idx]) == mustBeExposed );
- DiagramPoint matchedVertex {0.0, 0.0, DiagramPoint::DIAG, 1};
+ DgmPoint matchedVertex {0.0, 0.0, DgmPoint::DIAG, 1};
if ( idx % 2 == 0 ) {
assert( A.hasElement(augPath[idx]));
- if (!mustBeExposed) {
+ if (not mustBeExposed) {
getMatchedVertex(augPath[idx], matchedVertex);
assert(matchedVertex == augPath[idx - 1]);
}
} else {
assert( B.hasElement(augPath[idx]));
- if (!mustBeExposed) {
+ if (not mustBeExposed) {
getMatchedVertex(augPath[idx], matchedVertex);
assert(matchedVertex == augPath[idx + 1]);
}
@@ -188,25 +147,25 @@ void Matching::checkAugPath(const Path& augPath) const
// use augmenting path to increase
// the size of the matching
-void Matching::increase(const Path& augPath)
+template<class R>
+void Matching<R>::increase(const Path& augPath)
{
- //bool isDebug {false};
sanityCheck();
// check that augPath is an augmenting path
checkAugPath(augPath);
for(size_t idx = 0; idx < augPath.size() - 1; idx += 2) {
matchVertices( augPath[idx], augPath[idx + 1]);
}
- //printDebug(isDebug, "", *this);
sanityCheck();
}
-DiagramPointSet Matching::getExposedVertices(bool forA) const
+template<class R>
+DiagramPointSet<R> Matching<R>::getExposedVertices(bool forA) const
{
sanityCheck();
- DiagramPointSet result;
- const DiagramPointSet* setToSearch { forA ? &A : &B };
- const std::unordered_map<DiagramPoint, DiagramPoint, DiagramPointHash>* mapToSearch { forA ? &AToB : &BToA };
+ DgmPointSet result;
+ const DgmPointSet* setToSearch { forA ? &A : &B };
+ const std::unordered_map<DgmPoint, DgmPoint, DgmPointHash>* mapToSearch { forA ? &AToB : &BToA };
for(auto it = setToSearch->cbegin(); it != setToSearch->cend(); ++it) {
if (mapToSearch->find((*it)) == mapToSearch->cend()) {
result.insert((*it));
@@ -215,14 +174,15 @@ DiagramPointSet Matching::getExposedVertices(bool forA) const
return result;
}
-void Matching::getAllAdjacentVertices(const DiagramPointSet& setIn,
- DiagramPointSet& setOut,
+template<class R>
+void Matching<R>::getAllAdjacentVertices(const DgmPointSet& setIn,
+ DgmPointSet& setOut,
bool forA) const
{
sanityCheck();
//bool isDebug {false};
setOut.clear();
- const std::unordered_map<DiagramPoint, DiagramPoint, DiagramPointHash>* m;
+ const std::unordered_map<DgmPoint, DgmPoint, DgmPointHash>* m;
m = ( forA ) ? &BToA : &AToB;
for(auto pit = setIn.cbegin(); pit != setIn.cend(); ++pit) {
auto findRes = m->find(*pit);
@@ -230,25 +190,45 @@ void Matching::getAllAdjacentVertices(const DiagramPointSet& setIn,
setOut.insert((*findRes).second);
}
}
- //printDebug(isDebug, "got all adjacent vertices for ", setIn);
- //printDebug(isDebug, "the result is: ", setOut);
sanityCheck();
}
-bool Matching::isExposed(const DiagramPoint& p) const
+template<class R>
+bool Matching<R>::isExposed(const DgmPoint& p) const
{
return ( AToB.find(p) == AToB.end() ) && ( BToA.find(p) == BToA.end() );
}
+// remove all edges whose length is > newThreshold
+template<class R>
+void Matching<R>::trimMatching(const R newThreshold)
+{
+ //bool isDebug { false };
+ sanityCheck();
+ for(auto aToBIter = AToB.begin(); aToBIter != AToB.end(); ) {
+ if ( distLInf(aToBIter->first, aToBIter->second) > newThreshold ) {
+ // remove edge from AToB and BToA
+ BToA.erase(aToBIter->second);
+ aToBIter = AToB.erase(aToBIter);
+ } else {
+ aToBIter++;
+ }
+ }
+ sanityCheck();
+}
-BoundMatchOracle::BoundMatchOracle(DiagramPointSet psA, DiagramPointSet psB,
- double dEps, bool useRS) :
+// ------- BoundMatchOracle --------------
+
+template<class R, class NO>
+BoundMatchOracle<R, NO>::BoundMatchOracle(DgmPointSet psA, DgmPointSet psB,
+ Real dEps, bool useRS) :
A(psA), B(psB), M(A, B), distEpsilon(dEps), useRangeSearch(useRS), prevQueryValue(0.0)
{
- neighbOracle = new NeighbOracle(psB, 0, distEpsilon);
+ neighbOracle = std::unique_ptr<NeighbOracle>(new NeighbOracle(psB, 0, distEpsilon));
}
-bool BoundMatchOracle::isMatchLess(double r)
+template<class R, class NO>
+bool BoundMatchOracle<R, NO>::isMatchLess(Real r)
{
#ifdef VERBOSE_BOTTLENECK
std::chrono::high_resolution_clock hrClock;
@@ -266,9 +246,9 @@ bool BoundMatchOracle::isMatchLess(double r)
}
-void BoundMatchOracle::removeFromLayer(const DiagramPoint& p, const int layerIdx) {
+template<class R, class NO>
+void BoundMatchOracle<R, NO>::removeFromLayer(const DgmPoint& p, const int layerIdx) {
//bool isDebug {false};
- //printDebug(isDebug, "entered removeFromLayer, layerIdx == " + std::to_string(layerIdx) + ", p = ", p);
layerGraph[layerIdx].erase(p);
if (layerOracles[layerIdx]) {
layerOracles[layerIdx]->deletePoint(p);
@@ -278,29 +258,26 @@ void BoundMatchOracle::removeFromLayer(const DiagramPoint& p, const int layerIdx
// return true, if there exists an augmenting path from startVertex
// in this case the path is returned in result.
// startVertex must be an exposed vertex from L_1 (layer[0])
-bool BoundMatchOracle::buildAugmentingPath(const DiagramPoint startVertex, Path& result)
+template<class R, class NO>
+bool BoundMatchOracle<R, NO>::buildAugmentingPath(const DgmPoint startVertex, Path& result)
{
//bool isDebug {false};
- //printDebug(isDebug, "Entered buildAugmentingPath, startVertex: ", startVertex);
- DiagramPoint prevVertexA = startVertex;
+ DgmPoint prevVertexA = startVertex;
result.clear();
result.push_back(startVertex);
size_t evenLayerIdx {1};
while ( evenLayerIdx < layerGraph.size() ) {
//for(size_t evenLayerIdx = 1; evenLayerIdx < layerGraph.size(); evenLayerIdx += 2) {
- DiagramPoint nextVertexB{0.0, 0.0, DiagramPoint::DIAG, 1}; // next vertex from even layer
+ DgmPoint nextVertexB{0.0, 0.0, DgmPoint::DIAG, 1}; // next vertex from even layer
bool neighbFound = layerOracles[evenLayerIdx]->getNeighbour(prevVertexA, nextVertexB);
- //printDebug(isDebug, "Searched neighbours for ", prevVertexA);
- //printDebug(isDebug, "; the result is ", nextVertexB);
if (neighbFound) {
result.push_back(nextVertexB);
if ( layerGraph.size() == evenLayerIdx + 1) {
- //printDebug(isDebug, "Last layer reached, stopping; the path: ", result);
break;
} else {
// nextVertexB must be matched with some vertex from the next odd
// layer
- DiagramPoint nextVertexA {0.0, 0.0, DiagramPoint::DIAG, 1};
+ DgmPoint nextVertexA {0.0, 0.0, DgmPoint::DIAG, 1};
if (!M.getMatchedVertex(nextVertexB, nextVertexA)) {
#ifndef FOR_R_TDA
std::cerr << "Vertices in even layers must be matched! Unmatched: ";
@@ -311,7 +288,6 @@ bool BoundMatchOracle::buildAugmentingPath(const DiagramPoint startVertex, Path&
} else {
assert( ! (nextVertexA.getRealX() == 0 and nextVertexA.getRealY() == 0) );
result.push_back(nextVertexA);
- //printDebug(isDebug, "Matched vertex from the even layer added to the path, result: ", result);
prevVertexA = nextVertexA;
evenLayerIdx += 2;
continue;
@@ -323,20 +299,17 @@ bool BoundMatchOracle::buildAugmentingPath(const DiagramPoint startVertex, Path&
if (evenLayerIdx == 1) {
// startVertex is not connected to any vertices
// in the next layer, augm. path doesn't exist
- //printDebug(isDebug, "startVertex is not connected to any vertices in the next layer, augm. path doesn't exist");
removeFromLayer(startVertex, 0);
return false;
} else {
assert(evenLayerIdx >= 3);
assert(result.size() % 2 == 1);
result.pop_back();
- DiagramPoint prevVertexB = result.back();
+ DgmPoint prevVertexB = result.back();
result.pop_back();
- //printDebug(isDebug, "removing 2 previous vertices from layers, evenLayerIdx == ", evenLayerIdx);
removeFromLayer(prevVertexA, evenLayerIdx-1);
removeFromLayer(prevVertexB, evenLayerIdx-2);
// we should proceed from the previous odd layer
- //printDebug(isDebug, "Here! res.size == ", result.size());
assert(result.size() >= 1);
prevVertexA = result.back();
evenLayerIdx -= 2;
@@ -353,88 +326,50 @@ bool BoundMatchOracle::buildAugmentingPath(const DiagramPoint startVertex, Path&
}
-// remove all edges whose length is > newThreshold
-void Matching::trimMatching(const double newThreshold)
-{
- //bool isDebug { false };
- sanityCheck();
- for(auto aToBIter = AToB.begin(); aToBIter != AToB.end(); ) {
- if ( distLInf(aToBIter->first, aToBIter->second) > newThreshold ) {
- // remove edge from AToB and BToA
- //printDebug(isDebug, "removing edge ", aToBIter->first);
- //printDebug(isDebug, " <-> ", aToBIter->second);
- BToA.erase(aToBIter->second);
- aToBIter = AToB.erase(aToBIter);
- } else {
- aToBIter++;
- }
- }
- sanityCheck();
-}
-bool BoundMatchOracle::buildMatchingForThreshold(const double r)
+
+template<class R, class NO>
+bool BoundMatchOracle<R, NO>::buildMatchingForThreshold(const Real r)
{
//bool isDebug {false};
- //printDebug(isDebug,"Entered buildMatchingForThreshold, r = " + std::to_string(r));
if (prevQueryValue > r) {
M.trimMatching(r);
}
prevQueryValue = r;
while(true) {
buildLayerGraph(r);
- //printDebug(isDebug,"Layer graph built");
if (augPathExist) {
std::vector<Path> augmentingPaths;
- DiagramPointSet copyLG0;
- for(DiagramPoint p : layerGraph[0]) {
+ DgmPointSet copyLG0;
+ for(DgmPoint p : layerGraph[0]) {
copyLG0.insert(p);
}
- for(DiagramPoint exposedVertex : copyLG0) {
+ for(DgmPoint exposedVertex : copyLG0) {
Path augPath;
if (buildAugmentingPath(exposedVertex, augPath)) {
- //printDebug(isDebug, "Augmenting path found", augPath);
augmentingPaths.push_back(augPath);
- }
- /*
- else {
- printDebug(isDebug,"augmenting paths must exist, but were not found!", M);
- std::cerr << "augmenting paths must exist, but were not found!" << std::endl;
- std::cout.flush();
- std::cerr.flush();
- printLayerGraph();
- //throw "Something went wrong-1";
- //return M.isPerfect();
- // analyze: finished or no paths exist
- // can this actually happen?
}
- */
-
- }
+ }
if (augmentingPaths.empty()) {
- //printDebug(isDebug,"augmenting paths must exist, but were not found!", M);
#ifndef FOR_R_TDA
std::cerr << "augmenting paths must exist, but were not found!" << std::endl;
#endif
throw std::runtime_error("bad epsilon?");
}
// swap all augmenting paths with matching to increase it
- //printDebug(isDebug,"before increase with augmenting paths:", M);
for(auto& augPath : augmentingPaths ) {
- //printDebug(isDebug, "Increasing with augm. path ", augPath);
M.increase(augPath);
}
- //printDebug(isDebug,"after increase with augmenting paths:", M);
} else {
- //printDebug(isDebug,"no augmenting paths exist, matching returned is:", M);
return M.isPerfect();
}
- }
+ }
}
-void BoundMatchOracle::printLayerGraph(void)
+template<class R, class NO>
+void BoundMatchOracle<R, NO>::printLayerGraph(void)
{
#ifdef DEBUG_BOUND_MATCH
-#ifndef FOR_R_TDA
for(auto& layer : layerGraph) {
std::cout << "{ ";
for(auto& p : layer) {
@@ -443,33 +378,29 @@ void BoundMatchOracle::printLayerGraph(void)
std::cout << "\b\b }" << std::endl;
}
#endif
-#endif
}
-void BoundMatchOracle::buildLayerGraph(double r)
+template<class R, class NO>
+void BoundMatchOracle<R, NO>::buildLayerGraph(Real r)
{
#ifdef VERBOSE_BOTTLENECK
std::cout << "Entered buildLayerGraph, r = " << r << std::endl;
#endif
layerGraph.clear();
- DiagramPointSet L1 = M.getExposedVertices();
- //printDebug(isDebug,"Got exposed vertices");
+ DgmPointSet L1 = M.getExposedVertices();
layerGraph.push_back(L1);
neighbOracle->rebuild(B, r);
- //printDebug(isDebug,"Oracle rebuilt");
size_t k = 0;
- DiagramPointSet layerNextEven;
- DiagramPointSet layerNextOdd;
+ DgmPointSet layerNextEven;
+ DgmPointSet layerNextOdd;
bool exposedVerticesFound {false};
while(true) {
- //printDebug(isDebug, "k = ", k);
layerNextEven.clear();
for( auto p : layerGraph[k]) {
- //printDebug(isDebug,"looking for neighbours for ", p);
bool neighbFound;
- DiagramPoint neighbour {0.0, 0.0, DiagramPoint::DIAG, 1};
+ DgmPoint neighbour {0.0, 0.0, DgmPoint::DIAG, 1};
if (useRangeSearch) {
- std::vector<DiagramPoint> neighbVec;
+ std::vector<DgmPoint> neighbVec;
neighbOracle->getAllNeighbours(p, neighbVec);
neighbFound = !neighbVec.empty();
for(auto& neighbPt : neighbVec) {
@@ -481,29 +412,22 @@ void BoundMatchOracle::buildLayerGraph(double r)
while(true) {
neighbFound = neighbOracle->getNeighbour(p, neighbour);
if (neighbFound) {
- //printDebug(isDebug,"neighbour found, ", neighbour);
layerNextEven.insert(neighbour);
neighbOracle->deletePoint(neighbour);
- //printDebug(isDebug,"is exposed: " + std::to_string(M.isExposed(neighbour)));
if ((!exposedVerticesFound) && M.isExposed(neighbour)) {
exposedVerticesFound = true;
}
} else {
- //printDebug(isDebug,"no neighbours found for r = ", r);
break;
}
}
} // without range search
} // all vertices from previous odd layer processed
- //printDebug(isDebug,"Next even layer finished");
if (layerNextEven.empty()) {
- //printDebug(isDebug,"Next even layer is empty, augPathExist = false");
augPathExist = false;
- break;
+ break;
}
if (exposedVerticesFound) {
- //printDebug(isDebug,"Exposed vertices found in the even layer, aug. paths exist");
- //printDebug(isDebug,"Deleting all non-exposed from the last layer (we do not need them).");
for(auto it = layerNextEven.cbegin(); it != layerNextEven.cend(); ) {
if ( ! M.isExposed(*it) ) {
layerNextEven.erase(it++);
@@ -518,49 +442,32 @@ void BoundMatchOracle::buildLayerGraph(double r)
}
layerGraph.push_back(layerNextEven);
M.getAllAdjacentVertices(layerNextEven, layerNextOdd);
- //printDebug(isDebug,"Next odd layer finished");
layerGraph.push_back(layerNextOdd);
k += 2;
}
buildLayerOracles(r);
- //printDebug(isDebug,"layer oracles built, layer graph:");
printLayerGraph();
-#ifdef VERBOSE_BOTTLENECK
- std::cout << "Exit buildLayerGraph, r = " << r << std::endl;
-#endif
}
-
-
-BoundMatchOracle::~BoundMatchOracle()
-{
- for(auto& oracle : layerOracles) {
- delete oracle;
- }
- delete neighbOracle;
-}
-
// create geometric oracles for each even layer
// odd layers have NULL in layerOracles
-void BoundMatchOracle::buildLayerOracles(double r)
+template<class R, class NO>
+void BoundMatchOracle<R, NO>::buildLayerOracles(Real r)
{
//bool isDebug {false};
- //printDebug(isDebug,"entered buildLayerOracles");
// free previously constructed oracles
- for(auto& oracle : layerOracles) {
- delete oracle;
- }
layerOracles.clear();
- //printDebug(isDebug,"previous oracles deleted");
for(size_t layerIdx = 0; layerIdx < layerGraph.size(); ++layerIdx) {
if (layerIdx % 2 == 1) {
// even layer, build actual oracle
- layerOracles.push_back(new NeighbOracle(layerGraph[layerIdx], r, distEpsilon));
+ layerOracles.emplace_back(new NeighbOracle(layerGraph[layerIdx], r, distEpsilon));
} else {
// odd layer
- layerOracles.push_back(nullptr);
+ layerOracles.emplace_back(nullptr);
}
}
- //printDebug(isDebug,"exiting buildLayerOracles");
-}
}
+
+} // end namespace bt
+} // end namespace hera
+#endif // HERA_BOUND_MATCH_HPP
diff --git a/geom_bottleneck/include/def_debug_bt.h b/geom_bottleneck/include/def_debug_bt.h
new file mode 100644
index 0000000..21557e7
--- /dev/null
+++ b/geom_bottleneck/include/def_debug_bt.h
@@ -0,0 +1,42 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef DEF_DEBUG_BT_H
+#define DEF_DEBUG_BT_H
+
+//#define DEBUG_BOUND_MATCH
+//#define DEBUG_NEIGHBOUR_ORACLE
+//#define DEBUG_MATCHING
+//#define DEBUG_AUCTION
+// This symbol should be defined only in the version
+// for R package TDA, to comply with some CRAN rules
+// like no usage of cout, cerr, cin, exit, etc.
+//#define FOR_R_TDA
+//#define VERBOSE_BOTTLENECK
+
+#endif
diff --git a/geom_bottleneck/include/diagram_reader.h b/geom_bottleneck/include/diagram_reader.h
new file mode 100644
index 0000000..5bf106d
--- /dev/null
+++ b/geom_bottleneck/include/diagram_reader.h
@@ -0,0 +1,139 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_DIAGRAM_READER_H
+#define HERA_DIAGRAM_READER_H
+
+#ifndef FOR_R_TDA
+#include <iostream>
+#endif
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <cctype>
+
+namespace hera {
+// fill in result with points from file fname
+// return false if file can't be opened
+// or error occurred while reading
+// decPrecision is the maximal decimal precision in the input,
+// it is zero if all coordinates in the input are integers
+template<class RealType_ = double, class ContType_ = std::vector<std::pair<RealType_, RealType_>>>
+bool readDiagramPointSet(const char* fname, ContType_& result, int& decPrecision)
+{
+ size_t lineNumber { 0 };
+ result.clear();
+ std::ifstream f(fname);
+ if (!f.good()) {
+#ifndef FOR_R_TDA
+ std::cerr << "Cannot open file " << fname << std::endl;
+#endif
+ return false;
+ }
+ std::string line;
+ while(std::getline(f, line)) {
+ lineNumber++;
+ // process comments: remove everything after hash
+ auto hashPos = line.find_first_of("#", 0);
+ if( std::string::npos != hashPos) {
+ line = std::string(line.begin(), line.begin() + hashPos);
+ }
+ if (line.empty()) {
+ continue;
+ }
+ // trim whitespaces
+ auto whiteSpaceFront = std::find_if_not(line.begin(),line.end(),isspace);
+ auto whiteSpaceBack = std::find_if_not(line.rbegin(),line.rend(),isspace).base();
+ if (whiteSpaceBack <= whiteSpaceFront) {
+ // line consists of spaces only - move to the next line
+ continue;
+ }
+ line = std::string(whiteSpaceFront,whiteSpaceBack);
+ bool fracPart = false;
+ int currDecPrecision = 0;
+ for(auto c : line) {
+ if (c == '.') {
+ fracPart = true;
+ } else if (fracPart) {
+ if (isdigit(c)) {
+ currDecPrecision++;
+ } else {
+ fracPart = false;
+ if (currDecPrecision > decPrecision)
+ decPrecision = currDecPrecision;
+ currDecPrecision = 0;
+ }
+ }
+ }
+ RealType_ x, y;
+ std::istringstream iss(line);
+ if (not(iss >> x >> y)) {
+#ifndef FOR_R_TDA
+ std::cerr << "Error in file " << fname << ", line number " << lineNumber << ": cannot parse \"" << line << "\"" << std::endl;
+#endif
+ return false;
+ }
+ if ( x != y ) {
+ result.push_back(std::make_pair(x,y));
+ } else {
+#ifdef VERBOSE_BOTTLENECK
+ std::cerr << "Warning: in file " << fname << ", line number " << lineNumber << ", zero persistence point ignored: \"" << line << "\"" << std::endl;
+#endif
+ }
+ }
+ f.close();
+ return true;
+}
+
+
+// wrappers
+template<class RealType_ = double, class ContType_ = std::vector<std::pair<RealType_, RealType_>>>
+bool readDiagramPointSet(const std::string& fname, ContType_& result, int& decPrecision)
+{
+ return readDiagramPointSet<RealType_, ContType_>(fname.c_str(), result, decPrecision);
+}
+
+// these two functions are now just wrappers for the previous ones,
+// in case someone needs them; decPrecision is ignored
+template<class RealType_ = double, class ContType_ = std::vector<std::pair<RealType_, RealType_>>>
+bool readDiagramPointSet(const char* fname, ContType_& result)
+{
+ int decPrecision;
+ return readDiagramPointSet<RealType_, ContType_>(fname, result, decPrecision);
+}
+
+template<class RealType_ = double, class ContType_ = std::vector<std::pair<RealType_, RealType_>>>
+bool readDiagramPointSet(const std::string& fname, ContType_& result)
+{
+ int decPrecision;
+ return readDiagramPointSet<RealType_, ContType_>(fname.c_str(), result, decPrecision);
+}
+
+} // end namespace hera
+#endif // HERA_DIAGRAM_READER_H
diff --git a/geom_bottleneck/include/diagram_traits.h b/geom_bottleneck/include/diagram_traits.h
new file mode 100644
index 0000000..c8d4862
--- /dev/null
+++ b/geom_bottleneck/include/diagram_traits.h
@@ -0,0 +1,45 @@
+#ifndef HERA_DIAGRAM_TRAITS_H
+#define HERA_DIAGRAM_TRAITS_H
+
+namespace hera {
+
+template<class PairContainer_, class PointType_ = typename std::remove_reference< decltype(*std::declval<PairContainer_>().begin())>::type >
+struct DiagramTraits
+{
+ using Container = PairContainer_;
+ using PointType = PointType_;
+ using RealType = typename std::remove_reference< decltype(std::declval<PointType>()[0]) >::type;
+
+ static RealType get_x(const PointType& p) { return p[0]; }
+ static RealType get_y(const PointType& p) { return p[1]; }
+};
+
+
+template<class PairContainer_>
+struct DiagramTraits<PairContainer_, std::pair<double, double>>
+{
+ using PointType = std::pair<double, double>;
+ using RealType = double;
+ using Container = std::vector<PointType>;
+
+ static RealType get_x(const PointType& p) { return p.first; }
+ static RealType get_y(const PointType& p) { return p.second; }
+};
+
+
+template<class PairContainer_>
+struct DiagramTraits<PairContainer_, std::pair<float, float>>
+{
+ using PointType = std::pair<float, float>;
+ using RealType = float;
+ using Container = std::vector<PointType>;
+
+ static RealType get_x(const PointType& p) { return p.first; }
+ static RealType get_y(const PointType& p) { return p.second; }
+};
+
+
+} // end namespace hera
+
+
+#endif // HERA_DIAGRAM_TRAITS_H
diff --git a/geom_bottleneck/include/dnn/geometry/euclidean-fixed.h b/geom_bottleneck/include/dnn/geometry/euclidean-fixed.h
new file mode 100644
index 0000000..f45b980
--- /dev/null
+++ b/geom_bottleneck/include/dnn/geometry/euclidean-fixed.h
@@ -0,0 +1,162 @@
+#ifndef HERA_BT_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+#define HERA_BT_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+
+#include <boost/operators.hpp>
+#include <boost/array.hpp>
+#include <boost/range/value_type.hpp>
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/base_object.hpp>
+
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+
+#include "../parallel/tbb.h" // for dnn::vector<...>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ // TODO: wrap in another namespace (e.g., euclidean)
+
+ template<size_t D, typename Real = double>
+ struct Point:
+ boost::addable< Point<D,Real>,
+ boost::subtractable< Point<D,Real>,
+ boost::dividable2< Point<D, Real>, Real,
+ boost::multipliable2< Point<D, Real>, Real > > > >,
+ public boost::array<Real, D>
+ {
+ public:
+ typedef Real Coordinate;
+ typedef Real DistanceType;
+
+
+ public:
+ Point(size_t id = 0): id_(id) {}
+ template<size_t DD>
+ Point(const Point<DD,Real>& p, size_t id = 0):
+ id_(id) { *this = p; }
+
+ static size_t dimension() { return D; }
+
+ // Assign a point of different dimension
+ template<size_t DD>
+ Point& operator=(const Point<DD,Real>& p) { for (size_t i = 0; i < (D < DD ? D : DD); ++i) (*this)[i] = p[i]; if (DD < D) for (size_t i = DD; i < D; ++i) (*this)[i] = 0; return *this; }
+
+ Point& operator+=(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] += p[i]; return *this; }
+ Point& operator-=(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] -= p[i]; return *this; }
+ Point& operator/=(Real r) { for (size_t i = 0; i < D; ++i) (*this)[i] /= r; return *this; }
+ Point& operator*=(Real r) { for (size_t i = 0; i < D; ++i) (*this)[i] *= r; return *this; }
+
+ Real norm2() const { Real n = 0; for (size_t i = 0; i < D; ++i) n += (*this)[i] * (*this)[i]; return n; }
+ Real max_norm() const
+ {
+ Real m = std::fabs((*this)[0]);
+ for (size_t i = 1; i < D; ++i)
+ if (std::fabs((*this)[i]) > m)
+ m = std::fabs((*this)[i]);
+ return m; }
+
+ // quick and dirty for now; make generic later
+ //DistanceType distance(const Point& other) const { return sqrt(sq_distance(other)); }
+ //DistanceType sq_distance(const Point& other) const { return (other - *this).norm2(); }
+
+ DistanceType distance(const Point& other) const { return (other - *this).max_norm(); }
+ DistanceType sq_distance(const Point& other) const { DistanceType d = distance(other); return d*d; }
+
+ size_t id() const { return id_; }
+ size_t& id() { return id_; }
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object< boost::array<Real,D> >(*this) & id_; }
+
+ private:
+ size_t id_;
+ };
+
+ template<size_t D, typename Real>
+ std::ostream&
+ operator<<(std::ostream& out, const Point<D,Real>& p)
+ { out << p[0]; for (size_t i = 1; i < D; ++i) out << " " << p[i]; return out; }
+
+
+ template<class Point>
+ struct PointTraits; // intentionally undefined; should be specialized for each type
+
+ template<size_t D, typename Real>
+ struct PointTraits< Point<D, Real> > // specialization for dnn::Point
+ {
+ typedef Point<D,Real> PointType;
+ typedef const PointType* PointHandle;
+ typedef std::vector<PointType> PointContainer;
+
+ typedef typename PointType::Coordinate Coordinate;
+ typedef typename PointType::DistanceType DistanceType;
+
+ static DistanceType
+ distance(const PointType& p1, const PointType& p2) { return p1.distance(p2); }
+ static DistanceType
+ distance(PointHandle p1, PointHandle p2) { return distance(*p1,*p2); }
+ static DistanceType
+ sq_distance(const PointType& p1,
+ const PointType& p2) { return p1.sq_distance(p2); }
+ static DistanceType
+ sq_distance(PointHandle p1, PointHandle p2) { return sq_distance(*p1,*p2); }
+ static size_t dimension() { return D; }
+ static Real coordinate(const PointType& p, size_t i) { return p[i]; }
+ static Real& coordinate(PointType& p, size_t i) { return p[i]; }
+ static Real coordinate(PointHandle p, size_t i) { return coordinate(*p,i); }
+
+ static size_t id(const PointType& p) { return p.id(); }
+ static size_t& id(PointType& p) { return p.id(); }
+ static size_t id(PointHandle p) { return id(*p); }
+
+ static PointHandle
+ handle(const PointType& p) { return &p; }
+ static const PointType&
+ point(PointHandle ph) { return *ph; }
+
+ void swap(PointType& p1, PointType& p2) const { return std::swap(p1, p2); }
+
+ static PointContainer
+ container(size_t n = 0, const PointType& p = PointType()) { return PointContainer(n, p); }
+ static typename PointContainer::iterator
+ iterator(PointContainer& c, PointHandle ph) { return c.begin() + (ph - &c[0]); }
+ static typename PointContainer::const_iterator
+ iterator(const PointContainer& c, PointHandle ph) { return c.begin() + (ph - &c[0]); }
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) {}
+ };
+
+ template<class PointContainer>
+ void read_points(const std::string& filename, PointContainer& points)
+ {
+ typedef typename boost::range_value<PointContainer>::type Point;
+ typedef typename PointTraits<Point>::Coordinate Coordinate;
+
+ std::ifstream in(filename.c_str());
+ std::string line;
+ while(std::getline(in, line))
+ {
+ if (line[0] == '#') continue; // comment line in the file
+ std::stringstream linestream(line);
+ Coordinate x;
+ points.push_back(Point());
+ size_t i = 0;
+ while (linestream >> x)
+ points.back()[i++] = x;
+ }
+ }
+} // dnn
+} // bt
+} // hera
+#endif
diff --git a/geom_bottleneck/include/dnn/local/kd-tree.h b/geom_bottleneck/include/dnn/local/kd-tree.h
new file mode 100644
index 0000000..c1aed2b
--- /dev/null
+++ b/geom_bottleneck/include/dnn/local/kd-tree.h
@@ -0,0 +1,106 @@
+#ifndef HERA_BT_DNN_LOCAL_KD_TREE_H
+#define HERA_BT_DNN_LOCAL_KD_TREE_H
+
+#include "../utils.h"
+#include "search-functors.h"
+
+#include <unordered_map>
+#include <stack>
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/range/value_type.hpp>
+
+#include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ // Weighted KDTree
+ // Traits_ provides Coordinate, DistanceType, PointType, dimension(), distance(p1,p2), coordinate(p,i)
+ template< class Traits_ >
+ class KDTree
+ {
+ public:
+ typedef Traits_ Traits;
+ typedef hera::bt::dnn::HandleDistance<KDTree> HandleDistance;
+
+ typedef typename Traits::PointType Point;
+ typedef typename Traits::PointHandle PointHandle;
+ typedef typename Traits::Coordinate Coordinate;
+ typedef typename Traits::DistanceType DistanceType;
+ typedef std::vector<PointHandle> HandleContainer;
+ typedef std::vector<HandleDistance> HDContainer; // TODO: use tbb::scalable_allocator
+ typedef HDContainer Result;
+ typedef std::vector<DistanceType> DistanceContainer;
+ typedef std::unordered_map<PointHandle, size_t> HandleMap;
+ //private:
+ typedef typename HandleContainer::iterator HCIterator;
+ typedef std::tuple<HCIterator, HCIterator, size_t, ssize_t> KDTreeNode;
+ typedef std::tuple<HCIterator, HCIterator> KDTreeNodeNoCut;
+
+ //BOOST_STATIC_ASSERT_MSG(has_coordinates<Traits, PointHandle, int>::value, "KDTree requires coordinates");
+
+ public:
+ KDTree(const Traits& traits):
+ traits_(traits) {}
+
+ KDTree(const Traits& traits, HandleContainer&& handles);
+
+ template<class Range>
+ KDTree(const Traits& traits, const Range& range);
+
+ template<class Range>
+ void init(const Range& range);
+
+ HandleDistance find(PointHandle q) const;
+ Result findR(PointHandle q, DistanceType r) const; // all neighbors within r
+ Result findFirstR(PointHandle q, DistanceType r) const; // first neighbor within r
+ Result findK(PointHandle q, size_t k) const; // k nearest neighbors
+
+ HandleDistance find(const Point& q) const { return find(traits().handle(q)); }
+ Result findR(const Point& q, DistanceType r) const { return findR(traits().handle(q), r); }
+ Result findFirstR(const Point& q, DistanceType r) const { return findFirstR(traits().handle(q), r); }
+ Result findK(const Point& q, size_t k) const { return findK(traits().handle(q), k); }
+
+
+
+ template<class ResultsFunctor>
+ void search(PointHandle q, ResultsFunctor& rf) const;
+
+ const Traits& traits() const { return traits_; }
+
+ void get_path_to_root(const size_t idx, std::stack<KDTreeNodeNoCut>& s);
+ // to support deletion
+ void init_n_elems();
+ void delete_point(const size_t idx);
+ void delete_point(PointHandle p);
+ void update_n_elems(const ssize_t idx, const int delta);
+ void increase_n_elems(const ssize_t idx);
+ void decrease_n_elems(const ssize_t idx);
+ size_t get_num_points() const { return num_points_; }
+ //private:
+ void init();
+
+
+ struct CoordinateComparison;
+ struct OrderTree;
+
+ //private:
+ Traits traits_;
+ HandleContainer tree_;
+ std::vector<char> delete_flags_;
+ std::vector<int> subtree_n_elems;
+ HandleMap indices_;
+ std::vector<ssize_t> parents_;
+
+ size_t num_points_;
+ };
+} // dnn
+} // bt
+} // hera
+#include "kd-tree.hpp"
+
+#endif
diff --git a/geom_bottleneck/include/dnn/local/kd-tree.hpp b/geom_bottleneck/include/dnn/local/kd-tree.hpp
new file mode 100644
index 0000000..249fa55
--- /dev/null
+++ b/geom_bottleneck/include/dnn/local/kd-tree.hpp
@@ -0,0 +1,296 @@
+#include <boost/range/counting_range.hpp>
+#include <boost/range/algorithm_ext/push_back.hpp>
+#include <boost/range.hpp>
+
+#include <queue>
+
+#include "../parallel/tbb.h"
+
+template<class T>
+hera::bt::dnn::KDTree<T>::KDTree(const Traits& traits, HandleContainer&& handles):
+ traits_(traits),
+ tree_(std::move(handles)),
+ delete_flags_(handles.size(), static_cast<char>(0) ),
+ subtree_n_elems(handles.size(), static_cast<size_t>(0)),
+ num_points_(handles.size())
+{
+ init();
+}
+
+template<class T>
+template<class Range>
+hera::bt::dnn::KDTree<T>::KDTree(const Traits& traits, const Range& range):
+ traits_(traits)
+{
+ init(range);
+}
+
+template<class T>
+template<class Range>
+void hera::bt::dnn::KDTree<T>::init(const Range& range)
+{
+ size_t sz = std::distance(std::begin(range), std::end(range));
+ subtree_n_elems = std::vector<int>(sz, 0);
+ delete_flags_ = std::vector<char>(sz, 0);
+ num_points_ = sz;
+ tree_.reserve(sz);
+ for (PointHandle h : range)
+ tree_.push_back(h);
+ parents_.resize(sz, -1);
+ init();
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::init()
+{
+ if (tree_.empty())
+ return;
+
+#if defined(TBB)
+ task_group g;
+ g.run(OrderTree(this, tree_.begin(), tree_.end(), -1, 0, traits()));
+ g.wait();
+#else
+ OrderTree(this, tree_.begin(), tree_.end(), -1, 0, traits()).serial();
+#endif
+
+ for (size_t i = 0; i < tree_.size(); ++i)
+ indices_[tree_[i]] = i;
+ init_n_elems();
+}
+
+template<class T>
+struct
+hera::bt::dnn::KDTree<T>::OrderTree
+{
+ OrderTree(KDTree* tree_, HCIterator b_, HCIterator e_, ssize_t p_, size_t i_, const Traits& traits_):
+ tree(tree_), b(b_), e(e_), p(p_), i(i_), traits(traits_) {}
+
+ void operator()() const
+ {
+ if (e - b < 1000)
+ {
+ serial();
+ return;
+ }
+
+ HCIterator m = b + (e - b)/2;
+ ssize_t im = m - tree->tree_.begin();
+ tree->parents_[im] = p;
+
+ CoordinateComparison cmp(i, traits);
+ std::nth_element(b,m,e, cmp);
+ size_t next_i = (i + 1) % traits.dimension();
+
+ task_group g;
+ if (b < m - 1) g.run(OrderTree(tree, b, m, im, next_i, traits));
+ if (e > m + 2) g.run(OrderTree(tree, m+1, e, im, next_i, traits));
+ g.wait();
+ }
+
+ void serial() const
+ {
+ std::queue<KDTreeNode> q;
+ q.push(KDTreeNode(b,e,p,i));
+ while (!q.empty())
+ {
+ HCIterator b, e; ssize_t p; size_t i;
+ std::tie(b,e,p,i) = q.front();
+ q.pop();
+ HCIterator m = b + (e - b)/2;
+ ssize_t im = m - tree->tree_.begin();
+ tree->parents_[im] = p;
+
+ CoordinateComparison cmp(i, traits);
+ std::nth_element(b,m,e, cmp);
+ size_t next_i = (i + 1) % traits.dimension();
+
+ // Replace with a size condition instead?
+ if (b < m - 1)
+ q.push(KDTreeNode(b, m, im, next_i));
+ else if (b < m)
+ tree->parents_[im - 1] = im;
+ if (e > m + 2)
+ q.push(KDTreeNode(m+1, e, im, next_i));
+ else if (e > m + 1)
+ tree->parents_[im + 1] = im;
+ }
+ }
+
+ KDTree* tree;
+ HCIterator b, e;
+ ssize_t p;
+ size_t i;
+ const Traits& traits;
+};
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::update_n_elems(ssize_t idx, const int delta)
+// add delta to the number of points in node idx and update subtree_n_elems
+// for all parents of the node idx
+{
+ //std::cout << "subtree_n_elems.size = " << subtree_n_elems.size() << std::endl;
+ // update the node itself
+ while (idx != -1)
+ {
+ //std::cout << idx << std::endl;
+ subtree_n_elems[idx] += delta;
+ idx = parents_[idx];
+ }
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::increase_n_elems(const ssize_t idx)
+{
+ update_n_elems(idx, static_cast<ssize_t>(1));
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::decrease_n_elems(const ssize_t idx)
+{
+ update_n_elems(idx, static_cast<ssize_t>(-1));
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::init_n_elems()
+{
+ for(size_t idx = 0; idx < tree_.size(); ++idx) {
+ increase_n_elems(idx);
+ }
+}
+
+
+template<class T>
+template<class ResultsFunctor>
+void hera::bt::dnn::KDTree<T>::search(PointHandle q, ResultsFunctor& rf) const
+{
+ typedef typename HandleContainer::const_iterator HCIterator;
+ typedef std::tuple<HCIterator, HCIterator, size_t> KDTreeNode;
+
+ if (tree_.empty())
+ return;
+
+ DistanceType D = std::numeric_limits<DistanceType>::infinity();
+
+ // TODO: use tbb::scalable_allocator for the queue
+ std::queue<KDTreeNode> nodes;
+
+ nodes.push(KDTreeNode(tree_.begin(), tree_.end(), 0));
+
+ //std::cout << "started kdtree::search" << std::endl;
+
+ while (!nodes.empty())
+ {
+ HCIterator b, e; size_t i;
+ std::tie(b,e,i) = nodes.front();
+ nodes.pop();
+
+ CoordinateComparison cmp(i, traits());
+ i = (i + 1) % traits().dimension();
+
+ HCIterator m = b + (e - b)/2;
+ size_t m_idx = m - tree_.begin();
+ // ignore deleted points
+ if ( delete_flags_[m_idx] == 0 ) {
+ DistanceType dist = traits().distance(q, *m);
+ // + weights_[m - tree_.begin()];
+ //std::cout << "Supplied to functor: m : ";
+ //std::cout << "(" << (*(*m))[0] << ", " << (*(*m))[1] << ")";
+ //std::cout << " and q : ";
+ //std::cout << "(" << (*q)[0] << ", " << (*q)[1] << ")" << std::endl;
+ //std::cout << "dist^q + weight = " << dist << std::endl;
+ //std::cout << "weight = " << weights_[m - tree_.begin()] << std::endl;
+ //std::cout << "dist = " << traits().distance(q, *m) << std::endl;
+ //std::cout << "dist^q = " << pow(traits().distance(q, *m), wassersteinPower) << std::endl;
+
+ D = rf(*m, dist);
+ }
+ // we are really searching w.r.t L_\infty ball; could prune better with an L_2 ball
+ Coordinate diff = cmp.diff(q, *m); // diff returns signed distance
+ DistanceType diffToWasserPower = (diff > 0 ? 1.0 : -1.0) * fabs(diff);
+
+ size_t lm = m + 1 + (e - (m+1))/2 - tree_.begin();
+ if ( subtree_n_elems[lm] > 0 ) {
+ if (e > m + 1 && diffToWasserPower >= -D) {
+ nodes.push(KDTreeNode(m+1, e, i));
+ }
+ }
+
+ size_t rm = b + (m - b) / 2 - tree_.begin();
+ if ( subtree_n_elems[rm] > 0 ) {
+ if (b < m && diffToWasserPower <= D) {
+ nodes.push(KDTreeNode(b, m, i));
+ }
+ }
+ }
+ //std::cout << "exited kdtree::search" << std::endl;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::HandleDistance hera::bt::dnn::KDTree<T>::find(PointHandle q) const
+{
+ hera::bt::dnn::NNRecord<HandleDistance> nn;
+ search(q, nn);
+ return nn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findR(PointHandle q, DistanceType r) const
+{
+ hera::bt::dnn::rNNRecord<HandleDistance> rnn(r);
+ search(q, rnn);
+ //std::sort(rnn.result.begin(), rnn.result.end());
+ return rnn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findFirstR(PointHandle q, DistanceType r) const
+{
+ hera::bt::dnn::firstrNNRecord<HandleDistance> rnn(r);
+ search(q, rnn);
+ return rnn.result;
+}
+
+template<class T>
+typename hera::bt::dnn::KDTree<T>::Result hera::bt::dnn::KDTree<T>::findK(PointHandle q, size_t k) const
+{
+ hera::bt::dnn::kNNRecord<HandleDistance> knn(k);
+ search(q, knn);
+ // do we need this???
+ std::sort(knn.result.begin(), knn.result.end());
+ return knn.result;
+}
+
+template<class T>
+struct hera::bt::dnn::KDTree<T>::CoordinateComparison
+{
+ CoordinateComparison(size_t i, const Traits& traits):
+ i_(i), traits_(traits) {}
+
+ bool operator()(PointHandle p1, PointHandle p2) const { return coordinate(p1) < coordinate(p2); }
+ Coordinate diff(PointHandle p1, PointHandle p2) const { return coordinate(p1) - coordinate(p2); }
+
+ Coordinate coordinate(PointHandle p) const { return traits_.coordinate(p, i_); }
+ size_t axis() const { return i_; }
+
+ private:
+ size_t i_;
+ const Traits& traits_;
+};
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::delete_point(const size_t idx)
+{
+ // prevent double deletion
+ assert(delete_flags_[idx] == 0);
+ delete_flags_[idx] = 1;
+ decrease_n_elems(idx);
+ --num_points_;
+}
+
+template<class T>
+void hera::bt::dnn::KDTree<T>::delete_point(PointHandle p)
+{
+ delete_point(indices_[p]);
+}
+
diff --git a/geom_bottleneck/include/dnn/local/search-functors.h b/geom_bottleneck/include/dnn/local/search-functors.h
new file mode 100644
index 0000000..63ad11d
--- /dev/null
+++ b/geom_bottleneck/include/dnn/local/search-functors.h
@@ -0,0 +1,119 @@
+#ifndef HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
+#define HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
+
+#include <boost/range/algorithm/heap_algorithm.hpp>
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+
+template<class NN>
+struct HandleDistance
+{
+ typedef typename NN::PointHandle PointHandle;
+ typedef typename NN::DistanceType DistanceType;
+ typedef typename NN::HDContainer HDContainer;
+
+ HandleDistance() {}
+ HandleDistance(PointHandle pp, DistanceType dd):
+ p(pp), d(dd) {}
+ bool operator<(const HandleDistance& other) const { return d < other.d; }
+
+ PointHandle p;
+ DistanceType d;
+};
+
+template<class HandleDistance>
+struct NNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+
+ NNRecord() { result.d = std::numeric_limits<DistanceType>::infinity(); }
+ DistanceType operator()(PointHandle p, DistanceType d) { if (d < result.d) { result.p = p; result.d = d; } return result.d; }
+ HandleDistance result;
+};
+
+template<class HandleDistance>
+struct rNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ rNNRecord(DistanceType r_): r(r_) {}
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (d <= r)
+ result.push_back(HandleDistance(p,d));
+ return r;
+ }
+
+ DistanceType r;
+ HDContainer result;
+};
+
+template<class HandleDistance>
+struct firstrNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ firstrNNRecord(DistanceType r_): r(r_) {}
+
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (d <= r) {
+ result.push_back(HandleDistance(p,d));
+ return -100000000.0;
+ } else {
+ return r;
+ }
+ }
+
+ DistanceType r;
+ HDContainer result;
+};
+
+
+template<class HandleDistance>
+struct kNNRecord
+{
+ typedef typename HandleDistance::PointHandle PointHandle;
+ typedef typename HandleDistance::DistanceType DistanceType;
+ typedef typename HandleDistance::HDContainer HDContainer;
+
+ kNNRecord(unsigned k_): k(k_) {}
+ DistanceType operator()(PointHandle p, DistanceType d)
+ {
+ if (result.size() < k)
+ {
+ result.push_back(HandleDistance(p,d));
+ boost::push_heap(result);
+ if (result.size() < k)
+ return std::numeric_limits<DistanceType>::infinity();
+ } else if (d < result[0].d)
+ {
+ boost::pop_heap(result);
+ result.back() = HandleDistance(p,d);
+ boost::push_heap(result);
+ }
+ if ( result.size() > 1 ) {
+ assert( result[0].d >= result[1].d );
+ }
+ return result[0].d;
+ }
+
+ unsigned k;
+ HDContainer result;
+};
+
+} // dnn
+} // bt
+} // hera
+
+#endif // HERA_BT_DNN_LOCAL_SEARCH_FUNCTORS_H
diff --git a/geom_bottleneck/include/dnn/parallel/tbb.h b/geom_bottleneck/include/dnn/parallel/tbb.h
new file mode 100644
index 0000000..14f0093
--- /dev/null
+++ b/geom_bottleneck/include/dnn/parallel/tbb.h
@@ -0,0 +1,235 @@
+#ifndef HERA_BT_PARALLEL_H
+#define HERA_BT_PARALLEL_H
+
+#ifndef FOR_R_TDA
+#include <iostream>
+#endif
+
+#include <vector>
+
+#include <boost/range.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#ifdef TBB
+
+#include <tbb/tbb.h>
+#include <tbb/concurrent_hash_map.h>
+#include <tbb/scalable_allocator.h>
+
+#include <boost/serialization/split_free.hpp>
+#include <boost/serialization/collections_load_imp.hpp>
+#include <boost/serialization/collections_save_imp.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ using tbb::mutex;
+ using tbb::task_scheduler_init;
+ using tbb::task_group;
+ using tbb::task;
+
+ template<class T>
+ struct vector
+ {
+ typedef tbb::concurrent_vector<T> type;
+ };
+
+ template<class T>
+ struct atomic
+ {
+ typedef tbb::atomic<T> type;
+ static T compare_and_swap(type& v, T n, T o) { return v.compare_and_swap(n,o); }
+ };
+
+ template<class Iterator, class F>
+ void do_foreach(Iterator begin, Iterator end, const F& f) { tbb::parallel_do(begin, end, f); }
+
+ template<class Range, class F>
+ void for_each_range_(const Range& r, const F& f)
+ {
+ for (typename Range::iterator cur = r.begin(); cur != r.end(); ++cur)
+ f(*cur);
+ }
+
+ template<class F>
+ void for_each_range(size_t from, size_t to, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(from, to, f);
+ }
+
+ template<class Container, class F>
+ void for_each_range(const Container& c, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::const_range_type, F>, _1, f));
+ }
+
+ template<class Container, class F>
+ void for_each_range(Container& c, const F& f)
+ {
+ //static tbb::affinity_partitioner ap;
+ //tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f), ap);
+ tbb::parallel_for(c.range(), boost::bind(&for_each_range_<typename Container::range_type, F>, _1, f));
+ }
+
+ template<class ID, class NodePointer, class IDTraits, class Allocator>
+ struct map_traits
+ {
+ typedef tbb::concurrent_hash_map<ID, NodePointer, IDTraits, Allocator> type;
+ typedef typename type::range_type range;
+ };
+
+ struct progress_timer
+ {
+ progress_timer(): start(tbb::tick_count::now()) {}
+ ~progress_timer()
+ {
+#ifndef FOR_R_TDA
+ std::cout << (tbb::tick_count::now() - start).seconds() << " s" << std::endl;
+#endif
+ }
+
+ tbb::tick_count start;
+ };
+}
+}
+}
+
+// Serialization for tbb::concurrent_vector<...>
+namespace boost
+{
+ namespace serialization
+ {
+ template<class Archive, class T, class A>
+ void save(Archive& ar, const tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ { stl::save_collection(ar, v); }
+
+ template<class Archive, class T, class A>
+ void load(Archive& ar, tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ {
+ stl::load_collection<Archive,
+ tbb::concurrent_vector<T,A>,
+ stl::archive_input_seq< Archive, tbb::concurrent_vector<T,A> >,
+ stl::reserve_imp< tbb::concurrent_vector<T,A> >
+ >(ar, v);
+ }
+
+ template<class Archive, class T, class A>
+ void serialize(Archive& ar, tbb::concurrent_vector<T,A>& v, const unsigned int file_version)
+ { split_free(ar, v, file_version); }
+
+ template<class Archive, class T>
+ void save(Archive& ar, const tbb::atomic<T>& v, const unsigned int file_version)
+ { T v_ = v; ar << v_; }
+
+ template<class Archive, class T>
+ void load(Archive& ar, tbb::atomic<T>& v, const unsigned int file_version)
+ { T v_; ar >> v_; v = v_; }
+
+ template<class Archive, class T>
+ void serialize(Archive& ar, tbb::atomic<T>& v, const unsigned int file_version)
+ { split_free(ar, v, file_version); }
+ }
+}
+
+#else
+
+#include <algorithm>
+#include <map>
+#include <boost/progress.hpp>
+
+namespace hera {
+namespace bt {
+namespace dnn
+{
+ template<class T>
+ struct vector
+ {
+ typedef ::std::vector<T> type;
+ };
+
+ template<class T>
+ struct atomic
+ {
+ typedef T type;
+ static T compare_and_swap(type& v, T n, T o) { if (v != o) return v; v = n; return o; }
+ };
+
+ template<class Iterator, class F>
+ void do_foreach(Iterator begin, Iterator end, const F& f) { std::for_each(begin, end, f); }
+
+ template<class F>
+ void for_each_range(size_t from, size_t to, const F& f)
+ {
+ for (size_t i = from; i < to; ++i)
+ f(i);
+ }
+
+ template<class Container, class F>
+ void for_each_range(Container& c, const F& f)
+ {
+ BOOST_FOREACH(const typename Container::value_type& i, c)
+ f(i);
+ }
+
+ template<class Container, class F>
+ void for_each_range(const Container& c, const F& f)
+ {
+ BOOST_FOREACH(const typename Container::value_type& i, c)
+ f(i);
+ }
+
+ struct mutex
+ {
+ struct scoped_lock
+ {
+ scoped_lock() {}
+ scoped_lock(mutex& ) {}
+ void acquire(mutex& ) const {}
+ void release() const {}
+ };
+ };
+
+ struct task_scheduler_init
+ {
+ task_scheduler_init(unsigned) {}
+ void initialize(unsigned) {}
+ static const unsigned automatic = 0;
+ static const unsigned deferred = 0;
+ };
+
+ struct task_group
+ {
+ template<class Functor>
+ void run(const Functor& f) const { f(); }
+ void wait() const {}
+ };
+
+ template<class ID, class NodePointer, class IDTraits, class Allocator>
+ struct map_traits
+ {
+ typedef std::map<ID, NodePointer,
+ typename IDTraits::Comparison,
+ Allocator> type;
+ typedef type range;
+ };
+
+ using boost::progress_timer;
+}
+}
+}
+
+#endif // TBB
+
+namespace dnn
+{
+ template<class Range, class F>
+ void do_foreach(const Range& range, const F& f) { do_foreach(boost::begin(range), boost::end(range), f); }
+}
+
+#endif
diff --git a/geom_bottleneck/include/dnn/parallel/utils.h b/geom_bottleneck/include/dnn/parallel/utils.h
new file mode 100644
index 0000000..9809e77
--- /dev/null
+++ b/geom_bottleneck/include/dnn/parallel/utils.h
@@ -0,0 +1,100 @@
+#ifndef HERA_BT_PARALLEL_UTILS_H
+#define HERA_BT_PARALLEL_UTILS_H
+
+#include "../utils.h"
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+ // Assumes rng is synchronized across ranks
+ template<class DataVector, class RNGType, class SwapFunctor>
+ void shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty = DataVector());
+
+ template<class DataVector, class RNGType>
+ void shuffle(mpi::communicator& world, DataVector& data, RNGType& rng)
+ {
+ typedef decltype(data[0]) T;
+ shuffle(world, data, rng, [](T& x, T& y) { std::swap(x,y); });
+ }
+}
+}
+}
+
+template<class DataVector, class RNGType, class SwapFunctor>
+void
+hera::bt::dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty)
+{
+ // This is not a perfect shuffle: it dishes out data in chunks of 1/size.
+ // (It can be interpreted as generating a bistochastic matrix by taking the
+ // sum of size random permutation matrices.) Hopefully, it works for our purposes.
+
+ typedef typename RNGType::result_type RNGResult;
+
+ int size = world.size();
+ int rank = world.rank();
+
+ // Generate local seeds
+ boost::uniform_int<RNGResult> uniform;
+ RNGResult seed;
+ for (size_t i = 0; i < size; ++i)
+ {
+ RNGResult v = uniform(rng);
+ if (i == rank)
+ seed = v;
+ }
+ RNGType local_rng(seed);
+
+ // Shuffle local data
+ hera::bt::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+
+ // Decide how much of our data goes to i-th processor
+ std::vector<size_t> out_counts(size);
+ std::vector<int> ranks(boost::counting_iterator<int>(0),
+ boost::counting_iterator<int>(size));
+ for (size_t i = 0; i < size; ++i)
+ {
+ hera::bt::dnn::random_shuffle(ranks.begin(), ranks.end(), rng);
+ ++out_counts[ranks[rank]];
+ }
+
+ // Fill the outgoing array
+ size_t total = 0;
+ std::vector< DataVector > outgoing(size, empty);
+ for (size_t i = 0; i < size; ++i)
+ {
+ size_t count = data.size()*out_counts[i]/size;
+ if (total + count > data.size())
+ count = data.size() - total;
+
+ outgoing[i].reserve(count);
+ for (size_t j = total; j < total + count; ++j)
+ outgoing[i].push_back(data[j]);
+
+ total += count;
+ }
+
+ boost::uniform_int<size_t> uniform_outgoing(0,size-1); // in range [0,size-1]
+ while(total < data.size()) // send leftover to random processes
+ {
+ outgoing[uniform_outgoing(local_rng)].push_back(data[total]);
+ ++total;
+ }
+ data.clear();
+
+ // Exchange the data
+ std::vector< DataVector > incoming(size, empty);
+ mpi::all_to_all(world, outgoing, incoming);
+ outgoing.clear();
+
+ // Assemble our data
+ for(const DataVector& vec : incoming)
+ for (size_t i = 0; i < vec.size(); ++i)
+ data.push_back(vec[i]);
+ hera::bt::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+ // XXX: the final shuffle is irrelevant for our purposes. But it's also cheap.
+}
+
+#endif
diff --git a/geom_bottleneck/include/dnn/utils.h b/geom_bottleneck/include/dnn/utils.h
new file mode 100644
index 0000000..f4ce632
--- /dev/null
+++ b/geom_bottleneck/include/dnn/utils.h
@@ -0,0 +1,47 @@
+#ifndef HERA_BT_DNN_UTILS_H
+#define HERA_BT_DNN_UTILS_H
+
+#include <boost/random/uniform_int.hpp>
+#include <boost/foreach.hpp>
+#include <boost/typeof/typeof.hpp>
+
+namespace hera
+{
+namespace bt
+{
+namespace dnn
+{
+
+template <typename T, typename... Args>
+struct has_coordinates
+{
+ template <typename C, typename = decltype( std::declval<C>().coordinate(std::declval<Args>()...) )>
+ static std::true_type test(int);
+
+ template <typename C>
+ static std::false_type test(...);
+
+ static constexpr bool value = decltype(test<T>(0))::value;
+};
+
+template<class RandomIt, class UniformRandomNumberGenerator, class SwapFunctor>
+void random_shuffle(RandomIt first, RandomIt last, UniformRandomNumberGenerator& g, const SwapFunctor& swap)
+{
+ size_t n = last - first;
+ boost::uniform_int<size_t> uniform(0,n);
+ for (size_t i = n-1; i > 0; --i)
+ swap(first[i], first[uniform(g,i+1)]); // picks a random number in [0,i] range
+}
+
+template<class RandomIt, class UniformRandomNumberGenerator>
+void random_shuffle(RandomIt first, RandomIt last, UniformRandomNumberGenerator& g)
+{
+ typedef decltype(*first) T;
+ random_shuffle(first, last, g, [](T& x, T& y) { std::swap(x,y); });
+}
+
+} // dnn
+} // bt
+} // hera
+
+#endif
diff --git a/geom_bottleneck/include/neighb_oracle.h b/geom_bottleneck/include/neighb_oracle.h
new file mode 100644
index 0000000..c3751b3
--- /dev/null
+++ b/geom_bottleneck/include/neighb_oracle.h
@@ -0,0 +1,295 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_NEIGHB_ORACLE_H
+#define HERA_NEIGHB_ORACLE_H
+
+#include <unordered_map>
+#include <algorithm>
+#include <memory>
+
+#include "basic_defs_bt.h"
+#include "dnn/geometry/euclidean-fixed.h"
+#include "dnn/local/kd-tree.h"
+
+
+
+namespace hera {
+namespace bt {
+
+template<class Real>
+class NeighbOracleSimple
+{
+public:
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointSet = DiagramPointSet<Real>;
+
+private:
+ Real r;
+ Real distEpsilon;
+ DgmPointSet pointSet;
+
+public:
+
+ NeighbOracleSimple() : r(0.0) {}
+
+ NeighbOracleSimple(const DgmPointSet& _pointSet, const Real _r, const Real _distEpsilon) :
+ r(_r),
+ distEpsilon(_distEpsilon),
+ pointSet(_pointSet)
+ {}
+
+ void deletePoint(const DgmPoint& p)
+ {
+ pointSet.erase(p);
+ }
+
+ void rebuild(const DgmPointSet& S, const double rr)
+ {
+ pointSet = S;
+ r = rr;
+ }
+
+ bool getNeighbour(const DgmPoint& q, DgmPoint& result) const
+ {
+ for(auto pit = pointSet.cbegin(); pit != pointSet.cend(); ++pit) {
+ if ( distLInf(*pit, q) <= r) {
+ result = *pit;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void getAllNeighbours(const DgmPoint& q, std::vector<DgmPoint>& result)
+ {
+ result.clear();
+ for(const auto& point : pointSet) {
+ if ( distLInf(point, q) <= r) {
+ result.push_back(point);
+ }
+ }
+ for(auto& pt : result) {
+ deletePoint(pt);
+ }
+ }
+
+};
+
+template<class Real_>
+class NeighbOracleDnn
+{
+public:
+
+ using Real = Real_;
+ using DnnPoint = dnn::Point<2, double>;
+ using DnnTraits = dnn::PointTraits<DnnPoint>;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointSet = DiagramPointSet<Real>;
+ using DgmPointHash = DiagramPointHash<Real>;
+
+ Real r;
+ Real distEpsilon;
+ std::vector<DgmPoint> allPoints;
+ DgmPointSet diagonalPoints;
+ std::unordered_map<DgmPoint, size_t, DgmPointHash> pointIdxLookup;
+ // dnn-stuff
+ std::unique_ptr<dnn::KDTree<DnnTraits>> kdtree;
+ std::vector<DnnPoint> dnnPoints;
+ std::vector<DnnPoint*> dnnPointHandles;
+ std::vector<size_t> kdtreeItems;
+
+ NeighbOracleDnn(const DgmPointSet& S, const Real rr, const Real dEps) :
+ kdtree(nullptr)
+ {
+ assert(dEps >= 0);
+ distEpsilon = dEps;
+ rebuild(S, rr);
+ }
+
+
+ void deletePoint(const DgmPoint& p)
+ {
+ auto findRes = pointIdxLookup.find(p);
+ assert(findRes != pointIdxLookup.end());
+ //std::cout << "Deleting point " << p << std::endl;
+ size_t pointIdx { (*findRes).second };
+ //std::cout << "pointIdx = " << pointIdx << std::endl;
+ diagonalPoints.erase(p, false);
+ kdtree->delete_point(dnnPointHandles[kdtreeItems[pointIdx]]);
+ }
+
+ void rebuild(const DgmPointSet& S, const Real rr)
+ {
+ //std::cout << "Entered rebuild, r = " << rr << std::endl;
+ r = rr;
+ size_t dnnNumPoints = S.size();
+ //printDebug(isDebug, "S = ", S);
+ if (dnnNumPoints > 0) {
+ pointIdxLookup.clear();
+ pointIdxLookup.reserve(S.size());
+ allPoints.clear();
+ allPoints.reserve(S.size());
+ diagonalPoints.clear();
+ diagonalPoints.reserve(S.size() / 2);
+ for(auto pit = S.cbegin(); pit != S.cend(); ++pit) {
+ allPoints.push_back(*pit);
+ if (pit->isDiagonal()) {
+ diagonalPoints.insert(*pit);
+ }
+ }
+
+ size_t pointIdx = 0;
+ for(auto& dataPoint : allPoints) {
+ pointIdxLookup.insert( { dataPoint, pointIdx++ } );
+ }
+
+ size_t dnnItemIdx { 0 };
+ size_t trueIdx { 0 };
+ dnnPoints.clear();
+ kdtreeItems.clear();
+ dnnPointHandles.clear();
+ dnnPoints.clear();
+ kdtreeItems.reserve(S.size() );
+ // store normal items in kd-tree
+ for(const auto& g : allPoints) {
+ if (true) {
+ kdtreeItems[trueIdx] = dnnItemIdx;
+ // index of items is id of dnn-point
+ DnnPoint p(trueIdx);
+ p[0] = g.getRealX();
+ p[1] = g.getRealY();
+ dnnPoints.push_back(p);
+ assert(dnnItemIdx == dnnPoints.size() - 1);
+ dnnItemIdx++;
+ }
+ trueIdx++;
+ }
+ assert(dnnPoints.size() == allPoints.size() );
+ for(size_t i = 0; i < dnnPoints.size(); ++i) {
+ dnnPointHandles.push_back(&dnnPoints[i]);
+ }
+ DnnTraits traits;
+ //std::cout << "kdtree: " << dnnPointHandles.size() << " points" << std::endl;
+ kdtree.reset(new dnn::KDTree<DnnTraits>(traits, dnnPointHandles));
+ }
+ }
+
+
+ bool getNeighbour(const DgmPoint& q, DgmPoint& result) const
+ {
+ //std::cout << "getNeighbour for q = " << q << ", r = " << r << std::endl;
+ //std::cout << *this << std::endl;
+ // distance between two diagonal points
+ // is 0
+ if (q.isDiagonal()) {
+ if (!diagonalPoints.empty()) {
+ result = *diagonalPoints.cbegin();
+ //std::cout << "Neighbour found in diagonal points, res = " << result;
+ return true;
+ }
+ }
+ // check if kdtree is not empty
+ if (0 == kdtree->get_num_points() ) {
+ //std::cout << "empty tree, no neighb." << std::endl;
+ return false;
+ }
+ // if no neighbour found among diagonal points,
+ // search in kd_tree
+ DnnPoint queryPoint;
+ queryPoint[0] = q.getRealX();
+ queryPoint[1] = q.getRealY();
+ auto kdtreeResult = kdtree->findFirstR(queryPoint, r);
+ if (kdtreeResult.empty()) {
+ //std::cout << "no neighbour within " << r << "found." << std::endl;
+ return false;
+ }
+ if (kdtreeResult[0].d <= r + distEpsilon) {
+ result = allPoints[kdtreeResult[0].p->id()];
+ //std::cout << "Neighbour found with kd-tree, index = " << kdtreeResult[0].p->id() << std::endl;
+ //std::cout << "result = " << result << std::endl;
+ return true;
+ }
+ //std::cout << "No neighbour found for r = " << r << std::endl;
+ return false;
+ }
+
+
+
+ void getAllNeighbours(const DgmPoint& q, std::vector<DgmPoint>& result)
+ {
+ //std::cout << "Entered getAllNeighbours for q = " << q << std::endl;
+ result.clear();
+ // add diagonal points, if necessary
+ if ( q.isDiagonal() ) {
+ for( auto& diagPt : diagonalPoints ) {
+ result.push_back(diagPt);
+ }
+ }
+ // delete diagonal points we found
+ // to prevent finding them again
+ for(auto& pt : result) {
+ //std::cout << "deleting DIAG point pt = " << pt << std::endl;
+ deletePoint(pt);
+ }
+ size_t diagOffset = result.size();
+ std::vector<size_t> pointIndicesOut;
+ // perorm range search on kd-tree
+ DnnPoint queryPoint;
+ queryPoint[0] = q.getRealX();
+ queryPoint[1] = q.getRealY();
+ auto kdtreeResult = kdtree->findR(queryPoint, r);
+ pointIndicesOut.reserve(kdtreeResult.size());
+ for(auto& handleDist : kdtreeResult) {
+ if (handleDist.d <= r + distEpsilon) {
+ pointIndicesOut.push_back(handleDist.p->id());
+ } else {
+ break;
+ }
+ }
+ // get actual points in result
+ for(auto& ptIdx : pointIndicesOut) {
+ result.push_back(allPoints[ptIdx]);
+ }
+ // delete all points we found
+ for(auto ptIt = result.begin() + diagOffset; ptIt != result.end(); ++ptIt) {
+ //printDebug(isDebug, "deleting point pt = ", *ptIt);
+ deletePoint(*ptIt);
+ }
+ }
+
+ //DgmPointSet originalPointSet;
+ template<class R>
+ friend std::ostream& operator<<(std::ostream& out, const NeighbOracleDnn<R>& oracle);
+
+};
+
+} // end namespace bt
+} // end namespace hera
+
+#endif // HERA_NEIGHB_ORACLE_H
diff --git a/geom_bottleneck/license.txt b/geom_bottleneck/license.txt
new file mode 100644
index 0000000..8f7e4f5
--- /dev/null
+++ b/geom_bottleneck/license.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
diff --git a/geom_matching/wasserstein/CMakeLists.txt b/geom_matching/wasserstein/CMakeLists.txt
index c8d8a56..98f3cfd 100644
--- a/geom_matching/wasserstein/CMakeLists.txt
+++ b/geom_matching/wasserstein/CMakeLists.txt
@@ -1,5 +1,5 @@
project (wasserstein)
-cmake_minimum_required (VERSION 2.8.9)
+cmake_minimum_required (VERSION 3.5.1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -18,24 +18,36 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include
SYSTEM ${Boost_INCLUDE_DIR})
if(NOT WIN32)
- add_definitions(-std=c++11)
+ add_definitions(-std=c++14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
- set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb -D_GLIBCXX_DEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 ")
- set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3 -g")
+ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O2 -g -ggdb")
endif(NOT WIN32)
-file(GLOB WS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
+file(GLOB WS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp)
-add_library(wasserstein ${WS_SOURCES})
+#add_library(wasserstein ${WS_SOURCES})
-if (WIN32)
- GENERATE_EXPORT_HEADER(wasserstein
- BASE_NAME wasserstein
- EXPORT_MACRO_NAME wasserstein_EXPORT
- EXPORT_FILE_NAME wasserstein_export.h
- STATIC_DEFINE wasserstein_BUILT_AS_STATIC)
-endif(WIN32)
+#if (WIN32)
+ #GENERATE_EXPORT_HEADER(wasserstein
+ #BASE_NAME wasserstein
+ #EXPORT_MACRO_NAME wasserstein_EXPORT
+ #EXPORT_FILE_NAME wasserstein_export.h
+ #STATIC_DEFINE wasserstein_BUILT_AS_STATIC)
+#endif(WIN32)
-add_executable(wasserstein_dist ${CMAKE_CURRENT_SOURCE_DIR}/example/wasserstein_dist.cpp)
-target_link_libraries(wasserstein_dist PUBLIC wasserstein)
+find_package (Threads)
+set (libraries ${libraries} ${CMAKE_THREAD_LIBS_INIT})
+
+add_executable(wasserstein_dist ${CMAKE_CURRENT_SOURCE_DIR}/example/wasserstein_dist.cpp ${WS_HEADERS})
+target_link_libraries(wasserstein_dist PUBLIC ${libraries})
+
+# pure geometric version, arbitrary dimension
+add_executable(wasserstein_dist_point_cloud ${CMAKE_CURRENT_SOURCE_DIR}/example/wasserstein_dist_point_cloud.cpp ${WS_HEADERS})
+target_link_libraries(wasserstein_dist_point_cloud PUBLIC ${libraries})
+
+# Tests
+add_executable(wasserstein_test ${CMAKE_CURRENT_SOURCE_DIR}/tests/tests_main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_hera_wasserstein.cpp)
+#add_executable(wasserstein_test EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_hera_wasserstein.cpp)
+target_link_libraries(wasserstein_test PUBLIC ${libraries})
diff --git a/geom_matching/wasserstein/example/wasserstein_dist.cpp b/geom_matching/wasserstein/example/wasserstein_dist.cpp
index a2ed234..fcbc641 100644
--- a/geom_matching/wasserstein/example/wasserstein_dist.cpp
+++ b/geom_matching/wasserstein/example/wasserstein_dist.cpp
@@ -1,5 +1,5 @@
/*
-
+
Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
All rights reserved.
@@ -12,7 +12,7 @@ Redistribution and use in source and binary forms, with or without modification,
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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 HOLDER 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.
-
+
You are under no obligation whatsoever to provide any bug fixes, patches, or
upgrades to the features, functionality or performance of the source code
@@ -27,12 +27,14 @@ derivative works thereof, in binary and source code form.
*/
#include <iostream>
+#include <locale>
#include <iomanip>
-#include <fstream>
#include <vector>
-#include <algorithm>
-#include <limits>
-#include <random>
+
+//#define LOG_AUCTION
+
+//#include "auction_runner_fr.h"
+//#include "auction_runner_fr.hpp"
#include "wasserstein.h"
@@ -41,51 +43,88 @@ derivative works thereof, in binary and source code form.
int main(int argc, char* argv[])
{
- geom_ws::PairVector diagramA, diagramB;
+ using PairVector = std::vector<std::pair<double, double>>;
+ PairVector diagramA, diagramB;
+
+ hera::AuctionParams<double> params;
if (argc < 3 ) {
- std::cerr << "Usage: " << argv[0] << " file1 file2 [wasserstein_degree] [relative_error] [internal norm] [output_actual_error]. By default power is 1.0, relative error is 0.01, internal norm is l_infinity, actual relative error is not printed." << std::endl;
+ std::cerr << "Usage: " << argv[0] << " file1 file2 [wasserstein_degree] [relative_error] [internal norm] [initial epsilon] [epsilon_factor] [max_bids_per_round] [gamma_threshold][log_filename_prefix]. By default power is 1.0, relative error is 0.01, internal norm is l_infinity, initall epsilon is chosen automatically, epsilon factor is 5.0, Jacobi variant is used (max bids per round is maximal), gamma_threshold = 0.0." << std::endl;
return 1;
}
- if (!geom_ws::readDiagramPointSet(argv[1], diagramA)) {
+ if (!hera::read_diagram_point_set<double, PairVector>(argv[1], diagramA)) {
std::exit(1);
}
- if (!geom_ws::readDiagramPointSet(argv[2], diagramB)) {
+ if (!hera::read_diagram_point_set(argv[2], diagramB)) {
std::exit(1);
}
- double wasserPower = (4 <= argc) ? atof(argv[3]) : 1.0;
- if (wasserPower < 1.0) {
+ params.wasserstein_power = (4 <= argc) ? atof(argv[3]) : 1.0;
+ if (params.wasserstein_power < 1.0) {
std::cerr << "The third argument (wasserstein_degree) was \"" << argv[3] << "\", must be a number >= 1.0. Cannot proceed. " << std::endl;
std::exit(1);
}
- if (wasserPower == 1.0) {
- geom_ws::removeDuplicates(diagramA, diagramB);
+ if (params.wasserstein_power == 1.0) {
+ hera::remove_duplicates<double>(diagramA, diagramB);
}
//default relative error: 1%
- double delta = (5 <= argc) ? atof(argv[4]) : 0.01;
- if ( delta <= 0.0) {
+ params.delta = (5 <= argc) ? atof(argv[4]) : 0.01;
+ if ( params.delta <= 0.0) {
std::cerr << "The 4th argument (relative error) was \"" << argv[4] << "\", must be a number > 0.0. Cannot proceed. " << std::endl;
std::exit(1);
}
// default for internal metric is l_infinity
- double internal_p = ( 6 <= argc ) ? atof(argv[5]) : std::numeric_limits<double>::infinity();
- if (internal_p < 1.0) {
- std::cerr << "The 5th argument (internal norm) was \"" << argv[5] << "\", must be a number >= 1.0. Cannot proceed. " << std::endl;
+ params.internal_p = ( 6 <= argc ) ? atof(argv[5]) : hera::get_infinity<double>();
+ if (std::isinf(params.internal_p)) {
+ params.internal_p = hera::get_infinity<double>();
+ }
+
+
+ if (not hera::is_p_valid_norm<double>(params.internal_p)) {
+ std::cerr << "The 5th argument (internal norm) was \"" << argv[5] << "\", must be a number >= 1.0 or inf. Cannot proceed. " << std::endl;
std::exit(1);
}
// if you want to specify initial value for epsilon and the factor
// for epsilon-scaling
- double initialEpsilon= ( 7 <= argc ) ? atof(argv[6]) : 0.0 ;
- double epsFactor = ( 8 <= argc ) ? atof(argv[7]) : 0.0 ;
+ params.initial_epsilon= ( 7 <= argc ) ? atof(argv[6]) : 0.0 ;
+
+ if (params.initial_epsilon < 0.0) {
+ std::cerr << "The 6th argument (initial epsilon) was \"" << argv[6] << "\", must be a non-negative number. Cannot proceed." << std::endl;
+ std::exit(1);
+ }
+
+ params.epsilon_common_ratio = ( 8 <= argc ) ? atof(argv[7]) : 0.0 ;
+ if (params.epsilon_common_ratio <= 1.0 and params.epsilon_common_ratio != 0.0) {
+ std::cerr << "The 7th argument (epsilon factor) was \"" << argv[7] << "\", must be a number greater than 1. Cannot proceed." << std::endl;
+ std::exit(1);
+ }
+
+
+ params.max_bids_per_round = ( 9 <= argc ) ? atoi(argv[8]) : 0;
+ if (params.max_bids_per_round == 0)
+ params.max_bids_per_round = std::numeric_limits<size_t>::max();
+
+
+ params.gamma_threshold = (10 <= argc) ? atof(argv[9]) : 0.0;
+
+ std::string log_filename_prefix = ( 11 <= argc ) ? argv[10] : "";
+
+ params.max_num_phases = 800;
+
+#ifdef LOG_AUCTION
+ spdlog::set_level(spdlog::level::info);
+#endif
+
+ double res = hera::wasserstein_dist(diagramA, diagramB, params, log_filename_prefix);
- double res = geom_ws::wassersteinDist(diagramA, diagramB, wasserPower, delta, internal_p, initialEpsilon, epsFactor);
std::cout << std::setprecision(15) << res << std::endl;
+
return 0;
+
}
diff --git a/geom_matching/wasserstein/example/wasserstein_dist_point_cloud.cpp b/geom_matching/wasserstein/example/wasserstein_dist_point_cloud.cpp
new file mode 100644
index 0000000..6f699a4
--- /dev/null
+++ b/geom_matching/wasserstein/example/wasserstein_dist_point_cloud.cpp
@@ -0,0 +1,175 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#include <iostream>
+#include <locale>
+#include <iomanip>
+#include <vector>
+
+#include "wasserstein_pure_geom.hpp"
+
+int main(int argc, char* argv[])
+{
+ //{
+ //int n_points = 3;
+ //int dim = 3;
+ //using Traits = hera::ws::dnn::DynamicPointTraits<double>;
+ //hera::ws::dnn::DynamicPointTraits<double> traits(dim);
+ //hera::ws::dnn::DynamicPointVector<double> dgm_a = traits.container(n_points);
+ //hera::ws::dnn::DynamicPointVector<double> dgm_b = traits.container(n_points);
+
+ //dgm_a[0][0] = 0.0;
+ //dgm_a[0][1] = 0.0;
+ //dgm_a[0][2] = 0.0;
+
+ //dgm_a[1][0] = 1.0;
+ //dgm_a[1][1] = 0.0;
+ //dgm_a[1][2] = 0.0;
+
+ //dgm_a[2][0] = 0.0;
+ //dgm_a[2][1] = 1.0;
+ //dgm_a[2][2] = 1.0;
+
+ //dgm_b[0][0] = 0.0;
+ //dgm_b[0][1] = 0.1;
+ //dgm_b[0][2] = 0.1;
+
+ //dgm_b[1][0] = 1.1;
+ //dgm_b[1][1] = 0.0;
+ //dgm_b[1][2] = 0.0;
+
+ //dgm_b[2][0] = 0.0;
+ //dgm_b[2][1] = 1.1;
+ //dgm_b[2][2] = 0.9;
+
+
+ //hera::AuctionParams<double> params;
+ //params.dim = dim;
+
+
+ //std::cout << hera::ws::wasserstein_cost(dgm_a, dgm_b, params) << std::endl;
+
+ //return 0;
+ //}
+
+
+ using Real = double;
+ using PointVector = hera::ws::dnn::DynamicPointVector<Real>;
+ PointVector set_A, set_B;
+
+ hera::AuctionParams<Real> params;
+
+ if (argc < 3 ) {
+ std::cerr << "Usage: " << argv[0] << " file1 file2 [wasserstein_degree] [relative_error] [internal norm] [initial epsilon] [epsilon_factor] [max_bids_per_round] [gamma_threshold][log_filename_prefix]. By default power is 1.0, relative error is 0.01, internal norm is l_infinity, initall epsilon is chosen automatically, epsilon factor is 5.0, Jacobi variant is used (max bids per round is maximal), gamma_threshold = 0.0." << std::endl;
+ return 1;
+ }
+
+ int dimension_A, dimension_B;
+
+ if (!hera::read_point_cloud<Real>(argv[1], set_A, dimension_A)) {
+ std::exit(1);
+ }
+
+ if (!hera::read_point_cloud(argv[2], set_B, dimension_B)) {
+ std::exit(1);
+ }
+
+ if (dimension_A != dimension_B) {
+ std::cerr << "Dimension mismatch: " << dimension_A << " != " << dimension_B << std::endl;
+ std::exit(1);
+ }
+
+ params.dim = dimension_A;
+
+
+ params.wasserstein_power = (4 <= argc) ? atof(argv[3]) : 1.0;
+ if (params.wasserstein_power < 1.0) {
+ std::cerr << "The third argument (wasserstein_degree) was \"" << argv[3] << "\", must be a number >= 1.0. Cannot proceed. " << std::endl;
+ std::exit(1);
+ }
+
+ //if (params.wasserstein_power == 1.0) {
+ // hera::remove_duplicates<double?(set_A, set_B);
+ //}
+
+ //default relative error: 1%
+ params.delta = (5 <= argc) ? atof(argv[4]) : 0.01;
+ if ( params.delta <= 0.0) {
+ std::cerr << "The 4th argument (relative error) was \"" << argv[4] << "\", must be a number > 0.0. Cannot proceed. " << std::endl;
+ std::exit(1);
+ }
+
+ // default for internal metric is l_infinity
+ params.internal_p = ( 6 <= argc ) ? atof(argv[5]) : hera::get_infinity<Real>();
+ if (std::isinf(params.internal_p)) {
+ params.internal_p = hera::get_infinity<Real>();
+ }
+
+
+ if (not hera::is_p_valid_norm<Real>(params.internal_p)) {
+ std::cerr << "The 5th argument (internal norm) was \"" << argv[5] << "\", must be a number >= 1.0 or inf. Cannot proceed. " << std::endl;
+ std::exit(1);
+ }
+
+ // if you want to specify initial value for epsilon and the factor
+ // for epsilon-scaling
+ params.initial_epsilon= ( 7 <= argc ) ? atof(argv[6]) : 0.0 ;
+
+ if (params.initial_epsilon < 0.0) {
+ std::cerr << "The 6th argument (initial epsilon) was \"" << argv[6] << "\", must be a non-negative number. Cannot proceed." << std::endl;
+ std::exit(1);
+ }
+
+ params.epsilon_common_ratio = ( 8 <= argc ) ? atof(argv[7]) : 0.0 ;
+ if (params.epsilon_common_ratio <= 1.0 and params.epsilon_common_ratio != 0.0) {
+ std::cerr << "The 7th argument (epsilon factor) was \"" << argv[7] << "\", must be a number greater than 1. Cannot proceed." << std::endl;
+ std::exit(1);
+ }
+
+
+ params.max_bids_per_round = ( 9 <= argc ) ? atoi(argv[8]) : 0;
+ if (params.max_bids_per_round == 0)
+ params.max_bids_per_round = std::numeric_limits<size_t>::max();
+
+
+ params.gamma_threshold = (10 <= argc) ? atof(argv[9]) : 0.0;
+
+ std::string log_filename_prefix = ( 11 <= argc ) ? argv[10] : "";
+
+ params.max_num_phases = 800;
+
+#ifdef LOG_AUCTION
+ spdlog::set_level(spdlog::level::critical);
+#endif
+
+ Real res = hera::ws::wasserstein_dist(set_A, set_B, params);
+
+ std::cout << std::setprecision(15) << res << std::endl;
+
+ return 0;
+}
diff --git a/geom_matching/wasserstein/include/auction_oracle.h b/geom_matching/wasserstein/include/auction_oracle.h
index ef6ec53..d285a1f 100644
--- a/geom_matching/wasserstein/include/auction_oracle.h
+++ b/geom_matching/wasserstein/include/auction_oracle.h
@@ -26,285 +26,15 @@ derivative works thereof, in binary and source code form.
*/
-#ifndef AUCTION_ORACLE_H
-#define AUCTION_ORACLE_H
+#ifndef HERA_AUCTION_ORACLE_H
+#define HERA_AUCTION_ORACLE_H
+// all oracle classes are in separate h-hpp files
+// this file comprises all of them
-#define USE_BOOST_HEAP
+#include "auction_oracle_base.h"
+#include "auction_oracle_kdtree_restricted.h"
+#include "auction_oracle_kdtree_single_diag.h"
+#include "auction_oracle_stupid_sparse_restricted.h"
-#include <map>
-#include <memory>
-#include <set>
-#include <list>
-
-#ifdef USE_BOOST_HEAP
-#include <boost/heap/d_ary_heap.hpp>
-#endif
-
-#include "basic_defs_ws.h"
-#include "dnn/geometry/euclidean-fixed.h"
-#include "dnn/local/kd-tree.h"
-
-namespace geom_ws {
-
-struct CompPairsBySecondStruct {
- bool operator()(const IdxValPair& a, const IdxValPair& b) const
- {
- return a.second < b.second;
- }
-};
-
-//
-struct CompPairsBySecondGreaterStruct {
- bool operator()(const IdxValPair& a, const IdxValPair& b) const
- {
- return a.second > b.second;
- }
-};
-
-struct CompPairsBySecondLexStruct {
- bool operator()(const IdxValPair& a, const IdxValPair& b) const
- {
- return a.second < b.second or (a.second == b.second and a.first > b.first);
- }
-};
-
-struct CompPairsBySecondLexGreaterStruct {
- bool operator()(const IdxValPair& a, const IdxValPair& b) const
- {
- return a.second > b.second or (a.second == b.second and a.first > b.first);
- }
-};
-
-using ItemsTimePair = std::pair<IdxType, int>;
-
-using UpdateList = std::list<ItemsTimePair>;
-using UpdateListIter = UpdateList::iterator;
-
-
-#ifdef USE_BOOST_HEAP
-using LossesHeap = boost::heap::d_ary_heap<IdxValPair, boost::heap::arity<2>, boost::heap::mutable_<true>, boost::heap::compare<CompPairsBySecondGreaterStruct>>;
-#else
-template<class ComparisonStruct>
-class IdxValHeap {
-public:
- using InternalKeeper = std::set<IdxValPair, ComparisonStruct>;
- using handle_type = typename InternalKeeper::iterator;
- // methods
- handle_type push(const IdxValPair& val)
- {
- auto resPair = _heap.insert(val);
- assert(resPair.second);
- assert(resPair.first != _heap.end());
- return resPair.first;
- }
-
- void decrease(handle_type& handle, const IdxValPair& newVal)
- {
- _heap.erase(handle);
- handle = push(newVal);
- }
-
- void increase(handle_type& handle, const IdxValPair& newVal)
- {
- _heap.erase(handle);
- handle = push(newVal);
-
- size_t size() const
- {
- return _heap.size();
- }
-
- handle_type ordered_begin()
- {
- return _heap.begin();
- }
-
- handle_type ordered_end()
- {
- return _heap.end();
- }
-
-
-private:
- std::set<IdxValPair, ComparisonStruct> _heap;
-};
-
-// if we store losses, the minimal value should come first
-using LossesHeap = IdxValHeap<CompPairsBySecondLexStruct>;
-#endif
-
-struct DebugOptimalBid {
- DebugOptimalBid() : bestItemIdx(-1), bestItemValue(-666.666), secondBestItemIdx(-1), secondBestItemValue(-666.666) {};
- IdxType bestItemIdx;
- double bestItemValue;
- IdxType secondBestItemIdx;
- double secondBestItemValue;
-};
-
-struct AuctionOracleAbstract {
- AuctionOracleAbstract(const std::vector<DiagramPoint>& _bidders, const std::vector<DiagramPoint>& _items, const double _wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- ~AuctionOracleAbstract() {}
- virtual IdxValPair getOptimalBid(const IdxType bidderIdx) = 0;
- virtual void setPrice(const IdxType itemsIdx, const double newPrice) = 0;
- virtual void adjustPrices(void) = 0;
- double getEpsilon() { return epsilon; };
- virtual void setEpsilon(double newEpsilon) { assert(newEpsilon >= 0.0); epsilon = newEpsilon; };
- std::vector<double> getPrices() { return prices; }
-protected:
- const std::vector<DiagramPoint>& bidders;
- const std::vector<DiagramPoint>& items;
- std::vector<double> prices;
- double wassersteinPower;
- double epsilon;
- double internal_p;
- double getValueForBidder(size_t bidderIdx, size_t itemsIdx);
-};
-
-struct AuctionOracleLazyHeap final : AuctionOracleAbstract {
- AuctionOracleLazyHeap(const std::vector<DiagramPoint>& bidders, const std::vector<DiagramPoint>& items, const double wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- ~AuctionOracleLazyHeap();
- // data members
- // temporarily make everything public
- std::vector<std::vector<double>> weightMatrix;
- //double weightAdjConst;
- double maxVal;
- // vector of heaps to find the best items
- std::vector<LossesHeap*> lossesHeap;
- std::vector<std::vector<LossesHeap::handle_type>> lossesHeapHandles;
- // methods
- void fillInLossesHeap(void);
- void setPrice(const IdxType itemsIdx, const double newPrice) override final;
- IdxValPair getOptimalBid(const IdxType bidderIdx) override final;
- double getMatchingWeight(const std::vector<IdxType>& biddersToItems) const;
- void adjustPrices(void) override final;
- // to update the queue in lazy fashion
- std::vector<UpdateListIter> itemsIterators;
- UpdateList updateList;
- std::vector<int> biddersUpdateMoments;
- int updateCounter;
- void updateQueueForBidder(const IdxType bidderIdx);
- // debug
- DebugOptimalBid getOptimalBidDebug(const IdxType bidderIdx);
-};
-
-struct AuctionOracleLazyHeapRestricted final : AuctionOracleAbstract {
- AuctionOracleLazyHeapRestricted(const std::vector<DiagramPoint>& bidders, const std::vector<DiagramPoint>& items, const double wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- ~AuctionOracleLazyHeapRestricted();
- // data members
- // temporarily make everything public
- std::vector<std::vector<double>> weightMatrix;
- //double weightAdjConst;
- double maxVal;
- // vector of heaps to find the best items
- std::vector<LossesHeap*> lossesHeap;
- std::vector<std::vector<size_t>> itemsIndicesForHeapHandles;
- std::vector<std::vector<LossesHeap::handle_type>> lossesHeapHandles;
- // methods
- void fillInLossesHeap(void);
- void setPrice(const IdxType itemsIdx, const double newPrice) override final;
- IdxValPair getOptimalBid(const IdxType bidderIdx) override final;
- double getMatchingWeight(const std::vector<IdxType>& biddersToItems) const;
- void adjustPrices(void) override final;
- // to update the queue in lazy fashion
- std::vector<UpdateListIter> itemsIterators;
- UpdateList updateList;
- std::vector<int> biddersUpdateMoments;
- int updateCounter;
- void updateQueueForBidder(const IdxType bidderIdx);
- LossesHeap diagItemsHeap;
- std::vector<LossesHeap::handle_type> diagHeapHandles;
- std::vector<size_t> heapHandlesIndices;
- // debug
-
- DebugOptimalBid getOptimalBidDebug(const IdxType bidderIdx);
-
- // for diagonal points
- bool bestDiagonalItemsComputed;
- size_t bestDiagonalItemIdx;
- double bestDiagonalItemValue;
- size_t secondBestDiagonalItemIdx;
- double secondBestDiagonalItemValue;
-};
-
-struct AuctionOracleKDTree final : AuctionOracleAbstract {
- typedef dnn::Point<2, double> DnnPoint;
- typedef dnn::PointTraits<DnnPoint> DnnTraits;
-
- AuctionOracleKDTree(const std::vector<DiagramPoint>& bidders, const std::vector<DiagramPoint>& items, const double wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- ~AuctionOracleKDTree();
- // data members
- // temporarily make everything public
- double maxVal;
- double weightAdjConst;
- dnn::KDTree<DnnTraits>* kdtree;
- std::vector<DnnPoint> dnnPoints;
- std::vector<DnnPoint*> dnnPointHandles;
- dnn::KDTree<DnnTraits>* kdtreeAll;
- std::vector<DnnPoint> dnnPointsAll;
- std::vector<DnnPoint*> dnnPointHandlesAll;
- LossesHeap diagItemsHeap;
- std::vector<LossesHeap::handle_type> diagHeapHandles;
- std::vector<size_t> heapHandlesIndices;
- std::vector<size_t> kdtreeItems;
- // vector of heaps to find the best items
- void setPrice(const IdxType itemsIdx, const double newPrice) override final;
- IdxValPair getOptimalBid(const IdxType bidderIdx) override final;
- void adjustPrices(void) override final;
- // debug routines
- DebugOptimalBid getOptimalBidDebug(IdxType bidderIdx);
- void setEpsilon(double newVal) override final;
-};
-
-struct AuctionOracleKDTreeRestricted final : AuctionOracleAbstract {
- typedef dnn::Point<2, double> DnnPoint;
- typedef dnn::PointTraits<DnnPoint> DnnTraits;
-
- AuctionOracleKDTreeRestricted(const std::vector<DiagramPoint>& bidders, const std::vector<DiagramPoint>& items, const double wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- ~AuctionOracleKDTreeRestricted();
- // data members
- // temporarily make everything public
- double maxVal;
- double weightAdjConst;
- dnn::KDTree<DnnTraits>* kdtree;
- std::vector<DnnPoint> dnnPoints;
- std::vector<DnnPoint*> dnnPointHandles;
- std::vector<DnnPoint> dnnPointsAll;
- std::vector<DnnPoint*> dnnPointHandlesAll;
- LossesHeap diagItemsHeap;
- std::vector<LossesHeap::handle_type> diagHeapHandles;
- std::vector<size_t> heapHandlesIndices;
- std::vector<size_t> kdtreeItems;
- // vector of heaps to find the best items
- void setPrice(const IdxType itemsIdx, const double newPrice) override final;
- IdxValPair getOptimalBid(const IdxType bidderIdx) override final;
- void adjustPrices(void) override final;
- // debug routines
- DebugOptimalBid getOptimalBidDebug(IdxType bidderIdx);
- void setEpsilon(double newVal) override final;
-
-
- bool bestDiagonalItemsComputed;
- size_t bestDiagonalItemIdx;
- double bestDiagonalItemValue;
- size_t secondBestDiagonalItemIdx;
- double secondBestDiagonalItemValue;
-};
-
-struct AuctionOracleRestricted final : AuctionOracleAbstract {
- AuctionOracleRestricted(const std::vector<DiagramPoint>& bidders, const std::vector<DiagramPoint>& items, const double wassersteinPower, const double _internal_p = std::numeric_limits<double>::infinity());
- IdxValPair getOptimalBid(const IdxType bidderIdx) override;
- void setPrice(const IdxType itemsIdx, const double newPrice) override;
- void adjustPrices(void) override {};
- void setEpsilon(double newEpsilon) override { assert(newEpsilon >= 0.0); epsilon = newEpsilon; };
- // data
- std::vector<std::vector<double>> weightMatrix;
- double maxVal;
- constexpr static bool isRestricted = true;
-};
-
-std::ostream& operator<< (std::ostream& output, const DebugOptimalBid& db);
-
-} // end of namespace geom_ws
-
-#endif
+#endif // HERA_AUCTION_ORACLE_H
diff --git a/geom_matching/wasserstein/include/auction_oracle_base.h b/geom_matching/wasserstein/include/auction_oracle_base.h
new file mode 100644
index 0000000..08eaf00
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_base.h
@@ -0,0 +1,85 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_BASE_H
+#define AUCTION_ORACLE_BASE_H
+
+#include <map>
+#include <memory>
+#include <set>
+#include <list>
+
+#include "basic_defs_ws.h"
+
+namespace hera {
+namespace ws {
+
+
+template <class Real>
+struct DebugOptimalBid {
+ DebugOptimalBid() : best_item_idx(k_invalid_index), best_item_value(-666.666), second_best_item_idx(k_invalid_index), second_best_item_value(-666.666) {};
+ IdxType best_item_idx;
+ Real best_item_value;
+ IdxType second_best_item_idx;
+ Real second_best_item_value;
+};
+
+template <class Real = double, class PointContainer_ = std::vector<DiagramPoint<Real>>>
+struct AuctionOracleBase {
+ AuctionOracleBase(const PointContainer_& _bidders, const PointContainer_& _items, const AuctionParams<Real>& params);
+ ~AuctionOracleBase() {}
+ Real get_epsilon() const { return epsilon; };
+ void set_epsilon(Real new_epsilon) { assert(new_epsilon >= 0.0); epsilon = new_epsilon; };
+ const std::vector<Real>& get_prices() const { return prices; }
+ virtual Real get_price(const size_t item_idx) const { return prices[item_idx]; } // TODO make virtual?
+//protected:
+ const PointContainer_& bidders;
+ const PointContainer_& items;
+ const size_t num_bidders_;
+ const size_t num_items_;
+ std::vector<Real> prices;
+ const Real wasserstein_power;
+ Real epsilon;
+ const Real internal_p;
+ unsigned int dim; // used only in pure geometric version, not for persistence diagrams
+ Real get_value_for_bidder(size_t bidder_idx, size_t item_idx) const;
+ Real get_value_for_diagonal_bidder(size_t item_idx) const;
+ Real get_cost_for_diagonal_bidder(size_t item_idx) const;
+};
+
+
+template<class Real>
+std::ostream& operator<< (std::ostream& output, const DebugOptimalBid<Real>& db);
+
+} // ws
+} // hera
+
+
+#include "auction_oracle_base.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_base.hpp b/geom_matching/wasserstein/include/auction_oracle_base.hpp
new file mode 100644
index 0000000..b74c7fb
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_base.hpp
@@ -0,0 +1,97 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_BASE_HPP
+#define AUCTION_ORACLE_BASE_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_oracle.h"
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace hera {
+namespace ws {
+
+template<class Real, class PointContainer>
+AuctionOracleBase<Real, PointContainer>::AuctionOracleBase(const PointContainer& _bidders,
+ const PointContainer& _items,
+ const AuctionParams<Real>& params) :
+ bidders(_bidders),
+ items(_items),
+ num_bidders_(_bidders.size()),
+ num_items_(_items.size()),
+ prices(items.size(), Real(0.0)),
+ wasserstein_power(params.wasserstein_power),
+ internal_p(params.internal_p),
+ dim(params.dim)
+{
+ assert(bidders.size() == items.size() );
+}
+
+
+template<class Real, class PointContainer>
+Real AuctionOracleBase<Real, PointContainer>::get_value_for_bidder(size_t bidder_idx, size_t item_idx) const
+{
+ return std::pow(dist_lp<Real>(bidders[bidder_idx], items[item_idx], internal_p, dim), wasserstein_power) + get_price(item_idx);
+}
+
+template<class Real, class PointContainer>
+Real AuctionOracleBase<Real, PointContainer>::get_value_for_diagonal_bidder(size_t item_idx) const
+{
+ return get_cost_for_diagonal_bidder(item_idx) + get_price(item_idx);
+}
+
+template<class Real, class PointContainer>
+Real AuctionOracleBase<Real, PointContainer>::get_cost_for_diagonal_bidder(size_t item_idx) const
+{
+ return std::pow(items[item_idx].persistence_lp(internal_p), wasserstein_power);
+}
+
+
+
+template<class Real>
+std::ostream& operator<< (std::ostream& output, const DebugOptimalBid<Real>& db)
+{
+ output << "best_item_value = " << db.best_item_value;
+ output << "; best_item_idx = " << db.best_item_idx;
+ output << "; second_best_item_value = " << db.second_best_item_value;
+ output << "; second_best_item_idx = " << db.second_best_item_idx;
+ return output;
+}
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.h b/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.h
new file mode 100644
index 0000000..096583e
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.h
@@ -0,0 +1,97 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_KDTREE_PURE_GEOM_H
+#define AUCTION_ORACLE_KDTREE_PURE_GEOM_H
+
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include <boost/range/adaptor/transformed.hpp>
+
+namespace ba = boost::adaptors;
+
+#include "spdlog/spdlog.h"
+#include "basic_defs_ws.h"
+#include "auction_oracle_base.h"
+#include "dnn/geometry/euclidean-dynamic.h"
+#include "dnn/local/kd-tree.h"
+
+namespace hera
+{
+namespace ws
+{
+
+template <class Real_ = double, class PointContainer_ = hera::ws::dnn::DynamicPointVector<Real_>>
+struct AuctionOracleKDTreePureGeom : AuctionOracleBase<Real_, PointContainer_> {
+
+ using Real = Real_;
+ using DynamicPointTraitsR = typename hera::ws::dnn::DynamicPointTraits<Real>;
+ using DiagramPointR = typename DynamicPointTraitsR::PointType;
+ using PointHandleR = typename DynamicPointTraitsR::PointHandle;
+ using PointContainer = PointContainer_;
+ using DebugOptimalBidR = typename ws::DebugOptimalBid<Real>;
+
+ using DynamicPointTraits = hera::ws::dnn::DynamicPointTraits<Real>;
+ using KDTreeR = hera::ws::dnn::KDTree<DynamicPointTraits>;
+
+ AuctionOracleKDTreePureGeom(const PointContainer& bidders, const PointContainer& items, const AuctionParams<Real>& params);
+ ~AuctionOracleKDTreePureGeom();
+
+ // data members
+ // temporarily make everything public
+ DynamicPointTraits traits;
+ Real max_val_;
+ Real weight_adj_const_;
+ std::unique_ptr<KDTreeR> kdtree_;
+ std::vector<size_t> kdtree_items_;
+ // methods
+ void set_price(const IdxType items_idx, const Real new_price);
+ IdxValPair<Real> get_optimal_bid(const IdxType bidder_idx);
+ void adjust_prices();
+ void adjust_prices(const Real delta);
+
+ // debug routines
+ DebugOptimalBidR get_optimal_bid_debug(IdxType bidder_idx) const;
+ void sanity_check();
+
+ std::shared_ptr<spdlog::logger> console_logger;
+
+ std::pair<Real, Real> get_minmax_price() const;
+
+};
+
+} // ws
+} // hera
+
+
+#include "auction_oracle_kdtree_pure_geom.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.hpp b/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.hpp
new file mode 100644
index 0000000..a6bdf10
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_pure_geom.hpp
@@ -0,0 +1,244 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+#ifndef AUCTION_ORACLE_KDTREE_PURE_GEOM_HPP
+#define AUCTION_ORACLE_KDTREE_PURE_GEOM_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_oracle_kdtree_restricted.h"
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace hera {
+namespace ws {
+
+
+// *****************************
+// AuctionOracleKDTreePureGeom
+// *****************************
+
+
+
+template <class Real_, class PointContainer_>
+std::ostream& operator<<(std::ostream& output, const AuctionOracleKDTreePureGeom<Real_, PointContainer_>& oracle)
+{
+ output << "Oracle " << &oracle << std::endl;
+ output << fmt::format(" max_val_ = {0}\n",
+ oracle.max_val_);
+
+ output << fmt::format(" prices = {0}\n",
+ format_container_to_log(oracle.prices));
+
+ output << "end of oracle " << &oracle << std::endl;
+ return output;
+}
+
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreePureGeom<Real_, PointContainer_>::AuctionOracleKDTreePureGeom(const PointContainer_& _bidders,
+ const PointContainer_& _items,
+ const AuctionParams<Real_>& params) :
+ AuctionOracleBase<Real_, PointContainer_>(_bidders, _items, params),
+ traits(params.dim)
+{
+
+ traits.internal_p = params.internal_p;
+
+ std::vector<PointHandleR> item_handles(this->num_items_);
+ for(size_t i = 0; i < this->num_items_; ++i) {
+ item_handles[i] = traits.handle(this->items[i]);
+ }
+
+ //kdtree_ = std::unique_ptr<KDTreeR>(new KDTreeR(traits,
+ // this->items | ba::transformed([this](const DiagramPointR& p) { return traits.handle(p); }),
+ // params.wasserstein_power));
+
+ kdtree_ = std::unique_ptr<KDTreeR>(new KDTreeR(traits, item_handles, params.wasserstein_power));
+
+
+ max_val_ = 3*getFurthestDistance3Approx_pg(this->bidders, this->items, params.internal_p, params.dim);
+ max_val_ = std::pow(max_val_, params.wasserstein_power);
+ weight_adj_const_ = max_val_;
+
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+ console_logger->debug("KDTree Restricted oracle ctor done");
+}
+
+
+template<class Real_, class PointContainer_>
+typename AuctionOracleKDTreePureGeom<Real_, PointContainer_>::DebugOptimalBidR
+AuctionOracleKDTreePureGeom<Real_, PointContainer_>::get_optimal_bid_debug(IdxType bidder_idx) const
+{
+ auto bidder = this->bidders[bidder_idx];
+
+ size_t best_item_idx = k_invalid_index;
+ size_t second_best_item_idx = k_invalid_index;
+ Real best_item_value = std::numeric_limits<Real>::max();
+ Real second_best_item_value = std::numeric_limits<Real>::max();
+
+ for(IdxType item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ auto item = this->items[item_idx];
+ if (item.type != bidder.type and item_idx != bidder_idx)
+ continue;
+ auto item_value = std::pow(dist_lp(bidder, item, this->internal_p), this->wasserstein_power, this->dim) + this->prices[item_idx];
+ if (item_value < best_item_value) {
+ best_item_value = item_value;
+ best_item_idx = item_idx;
+ }
+ }
+
+ assert(best_item_idx != k_invalid_index);
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ auto item = this->items[item_idx];
+ if (item.type != bidder.type and item_idx != bidder_idx)
+ continue;
+ if (item_idx == best_item_idx)
+ continue;
+ auto item_value = std::pow(dist_lp(bidder, item, this->internal_p), this->wasserstein_power, this->dim) + this->prices[item_idx];
+ if (item_value < second_best_item_value) {
+ second_best_item_value = item_value;
+ second_best_item_idx = item_idx;
+ }
+ }
+
+ assert(second_best_item_idx != k_invalid_index);
+ assert(second_best_item_value >= best_item_value);
+
+ DebugOptimalBidR result;
+
+ result.best_item_idx = best_item_idx;
+ result.best_item_value = best_item_value;
+ result.second_best_item_idx = second_best_item_idx;
+ result.second_best_item_value = second_best_item_value;
+
+ return result;
+}
+
+
+template<class Real_, class PointContainer_>
+IdxValPair<Real_> AuctionOracleKDTreePureGeom<Real_, PointContainer_>::get_optimal_bid(IdxType bidder_idx)
+{
+ auto two_best_items = kdtree_->findK(this->bidders[bidder_idx], 2);
+ size_t best_item_idx = traits.id(two_best_items[0].p);
+ Real best_item_value = two_best_items[0].d;
+ Real second_best_item_value = two_best_items[1].d;
+
+ IdxValPair<Real> result;
+
+ assert( second_best_item_value >= best_item_value );
+
+ result.first = best_item_idx;
+ result.second = ( second_best_item_value - best_item_value ) + this->prices[best_item_idx] + this->epsilon;
+
+ return result;
+}
+
+/*
+a_{ij} = d_{ij}
+value_{ij} = a_{ij} + price_j
+*/
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreePureGeom<Real_, PointContainer_>::set_price(IdxType item_idx,
+ Real new_price)
+{
+
+ console_logger->debug("Enter set_price, item_idx = {0}, new_price = {1}, old price = {2}", item_idx, new_price, this->prices[item_idx]);
+
+ assert(this->prices.size() == this->items.size());
+ // adjust_prices decreases prices,
+ // also this variable must be true in reverse phases of FR-auction
+
+ this->prices[item_idx] = new_price;
+ kdtree_->change_weight( traits.handle(this->items[item_idx]), new_price);
+
+ console_logger->debug("Exit set_price, item_idx = {0}, new_price = {1}", item_idx, new_price);
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreePureGeom<Real_, PointContainer_>::adjust_prices(Real delta)
+{
+ //console_logger->debug("Enter adjust_prices, delta = {0}", delta);
+ //std::cerr << *this << std::endl;
+
+ if (delta == 0.0)
+ return;
+
+ for(auto& p : this->prices) {
+ p -= delta;
+ }
+
+ kdtree_->adjust_weights(delta);
+
+ //std::cerr << *this << std::endl;
+ //console_logger->debug("Exit adjust_prices, delta = {0}", delta);
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreePureGeom<Real_, PointContainer_>::adjust_prices()
+{
+ auto pr_begin = this->prices.begin();
+ auto pr_end = this->prices.end();
+ Real min_price = *(std::min_element(pr_begin, pr_end));
+ adjust_prices(min_price);
+}
+
+template<class Real_, class PointContainer_>
+std::pair<Real_, Real_> AuctionOracleKDTreePureGeom<Real_, PointContainer_>::get_minmax_price() const
+{
+ auto r = std::minmax_element(this->prices.begin(), this->prices.end());
+ return std::make_pair(*r.first, *r.second);
+}
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreePureGeom<Real_, PointContainer_>::~AuctionOracleKDTreePureGeom()
+{
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreePureGeom<Real_, PointContainer_>::sanity_check()
+{
+}
+
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.h b/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.h
new file mode 100644
index 0000000..1999147
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.h
@@ -0,0 +1,122 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_KDTREE_RESTRICTED_H
+#define AUCTION_ORACLE_KDTREE_RESTRICTED_H
+
+
+//#define USE_BOOST_HEAP
+
+#include <map>
+#include <memory>
+#include <set>
+
+
+#include "spdlog/spdlog.h"
+#include "basic_defs_ws.h"
+#include "diagonal_heap.h"
+#include "auction_oracle_base.h"
+#include "dnn/geometry/euclidean-fixed.h"
+#include "dnn/local/kd-tree.h"
+
+namespace hera {
+namespace ws {
+
+template <class Real_ = double, class PointContainer_ = std::vector<DiagramPoint<Real_>>>
+struct AuctionOracleKDTreeRestricted : AuctionOracleBase<Real_, PointContainer_> {
+
+ using PointContainer = PointContainer_;
+ using Real = Real_;
+
+ using LossesHeapR = typename ws::LossesHeapOld<Real>;
+ using LossesHeapRHandle = typename ws::LossesHeapOld<Real>::handle_type;
+ using DiagramPointR = typename ws::DiagramPoint<Real>;
+ using DebugOptimalBidR = typename ws::DebugOptimalBid<Real>;
+
+ using DnnPoint = dnn::Point<2, Real>;
+ using DnnTraits = dnn::PointTraits<DnnPoint>;
+
+ AuctionOracleKDTreeRestricted(const PointContainer& bidders, const PointContainer& items, const AuctionParams<Real>& params);
+ ~AuctionOracleKDTreeRestricted();
+ // data members
+ // temporarily make everything public
+ Real max_val_;
+ Real weight_adj_const_;
+ dnn::KDTree<DnnTraits>* kdtree_;
+ std::vector<DnnPoint> dnn_points_;
+ std::vector<DnnPoint*> dnn_point_handles_;
+ LossesHeapR diag_items_heap_;
+ std::vector<LossesHeapRHandle> diag_heap_handles_;
+ std::vector<size_t> heap_handles_indices_;
+ std::vector<size_t> kdtree_items_;
+ std::vector<size_t> top_diag_indices_;
+ std::vector<size_t> top_diag_lookup_;
+ size_t top_diag_counter_ { 0 };
+ bool best_diagonal_items_computed_ { false };
+ Real best_diagonal_item_value_;
+ size_t second_best_diagonal_item_idx_ { k_invalid_index };
+ Real second_best_diagonal_item_value_ { std::numeric_limits<Real>::max() };
+
+
+ // methods
+ void set_price(const IdxType items_idx, const Real new_price, const bool update_diag = true);
+ IdxValPair<Real> get_optimal_bid(const IdxType bidder_idx);
+ void adjust_prices();
+ void adjust_prices(const Real delta);
+
+ // debug routines
+ DebugOptimalBidR get_optimal_bid_debug(IdxType bidder_idx) const;
+ void sanity_check();
+
+
+ // heap top vector
+ size_t get_heap_top_size() const;
+ void recompute_top_diag_items(bool hard = false);
+ void recompute_second_best_diag();
+ void reset_top_diag_counter();
+ void increment_top_diag_counter();
+ void add_top_diag_index(const size_t item_idx);
+ void remove_top_diag_index(const size_t item_idx);
+ bool is_in_top_diag_indices(const size_t item_idx) const;
+
+ std::shared_ptr<spdlog::logger> console_logger;
+
+ std::pair<Real, Real> get_minmax_price() const;
+
+};
+
+template<class Real>
+std::ostream& operator<< (std::ostream& output, const DebugOptimalBid<Real>& db);
+
+} // ws
+} // hera
+
+
+#include "auction_oracle_kdtree_restricted.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.hpp b/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.hpp
new file mode 100644
index 0000000..0e6f780
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_restricted.hpp
@@ -0,0 +1,598 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+#ifndef AUCTION_ORACLE_KDTREE_RESTRICTED_HPP
+#define AUCTION_ORACLE_KDTREE_RESTRICTED_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_oracle_kdtree_restricted.h"
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace hera {
+namespace ws {
+
+
+// *****************************
+// AuctionOracleKDTreeRestricted
+// *****************************
+
+
+
+template <class Real_, class PointContainer_>
+std::ostream& operator<<(std::ostream& output, const AuctionOracleKDTreeRestricted<Real_, PointContainer_>& oracle)
+{
+ output << "Oracle " << &oracle << std::endl;
+ output << fmt::format(" max_val_ = {0}, best_diagonal_items_computed_ = {1}, best_diagonal_item_value_ = {2}, second_best_diagonal_item_idx_ = {3}, second_best_diagonal_item_value_ = {4}\n",
+ oracle.max_val_,
+ oracle.best_diagonal_items_computed_,
+ oracle.best_diagonal_item_value_,
+ oracle.second_best_diagonal_item_idx_,
+ oracle.second_best_diagonal_item_value_);
+
+ output << fmt::format(" prices = {0}\n",
+ format_container_to_log(oracle.prices));
+
+ output << fmt::format(" diag_items_heap_ = {0}\n",
+ losses_heap_to_string(oracle.diag_items_heap_));
+
+
+ output << fmt::format(" top_diag_indices_ = {0}\n",
+ format_container_to_log(oracle.top_diag_indices_));
+
+ output << fmt::format(" top_diag_counter_ = {0}\n",
+ oracle.top_diag_counter_);
+
+ output << fmt::format(" top_diag_lookup_ = {0}\n",
+ format_container_to_log(oracle.top_diag_lookup_));
+
+
+ output << "end of oracle " << &oracle << std::endl;
+ return output;
+}
+
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreeRestricted<Real_, PointContainer_>::AuctionOracleKDTreeRestricted(const PointContainer_& _bidders,
+ const PointContainer_& _items,
+ const AuctionParams<Real>& params) :
+ AuctionOracleBase<Real>(_bidders, _items, params),
+ heap_handles_indices_(_items.size(), k_invalid_index),
+ kdtree_items_(_items.size(), k_invalid_index),
+ top_diag_lookup_(_items.size(), k_invalid_index)
+{
+ size_t dnn_item_idx { 0 };
+ size_t true_idx { 0 };
+ dnn_points_.clear();
+ dnn_points_.reserve(this->items.size());
+ // store normal items in kd-tree
+ for(const auto& g : this->items) {
+ if (g.is_normal() ) {
+ kdtree_items_[true_idx] = dnn_item_idx;
+ // index of items is id of dnn-point
+ DnnPoint p(true_idx);
+ p[0] = g.getRealX();
+ p[1] = g.getRealY();
+ dnn_points_.push_back(p);
+ assert(dnn_item_idx == dnn_points_.size() - 1);
+ dnn_item_idx++;
+ }
+ true_idx++;
+ }
+
+ assert(dnn_points_.size() < _items.size() );
+ for(size_t i = 0; i < dnn_points_.size(); ++i) {
+ dnn_point_handles_.push_back(&dnn_points_[i]);
+ }
+ DnnTraits traits;
+ traits.internal_p = params.internal_p;
+ kdtree_ = new dnn::KDTree<DnnTraits>(traits, dnn_point_handles_, params.wasserstein_power);
+
+ size_t handle_idx {0};
+ for(size_t item_idx = 0; item_idx < _items.size(); ++item_idx) {
+ if (this->items[item_idx].is_diagonal()) {
+ heap_handles_indices_[item_idx] = handle_idx++;
+ diag_heap_handles_.push_back(diag_items_heap_.push(std::make_pair(item_idx, 0.0)));
+ }
+ }
+ max_val_ = 3*getFurthestDistance3Approx<>(_bidders, _items, params.internal_p);
+ max_val_ = std::pow(max_val_, params.wasserstein_power);
+ weight_adj_const_ = max_val_;
+
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+ console_logger->debug("KDTree Restricted oracle ctor done");
+}
+
+
+template<class Real_, class PointContainer_>
+bool AuctionOracleKDTreeRestricted<Real_, PointContainer_>::is_in_top_diag_indices(const size_t item_idx) const
+{
+ return top_diag_lookup_[item_idx] != k_invalid_index;
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::add_top_diag_index(const size_t item_idx)
+{
+ assert(find(top_diag_indices_.begin(), top_diag_indices_.end(), item_idx) == top_diag_indices_.end());
+ assert(this->items[item_idx].is_diagonal());
+
+ top_diag_indices_.push_back(item_idx);
+ top_diag_lookup_[item_idx] = top_diag_indices_.size() - 1;
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::remove_top_diag_index(const size_t item_idx)
+{
+ if (top_diag_indices_.size() > 1) {
+ // remove item_idx from top_diag_indices after swapping
+ // it with the last element, update index lookup appropriately
+ auto old_index = top_diag_lookup_[item_idx];
+ auto end_element = top_diag_indices_.back();
+ std::swap(top_diag_indices_[old_index], top_diag_indices_.back());
+ top_diag_lookup_[end_element] = old_index;
+ }
+
+ top_diag_indices_.pop_back();
+ top_diag_lookup_[item_idx] = k_invalid_index;
+ if (top_diag_indices_.size() < 2) {
+ recompute_second_best_diag();
+ }
+ best_diagonal_items_computed_ = not top_diag_indices_.empty();
+ reset_top_diag_counter();
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::increment_top_diag_counter()
+{
+ assert(top_diag_counter_ >= 0 and top_diag_counter_ < top_diag_indices_.size());
+
+ ++top_diag_counter_;
+ if (top_diag_counter_ >= top_diag_indices_.size()) {
+ top_diag_counter_ -= top_diag_indices_.size();
+ }
+
+ assert(top_diag_counter_ >= 0 and top_diag_counter_ < top_diag_indices_.size());
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::reset_top_diag_counter()
+{
+ top_diag_counter_ = 0;
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::recompute_top_diag_items(bool hard)
+{
+ console_logger->debug("Enter recompute_top_diag_items, hard = {0}", hard);
+ assert(hard or top_diag_indices_.empty());
+
+ if (hard) {
+ std::fill(top_diag_lookup_.begin(), top_diag_lookup_.end(), k_invalid_index);
+ top_diag_indices_.clear();
+ }
+
+ auto top_diag_iter = diag_items_heap_.ordered_begin();
+ best_diagonal_item_value_ = top_diag_iter->second;
+ add_top_diag_index(top_diag_iter->first);
+
+ ++top_diag_iter;
+
+ // traverse the heap while we see the same value
+ while(top_diag_iter != diag_items_heap_.ordered_end()) {
+ if ( top_diag_iter->second != best_diagonal_item_value_) {
+ break;
+ } else {
+ add_top_diag_index(top_diag_iter->first);
+ }
+ ++top_diag_iter;
+ }
+
+ recompute_second_best_diag();
+
+ best_diagonal_items_computed_ = true;
+ reset_top_diag_counter();
+ console_logger->debug("Exit recompute_top_diag_items, hard = {0}", hard);
+}
+
+template<class Real_, class PointContainer_>
+typename AuctionOracleKDTreeRestricted<Real_, PointContainer_>::DebugOptimalBidR
+AuctionOracleKDTreeRestricted<Real_, PointContainer_>::get_optimal_bid_debug(IdxType bidder_idx) const
+{
+ auto bidder = this->bidders[bidder_idx];
+
+ size_t best_item_idx = k_invalid_index;
+ size_t second_best_item_idx = k_invalid_index;
+ Real best_item_value = std::numeric_limits<Real>::max();
+ Real second_best_item_value = std::numeric_limits<Real>::max();
+
+ for(IdxType item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ auto item = this->items[item_idx];
+ if (item.type != bidder.type and item_idx != bidder_idx)
+ continue;
+ auto item_value = std::pow(dist_lp(bidder, item, this->internal_p), this->wasserstein_power) + this->prices[item_idx];
+ if (item_value < best_item_value) {
+ best_item_value = item_value;
+ best_item_idx = item_idx;
+ }
+ }
+
+ assert(best_item_idx != k_invalid_index);
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ auto item = this->items[item_idx];
+ if (item.type != bidder.type and item_idx != bidder_idx)
+ continue;
+ if (item_idx == best_item_idx)
+ continue;
+ auto item_value = std::pow(dist_lp(bidder, item, this->internal_p), this->wasserstein_power) + this->prices[item_idx];
+ if (item_value < second_best_item_value) {
+ second_best_item_value = item_value;
+ second_best_item_idx = item_idx;
+ }
+ }
+
+ assert(second_best_item_idx != k_invalid_index);
+ assert(second_best_item_value >= best_item_value);
+
+ DebugOptimalBidR result;
+
+ result.best_item_idx = best_item_idx;
+ result.best_item_value = best_item_value;
+ result.second_best_item_idx = second_best_item_idx;
+ result.second_best_item_value = second_best_item_value;
+
+ return result;
+}
+
+
+template<class Real_, class PointContainer_>
+IdxValPair<Real_> AuctionOracleKDTreeRestricted<Real_, PointContainer_>::get_optimal_bid(IdxType bidder_idx)
+{
+ auto bidder = this->bidders[bidder_idx];
+
+ // corresponding point is always considered as a candidate
+ // if bidder is a diagonal point, proj_item is a normal point,
+ // and vice versa.
+
+ size_t best_item_idx { k_invalid_index };
+ size_t second_best_item_idx __attribute__((unused)) { k_invalid_index };
+ size_t best_diagonal_item_idx { k_invalid_index };
+ Real best_item_value;
+ Real second_best_item_value;
+
+
+ size_t proj_item_idx = bidder_idx;
+ assert( 0 <= proj_item_idx and proj_item_idx < this->items.size() );
+ assert(this->items[proj_item_idx].type != bidder.type);
+ Real proj_item_value = this->get_value_for_bidder(bidder_idx, proj_item_idx);
+
+ if (bidder.is_diagonal()) {
+ // for diagonal bidder the only normal point has already been added
+ // the other 2 candidates are diagonal items only, get from the heap
+ // with prices
+
+ if (not best_diagonal_items_computed_) {
+ recompute_top_diag_items();
+ }
+
+ best_diagonal_item_idx = top_diag_indices_[top_diag_counter_];
+ increment_top_diag_counter();
+
+ if ( proj_item_value < best_diagonal_item_value_) {
+ best_item_idx = proj_item_idx;
+ best_item_value = proj_item_value;
+ second_best_item_value = best_diagonal_item_value_;
+ second_best_item_idx = best_diagonal_item_idx;
+ } else if (proj_item_value < second_best_diagonal_item_value_) {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value_;
+ second_best_item_value = proj_item_value;
+ second_best_item_idx = proj_item_idx;
+ } else {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value_;
+ second_best_item_value = second_best_diagonal_item_value_;
+ second_best_item_idx = second_best_diagonal_item_idx_;
+ }
+ } else {
+ // for normal bidder get 2 best items among non-diagonal points from
+ // kdtree_
+ DnnPoint bidder_dnn;
+ bidder_dnn[0] = bidder.getRealX();
+ bidder_dnn[1] = bidder.getRealY();
+ auto two_best_items = kdtree_->findK(bidder_dnn, 2);
+ size_t best_normal_item_idx { two_best_items[0].p->id() };
+ Real best_normal_item_value { two_best_items[0].d };
+ // if there is only one off-diagonal point in the second diagram,
+ // kd-tree will not return the second candidate.
+ // Set its value to inf, so it will always lose to the value of the projection
+ Real second_best_normal_item_value { two_best_items.size() == 1 ? std::numeric_limits<Real>::max() : two_best_items[1].d };
+
+ if ( proj_item_value < best_normal_item_value) {
+ best_item_idx = proj_item_idx;
+ best_item_value = proj_item_value;
+ second_best_item_value = best_normal_item_value;
+ } else if (proj_item_value < second_best_normal_item_value) {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = proj_item_value;
+ } else {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = second_best_normal_item_value;
+ }
+ }
+
+ IdxValPair<Real> result;
+
+ assert( second_best_item_value >= best_item_value );
+
+ result.first = best_item_idx;
+ result.second = ( second_best_item_value - best_item_value ) + this->prices[best_item_idx] + this->epsilon;
+
+#ifdef DEBUG_KDTREE_RESTR_ORACLE
+ auto db = get_optimal_bid_debug(bidder_idx);
+ assert(fabs(db.best_item_value - best_item_value) < 0.000001);
+ if (fabs(db.second_best_item_value - second_best_item_value) >= 0.000001) {
+ console_logger->debug("Bidder_idx = {0}, best_item_idx = {1}, true_best_item_idx = {2}", bidder_idx, best_item_idx, db.best_item_idx);
+ console_logger->debug("second_best_item_idx = {0}, true second_best_item_idx = {1}", second_best_item_idx, db.second_best_item_idx);
+ console_logger->debug("second_best_value = {0}, true second_best_item_value = {1}", second_best_item_value, db.second_best_item_value);
+ console_logger->debug("prices = {0}", format_container_to_log(this->prices));
+ console_logger->debug("top_diag_indices_ = {0}", format_container_to_log(top_diag_indices_));
+ console_logger->debug("second_best_diagonal_item_value_ = {0}", second_best_diagonal_item_value_);
+ }
+ assert(fabs(db.second_best_item_value - second_best_item_value) < 0.000001);
+ //std::cout << "bid OK" << std::endl;
+#endif
+
+ return result;
+}
+/*
+a_{ij} = d_{ij}
+value_{ij} = a_{ij} + price_j
+*/
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::recompute_second_best_diag()
+{
+
+ console_logger->debug("Enter recompute_second_best_diag");
+
+ if (top_diag_indices_.size() > 1) {
+ second_best_diagonal_item_value_ = best_diagonal_item_value_;
+ second_best_diagonal_item_idx_ = top_diag_indices_[0];
+ } else {
+ if (diag_items_heap_.size() == 1) {
+ second_best_diagonal_item_value_ = std::numeric_limits<Real>::max();
+ second_best_diagonal_item_idx_ = k_invalid_index;
+ } else {
+ auto diag_iter = diag_items_heap_.ordered_begin();
+ ++diag_iter;
+ second_best_diagonal_item_value_ = diag_iter->second;
+ second_best_diagonal_item_idx_ = diag_iter->first;
+ }
+ }
+
+ console_logger->debug("Exit recompute_second_best_diag, second_best_diagonal_item_value_ = {0}, second_best_diagonal_item_idx_ = {1}", second_best_diagonal_item_value_, second_best_diagonal_item_idx_);
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::set_price(IdxType item_idx,
+ Real new_price,
+ const bool update_diag)
+{
+
+ console_logger->debug("Enter set_price, item_idx = {0}, new_price = {1}, old price = {2}, update_diag = {3}", item_idx, new_price, this->prices[item_idx], update_diag);
+
+ assert(this->prices.size() == this->items.size());
+ assert( 0 < diag_heap_handles_.size() and diag_heap_handles_.size() <= this->items.size());
+ // adjust_prices decreases prices,
+ // also this variable must be true in reverse phases of FR-auction
+ bool item_goes_down = new_price > this->prices[item_idx];
+
+ this->prices[item_idx] = new_price;
+ if ( this->items[item_idx].is_normal() ) {
+ assert(0 <= item_idx and item_idx < static_cast<IdxType>(kdtree_items_.size()));
+ assert(0 <= kdtree_items_[item_idx] and kdtree_items_[item_idx] < dnn_point_handles_.size());
+ kdtree_->change_weight( dnn_point_handles_[kdtree_items_[item_idx]], new_price);
+ } else {
+ assert(diag_heap_handles_.size() > heap_handles_indices_.at(item_idx));
+ if (item_goes_down) {
+ diag_items_heap_.decrease(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ } else {
+ diag_items_heap_.increase(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ }
+ if (update_diag) {
+ // Update top_diag_indices_ only if necessary:
+ // normal bidders take their projections, which might not be on top
+ // also, set_price is called by adjust_prices, and we may have already
+ // removed the item from top_diag
+ if (is_in_top_diag_indices(item_idx)) {
+ remove_top_diag_index(item_idx);
+ }
+
+ if (item_idx == (IdxType)second_best_diagonal_item_idx_) {
+ recompute_second_best_diag();
+ }
+ }
+ }
+
+ console_logger->debug("Exit set_price, item_idx = {0}, new_price = {1}", item_idx, new_price);
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::adjust_prices(Real delta)
+{
+ //console_logger->debug("Enter adjust_prices, delta = {0}", delta);
+ //std::cerr << *this << std::endl;
+
+ if (delta == 0.0)
+ return;
+
+ for(auto& p : this->prices) {
+ p -= delta;
+ }
+
+ kdtree_->adjust_weights(delta);
+
+ bool price_goes_up = delta < 0;
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ if (this->items[item_idx].is_diagonal()) {
+ auto new_price = this->prices[item_idx];
+ if (price_goes_up) {
+ diag_items_heap_.decrease(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ } else {
+ diag_items_heap_.increase(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ }
+ }
+ }
+ best_diagonal_item_value_ -= delta;
+ second_best_diagonal_item_value_ -= delta;
+
+ //std::cerr << *this << std::endl;
+ //console_logger->debug("Exit adjust_prices, delta = {0}", delta);
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::adjust_prices()
+{
+ auto pr_begin = this->prices.begin();
+ auto pr_end = this->prices.end();
+ Real min_price = *(std::min_element(pr_begin, pr_end));
+ adjust_prices(min_price);
+}
+
+template<class Real_, class PointContainer_>
+size_t AuctionOracleKDTreeRestricted<Real_, PointContainer_>::get_heap_top_size() const
+{
+ return top_diag_indices_.size();
+}
+
+template<class Real_, class PointContainer_>
+std::pair<Real_, Real_> AuctionOracleKDTreeRestricted<Real_, PointContainer_>::get_minmax_price() const
+{
+ auto r = std::minmax_element(this->prices.begin(), this->prices.end());
+ return std::make_pair(*r.first, *r.second);
+}
+
+
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreeRestricted<Real_, PointContainer_>::~AuctionOracleKDTreeRestricted()
+{
+ delete kdtree_;
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeRestricted<Real_, PointContainer_>::sanity_check()
+{
+#ifdef DEBUG_KDTREE_RESTR_ORACLE
+ if (best_diagonal_items_computed_) {
+ std::vector<Real> diag_items_price_vec;
+ diag_items_price_vec.reserve(this->items.size());
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ if (this->items.at(item_idx).is_diagonal()) {
+ diag_items_price_vec.push_back(this->prices.at(item_idx));
+ } else {
+ diag_items_price_vec.push_back(std::numeric_limits<Real>::max());
+ }
+ }
+
+ auto best_iter = std::min_element(diag_items_price_vec.begin(), diag_items_price_vec.end());
+ assert(best_iter != diag_items_price_vec.end());
+ Real true_best_diag_value = *best_iter;
+ size_t true_best_diag_idx = best_iter - diag_items_price_vec.begin();
+ assert(true_best_diag_value != std::numeric_limits<Real>::max());
+
+ Real true_second_best_diag_value = std::numeric_limits<Real>::max();
+ size_t true_second_best_diag_idx = k_invalid_index;
+ for(size_t item_idx = 0; item_idx < diag_items_price_vec.size(); ++item_idx) {
+ if (this->items.at(item_idx).is_normal()) {
+ assert(top_diag_lookup_.at(item_idx) == k_invalid_index);
+ continue;
+ }
+
+ auto i_iter = std::find(top_diag_indices_.begin(), top_diag_indices_.end(), item_idx);
+ if (diag_items_price_vec.at(item_idx) == true_best_diag_value) {
+ assert(i_iter != top_diag_indices_.end());
+ assert(top_diag_lookup_.at(item_idx) == i_iter - top_diag_indices_.begin());
+ } else {
+ assert(top_diag_lookup_.at(item_idx) == k_invalid_index);
+ assert(i_iter == top_diag_indices_.end());
+ }
+
+ if (item_idx == true_best_diag_idx) {
+ continue;
+ }
+ if (diag_items_price_vec.at(item_idx) < true_second_best_diag_value) {
+ true_second_best_diag_value = diag_items_price_vec.at(item_idx);
+ true_second_best_diag_idx = item_idx;
+ }
+ }
+
+ if (true_best_diag_value != best_diagonal_item_value_) {
+ console_logger->debug("best_diagonal_item_value_ = {0}, true value = {1}", best_diagonal_item_value_, true_best_diag_value);
+ std::cerr << *this;
+ //console_logger->debug("{0}", *this);
+ }
+
+ assert(true_best_diag_value == best_diagonal_item_value_);
+
+ assert(true_second_best_diag_idx != k_invalid_index);
+
+ if (true_second_best_diag_value != second_best_diagonal_item_value_) {
+ console_logger->debug("second_best_diagonal_item_value_ = {0}, true value = {1}", second_best_diagonal_item_value_, true_second_best_diag_value);
+ //console_logger->debug("{0}", *this);
+ }
+
+ assert(true_second_best_diag_value == second_best_diagonal_item_value_);
+ }
+#endif
+}
+
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.h b/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.h
new file mode 100644
index 0000000..9192993
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.h
@@ -0,0 +1,219 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_KDTREE_SINGLE_DIAG_H
+#define AUCTION_ORACLE_KDTREE_SINGLE_DIAG_H
+
+
+#include <map>
+#include <memory>
+#include <set>
+#include <list>
+#include <ostream>
+
+#include "basic_defs_ws.h"
+#include "dnn/geometry/euclidean-fixed.h"
+#include "dnn/local/kd-tree.h"
+
+namespace hera {
+namespace ws {
+
+
+template <class Real = double>
+struct ItemSlice;
+
+template <class Real = double>
+bool operator<(const ItemSlice<Real>& s_1, const ItemSlice<Real>& s_2);
+
+template <class Real = double>
+bool operator>(const ItemSlice<Real>& s_1, const ItemSlice<Real>& s_2);
+
+template <class Real>
+struct ItemSlice {
+public:
+ using RealType = Real;
+
+ size_t item_idx;
+ Real loss;
+ ItemSlice(size_t _item_idx, const Real _loss);
+
+ void set_loss(const Real new_loss) { loss = new_loss; }
+ void adjust_loss(const Real delta) { loss -= delta; }
+
+ friend bool operator< <>(const ItemSlice<Real>&, const ItemSlice<Real>&);
+ friend bool operator> <>(const ItemSlice<Real>&, const ItemSlice<Real>&);
+
+private:
+};
+
+
+template <class Real = double>
+class LossesHeap {
+public:
+ using ItemSliceR = ItemSlice<Real>;
+ using KeeperTypeR = std::set<ItemSliceR, std::less<ItemSliceR> >;
+ using IterTypeR = typename KeeperTypeR::iterator;
+
+ LossesHeap() {}
+ LossesHeap(const std::vector<ItemSliceR>&);
+ void adjust_prices(const Real delta); // subtract delta from all values
+ ItemSliceR get_best_slice() const;
+ ItemSliceR get_second_best_slice() const;
+
+ template<typename ...Args>
+ decltype(auto) emplace(Args&&... args)
+ {
+ return keeper.emplace(std::forward<Args>(args)...);
+ }
+
+
+ IterTypeR begin() { return keeper.begin(); }
+ IterTypeR end() { return keeper.end(); }
+ void erase(IterTypeR iter) { assert(iter != keeper.end()); keeper.erase(iter); }
+ decltype(auto) insert(const ItemSliceR& item) { return keeper.insert(item); }
+ size_t size() const { return keeper.size(); }
+ bool empty() const { return keeper.empty(); }
+//private:
+ std::set<ItemSliceR, std::less<ItemSliceR> > keeper;
+};
+
+template <class Real>
+struct DiagonalBid {
+ DiagonalBid() {}
+
+ std::vector<size_t> assigned_normal_items;
+ std::vector<Real> assigned_normal_items_bid_values;
+
+ std::vector<size_t> best_item_indices;
+ std::vector<Real> bid_values;
+
+ // common bid value for diag-diag
+ Real diag_to_diag_value { 0.0 };
+
+ // analogous to second best item value; denoted by w in Bertsekas's paper on auction for transportation problem
+ Real almost_best_value { 0.0 };
+
+ // how many points to get from unassigned diagonal chunk
+ int num_from_unassigned_diag { 0 };
+};
+
+template <class Real_ = double, class PointContainer_ = std::vector<DiagramPoint<Real_>>>
+struct AuctionOracleKDTreeSingleDiag : AuctionOracleBase<Real_, PointContainer_> {
+
+ using PointContainer = PointContainer_;
+ using Real = Real_;
+
+ using DnnPoint = dnn::Point<2, Real>;
+ using DnnTraits = dnn::PointTraits<DnnPoint>;
+
+ using IdxValPairR = typename ws::IdxValPair<Real>;
+ using ItemSliceR = typename ws::ItemSlice<Real>;
+ using LossesHeapR = typename ws::LossesHeap<Real>;
+ using LossesHeapIterR = typename ws::LossesHeap<Real>::IterTypeR;
+ using DiagramPointR = typename ws::DiagramPoint<Real>;
+ using DiagonalBidR = typename ws::DiagonalBid<Real>;
+
+ AuctionOracleKDTreeSingleDiag(const PointContainer& bidders,
+ const PointContainer& items,
+ const AuctionParams<Real>& params);
+ ~AuctionOracleKDTreeSingleDiag();
+ // data members
+ // temporarily make everything public
+ Real max_val_;
+ size_t num_diag_items_;
+ size_t num_normal_items_;
+ size_t num_diag_bidders_;
+ size_t num_normal_bidders_;
+ dnn::KDTree<DnnTraits>* kdtree_;
+ std::vector<DnnPoint> dnn_points_;
+ std::vector<DnnPoint*> dnn_point_handles_;
+ std::vector<size_t> kdtree__items_;
+
+ // this heap is used by off-diagonal bidders to get the cheapest diagonal
+ // item; index in the IdxVal is a valid item index in the vector of items
+ // items in diag_assigned_to_diag_slice_ and in diag_unassigned_slice_
+ // are not stored in this heap
+ LossesHeapR diag_items_heap_;
+ // vector of iterators; if item_idx is in diag_assigned_to_diag_slice_ or
+ // in diag_unassigned_slice_, then diag_items_heap__iters_[item_idx] ==
+ // diag_items_heap_.end()
+ std::vector<LossesHeapIterR> diag_items_heap__iters_;
+
+
+ // this heap is used by _the_ diagonal bidder to get the cheapest items
+ // * value in IdxValPair is price + persistence (i.e., price for
+ // diagonal items)
+ // * index in IdxValPair is a valid item index in the vector of items
+ // items in diag_assigned_to_diag_slice_ and in diag_unassigned_slice_
+ // are not stored in this heap
+ LossesHeapR all_items_heap_;
+ std::vector<LossesHeapIterR> all_items_heap__iters_;
+
+ std::unordered_set<size_t> diag_assigned_to_diag_slice_;
+ std::unordered_set<size_t> diag_unassigned_slice_;
+
+
+ std::unordered_set<size_t> normal_items_assigned_to_diag_;
+
+ Real diag_to_diag_price_;
+ Real diag_unassigned_price_;
+
+ // methods
+ Real get_price(const size_t item_idx) const override;
+ void set_price(const size_t item_idx,
+ const Real new_price,
+ const bool item_is_diagonal,
+ const bool bidder_is_diagonal,
+ const OwnerType old_owner_type);
+
+ IdxValPair<Real> get_optimal_bid(const IdxType bidder_idx);
+
+ DiagonalBidR get_optimal_bids_for_diagonal(int unassigned_mass);
+ void process_unassigned_diagonal(const int unassigned_mass,
+ int& accumulated_mass,
+ bool& saw_diagonal_slice,
+ int& num_classes,
+ Real& w,
+ DiagonalBidR& result,
+ bool& found_w);
+
+ void adjust_prices();
+ void flush_assignment();
+ void sanity_check();
+
+ bool is_item_diagonal(const size_t item_idx) const;
+ bool is_item_normal(const size_t item_idx) const { return not is_item_diagonal(item_idx); }
+
+};
+
+} // ws
+} // hera
+
+#include "auction_oracle_kdtree_single_diag.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.hpp b/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.hpp
new file mode 100644
index 0000000..42677ab
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_kdtree_single_diag.hpp
@@ -0,0 +1,717 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+#ifndef AUCTION_ORACLE_KDTREE_RESTRICTED_SINGLE_DIAG_HPP
+#define AUCTION_ORACLE_KDTREE_RESTRICTED_SINGLE_DIAG_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_oracle.h"
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace hera {
+namespace ws {
+
+// *****************************
+// AuctionOracleKDTreeSingleDiag
+// *****************************
+
+
+
+template <class Real>
+ItemSlice<Real>::ItemSlice(size_t _item_idx,
+ const Real _loss) :
+ item_idx(_item_idx),
+ loss(_loss)
+{
+}
+
+
+template <class Real>
+bool operator<(const ItemSlice<Real>& s_1, const ItemSlice<Real>& s_2)
+{
+ return s_1.loss < s_2.loss
+ or (s_1.loss == s_2.loss and s_1.item_idx < s_2.item_idx);
+}
+
+template <class Real>
+bool operator>(const ItemSlice<Real>& s_1, const ItemSlice<Real>& s_2)
+{
+ return s_1.loss > s_2.loss
+ or (s_1.loss == s_2.loss and s_1.item_idx > s_2.item_idx);
+}
+
+template<class Real>
+std::ostream& operator<<(std::ostream& s, const ItemSlice<Real>& x)
+{
+ s << "(" << x.item_idx << ", " << x.loss << ")";
+ return s;
+}
+
+// *****************************
+// LossesHeap
+// *****************************
+
+
+template <class Real>
+void LossesHeap<Real>::adjust_prices(const Real delta)
+{
+ throw std::runtime_error("not implemented");
+}
+
+template <class Real>
+typename LossesHeap<Real>::ItemSliceR LossesHeap<Real>::get_best_slice() const
+{
+ return *(keeper.begin());
+}
+
+template <class Real>
+typename LossesHeap<Real>::ItemSliceR LossesHeap<Real>::get_second_best_slice() const
+{
+ if (keeper.size() > 1) {
+ return *std::next(keeper.begin());
+ } else {
+ return ItemSliceR(k_invalid_index, std::numeric_limits<Real>::max());
+ }
+}
+
+template<class Real>
+std::ostream& operator<<(std::ostream& s, const LossesHeap<Real>& x)
+{
+ s << "Heap[ ";
+ for(auto iter = x.keeper.begin(); iter != x.keeper.end(); ++iter) {
+ s << *iter << "\n";
+ }
+ s << "]\n";
+ return s;
+}
+
+// *****************************
+// DiagonalBid
+// *****************************
+
+template <class Real>
+std::ostream& operator<<(std::ostream& s, const DiagonalBid<Real>& b)
+{
+ s << "DiagonalBid { num_from_unassigned_diag = " << b.num_from_unassigned_diag;
+ s << ", diag_to_diag_value = " << b.diag_to_diag_value;
+ s << ", almost_best_value = " << b.almost_best_value;
+ s << ",\nbest_item_indices = [";
+ for(const auto i : b.best_item_indices) {
+ s << i << ", ";
+ }
+ s << "]\n";
+
+ s << ",\nbid_values= [";
+ for(const auto v : b.bid_values) {
+ s << v << ", ";
+ }
+ s << "]\n";
+
+ s << ",\nassigned_normal_items= [";
+ for(const auto i : b.assigned_normal_items) {
+ s << i << ", ";
+ }
+ s << "]\n";
+
+ s << ",\nassigned_normal_items_bid_values = [";
+ for(const auto v : b.assigned_normal_items_bid_values) {
+ s << v << ", ";
+ }
+ s << "]\n";
+
+ return s;
+}
+
+// *****************************
+// AuctionOracleKDTreeSingleDiag
+// *****************************
+
+template<class Real_, class PointContainer_>
+std::ostream& operator<<(std::ostream& s, const AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>& x)
+{
+ s << "oracle: bidders" << std::endl;
+ for(const auto& p : x.bidders) {
+ s << p << "\n";
+ }
+ s << "items:";
+
+ for(const auto& p : x.items) {
+ s << p << "\n";
+ }
+
+ s << "diag_unassigned_slice_.size = " << x.diag_unassigned_slice_.size() << ", ";
+ s << "diag_unassigned_price_ = " << x.diag_unassigned_price_ << ", ";
+ s << "diag unassigned slice [";
+
+ for(const auto& i : x.diag_unassigned_slice_) {
+ s << i << ", ";
+ }
+ s << "]\n ";
+
+ s << "diag_assigned_to_diag_slice_.size = " << x.diag_assigned_to_diag_slice_.size() << ", ";
+ s << "diag_assigned_to_diag_price = " << x.diag_to_diag_price_ << "\n";
+ s << "diag_assigned_to_diag_slice_ [";
+
+ for(const auto& i : x.diag_assigned_to_diag_slice_) {
+ s << i << ", ";
+ }
+ s << "]\n ";
+
+ s << "diag_items_heap_.size = " << x.diag_items_heap_.size() << "\n ";
+ s << x.diag_items_heap_;
+
+ s << "all_items_heap_.size = " << x.all_items_heap_.size() << "\n ";
+ s << x.all_items_heap_;
+
+ s << "epsilon = " << x.epsilon << std::endl;
+
+ return s;
+}
+
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::AuctionOracleKDTreeSingleDiag(const PointContainer_& _bidders,
+ const PointContainer_& _items,
+ const AuctionParams<Real>& params) :
+ AuctionOracleBase<Real_, PointContainer_>(_bidders, _items, params),
+ max_val_(std::pow( 3.0 * getFurthestDistance3Approx<>(_bidders, _items, params.internal_p), params.wasserstein_power)),
+ num_diag_items_(0),
+ kdtree__items_(_items.size(), k_invalid_index)
+{
+ size_t dnn_item_idx { 0 };
+ dnn_points_.clear();
+
+ all_items_heap__iters_.clear();
+ all_items_heap__iters_.reserve( 4 * _items.size() / 7);
+
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ const auto& item = this->items[item_idx];
+ if (item.is_normal() ) {
+ // store normal items in kd-tree
+ kdtree__items_[item_idx] = dnn_item_idx;
+ // index of items is id of dnn-point
+ DnnPoint p(item_idx);
+ p[0] = item.x;
+ p[1] = item.y;
+ dnn_points_.push_back(p);
+ assert(dnn_item_idx == dnn_points_.size() - 1);
+ dnn_item_idx++;
+ // add slice to vector
+ auto ins_res = all_items_heap_.emplace(item_idx, this->get_value_for_diagonal_bidder(item_idx));
+ all_items_heap__iters_.push_back(ins_res.first);
+ assert(ins_res.second);
+ } else {
+ // all diagonal items are initially in the unassigned slice
+ diag_unassigned_slice_.insert(item_idx);
+ all_items_heap__iters_.push_back(all_items_heap_.end());
+ diag_items_heap__iters_.push_back(diag_items_heap_.end());
+ ++num_diag_items_;
+ }
+ }
+
+ num_normal_items_ = this->items.size() - num_diag_items_;
+ num_normal_bidders_ = num_diag_items_;
+ num_diag_bidders_ = this->bidders.size() - num_normal_bidders_;
+
+ assert(dnn_points_.size() < _items.size() );
+ for(size_t i = 0; i < dnn_points_.size(); ++i) {
+ dnn_point_handles_.push_back(&dnn_points_[i]);
+ }
+
+ DnnTraits traits;
+ traits.internal_p = params.internal_p;
+
+ kdtree_ = new dnn::KDTree<DnnTraits>(traits, dnn_point_handles_, this->wasserstein_power);
+
+ sanity_check();
+
+ //std::cout << "IN CTOR: " << *this << std::endl;
+
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::process_unassigned_diagonal(const int unassigned_mass, int& accumulated_mass, bool& saw_diagonal_slice, int& num_classes, Real& w, DiagonalBidR& result, bool& found_w)
+{
+ result.num_from_unassigned_diag = std::min(static_cast<int>(diag_unassigned_slice_.size()), static_cast<int>(unassigned_mass - accumulated_mass));
+ if (not saw_diagonal_slice) {
+ saw_diagonal_slice = true;
+ ++num_classes;
+ }
+
+ accumulated_mass += result.num_from_unassigned_diag;
+ //std::cout << "got mass from diagunassigned_slice, result.num_from_unassigned_diag = " << result.num_from_unassigned_diag << ", accumulated_mass = " << accumulated_mass << std::endl;
+
+ if (static_cast<int>(diag_unassigned_slice_.size()) > result.num_from_unassigned_diag and num_classes >= 2) {
+ found_w = true;
+ w = diag_unassigned_price_;
+ //std::cout << "w found from diag_unassigned_slice_, too, w = " << w << std::endl;
+ result.almost_best_value = w;
+ }
+
+}
+
+
+template<class Real_, class PointContainer_>
+typename AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::DiagonalBidR AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::get_optimal_bids_for_diagonal(int unassigned_mass)
+{
+ sanity_check();
+
+ assert(unassigned_mass == static_cast<decltype(unassigned_mass)>(num_diag_bidders_)
+ - static_cast<decltype(unassigned_mass)>(normal_items_assigned_to_diag_.size())
+ - static_cast<decltype(unassigned_mass)>(diag_assigned_to_diag_slice_.size()) );
+ assert(unassigned_mass > 0);
+
+ DiagonalBidR result;
+
+
+ // number of similarity classes already assigned to diagonal bidder
+ // each normal point is a single class
+ // all diagonal points are in one class
+ int num_classes = normal_items_assigned_to_diag_.size() + ( diag_assigned_to_diag_slice_.empty() ? 0 : 1 );
+ bool saw_diagonal_slice = not diag_assigned_to_diag_slice_.empty();
+ bool found_w = false;
+
+ //std::cout << "Enter get_optimal_bids_for_diagonal, unassigned_mass = " << unassigned_mass <<", num_classes = " << num_classes << ", saw_diagonal_slice = " << std::boolalpha << saw_diagonal_slice << std::endl;
+
+ decltype(unassigned_mass) accumulated_mass = 0;
+
+
+ Real w { std::numeric_limits<Real>::max() };
+ bool unassigned_not_processed = not diag_unassigned_slice_.empty();
+
+ for(auto slice_iter = all_items_heap_.begin(); slice_iter != all_items_heap_.end(); ++slice_iter) {
+
+ auto slice = *slice_iter;
+
+ if ( is_item_normal(slice.item_idx) and normal_items_assigned_to_diag_.count(slice.item_idx) == 1) {
+ //std::cout << __LINE__ << ": skipping slice " << slice << std::endl;
+ // this item is already assigned to diagonal bidder, skip
+ continue;
+ }
+
+ if (unassigned_not_processed and slice.loss >= diag_unassigned_price_) {
+ // diag_unassigned slice is better,
+ // process it first
+ process_unassigned_diagonal(unassigned_mass, accumulated_mass, saw_diagonal_slice, num_classes, w, result, found_w);
+ unassigned_not_processed = false;
+ if (accumulated_mass >= unassigned_mass and found_w) {
+ break;
+ }
+ }
+
+
+ if (is_item_normal(slice.item_idx)) {
+ // all off-diagonal items are distinct
+ ++num_classes;
+ } else if (not saw_diagonal_slice) {
+ saw_diagonal_slice = true;
+ ++num_classes;
+ }
+
+ if (accumulated_mass < unassigned_mass) {
+ //std::cout << __LINE__ << ": added slice to best items " << slice << std::endl;
+ result.best_item_indices.push_back(slice.item_idx);
+ }
+
+ if (accumulated_mass >= unassigned_mass and num_classes >= 2) {
+ //std::cout << "Found w, slice = " << slice << std::endl;
+ w = slice.loss;
+ found_w = true;
+ result.almost_best_value = w;
+ break;
+ }
+
+ // all items in all_items heap have mass 1
+ ++accumulated_mass;
+ //std::cout << "accumulated_mass = " << accumulated_mass << std::endl;
+
+ }
+
+ if (unassigned_not_processed and (accumulated_mass < unassigned_mass or not found_w)) {
+ process_unassigned_diagonal(unassigned_mass, accumulated_mass, saw_diagonal_slice, num_classes, w, result, found_w);
+ }
+
+ assert(found_w);
+
+ //if (w == std::numeric_limits<Real>::max()) { std::cout << "HERE: " << *this << std::endl; }
+ assert(w != std::numeric_limits<Real>::max());
+
+ result.assigned_normal_items.clear();
+ result.assigned_normal_items_bid_values.clear();
+
+ result.assigned_normal_items.reserve(normal_items_assigned_to_diag_.size());
+ result.assigned_normal_items_bid_values.reserve(normal_items_assigned_to_diag_.size());
+
+ // add already assigned normal items and their new prices to bid
+ for(const auto item_idx : normal_items_assigned_to_diag_) {
+ assert( all_items_heap__iters_[item_idx] != all_items_heap_.end() );
+ assert( is_item_normal(item_idx) );
+
+ result.assigned_normal_items.push_back(item_idx);
+ Real bid_value = w - this->get_cost_for_diagonal_bidder(item_idx) + this->epsilon;
+ //if ( bid_value <= this->get_price(item_idx) ) {
+ //std::cout << bid_value << " vs price " << this->get_price(item_idx) << std::endl;
+ //std::cout << *this << std::endl;
+ //}
+ assert( bid_value >= this->get_price(item_idx) );
+ result.assigned_normal_items_bid_values.push_back(bid_value);
+ }
+
+ // calculate bid values
+ // diag-to-diag items all have the same bid value
+ if (saw_diagonal_slice) {
+ result.diag_to_diag_value = w + this->epsilon;
+ } else {
+ result.diag_to_diag_value = std::numeric_limits<Real>::max();
+ }
+
+ result.bid_values.reserve(result.best_item_indices.size());
+ for(const auto item_idx : result.best_item_indices) {
+ Real bid_value = w - this->get_cost_for_diagonal_bidder(item_idx) + this->epsilon;
+ result.bid_values.push_back(bid_value);
+ }
+
+ return result;
+}
+
+
+template<class Real_, class PointContainer_>
+IdxValPair<Real_> AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::get_optimal_bid(IdxType bidder_idx)
+{
+ //std::cout << "enter get_optimal_bid" << std::endl;
+ sanity_check();
+
+ auto bidder = this->bidders[bidder_idx];
+
+ size_t best_item_idx;
+ Real best_item_price;
+ Real best_item_value;
+ Real second_best_item_value;
+
+ // this function is for normal bidders only
+ assert(bidder.is_normal());
+
+
+ // get 2 best items among non-diagonal points from kdtree_
+ DnnPoint bidder_dnn;
+ bidder_dnn[0] = bidder.getRealX();
+ bidder_dnn[1] = bidder.getRealY();
+ auto two_best_items = kdtree_->findK(bidder_dnn, 2);
+ size_t best_normal_item_idx { two_best_items[0].p->id() };
+ Real best_normal_item_value { two_best_items[0].d };
+ // if there is only one off-diagonal point in the second diagram,
+ // kd-tree will not return the second candidate.
+ // Set its price to inf, so it will always lose to the price of the projection
+ Real second_best_normal_item_value { two_best_items.size() == 1 ? std::numeric_limits<Real>::max() : two_best_items[1].d };
+
+ size_t best_diag_item_idx;
+ Real best_diag_value;
+ Real best_diag_price;
+
+ {
+ Real diag_edge_cost = std::pow(bidder.persistence_lp(this->internal_p), this->wasserstein_power);
+ auto best_diag_price_in_heap = diag_items_heap_.empty() ? std::numeric_limits<Real>::max() : diag_items_heap_.get_best_slice().loss;
+ auto best_diag_idx_in_heap = diag_items_heap_.empty() ? k_invalid_index : diag_items_heap_.get_best_slice().item_idx;
+ // if unassigned_diag_slice is empty, its price is max,
+ // same for diag-diag assigned slice, so the ifs below will work
+
+ if (best_diag_price_in_heap <= diag_to_diag_price_ and best_diag_price_in_heap <= diag_unassigned_price_) {
+ best_diag_item_idx = best_diag_idx_in_heap;
+ best_diag_value = diag_edge_cost + best_diag_price_in_heap;
+ best_diag_price = best_diag_price_in_heap;
+ } else if (diag_to_diag_price_ < best_diag_price_in_heap and diag_to_diag_price_ < diag_unassigned_price_) {
+ best_diag_item_idx = *diag_assigned_to_diag_slice_.begin();
+ best_diag_value = diag_edge_cost + diag_to_diag_price_;
+ best_diag_price = diag_to_diag_price_;
+ } else {
+ best_diag_item_idx = *diag_unassigned_slice_.begin();
+ best_diag_value = diag_edge_cost + diag_unassigned_price_;
+ best_diag_price = diag_unassigned_price_;
+ }
+
+ }
+
+ if ( best_diag_value < best_normal_item_value) {
+ best_item_idx = best_diag_item_idx;
+ best_item_price = best_diag_price;
+ best_item_value = best_diag_value;
+ second_best_item_value = best_normal_item_value;
+ } else if (best_diag_value < second_best_normal_item_value) {
+ best_item_idx = best_normal_item_idx;
+ best_item_price = this->get_price(best_item_idx);
+ best_item_value = best_normal_item_value;
+ second_best_item_value = best_diag_value;
+ } else {
+ best_item_idx = best_normal_item_idx;
+ best_item_price = this->get_price(best_item_idx);
+ best_item_value = best_normal_item_value;
+ second_best_item_value = second_best_normal_item_value;
+ }
+
+ IdxValPair<Real> result;
+
+ result.first = best_item_idx;
+ result.second = ( second_best_item_value - best_item_value ) + best_item_price + this->epsilon;
+
+ //std::cout << "bidder_idx = " << bidder_idx << ", best_item_idx = " << best_item_idx << ", best_item_value = " << best_item_value << ", second_best_item_value = " << second_best_item_value << ", eps = " << this->epsilon << std::endl;
+ assert( second_best_item_value >= best_item_value );
+ //assert( best_item_price == this->get_price(best_item_idx) );
+ assert(result.second >= best_item_price);
+ sanity_check();
+
+ return result;
+}
+/*
+a_{ij} = d_{ij}
+price_{ij} = a_{ij} + price_j
+*/
+
+
+
+//template<class Real_, class PointContainer_>
+//std::vector<IdxValPairR> AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::increase_price_of_assigned_to_diag(WHAT)
+//{
+ //WHAT;
+//}
+//
+
+template<class Real_, class PointContainer_>
+Real_ AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::get_price(const size_t item_idx) const
+{
+ if (is_item_diagonal(item_idx)) {
+ if (diag_assigned_to_diag_slice_.count(item_idx) == 1) {
+ return diag_to_diag_price_;
+ } else if (diag_unassigned_slice_.count(item_idx) == 1) {
+ return diag_unassigned_price_;
+ }
+ }
+ return this-> prices[item_idx];
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::set_price(const size_t item_idx,
+ const Real new_price,
+ const bool item_is_diagonal,
+ const bool bidder_is_diagonal,
+ const OwnerType old_owner_type)
+{
+
+ //std::cout << std::boolalpha << "enter set_price, item_idx = " << item_idx << ", new_price = " << new_price << ", old price = " << this->get_price(item_idx);
+ //std::cout << ", item_is_diagonal = " << item_is_diagonal << ", bidder_is_diagonal = " << bidder_is_diagonal << ", old_owner_type = " << old_owner_type << std::endl;
+
+ bool item_is_normal = not item_is_diagonal;
+ bool bidder_is_normal = not bidder_is_diagonal;
+
+ assert( new_price >= this->get_price(item_idx) );
+
+ // update vector prices
+ if (item_is_normal or bidder_is_normal) {
+ this->prices[item_idx] = new_price;
+ }
+
+ // update kdtree_
+ if (item_is_normal) {
+ assert(0 <= item_idx and item_idx < kdtree__items_.size());
+ assert(0 <= kdtree__items_[item_idx] and kdtree__items_[item_idx] < dnn_point_handles_.size());
+ kdtree_->change_weight( dnn_point_handles_[kdtree__items_[item_idx]], new_price);
+ }
+
+ // update all_items_heap_
+ if (bidder_is_diagonal and item_is_diagonal) {
+ // remove slice (item is buried in diag_assigned_to_diag_slice_)
+ assert(old_owner_type != OwnerType::k_diagonal);
+ auto iter = all_items_heap__iters_[item_idx];
+ assert(iter != all_items_heap_.end());
+ all_items_heap_.erase(iter);
+ all_items_heap__iters_[item_idx] = all_items_heap_.end();
+ } else {
+ auto iter = all_items_heap__iters_[item_idx];
+ if (iter != all_items_heap_.end()) {
+ // update existing element
+ ItemSliceR x = *iter;
+ x.set_loss( this->get_value_for_diagonal_bidder(item_idx) );
+ all_items_heap_.erase(iter);
+ auto ins_res = all_items_heap_.insert(x);
+ all_items_heap__iters_[item_idx] = ins_res.first;
+ assert(ins_res.second);
+ } else {
+ // insert new slice
+ // for diagonal items value = price
+ ItemSliceR x { item_idx, new_price };
+ auto ins_res = all_items_heap_.insert(x);
+ all_items_heap__iters_[item_idx] = ins_res.first;
+ assert(ins_res.second);
+ }
+ }
+
+ // update diag_items_heap_
+ if (item_is_diagonal and bidder_is_normal) {
+ // update existing element
+ auto iter = diag_items_heap__iters_[item_idx];
+ if (iter != diag_items_heap_.end()) {
+ ItemSliceR x = *iter;
+ x.set_loss( new_price );
+ diag_items_heap_.erase(iter);
+ auto ins_res = diag_items_heap_.insert(x);
+ diag_items_heap__iters_[item_idx] = ins_res.first;
+ assert(ins_res.second);
+ } else {
+ // insert new slice
+ // for diagonal items value = price
+ ItemSliceR x { item_idx, new_price };
+ auto ins_res = diag_items_heap_.insert(x);
+ diag_items_heap__iters_[item_idx] = ins_res.first;
+ assert(ins_res.second);
+ }
+ } else if (bidder_is_diagonal and item_is_diagonal ) {
+ // remove slice (item is buried in diag_assigned_to_diag_slice_)
+ assert(old_owner_type != OwnerType::k_diagonal);
+ auto iter = diag_items_heap__iters_[item_idx];
+ assert(iter != diag_items_heap_.end());
+ diag_items_heap_.erase(iter);
+ diag_items_heap__iters_[item_idx] = diag_items_heap_.end();
+ }
+
+ // update diag_unassigned_price_
+ if (item_is_diagonal and old_owner_type == OwnerType::k_none and diag_unassigned_slice_.empty()) {
+ diag_unassigned_price_ = std::numeric_limits<Real>::max();
+ }
+
+}
+
+
+template<class Real_, class PointContainer_>
+bool AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::is_item_diagonal(const size_t item_idx) const
+{
+ return item_idx < this->num_diag_items_;
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::flush_assignment()
+{
+ //std::cout << "enter oracle->flush_assignment" << std::endl;
+ sanity_check();
+
+ for(const auto item_idx : diag_assigned_to_diag_slice_) {
+ diag_unassigned_slice_.insert(item_idx);
+ }
+ diag_assigned_to_diag_slice_.clear();
+
+ // common price of diag-diag items becomes price of diag-unassigned-slice
+ // diag_to_diag_slice is now empty, set its price to max
+ // so that get_optimal_bid works correctly
+ diag_unassigned_price_ = diag_to_diag_price_;
+ diag_to_diag_price_ = std::numeric_limits<Real>::max();
+
+ normal_items_assigned_to_diag_.clear();
+
+ sanity_check();
+}
+
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::adjust_prices()
+{
+ return;
+
+ throw std::runtime_error("not implemented");
+ auto pr_begin = this->prices.begin();
+ auto pr_end = this->prices.end();
+
+ Real min_price = *(std::min_element(pr_begin, pr_end));
+
+ for(auto& p : this->prices) {
+ p -= min_price;
+ }
+
+ kdtree_->adjust_weights(min_price);
+ diag_items_heap_.adjust_prices(min_price);
+ all_items_heap_.adjust_prices(min_price);
+}
+
+
+template<class Real_, class PointContainer_>
+AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::~AuctionOracleKDTreeSingleDiag()
+{
+ delete kdtree_;
+}
+
+template<class Real_, class PointContainer_>
+void AuctionOracleKDTreeSingleDiag<Real_, PointContainer_>::sanity_check()
+{
+#ifdef DEBUG_AUCTION
+
+ //std::cout << "ORACLE CURRENT STATE IN SANITY CHECK" << *this << std::endl;
+
+ assert( diag_items_heap_.size() + diag_assigned_to_diag_slice_.size() + diag_unassigned_slice_.size() == num_diag_items_ );
+ assert( diag_items_heap__iters_.size() == num_diag_items_ );
+ for(size_t i = 0; i < num_diag_items_; ++i) {
+ if (diag_items_heap__iters_.at(i) != diag_items_heap_.end()) {
+ assert(diag_items_heap__iters_[i]->item_idx == i);
+ }
+ }
+
+ assert( all_items_heap_.size() + diag_assigned_to_diag_slice_.size() + diag_unassigned_slice_.size() == this->num_items_ );
+ assert( all_items_heap__iters_.size() == this->num_items_ );
+ for(size_t i = 0; i < this->num_items_; ++i) {
+ if (all_items_heap__iters_.at(i) != all_items_heap_.end()) {
+ assert(all_items_heap__iters_[i]->item_idx == i);
+ } else {
+ assert( i < num_diag_items_ );
+ }
+ }
+
+ for(size_t i = 0; i < num_diag_items_; ++i) {
+ int is_in_assigned_slice = diag_assigned_to_diag_slice_.count(i);
+ int is_in_unassigned_slice = diag_unassigned_slice_.count(i);
+ int is_in_heap = diag_items_heap__iters_[i] != diag_items_heap_.end();
+ assert( is_in_assigned_slice + is_in_unassigned_slice + is_in_heap == 1);
+ }
+
+ //assert((diag_assigned_to_diag_slice_.empty() and diag_to_diag_price_ == std::numeric_limits<Real>::max()) or (not diag_assigned_to_diag_slice_.empty() and diag_to_diag_price_ != std::numeric_limits<Real>::max()));
+ //assert((diag_unassigned_slice_.empty() and diag_unassigned_price_ == std::numeric_limits<Real>::max()) or (not diag_unassigned_slice_.empty() and diag_unassigned_price_ != std::numeric_limits<Real>::max()));
+
+ assert(diag_assigned_to_diag_slice_.empty() or diag_to_diag_price_ != std::numeric_limits<Real>::max());
+ assert(diag_unassigned_slice_.empty() or diag_unassigned_price_ != std::numeric_limits<Real>::max());
+#endif
+}
+
+
+} // ws
+} // hera
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_lazy_heap.h b/geom_matching/wasserstein/include/auction_oracle_lazy_heap.h
new file mode 100644
index 0000000..8b37421
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_lazy_heap.h
@@ -0,0 +1,191 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_LAZY_HEAP_H
+#define AUCTION_ORACLE_LAZY_HEAP_H
+
+
+#define USE_BOOST_HEAP
+
+#include <map>
+#include <memory>
+#include <set>
+#include <list>
+
+#ifdef USE_BOOST_HEAP
+#include <boost/heap/d_ary_heap.hpp>
+#endif
+
+#include "basic_defs_ws.h"
+
+namespace ws {
+
+template <typename T>
+struct CompPairsBySecondStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second < b.second;
+ }
+};
+
+
+template <typename T>
+struct CompPairsBySecondGreaterStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second > b.second;
+ }
+};
+
+template <typename T>
+struct CompPairsBySecondLexStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second < b.second or (a.second == b.second and a.first > b.first);
+ }
+};
+
+template <typename T>
+struct CompPairsBySecondLexGreaterStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second > b.second or (a.second == b.second and a.first > b.first);
+ }
+};
+
+using ItemsTimePair = std::pair<IdxType, int>;
+using UpdateList = std::list<ItemsTimePair>;
+using UpdateListIter = UpdateList::iterator;
+
+
+#ifdef USE_BOOST_HEAP
+template <class Real>
+using LossesHeap = boost::heap::d_ary_heap<IdxValPair<Real>, boost::heap::arity<2>, boost::heap::mutable_<true>, boost::heap::compare<CompPairsBySecondGreaterStruct<Real>>>;
+#else
+template<typename T, class ComparisonStruct>
+class IdxValHeap {
+public:
+ using InternalKeeper = std::set<IdxValPair<T>, ComparisonStruct>;
+ using handle_type = typename InternalKeeper::iterator;
+ // methods
+ handle_type push(const IdxValPair<T>& val)
+ {
+ auto res_pair = _heap.insert(val);
+ assert(res_pair.second);
+ assert(res_pair.first != _heap.end());
+ return res_pair.first;
+ }
+
+ void decrease(handle_type& handle, const IdxValPair<T>& new_val)
+ {
+ _heap.erase(handle);
+ handle = push(new_val);
+ }
+
+ void increase(handle_type& handle, const IdxValPair<T>& new_val)
+ {
+ _heap.erase(handle);
+ handle = push(new_val);
+
+ size_t size() const
+ {
+ return _heap.size();
+ }
+
+ handle_type ordered_begin()
+ {
+ return _heap.begin();
+ }
+
+ handle_type ordered_end()
+ {
+ return _heap.end();
+ }
+
+
+private:
+ std::set<IdxValPair<T>, ComparisonStruct> _heap;
+};
+
+// if we store losses, the minimal value should come first
+template <class Real>
+using LossesHeap = IdxValHeap<Real, CompPairsBySecondLexStruct>;
+#endif
+
+
+template <class Real = double>
+struct AuctionOracleLazyHeapRestricted : AuctionOracleBase<Real> {
+
+ using LossesHeapR = typename ws::LossesHeap<Real>;
+ using LossesHeapRHandle = typename ws::LossesHeap<Real>::handle_type;
+ using DiagramPointR = typename ws::DiagramPoint<Real>;
+
+
+ AuctionOracleLazyHeapRestricted(const std::vector<DiagramPointR>& bidders, const std::vector<DiagramPointR>& items, const Real wasserstein_power, const Real _internal_p = get_infinity<Real>());
+ ~AuctionOracleLazyHeapRestricted();
+ // data members
+ // temporarily make everything public
+ std::vector<std::vector<Real>> weight_matrix;
+ //Real weight_adj_const;
+ Real max_val;
+ // vector of heaps to find the best items
+ std::vector<LossesHeapR*> losses_heap;
+ std::vector<std::vector<size_t>> items_indices_for_heap_handles;
+ std::vector<std::vector<LossesHeapRHandle>> losses_heap_handles;
+ // methods
+ void fill_in_losses_heap();
+ void set_price(const IdxType items_idx, const Real new_price);
+ IdxValPair<Real> get_optimal_bid(const IdxType bidder_idx);
+ Real get_matching_weight(const std::vector<IdxType>& bidders_to_items) const;
+ void adjust_prices();
+ // to update the queue in lazy fashion
+ std::vector<UpdateListIter> items_iterators;
+ UpdateList update_list;
+ std::vector<int> bidders_update_moments;
+ int update_counter;
+ void update_queue_for_bidder(const IdxType bidder_idx);
+ LossesHeapR diag_items_heap;
+ std::vector<LossesHeapRHandle> diag_heap_handles;
+ std::vector<size_t> heap_handles_indices;
+ // debug
+
+ DebugOptimalBid<Real> get_optimal_bid_debug(const IdxType bidder_idx);
+
+ // for diagonal points
+ bool best_diagonal_items_computed;
+ size_t best_diagonal_item_idx;
+ Real best_diagonal_item_value;
+ size_t second_best_diagonal_item_idx;
+ Real second_best_diagonal_item_value;
+};
+
+} // end of namespace ws
+
+#include "auction_oracle_lazy_heap.h"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_lazy_heap.hpp b/geom_matching/wasserstein/include/auction_oracle_lazy_heap.hpp
new file mode 100644
index 0000000..d179b3d
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_lazy_heap.hpp
@@ -0,0 +1,465 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_oracle.h"
+
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace ws {
+
+// *****************************
+// AuctionOracleLazyHeapRestricted
+// *****************************
+
+
+template<class Real>
+AuctionOracleLazyHeapRestricted<Real>::AuctionOracleLazyHeapRestricted(const std::vector<DiagramPoint<Real>>& _bidders,
+ const std::vector<DiagramPoint<Real>>& _items,
+ Real _wasserstein_power,
+ Real _internal_p) :
+ AuctionOracleAbstract<Real>(_bidders, _items, _wasserstein_power, _internal_p),
+ max_val(0.0),
+ bidders_update_moments(_bidders.size(), 0),
+ update_counter(0),
+ heap_handles_indices(_items.size(), k_invalid_index),
+ best_diagonal_items_computed(false)
+{
+ weight_matrix.reserve(_bidders.size());
+ //const Real max_dist_upper_bound = 3 * getFurthestDistance3Approx(b, g);
+ //weight_adj_const = pow(max_dist_upper_bound, wasserstein_power);
+ // init weight matrix
+ for(const auto& point_A : _bidders) {
+ std::vector<Real> weight_vec;
+ weight_vec.clear();
+ weight_vec.reserve(_bidders.size());
+ for(const auto& point_B : _items) {
+ Real val = pow(dist_lp(point_A, point_B, _internal_p), _wasserstein_power);
+ weight_vec.push_back( val );
+ if ( val > max_val )
+ max_val = val;
+ }
+ weight_matrix.push_back(weight_vec);
+ }
+ fill_in_losses_heap();
+ for(size_t item_idx = 0; item_idx < _items.size(); ++item_idx) {
+ update_list.push_back(std::make_pair(static_cast<IdxType>(item_idx), 0));
+ }
+ for(auto update_list_iter = update_list.begin(); update_list_iter != update_list.end(); ++update_list_iter) {
+ items_iterators.push_back(update_list_iter);
+ }
+
+ size_t handle_idx {0};
+ for(size_t item_idx = 0; item_idx < _items.size(); ++item_idx) {
+ if (_items[item_idx].is_diagonal() ) {
+ heap_handles_indices[item_idx] = handle_idx++;
+ diag_heap_handles.push_back(diag_items_heap.push(std::make_pair(item_idx, 0)));
+ }
+ }
+}
+
+
+template<class Real>
+void AuctionOracleLazyHeapRestricted<Real>::update_queue_for_bidder(IdxType bidder_idx)
+{
+ assert(0 <= bidder_idx and bidder_idx < static_cast<int>(this->bidders.size()));
+ assert(bidder_idx < static_cast<int>(bidders_update_moments.size()));
+ assert(losses_heap[bidder_idx] != nullptr );
+
+ int bidder_last_update_time = bidders_update_moments[bidder_idx];
+ auto iter = update_list.begin();
+ while (iter != update_list.end() and iter->second >= bidder_last_update_time) {
+ IdxType item_idx = iter->first;
+ size_t handle_idx = items_indices_for_heap_handles[bidder_idx][item_idx];
+ if (handle_idx < this->items.size() ) {
+ IdxValPair<Real> new_val { item_idx, weight_matrix[bidder_idx][item_idx] + this->prices[item_idx]};
+ // to-do: change indexing of losses_heap_handles
+ losses_heap[bidder_idx]->decrease(losses_heap_handles[bidder_idx][handle_idx], new_val);
+ }
+ iter++;
+ }
+ bidders_update_moments[bidder_idx] = update_counter;
+}
+
+
+template<class Real>
+void AuctionOracleLazyHeapRestricted<Real>::fill_in_losses_heap()
+{
+ using LossesHeapR = typename ws::LossesHeap<Real>;
+ using LossesHeapRHandleVec = typename std::vector<typename ws::LossesHeap<Real>::handle_type>;
+
+ for(size_t bidder_idx = 0; bidder_idx < this->bidders.size(); ++bidder_idx) {
+ DiagramPoint<Real> bidder { this->bidders[bidder_idx] };
+ // no heap for diagonal bidders
+ if ( bidder.is_diagonal() ) {
+ losses_heap.push_back( nullptr );
+ losses_heap_handles.push_back(LossesHeapRHandleVec());
+ items_indices_for_heap_handles.push_back( std::vector<size_t>() );
+ continue;
+ } else {
+ losses_heap.push_back( new LossesHeapR() );
+
+ assert( losses_heap.at(bidder_idx) != nullptr );
+
+ items_indices_for_heap_handles.push_back( std::vector<size_t>(this->items.size(), k_invalid_index) );
+ LossesHeapRHandleVec handles_vec;
+ losses_heap_handles.push_back(handles_vec);
+ size_t handle_idx { 0 };
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ assert( items_indices_for_heap_handles.at(bidder_idx).at(item_idx) > 0 );
+ DiagramPoint<Real> item { this->items[item_idx] };
+ if ( item.is_normal() ) {
+ // item can be assigned to bidder, store in heap
+ IdxValPair<Real> vp { item_idx, weight_matrix[bidder_idx][item_idx] + this->prices[item_idx] };
+ losses_heap_handles[bidder_idx].push_back( losses_heap[bidder_idx]->push(vp) );
+ // keep corresponding index in items_indices_for_heap_handles
+ items_indices_for_heap_handles[bidder_idx][item_idx] = handle_idx++;
+ }
+ }
+ }
+ }
+}
+
+
+template<class Real>
+AuctionOracleLazyHeapRestricted<Real>::~AuctionOracleLazyHeapRestricted()
+{
+ for(auto h : losses_heap) {
+ delete h;
+ }
+}
+
+
+template<class Real>
+void AuctionOracleLazyHeapRestricted<Real>::set_price(IdxType item_idx, Real new_price)
+{
+ assert( this->prices.at(item_idx) < new_price );
+#ifdef DEBUG_AUCTION
+ std::cout << "price incremented by " << this->prices.at(item_idx) - new_price << std::endl;
+#endif
+ this->prices[item_idx] = new_price;
+ if (this->items[item_idx].is_normal() ) {
+ // lazy: record the moment we updated the price of the items,
+ // do not update queues.
+ // 1. move the items with updated price to the front of the update_list,
+ update_list.splice(update_list.begin(), update_list, items_iterators[item_idx]);
+ // 2. record the moment we updated the price and increase the counter
+ update_list.front().second = update_counter++;
+ } else {
+ // diagonal items are stored in one heap
+ diag_items_heap.decrease(diag_heap_handles[heap_handles_indices[item_idx]], std::make_pair(item_idx, new_price));
+ best_diagonal_items_computed = false;
+ }
+}
+
+// subtract min. price from all prices
+template<class Real>
+void AuctionOracleLazyHeapRestricted<Real>::adjust_prices()
+{
+}
+
+
+template<class Real>
+DebugOptimalBid<Real> AuctionOracleLazyHeapRestricted<Real>::get_optimal_bid_debug(IdxType bidder_idx)
+{
+ DebugOptimalBid<Real> result;
+ assert(bidder_idx >=0 and bidder_idx < static_cast<IdxType>(this->bidders.size()) );
+
+ auto bidder = this->bidders[bidder_idx];
+ std::vector<IdxValPair<Real>> cand_items;
+ // corresponding point is always considered as a candidate
+
+ size_t proj_item_idx = bidder_idx;
+ assert( 0 <= proj_item_idx and proj_item_idx < this->items.size() );
+ auto proj_item = this->items[proj_item_idx];
+ assert(proj_item.type != bidder.type);
+ //assert(proj_item.proj_id == bidder.id);
+ //assert(proj_item.id == bidder.proj_id);
+ // todo: store precomputed distance?
+ Real proj_item_value = this->get_value_for_bidder(bidder_idx, proj_item_idx);
+ cand_items.push_back( std::make_pair(proj_item_idx, proj_item_value) );
+
+ if (bidder.is_normal()) {
+ assert(losses_heap.at(bidder_idx) != nullptr);
+ assert(losses_heap[bidder_idx]->size() >= 2);
+ update_queue_for_bidder(bidder_idx);
+ auto pHeap = losses_heap[bidder_idx];
+ assert( pHeap != nullptr );
+ auto top_iter = pHeap->ordered_begin();
+ cand_items.push_back( *top_iter );
+ ++top_iter; // now points to the second-best items
+ cand_items.push_back( *top_iter );
+ std::sort(cand_items.begin(), cand_items.end(), CompPairsBySecondStruct<Real>());
+ assert(cand_items[1].second >= cand_items[0].second);
+ } else {
+ // for diagonal bidder the only normal point has already been added
+ // the other 2 candidates are diagonal items only, get from the heap
+ // with prices
+ assert(diag_items_heap.size() > 1);
+ auto top_diag_iter = diag_items_heap.ordered_begin();
+ auto topDiag1 = *top_diag_iter++;
+ auto topDiag2 = *top_diag_iter;
+ cand_items.push_back(topDiag1);
+ cand_items.push_back(topDiag2);
+ std::sort(cand_items.begin(), cand_items.end(), CompPairsBySecondStruct<Real>());
+ assert(cand_items.size() == 3);
+ assert(cand_items[2].second >= cand_items[1].second);
+ assert(cand_items[1].second >= cand_items[0].second);
+ }
+
+ result.best_item_idx = cand_items[0].first;
+ result.second_best_item_idx = cand_items[1].first;
+ result.best_item_value = cand_items[0].second;
+ result.second_best_item_value = cand_items[1].second;
+
+ // checking code
+
+ //DebugOptimalBid<Real> debug_my_result(result);
+ //DebugOptimalBid<Real> debug_naive_result;
+ //debug_naive_result.best_item_value = 1e20;
+ //debug_naive_result.second_best_item_value = 1e20;
+ //Real curr_item_value;
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //if ( this->bidders[bidder_idx].type != this->items[item_idx].type and
+ //this->bidders[bidder_idx].proj_id != this->items[item_idx].id)
+ //continue;
+
+ //curr_item_value = pow(dist_lp(this->bidders[bidder_idx], this->items[item_idx]), wasserstein_power) + this->prices[item_idx];
+ //if (curr_item_value < debug_naive_result.best_item_value) {
+ //debug_naive_result.best_item_value = curr_item_value;
+ //debug_naive_result.best_item_idx = item_idx;
+ //}
+ //}
+
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //if (item_idx == debug_naive_result.best_item_idx) {
+ //continue;
+ //}
+ //if ( this->bidders[bidder_idx].type != this->items[item_idx].type and
+ //this->bidders[bidder_idx].proj_id != this->items[item_idx].id)
+ //continue;
+
+ //curr_item_value = pow(dist_lp(this->bidders[bidder_idx], this->items[item_idx]), wasserstein_power) + this->prices[item_idx];
+ //if (curr_item_value < debug_naive_result.second_best_item_value) {
+ //debug_naive_result.second_best_item_value = curr_item_value;
+ //debug_naive_result.second_best_item_idx = item_idx;
+ //}
+ //}
+
+ //if ( fabs( debug_my_result.best_item_value - debug_naive_result.best_item_value ) > 1e-6 or
+ //fabs( debug_naive_result.second_best_item_value - debug_my_result.second_best_item_value) > 1e-6 ) {
+ //std::cerr << "bidder_idx = " << bidder_idx << "; ";
+ //std::cerr << this->bidders[bidder_idx] << std::endl;
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //std::cout << item_idx << ": " << this->items[item_idx] << "; price = " << this->prices[item_idx] << std::endl;
+ //}
+ //std::cerr << "debug_my_result: " << debug_my_result << std::endl;
+ //std::cerr << "debug_naive_result: " << debug_naive_result << std::endl;
+ //auto pHeap = losses_heap[bidder_idx];
+ //assert( pHeap != nullptr );
+ //for(auto top_iter = pHeap->ordered_begin(); top_iter != pHeap->ordered_end(); ++top_iter) {
+ //std::cerr << "in heap: " << top_iter->first << ": " << top_iter->second << "; real value = " << dist_lp(bidder, this->items[top_iter->first]) + this->prices[top_iter->first] << std::endl;
+ //}
+ //for(auto ci : cand_items) {
+ //std::cout << "ci.idx = " << ci.first << ", value = " << ci.second << std::endl;
+ //}
+
+ ////std::cerr << "two_best_items: " << two_best_items[0].d << " " << two_best_items[1].d << std::endl;
+ //assert(false);
+ //}
+
+
+ //std::cout << "get_optimal_bid: bidder_idx = " << bidder_idx << "; best_item_idx = " << best_item_idx << "; best_item_value = " << best_item_value << "; best_items_price = " << this->prices[best_item_idx] << "; second_best_item_idx = " << top_iter->first << "; second_best_value = " << second_best_item_value << "; second_best_price = " << this->prices[top_iter->first] << "; bid = " << this->prices[best_item_idx] + ( best_item_value - second_best_item_value ) + epsilon << "; epsilon = " << epsilon << std::endl;
+ //std::cout << "get_optimal_bid: bidder_idx = " << bidder_idx << "; best_item_idx = " << best_item_idx << "; best_items_dist= " << (weight_adj_const - best_item_value) << "; best_items_price = " << this->prices[best_item_idx] << "; second_best_item_idx = " << top_iter->first << "; second_best_dist= " << (weight_adj_const - second_best_item_value) << "; second_best_price = " << this->prices[top_iter->first] << "; bid = " << this->prices[best_item_idx] + ( best_item_value - second_best_item_value ) + epsilon << "; epsilon = " << epsilon << std::endl;
+
+ return result;
+}
+
+
+template<class Real>
+IdxValPair<Real> AuctionOracleLazyHeapRestricted<Real>::get_optimal_bid(const IdxType bidder_idx)
+{
+ IdxType best_item_idx;
+ //IdxType second_best_item_idx;
+ Real best_item_value;
+ Real second_best_item_value;
+
+ auto& bidder = this->bidders[bidder_idx];
+ IdxType proj_item_idx = bidder_idx;
+ assert( 0 <= proj_item_idx and proj_item_idx < this->items.size() );
+ auto proj_item = this->items[proj_item_idx];
+ assert(proj_item.type != bidder.type);
+ //assert(proj_item.proj_id == bidder.id);
+ //assert(proj_item.id == bidder.proj_id);
+ // todo: store precomputed distance?
+ Real proj_item_value = this->get_value_for_bidder(bidder_idx, proj_item_idx);
+
+ if (bidder.is_diagonal()) {
+ // for diagonal bidder the only normal point has already been added
+ // the other 2 candidates are diagonal items only, get from the heap
+ // with prices
+ assert(diag_items_heap.size() > 1);
+ if (!best_diagonal_items_computed) {
+ auto top_diag_iter = diag_items_heap.ordered_begin();
+ best_diagonal_item_idx = top_diag_iter->first;
+ best_diagonal_item_value = top_diag_iter->second;
+ top_diag_iter++;
+ second_best_diagonal_item_idx = top_diag_iter->first;
+ second_best_diagonal_item_value = top_diag_iter->second;
+ best_diagonal_items_computed = true;
+ }
+
+ if ( proj_item_value < best_diagonal_item_value) {
+ best_item_idx = proj_item_idx;
+ best_item_value = proj_item_value;
+ second_best_item_value = best_diagonal_item_value;
+ //second_best_item_idx = best_diagonal_item_idx;
+ } else if (proj_item_value < second_best_diagonal_item_value) {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value;
+ second_best_item_value = proj_item_value;
+ //second_best_item_idx = proj_item_idx;
+ } else {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value;
+ second_best_item_value = second_best_diagonal_item_value;
+ //second_best_item_idx = second_best_diagonal_item_idx;
+ }
+ } else {
+ // for normal bidder get 2 best items among non-diagonal (=normal) points
+ // from the corresponding heap
+ assert(diag_items_heap.size() > 1);
+ update_queue_for_bidder(bidder_idx);
+ auto top_norm_iter = losses_heap[bidder_idx]->ordered_begin();
+ IdxType best_normal_item_idx { top_norm_iter->first };
+ Real best_normal_item_value { top_norm_iter->second };
+ top_norm_iter++;
+ Real second_best_normal_item_value { top_norm_iter->second };
+ //IdxType second_best_normal_item_idx { top_norm_iter->first };
+
+ if ( proj_item_value < best_normal_item_value) {
+ best_item_idx = proj_item_idx;
+ best_item_value = proj_item_value;
+ second_best_item_value = best_normal_item_value;
+ //second_best_item_idx = best_normal_item_idx;
+ } else if (proj_item_value < second_best_normal_item_value) {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = proj_item_value;
+ //second_best_item_idx = proj_item_idx;
+ } else {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = second_best_normal_item_value;
+ //second_best_item_idx = second_best_normal_item_idx;
+ }
+ }
+
+ IdxValPair<Real> result;
+
+ assert( second_best_item_value >= best_item_value );
+
+ result.first = best_item_idx;
+ result.second = ( second_best_item_value - best_item_value ) + this->prices[best_item_idx] + this->epsilon;
+
+
+ // checking code
+
+ //DebugOptimalBid<Real> debug_my_result;
+ //debug_my_result.best_item_idx = best_item_idx;
+ //debug_my_result.best_item_value = best_item_value;
+ //debug_my_result.second_best_item_idx = second_best_item_idx;
+ //debug_my_result.second_best_item_value = second_best_item_value;
+ //DebugOptimalBid<Real> debug_naive_result;
+ //debug_naive_result.best_item_value = 1e20;
+ //debug_naive_result.second_best_item_value = 1e20;
+ //Real curr_item_value;
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //if ( this->bidders[bidder_idx].type != this->items[item_idx].type and
+ //this->bidders[bidder_idx].proj_id != this->items[item_idx].id)
+ //continue;
+
+ //curr_item_value = this->get_value_for_bidder(bidder_idx, item_idx);
+ //if (curr_item_value < debug_naive_result.best_item_value) {
+ //debug_naive_result.best_item_value = curr_item_value;
+ //debug_naive_result.best_item_idx = item_idx;
+ //}
+ //}
+
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //if (item_idx == debug_naive_result.best_item_idx) {
+ //continue;
+ //}
+ //if ( this->bidders[bidder_idx].type != this->items[item_idx].type and
+ //this->bidders[bidder_idx].proj_id != this->items[item_idx].id)
+ //continue;
+
+ //curr_item_value = this->get_value_for_bidder(bidder_idx, item_idx);
+ //if (curr_item_value < debug_naive_result.second_best_item_value) {
+ //debug_naive_result.second_best_item_value = curr_item_value;
+ //debug_naive_result.second_best_item_idx = item_idx;
+ //}
+ //}
+ ////std::cout << "got naive result" << std::endl;
+
+ //if ( fabs( debug_my_result.best_item_value - debug_naive_result.best_item_value ) > 1e-6 or
+ //fabs( debug_naive_result.second_best_item_value - debug_my_result.second_best_item_value) > 1e-6 ) {
+ //std::cerr << "bidder_idx = " << bidder_idx << "; ";
+ //std::cerr << this->bidders[bidder_idx] << std::endl;
+ //for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ //std::cout << item_idx << ": " << this->items[item_idx] << "; price = " << this->prices[item_idx] << std::endl;
+ //}
+ //std::cerr << "debug_my_result: " << debug_my_result << std::endl;
+ //std::cerr << "debug_naive_result: " << debug_naive_result << std::endl;
+ //auto pHeap = losses_heap[bidder_idx];
+ //if ( pHeap != nullptr ) {
+ //for(auto top_iter = pHeap->ordered_begin(); top_iter != pHeap->ordered_end(); ++top_iter) {
+ //std::cerr << "in heap: " << top_iter->first << ": " << top_iter->second << "; real value = " << dist_lp(bidder, this->items[top_iter->first]) + this->prices[top_iter->first] << std::endl;
+ //}
+ //}
+ ////for(auto ci : cand_items) {
+ ////std::cout << "ci.idx = " << ci.first << ", value = " << ci.second << std::endl;
+ ////}
+
+ ////std::cerr << "two_best_items: " << two_best_items[0].d << " " << two_best_items[1].d << std::endl;
+ //assert(false);
+ // }
+ //std::cout << "get_optimal_bid: bidder_idx = " << bidder_idx << "; best_item_idx = " << best_item_idx << "; best_item_value = " << best_item_value << "; best_items_price = " << this->prices[best_item_idx] << "; second_best_item_idx = " << top_iter->first << "; second_best_value = " << second_best_item_value << "; second_best_price = " << this->prices[top_iter->first] << "; bid = " << this->prices[best_item_idx] + ( best_item_value - second_best_item_value ) + epsilon << "; epsilon = " << epsilon << std::endl;
+ //std::cout << "get_optimal_bid: bidder_idx = " << bidder_idx << "; best_item_idx = " << best_item_idx << "; best_items_dist= " << (weight_adj_const - best_item_value) << "; best_items_price = " << this->prices[best_item_idx] << "; second_best_item_idx = " << top_iter->first << "; second_best_dist= " << (weight_adj_const - second_best_item_value) << "; second_best_price = " << this->prices[top_iter->first] << "; bid = " << this->prices[best_item_idx] + ( best_item_value - second_best_item_value ) + epsilon << "; epsilon = " << epsilon << std::endl;
+
+ return result;
+}
+
+} // end of namespace ws
diff --git a/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.h b/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.h
new file mode 100644
index 0000000..c932396
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.h
@@ -0,0 +1,114 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_ORACLE_STUPID_SPARSE_RESTRICTED_H
+#define AUCTION_ORACLE_STUPID_SPARSE_RESTRICTED_H
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "basic_defs_ws.h"
+#include "diagonal_heap.h"
+#include "auction_oracle_base.h"
+#include "dnn/geometry/euclidean-fixed.h"
+#include "dnn/local/kd-tree.h"
+
+
+namespace hera {
+namespace ws {
+
+template <int k_max_nn, class Real_ = double, class PointContainer_ = std::vector<DiagramPoint<Real_>>>
+struct AuctionOracleStupidSparseRestricted : AuctionOracleBase<Real_, PointContainer_> {
+
+ using PointContainer = PointContainer_;
+ using Real = Real_;
+
+ using LossesHeapR = typename ws::LossesHeapOld<Real>;
+ using LossesHeapRHandle = typename ws::LossesHeapOld<Real>::handle_type;
+ using DiagramPointR = typename ws::DiagramPoint<Real>;
+ using DebugOptimalBidR = typename ws::DebugOptimalBid<Real>;
+
+ using DnnPoint = dnn::Point<2, Real>;
+ using DnnTraits = dnn::PointTraits<DnnPoint>;
+
+
+ AuctionOracleStupidSparseRestricted(const PointContainer& bidders, const PointContainer& items, const AuctionParams<Real>& params);
+ // data members
+ // temporarily make everything public
+ std::vector<std::vector<size_t>> admissible_items_;
+ Real max_val_;
+ LossesHeapR diag_items_heap_;
+ std::vector<LossesHeapRHandle> diag_heap_handles_;
+ std::vector<size_t> heap_handles_indices_;
+
+// std::vector<size_t> kdtree_items_;
+
+ std::vector<size_t> top_diag_indices_;
+ std::vector<size_t> top_diag_lookup_;
+ size_t top_diag_counter_ { 0 };
+ bool best_diagonal_items_computed_ { false };
+ Real best_diagonal_item_value_;
+ Real second_best_diagonal_item_idx_ { k_invalid_index };
+ Real second_best_diagonal_item_value_ { std::numeric_limits<Real>::max() };
+
+
+ // methods
+ void set_price(const IdxType items_idx, const Real new_price, const bool update_diag = true);
+ IdxValPair<Real> get_optimal_bid(const IdxType bidder_idx);
+ void adjust_prices();
+ void adjust_prices(const Real delta);
+
+ // debug routines
+ DebugOptimalBidR get_optimal_bid_debug(IdxType bidder_idx) const;
+ void sanity_check();
+
+
+ // heap top vector
+ size_t get_heap_top_size() const;
+ void recompute_top_diag_items(bool hard = false);
+ void recompute_second_best_diag();
+ void reset_top_diag_counter();
+ void increment_top_diag_counter();
+ void add_top_diag_index(const size_t item_idx);
+ void remove_top_diag_index(const size_t item_idx);
+ bool is_in_top_diag_indices(const size_t item_idx) const;
+
+ std::shared_ptr<spdlog::logger> console_logger;
+
+ std::pair<Real, Real> get_minmax_price() const;
+
+};
+
+} // ws
+} // hera
+
+
+#include "auction_oracle_stupid_sparse_restricted.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.hpp b/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.hpp
new file mode 100644
index 0000000..8f4504d
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_oracle_stupid_sparse_restricted.hpp
@@ -0,0 +1,568 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+#ifndef AUCTION_ORACLE_STUPID_SPARSE_HPP
+#define AUCTION_ORACLE_STUPID_SPARSE_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "basic_defs_ws.h"
+#include "auction_oracle_stupid_sparse_restricted.h"
+
+#ifdef FOR_R_TDA
+#undef DEBUG_AUCTION
+#endif
+
+namespace hera {
+namespace ws {
+
+
+// *****************************
+// AuctionOracleStupidSparseRestricted
+// *****************************
+
+
+template <int k_max_nn, class Real_, class PointContainer_>
+std::ostream& operator<<(std::ostream& output, const AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>& oracle)
+{
+ output << "Oracle " << &oracle << std::endl;
+ output << fmt::format(" max_val_ = {0}, best_diagonal_items_computed_ = {1}, best_diagonal_item_value_ = {2}, second_best_diagonal_item_idx_ = {3}, second_best_diagonal_item_value_ = {4}\n",
+ oracle.max_val_,
+ oracle.best_diagonal_items_computed_,
+ oracle.best_diagonal_item_value_,
+ oracle.second_best_diagonal_item_idx_,
+ oracle.second_best_diagonal_item_value_);
+
+ output << fmt::format(" prices = {0}\n",
+ format_container_to_log(oracle.prices));
+
+ output << fmt::format(" diag_items_heap_ = {0}\n",
+ losses_heap_to_string(oracle.diag_items_heap_));
+
+
+ output << fmt::format(" top_diag_indices_ = {0}\n",
+ format_container_to_log(oracle.top_diag_indices_));
+
+ output << fmt::format(" top_diag_counter_ = {0}\n",
+ oracle.top_diag_counter_);
+
+ output << fmt::format(" top_diag_lookup_ = {0}\n",
+ format_container_to_log(oracle.top_diag_lookup_));
+
+
+ output << "end of oracle " << &oracle << std::endl;
+ return output;
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::AuctionOracleStupidSparseRestricted(const PointContainer_& _bidders,
+ const PointContainer_& _items,
+ const AuctionParams<Real_>& params) :
+ AuctionOracleBase<Real_, PointContainer_>(_bidders, _items, params),
+ admissible_items_(_bidders.size(), std::vector<size_t>()),
+ heap_handles_indices_(_items.size(), k_invalid_index),
+ top_diag_lookup_(_items.size(), k_invalid_index)
+{
+ // initialize admissible edges
+ std::vector<size_t> kdtree_items_(_items.size(), k_invalid_index);
+ std::vector<DnnPoint> dnn_points_;
+ std::vector<DnnPoint*> dnn_point_handles_;
+ size_t dnn_item_idx { 0 };
+ size_t true_idx { 0 };
+ dnn_points_.reserve(this->items.size());
+ // store normal items in kd-tree
+ for(const auto& g : this->items) {
+ if (g.is_normal() ) {
+ kdtree_items_[true_idx] = dnn_item_idx;
+ // index of items is id of dnn-point
+ DnnPoint p(true_idx);
+ p[0] = g.getRealX();
+ p[1] = g.getRealY();
+ dnn_points_.push_back(p);
+ assert(dnn_item_idx == dnn_points_.size() - 1);
+ dnn_item_idx++;
+ }
+ true_idx++;
+ }
+ assert(dnn_points_.size() < _items.size() );
+ for(size_t i = 0; i < dnn_points_.size(); ++i) {
+ dnn_point_handles_.push_back(&dnn_points_[i]);
+ }
+ DnnTraits traits;
+ traits.internal_p = params.internal_p;
+ dnn::KDTree<DnnTraits> kdtree_(traits, dnn_point_handles_, params.wasserstein_power);
+
+ // loop over normal bidders, find nearest neighbours
+ size_t bidder_idx = 0;
+ for(const auto& b : this->bidders) {
+ if (b.is_normal()) {
+ admissible_items_[bidder_idx].reserve(k_max_nn);
+ DnnPoint bidder_dnn;
+ bidder_dnn[0] = b.getRealX();
+ bidder_dnn[1] = b.getRealY();
+ auto nearest_neighbours = kdtree_.findK(bidder_dnn, k_max_nn);
+ assert(nearest_neighbours.size() == k_max_nn);
+ for(const auto& x : nearest_neighbours) {
+ admissible_items_[bidder_idx].push_back(x.p->id());
+ }
+ }
+ bidder_idx++;
+ }
+
+ size_t handle_idx {0};
+ for(size_t item_idx = 0; item_idx < _items.size(); ++item_idx) {
+ if (this->items[item_idx].is_diagonal()) {
+ heap_handles_indices_[item_idx] = handle_idx++;
+ diag_heap_handles_.push_back(diag_items_heap_.push(std::make_pair(item_idx, 0.0)));
+ }
+ }
+ max_val_ = 3*getFurthestDistance3Approx<>(_bidders, _items, params.internal_p);
+ max_val_ = std::pow(max_val_, params.wasserstein_power);
+
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+ console_logger->info("Stupid sparse oracle ctor done, k = {0}", k_max_nn);
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+bool AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::is_in_top_diag_indices(const size_t item_idx) const
+{
+ return top_diag_lookup_[item_idx] != k_invalid_index;
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::add_top_diag_index(const size_t item_idx)
+{
+ assert(find(top_diag_indices_.begin(), top_diag_indices_.end(), item_idx) == top_diag_indices_.end());
+ assert(this->items[item_idx].is_diagonal());
+
+ top_diag_indices_.push_back(item_idx);
+ top_diag_lookup_[item_idx] = top_diag_indices_.size() - 1;
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::remove_top_diag_index(const size_t item_idx)
+{
+ if (top_diag_indices_.size() > 1) {
+ // remove item_idx from top_diag_indices after swapping
+ // it with the last element, update index lookup appropriately
+ auto old_index = top_diag_lookup_[item_idx];
+ auto end_element = top_diag_indices_.back();
+ std::swap(top_diag_indices_[old_index], top_diag_indices_.back());
+ top_diag_lookup_[end_element] = old_index;
+ }
+
+ top_diag_indices_.pop_back();
+ top_diag_lookup_[item_idx] = k_invalid_index;
+ if (top_diag_indices_.size() < 2) {
+ recompute_second_best_diag();
+ }
+ best_diagonal_items_computed_ = not top_diag_indices_.empty();
+ reset_top_diag_counter();
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::increment_top_diag_counter()
+{
+ assert(top_diag_counter_ >= 0 and top_diag_counter_ < top_diag_indices_.size());
+
+ ++top_diag_counter_;
+ if (top_diag_counter_ >= top_diag_indices_.size()) {
+ top_diag_counter_ -= top_diag_indices_.size();
+ }
+
+ assert(top_diag_counter_ >= 0 and top_diag_counter_ < top_diag_indices_.size());
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::reset_top_diag_counter()
+{
+ top_diag_counter_ = 0;
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::recompute_top_diag_items(bool hard)
+{
+ console_logger->debug("Enter recompute_top_diag_items, hard = {0}", hard);
+ assert(hard or top_diag_indices_.empty());
+
+ if (hard) {
+ std::fill(top_diag_lookup_.begin(), top_diag_lookup_.end(), k_invalid_index);
+ top_diag_indices_.clear();
+ }
+
+ auto top_diag_iter = diag_items_heap_.ordered_begin();
+ best_diagonal_item_value_ = top_diag_iter->second;
+ add_top_diag_index(top_diag_iter->first);
+
+ ++top_diag_iter;
+
+ // traverse the heap while we see the same value
+ while(top_diag_iter != diag_items_heap_.ordered_end()) {
+ if ( top_diag_iter->second != best_diagonal_item_value_) {
+ break;
+ } else {
+ add_top_diag_index(top_diag_iter->first);
+ }
+ ++top_diag_iter;
+ }
+
+ recompute_second_best_diag();
+
+ best_diagonal_items_computed_ = true;
+ reset_top_diag_counter();
+ console_logger->debug("Exit recompute_top_diag_items, hard = {0}", hard);
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+typename AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::DebugOptimalBidR
+AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::get_optimal_bid_debug(IdxType bidder_idx) const
+{
+ DebugOptimalBidR result;
+ throw std::runtime_error("Not implemented");
+ return result;
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+IdxValPair<Real_> AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::get_optimal_bid(IdxType bidder_idx)
+{
+ auto bidder = this->bidders[bidder_idx];
+
+ // corresponding point is always considered as a candidate
+ // if bidder is a diagonal point, proj_item is a normal point,
+ // and vice versa.
+
+ size_t best_item_idx { k_invalid_index };
+ size_t second_best_item_idx { k_invalid_index };
+ size_t best_diagonal_item_idx { k_invalid_index };
+ Real best_item_value;
+ Real second_best_item_value;
+
+
+ size_t proj_item_idx = bidder_idx;
+ assert( 0 <= proj_item_idx and proj_item_idx < this->items.size() );
+ assert(this->items[proj_item_idx].type != bidder.type);
+ Real proj_item_value = this->get_value_for_bidder(bidder_idx, proj_item_idx);
+
+ if (bidder.is_diagonal()) {
+ // for diagonal bidder the only normal point has already been added
+ // the other 2 candidates are diagonal items only, get from the heap
+ // with prices
+
+ if (not best_diagonal_items_computed_) {
+ recompute_top_diag_items();
+ }
+
+ best_diagonal_item_idx = top_diag_indices_[top_diag_counter_];
+ increment_top_diag_counter();
+
+ if ( proj_item_value < best_diagonal_item_value_) {
+ best_item_idx = proj_item_idx;
+ best_item_value = proj_item_value;
+ second_best_item_value = best_diagonal_item_value_;
+ second_best_item_idx = best_diagonal_item_idx;
+ } else if (proj_item_value < second_best_diagonal_item_value_) {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value_;
+ second_best_item_value = proj_item_value;
+ second_best_item_idx = proj_item_idx;
+ } else {
+ best_item_idx = best_diagonal_item_idx;
+ best_item_value = best_diagonal_item_value_;
+ second_best_item_value = second_best_diagonal_item_value_;
+ second_best_item_idx = second_best_diagonal_item_idx_;
+ }
+ } else {
+
+ size_t best_normal_item_idx { k_invalid_index };
+ size_t second_best_normal_item_idx { k_invalid_index };
+ Real best_normal_item_value { std::numeric_limits<Real>::max() };
+ Real second_best_normal_item_value { std::numeric_limits<Real>::max() };
+
+ // find best item
+ for(const auto curr_item_idx : admissible_items_[bidder_idx]) {
+ auto curr_item_value = this->get_value_for_bidder(bidder_idx, curr_item_idx);
+ if (curr_item_value < best_normal_item_value) {
+ best_normal_item_idx = curr_item_idx;
+ best_normal_item_value = curr_item_value;
+ }
+ }
+
+ // find second-best item
+ for(const auto curr_item_idx : admissible_items_[bidder_idx]) {
+ if (curr_item_idx == best_normal_item_idx) {
+ continue;
+ }
+ auto curr_item_value = this->get_value_for_bidder(bidder_idx, curr_item_idx);
+ if (curr_item_value < second_best_normal_item_value) {
+ second_best_normal_item_idx = curr_item_idx;
+ second_best_normal_item_value = curr_item_value;
+ }
+ }
+
+ if ( proj_item_value < best_normal_item_value) {
+ best_item_idx = proj_item_idx;
+ increment_top_diag_counter();
+ best_item_value = proj_item_value;
+ second_best_item_value = best_normal_item_value;
+ } else if (proj_item_value < second_best_normal_item_value) {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = proj_item_value;
+ } else {
+ best_item_idx = best_normal_item_idx;
+ best_item_value = best_normal_item_value;
+ second_best_item_value = second_best_normal_item_value;
+ }
+ }
+
+ IdxValPair<Real> result;
+
+ assert( second_best_item_value >= best_item_value );
+
+ result.first = best_item_idx;
+ result.second = ( second_best_item_value - best_item_value ) + this->prices[best_item_idx] + this->epsilon;
+
+ return result;
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::recompute_second_best_diag()
+{
+
+ console_logger->debug("Enter recompute_second_best_diag");
+
+ if (top_diag_indices_.size() > 1) {
+ second_best_diagonal_item_value_ = best_diagonal_item_value_;
+ second_best_diagonal_item_idx_ = top_diag_indices_[0];
+ } else {
+ if (diag_items_heap_.size() == 1) {
+ second_best_diagonal_item_value_ == std::numeric_limits<Real>::max();
+ second_best_diagonal_item_idx_ = k_invalid_index;
+ } else {
+ auto diag_iter = diag_items_heap_.ordered_begin();
+ ++diag_iter;
+ second_best_diagonal_item_value_ = diag_iter->second;
+ second_best_diagonal_item_idx_ = diag_iter->first;
+ }
+ }
+
+ console_logger->debug("Exit recompute_second_best_diag, second_best_diagonal_item_value_ = {0}, second_best_diagonal_item_idx_ = {1}", second_best_diagonal_item_value_, second_best_diagonal_item_idx_);
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::set_price(IdxType item_idx,
+ Real new_price,
+ const bool update_diag)
+{
+
+ console_logger->debug("Enter set_price, item_idx = {0}, new_price = {1}, old price = {2}, update_diag = {3}", item_idx, new_price, this->prices[item_idx], update_diag);
+
+ assert(this->prices.size() == this->items.size());
+ assert( 0 < diag_heap_handles_.size() and diag_heap_handles_.size() <= this->items.size());
+ // adjust_prices decreases prices,
+ // also this variable must be true in reverse phases of FR-auction
+ bool item_goes_down = new_price > this->prices[item_idx];
+
+ this->prices[item_idx] = new_price;
+ if ( this->items[item_idx].is_diagonal() ) {
+ assert(diag_heap_handles_.size() > heap_handles_indices_.at(item_idx));
+ if (item_goes_down) {
+ diag_items_heap_.decrease(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ } else {
+ diag_items_heap_.increase(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ }
+ if (update_diag) {
+ // Update top_diag_indices_ only if necessary:
+ // normal bidders take their projections, which might not be on top
+ // also, set_price is called by adjust_prices, and we may have already
+ // removed the item from top_diag
+ if (is_in_top_diag_indices(item_idx)) {
+ remove_top_diag_index(item_idx);
+ }
+
+ if (item_idx == second_best_diagonal_item_idx_) {
+ recompute_second_best_diag();
+ }
+ }
+ }
+
+ console_logger->debug("Exit set_price, item_idx = {0}, new_price = {1}", item_idx, new_price);
+}
+
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::adjust_prices(Real delta)
+{
+ //console_logger->debug("Enter adjust_prices, delta = {0}", delta);
+ //std::cerr << *this << std::endl;
+
+ if (delta == 0.0)
+ return;
+
+ for(auto& p : this->prices) {
+ p -= delta;
+ }
+
+ bool price_goes_up = delta < 0;
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ if (this->items[item_idx].is_diagonal()) {
+ auto new_price = this->prices[item_idx];
+ if (price_goes_up) {
+ diag_items_heap_.decrease(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ } else {
+ diag_items_heap_.increase(diag_heap_handles_[heap_handles_indices_[item_idx]], std::make_pair(item_idx, new_price));
+ }
+ }
+ }
+ best_diagonal_item_value_ -= delta;
+ second_best_diagonal_item_value_ -= delta;
+
+ //std::cerr << *this << std::endl;
+ //console_logger->debug("Exit adjust_prices, delta = {0}", delta);
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::adjust_prices()
+{
+ auto pr_begin = this->prices.begin();
+ auto pr_end = this->prices.end();
+ Real min_price = *(std::min_element(pr_begin, pr_end));
+ adjust_prices(min_price);
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+size_t AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::get_heap_top_size() const
+{
+ return top_diag_indices_.size();
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+std::pair<Real_, Real_> AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::get_minmax_price() const
+{
+ auto r = std::minmax_element(this->prices.begin(), this->prices.end());
+ return std::make_pair(*r.first, *r.second);
+}
+
+template<int k_max_nn, class Real_, class PointContainer_>
+void AuctionOracleStupidSparseRestricted<k_max_nn, Real_, PointContainer_>::sanity_check()
+{
+#ifdef DEBUG_STUPID_SPARSE_RESTR_ORACLE
+
+ assert(admissible_items_.size() == this->bidders.size());
+
+ for(size_t bidder_idx = 0; bidder_idx < this->bidders.size(); ++bidder_idx) {
+ if (this->bidders[bidder_idx].is_normal()) {
+ assert(admissible_items_[bidder_idx].size() == k_max_nn);
+ } else {
+ assert(admissible_items_[bidder_idx].size() == 0);
+ }
+ }
+
+ if (best_diagonal_items_computed_) {
+ std::vector<Real> diag_items_price_vec;
+ diag_items_price_vec.reserve(this->items.size());
+
+ for(size_t item_idx = 0; item_idx < this->items.size(); ++item_idx) {
+ if (this->items.at(item_idx).is_diagonal()) {
+ diag_items_price_vec.push_back(this->prices.at(item_idx));
+ } else {
+ diag_items_price_vec.push_back(std::numeric_limits<Real>::max());
+ }
+ }
+
+ auto best_iter = std::min_element(diag_items_price_vec.begin(), diag_items_price_vec.end());
+ assert(best_iter != diag_items_price_vec.end());
+ Real true_best_diag_value = *best_iter;
+ size_t true_best_diag_idx = best_iter - diag_items_price_vec.begin();
+ assert(true_best_diag_value != std::numeric_limits<Real>::max());
+
+ Real true_second_best_diag_value = std::numeric_limits<Real>::max();
+ size_t true_second_best_diag_idx = k_invalid_index;
+ for(size_t item_idx = 0; item_idx < diag_items_price_vec.size(); ++item_idx) {
+ if (this->items.at(item_idx).is_normal()) {
+ assert(top_diag_lookup_.at(item_idx) == k_invalid_index);
+ continue;
+ }
+
+ auto i_iter = std::find(top_diag_indices_.begin(), top_diag_indices_.end(), item_idx);
+ if (diag_items_price_vec.at(item_idx) == true_best_diag_value) {
+ assert(i_iter != top_diag_indices_.end());
+ assert(top_diag_lookup_.at(item_idx) == i_iter - top_diag_indices_.begin());
+ } else {
+ assert(top_diag_lookup_.at(item_idx) == k_invalid_index);
+ assert(i_iter == top_diag_indices_.end());
+ }
+
+ if (item_idx == true_best_diag_idx) {
+ continue;
+ }
+ if (diag_items_price_vec.at(item_idx) < true_second_best_diag_value) {
+ true_second_best_diag_value = diag_items_price_vec.at(item_idx);
+ true_second_best_diag_idx = item_idx;
+ }
+ }
+
+ if (true_best_diag_value != best_diagonal_item_value_) {
+ console_logger->debug("best_diagonal_item_value_ = {0}, true value = {1}", best_diagonal_item_value_, true_best_diag_value);
+ std::cerr << *this;
+ //console_logger->debug("{0}", *this);
+ }
+
+ assert(true_best_diag_value == best_diagonal_item_value_);
+
+ assert(true_second_best_diag_idx != k_invalid_index);
+
+ if (true_second_best_diag_value != second_best_diagonal_item_value_) {
+ console_logger->debug("second_best_diagonal_item_value_ = {0}, true value = {1}", second_best_diagonal_item_value_, true_second_best_diag_value);
+ //console_logger->debug("{0}", *this);
+ }
+
+ assert(true_second_best_diag_value == second_best_diagonal_item_value_);
+ }
+#endif
+}
+
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_fr.h b/geom_matching/wasserstein/include/auction_runner_fr.h
new file mode 100644
index 0000000..1abca20
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_fr.h
@@ -0,0 +1,289 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_RUNNER_FR_H
+#define AUCTION_RUNNER_FR_H
+
+#define ORDERED_BY_PERSISTENCE
+
+#include <unordered_set>
+
+#include "auction_oracle.h"
+
+namespace hera {
+namespace ws {
+
+// the two parameters that you can tweak in auction algorithm are:
+// 1. epsilon_common_ratio
+// 2. max_num_phases
+
+template<class RealType_, class AuctionOracle_ = AuctionOracleKDTreeRestricted<RealType_>> // alternatively: AuctionOracleLazyHeap --- TODO
+class AuctionRunnerFR {
+public:
+
+ using Real = RealType_;
+ using AuctionOracle = AuctionOracle_;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointVec = std::vector<DgmPoint>;
+ using IdxValPairR = IdxValPair<Real>;
+
+ const Real k_lowest_bid_value = -(std::numeric_limits<Real>::max() - 1); // all bid values must be positive
+
+
+ AuctionRunnerFR(const std::vector<DgmPoint>& A,
+ const std::vector<DgmPoint>& B,
+ const Real q,
+ const Real _delta,
+ const Real _internal_p,
+ const Real _initial_epsilon = 0.0,
+ const Real _eps_factor = 5.0,
+ const int _max_num_phases = std::numeric_limits<int>::max(),
+ const Real _gamma_threshold = 0.0,
+ const size_t _max_bids_per_round = std::numeric_limits<size_t>::max(),
+ const std::string& _log_filename_prefix = "");
+
+ void set_epsilon(Real new_val);
+ Real get_epsilon() const { return epsilon; }
+ void run_auction();
+ Real get_wasserstein_distance();
+ Real get_wasserstein_cost();
+ Real get_relative_error(const bool debug_output = false) const;
+ bool phase_can_be_final() const;
+private:
+ // private data
+ std::vector<DgmPoint> bidders, items;
+ const size_t num_bidders;
+ const size_t num_items;
+ std::vector<IdxType> items_to_bidders;
+ std::vector<IdxType> bidders_to_items;
+ Real wasserstein_power;
+ Real epsilon;
+ Real delta;
+ Real internal_p;
+ Real initial_epsilon;
+ const Real epsilon_common_ratio; // next epsilon = current epsilon / epsilon_common_ratio
+ Real cumulative_epsilon_factor { 1.0 };
+ const int max_num_phases; // maximal number of phases of epsilon-scaling
+ bool is_forward { true }; // to use in distributed version only
+ Real weight_adj_const;
+ Real wasserstein_cost;
+ std::vector<IdxValPairR> forward_bid_table;
+ std::vector<IdxValPairR> reverse_bid_table;
+ // to get the 2 best items
+ AuctionOracle forward_oracle;
+ AuctionOracle reverse_oracle;
+ std::unordered_set<size_t> unassigned_bidders;
+ std::unordered_set<size_t> unassigned_items;
+ std::unordered_set<size_t> items_with_bids;
+ std::unordered_set<size_t> bidders_with_bids;
+
+#ifdef ORDERED_BY_PERSISTENCE
+ // to process unassigned by persistence
+ size_t batch_size;
+ using RealIdxPair = std::pair<Real, size_t>;
+ std::set<RealIdxPair, std::greater<RealIdxPair>> unassigned_bidders_by_persistence;
+ std::set<RealIdxPair, std::greater<RealIdxPair>> unassigned_items_by_persistence;
+#endif
+
+
+ // to imitate Gauss-Seidel
+ const size_t max_bids_per_round;
+
+ // to stop earlier in the last phase
+ const Real total_items_persistence;
+ const Real total_bidders_persistence;
+ Real partial_cost;
+ Real unassigned_bidders_persistence;
+ Real unassigned_items_persistence;
+ Real gamma_threshold;
+ size_t unassigned_threshold;
+
+ bool is_distance_computed { false };
+ int num_rounds { 0 };
+ int num_rounds_non_cumulative { 0 };
+ int num_phase { 0 };
+
+
+ size_t num_diag_items { 0 };
+ size_t num_normal_items { 0 };
+ size_t num_diag_bidders { 0 };
+ size_t num_normal_bidders { 0 };
+
+
+
+ // private methods
+ void assign_forward(const IdxType item_idx, const IdxType bidder_idx);
+ void assign_reverse(const IdxType item_idx, const IdxType bidder_idx);
+ void assign_to_best_bidder(const IdxType item_idx);
+ void assign_to_best_item(const IdxType bidder_idx);
+ void clear_forward_bid_table();
+ void clear_reverse_bid_table();
+ void assign_diag_to_diag();
+ void run_auction_phases(const int max_num_phases, const Real _initial_epsilon);
+ void run_auction_phase();
+ void run_forward_auction_phase();
+ void run_reverse_auction_phase();
+ void submit_forward_bid(IdxType bidder_idx, const IdxValPairR& bid);
+ void submit_reverse_bid(IdxType item_idx, const IdxValPairR& bid);
+ void flush_assignment();
+ Real get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx) const;
+ Real get_cost_to_diagonal(const DgmPoint& pt) const;
+ Real get_gamma() const;
+
+ template<class Range>
+ void run_forward_bidding_step(const Range& r);
+
+ template<class Range>
+ void run_reverse_bidding_step(const Range& r);
+
+ void add_unassigned_bidder(const size_t bidder_idx);
+ void add_unassigned_item(const size_t item_idx);
+ void remove_unassigned_bidder(const size_t bidder_idx);
+ void remove_unassigned_item(const size_t item_idx);
+
+ bool is_item_diagonal(const size_t item_idx) const { return item_idx < num_diag_items; }
+ bool is_item_normal(const size_t item_idx) const { return not is_item_diagonal(item_idx); }
+ bool is_bidder_diagonal(const size_t bidder_idx) const { return bidder_idx >= num_normal_bidders; }
+ bool is_bidder_normal(const size_t bidder_idx) const { return not is_bidder_diagonal(bidder_idx); }
+
+ size_t num_forward_bids_submitted { 0 };
+ size_t num_reverse_bids_submitted { 0 };
+
+ void decrease_epsilon();
+ // stopping criteria
+ bool continue_forward(const size_t, const size_t);
+ bool continue_reverse(const size_t, const size_t);
+ bool continue_phase();
+
+
+
+ // for debug only
+ void sanity_check();
+ void check_epsilon_css();
+ void print_debug();
+ void print_matching();
+
+ std::string log_filename_prefix;
+ const Real k_max_relative_error = 2.0; // if relative error cannot be estimated or is too large, use this value
+ void reset_round_stat(); // empty, if logging is disable
+ void reset_phase_stat();
+
+ std::unordered_set<size_t> never_assigned_bidders;
+ std::unordered_set<size_t> never_assigned_items;
+
+ std::shared_ptr<spdlog::logger> console_logger;
+#ifdef LOG_AUCTION
+ std::unordered_set<size_t> unassigned_normal_bidders;
+ std::unordered_set<size_t> unassigned_diag_bidders;
+
+ std::unordered_set<size_t> unassigned_normal_items;
+ std::unordered_set<size_t> unassigned_diag_items;
+
+
+ size_t all_assigned_round { 0 };
+ size_t all_assigned_round_found { false };
+
+ int num_forward_rounds_non_cumulative { 0 };
+ int num_forward_rounds { 0 };
+
+ int num_reverse_rounds_non_cumulative { 0 };
+ int num_reverse_rounds { 0 };
+
+ // all per-round vars are non-cumulative
+
+ // forward rounds
+ int num_normal_forward_bids_submitted { 0 };
+ int num_diag_forward_bids_submitted { 0 };
+
+ int num_forward_diag_to_diag_assignments { 0 };
+ int num_forward_diag_to_normal_assignments { 0 };
+ int num_forward_normal_to_diag_assignments { 0 };
+ int num_forward_normal_to_normal_assignments { 0 };
+
+ int num_forward_diag_from_diag_thefts { 0 };
+ int num_forward_diag_from_normal_thefts { 0 };
+ int num_forward_normal_from_diag_thefts { 0 };
+ int num_forward_normal_from_normal_thefts { 0 };
+
+ // reverse rounds
+ int num_normal_reverse_bids_submitted { 0 };
+ int num_diag_reverse_bids_submitted { 0 };
+
+ int num_reverse_diag_to_diag_assignments { 0 };
+ int num_reverse_diag_to_normal_assignments { 0 };
+ int num_reverse_normal_to_diag_assignments { 0 };
+ int num_reverse_normal_to_normal_assignments { 0 };
+
+ int num_reverse_diag_from_diag_thefts { 0 };
+ int num_reverse_diag_from_normal_thefts { 0 };
+ int num_reverse_normal_from_diag_thefts { 0 };
+ int num_reverse_normal_from_normal_thefts { 0 };
+
+ // price change statistics
+ std::vector<std::vector<size_t>> forward_price_change_cnt_vec;
+ std::vector<std::vector<size_t>> reverse_price_change_cnt_vec;
+
+ const char* forward_plot_logger_name = "forward_plot_logger";
+ const char* reverse_plot_logger_name = "reverse_plot_logger";
+ const char* forward_price_stat_logger_name = "forward_price_stat_logger";
+ const char* reverse_price_stat_logger_name = "reverse_price_stat_logger";
+
+ std::string forward_plot_logger_file_name;
+ std::string reverse_plot_logger_file_name;
+ std::string forward_price_stat_logger_file_name;
+ std::string reverse_price_stat_logger_file_name;
+
+ std::shared_ptr<spdlog::logger> forward_plot_logger;
+ std::shared_ptr<spdlog::logger> reverse_plot_logger;
+ std::shared_ptr<spdlog::logger> forward_price_stat_logger;
+ std::shared_ptr<spdlog::logger> reverse_price_stat_logger;
+
+
+ size_t parallel_threshold = 5000;
+ int num_parallelizable_rounds { 0 };
+ int num_parallelizable_forward_rounds { 0 };
+ int num_parallelizable_reverse_rounds { 0 };
+
+ int num_parallel_bids { 0 };
+ int num_total_bids { 0 };
+
+ int num_parallel_assignments { 0 };
+ int num_total_assignments { 0 };
+#endif
+
+};
+
+
+
+} // ws
+} // hera
+
+#include "auction_runner_fr.hpp"
+
+#undef ORDERED_BY_PERSISTENCE
+#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_fr.hpp b/geom_matching/wasserstein/include/auction_runner_fr.hpp
new file mode 100644
index 0000000..07c1459
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_fr.hpp
@@ -0,0 +1,1440 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_RUNNER_FR_HPP
+#define AUCTION_RUNNER_FR_HPP
+
+#include <cassert>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+
+#include "auction_runner_fr.h"
+
+#ifdef FOR_R_TDA
+#include "Rcpp.h"
+#undef DEBUG_FR_AUCTION
+#endif
+
+
+namespace hera {
+namespace ws {
+
+
+// *****************************
+// AuctionRunnerFR
+// *****************************
+
+template<class R, class AO>
+AuctionRunnerFR<R, AO>::AuctionRunnerFR(const std::vector<DgmPoint>& A,
+ const std::vector<DgmPoint>& B,
+ const Real q,
+ const Real _delta,
+ const Real _internal_p,
+ const Real _initial_epsilon,
+ const Real _eps_factor,
+ const int _max_num_phases,
+ const Real _gamma_threshold,
+ const size_t _max_bids_per_round,
+ const std::string& _log_filename_prefix
+ ) :
+ bidders(A),
+ items(B),
+ num_bidders(A.size()),
+ num_items(A.size()),
+ items_to_bidders(A.size(), k_invalid_index),
+ bidders_to_items(A.size(), k_invalid_index),
+ wasserstein_power(q),
+ delta(_delta),
+ internal_p(_internal_p),
+ initial_epsilon(_initial_epsilon),
+ epsilon_common_ratio(_eps_factor == 0.0 ? 5.0 : _eps_factor),
+ max_num_phases(_max_num_phases),
+ forward_bid_table(A.size(), std::make_pair(k_invalid_index, k_lowest_bid_value) ),
+ reverse_bid_table(B.size(), std::make_pair(k_invalid_index, k_lowest_bid_value) ),
+ forward_oracle(bidders, items, q, _internal_p),
+ reverse_oracle(items, bidders, q, _internal_p),
+ max_bids_per_round(_max_bids_per_round),
+ total_items_persistence(std::accumulate(items.begin(),
+ items.end(),
+ R(0.0),
+ [_internal_p, q](const Real& ps, const DgmPoint& item)
+ { return ps + std::pow(item.persistence_lp(_internal_p), q); }
+ )),
+ total_bidders_persistence(std::accumulate(bidders.begin(),
+ bidders.end(),
+ R(0.0),
+ [_internal_p, q](const Real& ps, const DgmPoint& bidder)
+ { return ps + std::pow(bidder.persistence_lp(_internal_p), q); }
+ )),
+ partial_cost(0.0),
+ unassigned_bidders_persistence(total_bidders_persistence),
+ unassigned_items_persistence(total_items_persistence),
+ gamma_threshold(_gamma_threshold),
+ log_filename_prefix(_log_filename_prefix)
+{
+ assert(A.size() == B.size());
+ for(const auto& p : bidders) {
+ if (p.is_normal()) {
+ num_normal_bidders++;
+ num_diag_items++;
+ } else {
+ num_normal_items++;
+ num_diag_bidders++;
+ }
+ }
+
+#ifdef ORDERED_BY_PERSISTENCE
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_bidders_by_persistence.insert(std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx));
+ }
+
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ unassigned_items_by_persistence.insert(std::make_pair(items[item_idx].persistence_lp(1.0), item_idx));
+ }
+#endif
+
+ // for experiments
+ unassigned_threshold = bidders.size() / 200;
+ unassigned_threshold = 0;
+
+#ifdef ORDERED_BY_PERSISTENCE
+ batch_size = 5000;
+#endif
+
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+ console_logger->info("Forward-reverse runnder, max_num_phases = {0}, max_bids_per_round = {1}, gamma_threshold = {2}, unassigned_threshold = {3}",
+ max_num_phases,
+ max_bids_per_round,
+ gamma_threshold,
+ unassigned_threshold);
+
+
+// check_epsilon_css();
+#ifdef LOG_AUCTION
+ parallel_threshold = bidders.size() / 100;
+ forward_plot_logger_file_name = log_filename_prefix + "_forward_plot.txt";
+ forward_plot_logger = spdlog::get(forward_plot_logger_name);
+ if (not forward_plot_logger) {
+ forward_plot_logger = spdlog::basic_logger_st(forward_plot_logger_name, forward_plot_logger_file_name);
+ }
+ forward_plot_logger->info("New forward plot starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ forward_plot_logger->set_pattern("%v");
+
+ reverse_plot_logger_file_name = log_filename_prefix + "_reverse_plot.txt";
+ reverse_plot_logger = spdlog::get(reverse_plot_logger_name);
+ if (not reverse_plot_logger) {
+ reverse_plot_logger = spdlog::basic_logger_st(reverse_plot_logger_name, reverse_plot_logger_file_name);
+ }
+ reverse_plot_logger->info("New reverse plot starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ reverse_plot_logger->set_pattern("%v");
+
+
+
+ forward_price_stat_logger_file_name = log_filename_prefix + "_forward_price_change_stat";
+ forward_price_stat_logger = spdlog::get(forward_price_stat_logger_name);
+ if (not forward_price_stat_logger) {
+ forward_price_stat_logger = spdlog::basic_logger_st(forward_price_stat_logger_name,
+ forward_price_stat_logger_file_name);
+ }
+ forward_price_stat_logger->info("New forward price statistics starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ forward_price_stat_logger->set_pattern("%v");
+
+ reverse_price_stat_logger_file_name = log_filename_prefix + "_reverse_price_change_stat";
+ reverse_price_stat_logger = spdlog::get(reverse_price_stat_logger_name);
+ if (not reverse_price_stat_logger) {
+ reverse_price_stat_logger = spdlog::basic_logger_st(reverse_price_stat_logger_name,
+ reverse_price_stat_logger_file_name);
+ }
+ reverse_price_stat_logger->info("New reverse price statistics starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ reverse_price_stat_logger->set_pattern("%v");
+#endif
+}
+
+template<class R, class AO>
+typename AuctionRunnerFR<R, AO>::Real
+AuctionRunnerFR<R, AO>::get_cost_to_diagonal(const DgmPoint& pt) const
+{
+ if (1.0 == wasserstein_power) {
+ return pt.persistence_lp(internal_p);
+ } else {
+ return std::pow(pt.persistence_lp(internal_p), wasserstein_power);
+ }
+}
+
+
+template<class R, class AO>
+typename AuctionRunnerFR<R, AO>::Real
+AuctionRunnerFR<R, AO>::get_gamma() const
+{
+ if (1.0 == wasserstein_power) {
+ return unassigned_items_persistence + unassigned_bidders_persistence;
+ } else {
+ return std::pow(unassigned_items_persistence + unassigned_bidders_persistence,
+ 1.0 / wasserstein_power);
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::reset_phase_stat()
+{
+ num_rounds_non_cumulative = 0;
+#ifdef LOG_AUCTION
+ num_parallelizable_rounds = 0;
+ num_parallelizable_forward_rounds = 0;
+ num_parallelizable_reverse_rounds = 0;
+ num_forward_rounds_non_cumulative = 0;
+ num_reverse_rounds_non_cumulative = 0;
+#endif
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::reset_round_stat()
+{
+ num_forward_bids_submitted = 0;
+ num_reverse_bids_submitted = 0;
+#ifdef LOG_AUCTION
+ num_normal_forward_bids_submitted = 0;
+ num_diag_forward_bids_submitted = 0;
+
+ num_forward_diag_to_diag_assignments = 0;
+ num_forward_diag_to_normal_assignments = 0;
+ num_forward_normal_to_diag_assignments = 0;
+ num_forward_normal_to_normal_assignments = 0;
+
+ num_forward_diag_from_diag_thefts = 0;
+ num_forward_diag_from_normal_thefts = 0;
+ num_forward_normal_from_diag_thefts = 0;
+ num_forward_normal_from_normal_thefts = 0;
+
+ // reverse rounds
+ num_normal_reverse_bids_submitted = 0;
+ num_diag_reverse_bids_submitted = 0;
+
+ num_reverse_diag_to_diag_assignments = 0;
+ num_reverse_diag_to_normal_assignments = 0;
+ num_reverse_normal_to_diag_assignments = 0;
+ num_reverse_normal_to_normal_assignments = 0;
+
+ num_reverse_diag_from_diag_thefts = 0;
+ num_reverse_diag_from_normal_thefts = 0;
+ num_reverse_normal_from_diag_thefts = 0;
+ num_reverse_normal_from_normal_thefts = 0;
+#endif
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::assign_forward(IdxType item_idx, IdxType bidder_idx)
+{
+ console_logger->debug("Enter assign_forward, item_idx = {0}, bidder_idx = {1}", item_idx, bidder_idx);
+ sanity_check();
+ // only unassigned bidders submit bids
+ assert(bidders_to_items[bidder_idx] == k_invalid_index);
+
+ IdxType old_item_owner = items_to_bidders[item_idx];
+
+ // set new owner
+ bidders_to_items[bidder_idx] = item_idx;
+ items_to_bidders[item_idx] = bidder_idx;
+
+ // remove bidder and item from the sets of unassigned bidders/items
+ remove_unassigned_bidder(bidder_idx);
+
+ if (k_invalid_index != old_item_owner) {
+ // old owner of item becomes unassigned
+ bidders_to_items[old_item_owner] = k_invalid_index;
+ add_unassigned_bidder(old_item_owner);
+ // existing edge was removed, decrease partial_cost
+ partial_cost -= get_item_bidder_cost(item_idx, old_item_owner);
+ } else {
+ // item was unassigned before
+ remove_unassigned_item(item_idx);
+ }
+
+ // new edge was added to matching, increase partial cost
+ partial_cost += get_item_bidder_cost(item_idx, bidder_idx);
+
+#ifdef LOG_AUCTION
+
+ if (unassigned_bidders.size() > parallel_threshold) {
+ num_parallel_assignments++;
+ }
+ num_total_assignments++;
+
+
+ int it_d = is_item_diagonal(item_idx);
+ int b_d = is_bidder_diagonal(bidder_idx);
+ // 2 - None
+ int old_d = ( k_invalid_index == old_item_owner ) ? 2 : is_bidder_diagonal(old_item_owner);
+ int key = 100 * old_d + 10 * b_d + it_d;
+ switch(key) {
+ case 211 : num_forward_diag_to_diag_assignments++;
+ break;
+ case 210 : num_forward_diag_to_normal_assignments++;
+ break;
+ case 201 : num_forward_normal_to_diag_assignments++;
+ break;
+ case 200 : num_forward_normal_to_normal_assignments++;
+ break;
+
+ case 111 : num_forward_diag_to_diag_assignments++;
+ num_forward_diag_from_diag_thefts++;
+ break;
+ case 110 : num_forward_diag_to_normal_assignments++;
+ num_forward_diag_from_diag_thefts++;
+ break;
+ break;
+ case 101 : num_forward_normal_to_diag_assignments++;
+ num_forward_normal_from_diag_thefts++;
+ break;
+ break;
+ case 100 : num_forward_normal_to_normal_assignments++;
+ num_forward_normal_from_diag_thefts++;
+ break;
+
+ case 11 : num_forward_diag_to_diag_assignments++;
+ num_forward_diag_from_normal_thefts++;
+ break;
+ case 10 : num_forward_diag_to_normal_assignments++;
+ num_forward_diag_from_normal_thefts++;
+ break;
+ break;
+ case 1 : num_forward_normal_to_diag_assignments++;
+ num_forward_normal_from_normal_thefts++;
+ break;
+ break;
+ case 0 : num_forward_normal_to_normal_assignments++;
+ num_forward_normal_from_normal_thefts++;
+ break;
+ default : std::cerr << "key = " << key << std::endl;
+ throw std::runtime_error("Bug in logging, wrong key");
+ break;
+ }
+#endif
+
+ sanity_check();
+ console_logger->debug("Exit assign_forward, item_idx = {0}, bidder_idx = {1}", item_idx, bidder_idx);
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::assign_reverse(IdxType item_idx, IdxType bidder_idx)
+{
+ console_logger->debug("Enter assign_reverse, item_idx = {0}, bidder_idx = {1}", item_idx, bidder_idx);
+ // only unassigned items submit bids in reverse phase
+ assert(items_to_bidders[item_idx] == k_invalid_index);
+
+ IdxType old_bidder_owner = bidders_to_items[bidder_idx];
+
+ // set new owner
+ bidders_to_items[bidder_idx] = item_idx;
+ items_to_bidders[item_idx] = bidder_idx;
+
+ // remove bidder and item from the sets of unassigned bidders/items
+ remove_unassigned_item(item_idx);
+
+ if (k_invalid_index != old_bidder_owner) {
+ // old owner of item becomes unassigned
+ items_to_bidders[old_bidder_owner] = k_invalid_index;
+ add_unassigned_item(old_bidder_owner);
+ // existing edge was removed, decrease partial_cost
+ partial_cost -= get_item_bidder_cost(old_bidder_owner, bidder_idx);
+ } else {
+ // item was unassigned before
+ remove_unassigned_bidder(bidder_idx);
+ }
+
+ // new edge was added to matching, increase partial cost
+ partial_cost += get_item_bidder_cost(item_idx, bidder_idx);
+
+#ifdef LOG_AUCTION
+ if (unassigned_items.size() > parallel_threshold) {
+ num_parallel_assignments++;
+ }
+ num_total_assignments++;
+
+ int it_d = is_item_diagonal(item_idx);
+ int b_d = is_bidder_diagonal(bidder_idx);
+ // 2 - None
+ int old_d = (k_invalid_index == old_bidder_owner) ? 2 : is_item_diagonal(old_bidder_owner);
+ int key = 100 * old_d + 10 * it_d + b_d;
+ switch(key) {
+ case 211 : num_reverse_diag_to_diag_assignments++;
+ break;
+ case 210 : num_reverse_diag_to_normal_assignments++;
+ break;
+ case 201 : num_reverse_normal_to_diag_assignments++;
+ break;
+ case 200 : num_reverse_normal_to_normal_assignments++;
+ break;
+
+ case 111 : num_reverse_diag_to_diag_assignments++;
+ num_reverse_diag_from_diag_thefts++;
+ break;
+ case 110 : num_reverse_diag_to_normal_assignments++;
+ num_reverse_diag_from_diag_thefts++;
+ break;
+ break;
+ case 101 : num_reverse_normal_to_diag_assignments++;
+ num_reverse_normal_from_diag_thefts++;
+ break;
+ break;
+ case 100 : num_reverse_normal_to_normal_assignments++;
+ num_reverse_normal_from_diag_thefts++;
+ break;
+
+ case 11 : num_reverse_diag_to_diag_assignments++;
+ num_reverse_diag_from_normal_thefts++;
+ break;
+ case 10 : num_reverse_diag_to_normal_assignments++;
+ num_reverse_diag_from_normal_thefts++;
+ break;
+ break;
+ case 1 : num_reverse_normal_to_diag_assignments++;
+ num_reverse_normal_from_normal_thefts++;
+ break;
+ break;
+ case 0 : num_reverse_normal_to_normal_assignments++;
+ num_reverse_normal_from_normal_thefts++;
+ break;
+ default : std::cerr << "key = " << key << std::endl;
+ throw std::runtime_error("Bug in logging, wrong key");
+ break;
+ }
+
+#endif
+ console_logger->debug("Exit assign_reverse, item_idx = {0}, bidder_idx = {1}", item_idx, bidder_idx);
+}
+
+template<class R, class AO>
+typename AuctionRunnerFR<R, AO>::Real
+AuctionRunnerFR<R, AO>::get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx) const
+{
+ if (wasserstein_power == 1.0) {
+ return dist_lp(bidders[bidder_idx], items[item_idx], internal_p);
+ } else {
+ return std::pow(dist_lp(bidders[bidder_idx], items[item_idx], internal_p),
+ wasserstein_power);
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::assign_to_best_bidder(IdxType item_idx)
+{
+ console_logger->debug("Enter assign_to_best_bidder, item_idx = {0}", item_idx);
+ assert( item_idx >= 0 and item_idx < static_cast<IdxType>(num_items) );
+ assert( forward_bid_table[item_idx].first != k_invalid_index);
+
+ auto best_bidder_idx = forward_bid_table[item_idx].first;
+ auto best_bid_value = forward_bid_table[item_idx].second;
+ assign_forward(item_idx, best_bidder_idx);
+ forward_oracle.sanity_check();
+ forward_oracle.set_price(item_idx, best_bid_value, true);
+ forward_oracle.sanity_check();
+ auto new_bidder_price = -get_item_bidder_cost(item_idx, best_bidder_idx) - best_bid_value;
+ reverse_oracle.set_price(best_bidder_idx, new_bidder_price, false);
+ check_epsilon_css();
+#ifdef LOG_AUCTION
+ forward_price_change_cnt_vec.back()[item_idx]++;
+ reverse_price_change_cnt_vec.back()[best_bidder_idx]++;
+#endif
+ console_logger->debug("Exit assign_to_best_bidder, item_idx = {0}", item_idx);
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::assign_to_best_item(IdxType bidder_idx)
+{
+ console_logger->debug("Enter assign_to_best_item, bidder_idx = {0}", bidder_idx);
+ check_epsilon_css();
+ assert( bidder_idx >= 0 and bidder_idx < static_cast<IdxType>(num_bidders) );
+ assert( reverse_bid_table[bidder_idx].first != k_invalid_index);
+ auto best_item_idx = reverse_bid_table[bidder_idx].first;
+ auto best_bid_value = reverse_bid_table[bidder_idx].second;
+ // both assign_forward and assign_reverse take item index first, bidder index second!
+ assign_reverse(best_item_idx, bidder_idx);
+ reverse_oracle.sanity_check();
+ reverse_oracle.set_price(bidder_idx, best_bid_value, true);
+ reverse_oracle.sanity_check();
+ auto new_item_price = -get_item_bidder_cost(best_item_idx, bidder_idx) - best_bid_value;
+ forward_oracle.set_price(best_item_idx, new_item_price, false);
+#ifdef LOG_AUCTION
+ forward_price_change_cnt_vec.back()[best_item_idx]++;
+ reverse_price_change_cnt_vec.back()[bidder_idx]++;
+#endif
+ check_epsilon_css();
+ console_logger->debug("Exit assign_to_best_item, bidder_idx = {0}", bidder_idx);
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::clear_forward_bid_table()
+{
+ auto item_iter = items_with_bids.begin();
+ while(item_iter != items_with_bids.end()) {
+ auto item_with_bid_idx = *item_iter;
+ forward_bid_table[item_with_bid_idx].first = k_invalid_index;
+ forward_bid_table[item_with_bid_idx].second = k_lowest_bid_value;
+ item_iter = items_with_bids.erase(item_iter);
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::clear_reverse_bid_table()
+{
+ auto bidder_iter = bidders_with_bids.begin();
+ while(bidder_iter != bidders_with_bids.end()) {
+ auto bidder_with_bid_idx = *bidder_iter;
+ reverse_bid_table[bidder_with_bid_idx].first = k_invalid_index;
+ reverse_bid_table[bidder_with_bid_idx].second = k_lowest_bid_value;
+ bidder_iter = bidders_with_bids.erase(bidder_iter);
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::submit_forward_bid(IdxType bidder_idx, const IdxValPairR& bid)
+{
+ IdxType best_item_idx = bid.first;
+ Real bid_value = bid.second;
+ assert( best_item_idx >= 0 );
+
+ auto value_in_bid_table = forward_bid_table[best_item_idx].second;
+ bool new_bid_wins = (value_in_bid_table < bid_value);
+ // if we have tie, lower persistence wins
+// if (value_in_bid_table == bid_value) {
+//
+// assert(forward_bid_table.at(best_item_idx).first != k_invalid_index);
+// assert(&bidders.at(forward_bid_table.at(best_item_idx).first));
+//
+// auto bidder_in_bid_table = bidders[forward_bid_table[best_item_idx].first];
+// new_bid_wins = bidders[best_item_idx].persistence_lp(internal_p) < bidder_in_bid_table.persistence_lp(internal_p);
+// }
+
+ if (new_bid_wins) {
+ forward_bid_table[best_item_idx].first = bidder_idx;
+ forward_bid_table[best_item_idx].second = bid_value;
+ }
+
+ items_with_bids.insert(best_item_idx);
+
+#ifdef LOG_AUCTION
+
+ if (unassigned_bidders.size() > parallel_threshold) {
+ num_parallel_bids++;
+ }
+ num_total_bids++;
+
+
+ if (is_bidder_diagonal(bidder_idx)) {
+ num_diag_forward_bids_submitted++;
+ } else {
+ num_normal_forward_bids_submitted++;
+ }
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::submit_reverse_bid(IdxType item_idx, const IdxValPairR& bid)
+{
+ assert( items.at(item_idx).is_diagonal() or items.at(item_idx).is_normal() );
+ IdxType best_bidder_idx = bid.first;
+ assert( bidders.at(best_bidder_idx).is_diagonal() or bidders.at(best_bidder_idx).is_normal() );
+ Real bid_value = bid.second;
+ assert(bid_value > k_lowest_bid_value);
+ auto value_in_bid_table = reverse_bid_table[best_bidder_idx].second;
+ bool new_bid_wins = (value_in_bid_table < bid_value);
+ // if we have tie, lower persistence wins
+// if (value_in_bid_table == bid_value) {
+// assert(reverse_bid_table[best_bidder_idx].first != k_invalid_index);
+// auto bidder_in_bid_table = bidders[reverse_bid_table[best_bidder_idx].first];
+// new_bid_wins = bidders[best_bidder_idx].persistence_lp(internal_p) < bidder_in_bid_table.persistence_lp(internal_p);
+// }
+ if (new_bid_wins) {
+ reverse_bid_table[best_bidder_idx].first = item_idx;
+ reverse_bid_table[best_bidder_idx].second = bid_value;
+ }
+ bidders_with_bids.insert(best_bidder_idx);
+
+#ifdef LOG_AUCTION
+
+ if (unassigned_items.size() > parallel_threshold) {
+ num_parallel_bids++;
+ }
+ num_total_bids++;
+
+ if (is_item_diagonal(item_idx)) {
+ num_diag_reverse_bids_submitted++;
+ } else {
+ num_normal_reverse_bids_submitted++;
+ }
+#endif
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::print_debug()
+{
+#ifdef DEBUG_FR_AUCTION
+ std::cout << "**********************" << std::endl;
+ std::cout << "Current assignment:" << std::endl;
+ for(size_t idx = 0; idx < bidders_to_items.size(); ++idx) {
+ std::cout << idx << " <--> " << bidders_to_items[idx] << std::endl;
+ }
+ std::cout << "Weights: " << std::endl;
+ //for(size_t i = 0; i < num_bidders; ++i) {
+ //for(size_t j = 0; j < num_items; ++j) {
+ //std::cout << oracle.weight_matrix[i][j] << " ";
+ //}
+ //std::cout << std::endl;
+ //}
+ std::cout << "Bidder prices: " << std::endl;
+ for(const auto price : forward_oracle.get_prices()) {
+ std::cout << price << std::endl;
+ }
+ std::cout << "**********************" << std::endl;
+#endif
+}
+
+
+template<class R, class AO>
+typename AuctionRunnerFR<R,AO>::Real
+AuctionRunnerFR<R, AO>::get_relative_error(const bool debug_output) const
+{
+ Real result;
+ Real gamma = get_gamma();
+ // cost minus n epsilon
+ Real reduced_cost = partial_cost - num_bidders * get_epsilon();
+ if ( reduced_cost < 0) {
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->debug("Epsilon too large, reduced_cost = {0}", reduced_cost);
+ }
+#endif
+ result = k_max_relative_error;
+ } else {
+ Real denominator = std::pow(reduced_cost, 1.0 / wasserstein_power) - gamma;
+ if (denominator <= 0) {
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->debug("Epsilon too large, reduced_cost = {0}, denominator = {1}, gamma = {2}", reduced_cost, denominator, gamma);
+ }
+#endif
+ result = k_max_relative_error;
+ } else {
+ Real numerator = 2 * gamma +
+ std::pow(partial_cost, 1.0 / wasserstein_power) -
+ std::pow(reduced_cost, 1.0 / wasserstein_power);
+
+ result = numerator / denominator;
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->debug("Reduced_cost = {0}, denominator = {1}, numerator {2}, error = {3}, gamma = {4}",
+ reduced_cost,
+ denominator,
+ numerator,
+ result,
+ gamma);
+ }
+#endif
+ }
+ }
+ return result;
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::flush_assignment()
+{
+ console_logger->debug("Enter flush_assignment");
+ for(auto& b2i : bidders_to_items) {
+ b2i = k_invalid_index;
+ }
+ for(auto& i2b : items_to_bidders) {
+ i2b = k_invalid_index;
+ }
+
+ // all bidders and items become unassigned
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_bidders.insert(bidder_idx);
+ }
+
+ // all items and items become unassigned
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ unassigned_items.insert(item_idx);
+ }
+
+
+ //forward_oracle.adjust_prices();
+ //reverse_oracle.adjust_prices();
+
+ partial_cost = 0.0;
+ unassigned_bidders_persistence = total_bidders_persistence;
+ unassigned_items_persistence = total_items_persistence;
+
+#ifdef ORDERED_BY_PERSISTENCE
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_bidders_by_persistence.insert(std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx));
+ }
+
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ unassigned_items_by_persistence.insert(std::make_pair(items[item_idx].persistence_lp(1.0), item_idx));
+ }
+#endif
+
+#ifdef LOG_AUCTION
+
+ reset_phase_stat();
+
+ forward_price_change_cnt_vec.push_back(std::vector<size_t>(num_items, 0));
+ reverse_price_change_cnt_vec.push_back(std::vector<size_t>(num_bidders, 0));
+
+ // all bidders and items become unassigned
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders.insert(bidder_idx);
+ } else {
+ unassigned_diag_bidders.insert(bidder_idx);
+ }
+ }
+
+ never_assigned_bidders = unassigned_bidders;
+
+ for(size_t item_idx = 0; item_idx < items.size(); ++item_idx) {
+ if (is_item_normal(item_idx)) {
+ unassigned_normal_items.insert(item_idx);
+ } else {
+ unassigned_diag_items.insert(item_idx);
+ }
+ }
+
+ never_assigned_items = unassigned_items;
+#endif
+ check_epsilon_css();
+ console_logger->debug("Exit flush_assignment");
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::set_epsilon(Real new_val)
+{
+ assert(new_val > 0.0);
+ epsilon = new_val;
+ forward_oracle.set_epsilon(new_val);
+ reverse_oracle.set_epsilon(new_val);
+}
+
+
+template<class R, class AO>
+bool AuctionRunnerFR<R, AO>::continue_forward(const size_t original_unassigned_bidders, const size_t min_forward_matching_increment)
+{
+// if (unassigned_threshold == 0) {
+// return not unassigned_bidders.empty() and get_relative_error(false) > delta;
+// }
+ //return unassigned_bidders.size() > unassigned_threshold and
+ //static_cast<int>(unassigned_bidders.size()) >= static_cast<int>(original_unassigned_bidders) - static_cast<int>(min_forward_matching_increment);
+ return unassigned_bidders.size() > unassigned_threshold and
+ static_cast<int>(unassigned_bidders.size()) >= static_cast<int>(original_unassigned_bidders) - static_cast<int>(min_forward_matching_increment) and
+ get_relative_error() >= delta;
+// return not unassigned_bidders.empty() and
+// static_cast<int>(unassigned_bidders.size()) >= static_cast<int>(original_unassigned_bidders) - static_cast<int>(min_forward_matching_increment) and
+// get_relative_error() >= delta;
+}
+
+
+template<class R, class AO>
+bool AuctionRunnerFR<R, AO>::continue_reverse(const size_t original_unassigned_items, const size_t min_reverse_matching_increment)
+{
+ //return unassigned_items.size() > unassigned_threshold and
+ //static_cast<int>(unassigned_items.size()) >= static_cast<int>(original_unassigned_items) - static_cast<int>(min_reverse_matching_increment);
+ return unassigned_items.size() > unassigned_threshold and
+ static_cast<int>(unassigned_items.size()) >= static_cast<int>(original_unassigned_items) - static_cast<int>(min_reverse_matching_increment) and
+ get_relative_error() >= delta;
+// return not unassigned_items.empty() and
+// static_cast<int>(unassigned_items.size()) >= static_cast<int>(original_unassigned_items) - static_cast<int>(min_reverse_matching_increment) and
+// get_relative_error() >= delta;
+}
+
+
+template<class R, class AO>
+bool AuctionRunnerFR<R, AO>::continue_phase()
+{
+ //return not unassigned_bidders.empty();
+ return unassigned_bidders.size() > unassigned_threshold and get_relative_error() >= delta;
+// return not never_assigned_bidders.empty() or
+// not never_assigned_items.empty() or
+// unassigned_bidders.size() > unassigned_threshold and get_relative_error() >= delta;
+}
+
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::run_auction_phase()
+{
+ num_phase++;
+ while(continue_phase()) {
+ forward_oracle.recompute_top_diag_items(true);
+ forward_oracle.sanity_check();
+ console_logger->debug("forward_oracle recompute_top_diag_items done");
+ run_forward_auction_phase();
+ reverse_oracle.recompute_top_diag_items(true);
+ console_logger->debug("reverse_oracle recompute_top_diag_items done");
+ reverse_oracle.sanity_check();
+ run_reverse_auction_phase();
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::run_auction_phases(const int max_num_phases, const Real _initial_epsilon)
+{
+ set_epsilon(_initial_epsilon);
+ assert( forward_oracle.get_epsilon() > 0 );
+ assert( reverse_oracle.get_epsilon() > 0 );
+ for(int phase_num = 0; phase_num < max_num_phases; ++phase_num) {
+ flush_assignment();
+ console_logger->info("Phase {0} started: eps = {1}",
+ num_phase,
+ get_epsilon());
+
+ run_auction_phase();
+ Real current_result = partial_cost;
+#ifdef LOG_AUCTION
+ console_logger->info("Phase {0} done: current_result = {1}, eps = {2}, unassigned_threshold = {3}, unassigned = {4}, error = {5}, gamma = {6}",
+ num_phase,
+ partial_cost,
+ get_epsilon(),
+ format_int<>(unassigned_threshold),
+ unassigned_bidders.size(),
+ get_relative_error(false),
+ get_gamma());
+
+ console_logger->info("Phase {0} done: num_rounds / num_parallelizable_rounds = {1} / {2} = {3}, cumulative rounds = {4}",
+ num_phase,
+ format_int(num_rounds_non_cumulative),
+ format_int(num_parallelizable_rounds),
+ static_cast<double>(num_parallelizable_rounds) / static_cast<double>(num_rounds_non_cumulative),
+ format_int(num_rounds)
+ );
+
+ console_logger->info("parallelizable_forward_rounds / num_forward_rounds = {0} / {1} = {2}",
+ format_int<>(num_parallelizable_forward_rounds),
+ format_int<>(num_forward_rounds_non_cumulative),
+ static_cast<double>(num_parallelizable_forward_rounds) / static_cast<double>(num_forward_rounds_non_cumulative)
+ );
+
+ num_parallelizable_forward_rounds = 0;
+ num_forward_rounds_non_cumulative = 0;
+
+ console_logger->info("parallelizable_reverse_rounds / num_reverse_rounds = {0} / {1} = {2}",
+ format_int<>(num_parallelizable_reverse_rounds),
+ format_int<>(num_reverse_rounds_non_cumulative),
+ static_cast<double>(num_parallelizable_reverse_rounds) / static_cast<double>(num_reverse_rounds_non_cumulative)
+ );
+
+ num_parallelizable_reverse_rounds = 0;
+ num_reverse_rounds_non_cumulative = 0;
+
+ console_logger->info("num_parallel_bids / num_total_bids = {0} / {1} = {2}, num_parallel_assignments / num_total_assignments = {3} / {4} = {5}",
+ format_int<>(num_parallel_bids),
+ format_int<>(num_total_bids),
+ static_cast<double>(num_parallel_bids) / static_cast<double>(num_total_bids),
+ format_int<>(num_parallel_assignments),
+ format_int<>(num_total_assignments),
+ static_cast<double>(num_parallel_assignments) / static_cast<double>(num_total_assignments)
+ );
+
+ auto forward_min_max_price = forward_oracle.get_minmax_price();
+ auto reverse_min_max_price = reverse_oracle.get_minmax_price();
+
+ console_logger->info("forward min price = {0}, max price = {1}; reverse min price = {2}, reverse max price = {3}",
+ forward_min_max_price.first,
+ forward_min_max_price.second,
+ reverse_min_max_price.first,
+ reverse_min_max_price.second
+ );
+
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ forward_price_stat_logger->info("{0} {1} {2} {3} {4}",
+ phase_num,
+ item_idx,
+ items[item_idx].getRealX(),
+ items[item_idx].getRealY(),
+ forward_price_change_cnt_vec.back()[item_idx]
+ );
+ }
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ reverse_price_stat_logger->info("{0} {1} {2} {3} {4}",
+ phase_num,
+ bidder_idx,
+ bidders[bidder_idx].getRealX(),
+ bidders[bidder_idx].getRealY(),
+ reverse_price_change_cnt_vec.back()[bidder_idx]
+ );
+ }
+#endif
+
+ if (get_relative_error(true) <= delta) {
+ break;
+ }
+ // decrease epsilon for the next iteration
+ decrease_epsilon();
+
+ unassigned_threshold = std::floor( static_cast<double>(unassigned_threshold) / 1.1 );
+
+ if (phase_can_be_final()) {
+ unassigned_threshold = 0;
+#ifdef LOG_AUCTION
+ console_logger->info("Unassigned threshold set to zero!");
+#endif
+ }
+ }
+}
+
+template<class R, class AO>
+bool AuctionRunnerFR<R, AO>::phase_can_be_final() const
+{
+ Real estimated_error;
+ // cost minus n epsilon
+ Real reduced_cost = partial_cost - num_bidders * get_epsilon();
+ if (reduced_cost <= 0.0) {
+ return false;
+ } else {
+ Real denominator = std::pow(reduced_cost, 1.0 / wasserstein_power);
+ if (denominator <= 0) {
+ return false;
+ } else {
+ Real numerator = std::pow(partial_cost, 1.0 / wasserstein_power) -
+ std::pow(reduced_cost, 1.0 / wasserstein_power);
+
+ estimated_error = numerator / denominator;
+ return estimated_error <= delta;
+ }
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::run_auction()
+{
+ double init_eps = ( initial_epsilon > 0.0 ) ? initial_epsilon : std::min(forward_oracle.max_val_, reverse_oracle.max_val_) / 4.0 ;
+ assert(init_eps > 0.0);
+ run_auction_phases(max_num_phases, init_eps);
+ is_distance_computed = true;
+ wasserstein_cost = partial_cost;
+ if (get_relative_error() > delta) {
+#ifndef FOR_R_TDA
+ std::cerr << "Maximum iteration number exceeded, exiting. Current result is: ";
+ std::cerr << get_wasserstein_distance() << std::endl;
+#endif
+ throw std::runtime_error("Maximum iteration number exceeded");
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::add_unassigned_bidder(const size_t bidder_idx)
+{
+ const DgmPoint& bidder = bidders[bidder_idx];
+ unassigned_bidders.insert(bidder_idx);
+ unassigned_bidders_persistence += get_cost_to_diagonal(bidder);
+
+#ifdef ORDERED_BY_PERSISTENCE
+ unassigned_bidders_by_persistence.insert(std::make_pair(bidder.persistence_lp(1.0), bidder_idx));
+#endif
+
+#ifdef LOG_AUCTION
+ if (is_bidder_diagonal(bidder_idx)) {
+ unassigned_diag_bidders.insert(bidder_idx);
+ } else {
+ unassigned_normal_bidders.insert(bidder_idx);
+ }
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::add_unassigned_item(const size_t item_idx)
+{
+ const DgmPoint& item = items[item_idx];
+ unassigned_items.insert(item_idx);
+ unassigned_items_persistence += get_cost_to_diagonal(item);
+
+#ifdef ORDERED_BY_PERSISTENCE
+ unassigned_items_by_persistence.insert(std::make_pair(item.persistence_lp(1.0), item_idx));
+#endif
+
+#ifdef LOG_AUCTION
+ if (is_item_diagonal(item_idx)) {
+ unassigned_diag_items.insert(item_idx);
+ } else {
+ unassigned_normal_items.insert(item_idx);
+ }
+#endif
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::remove_unassigned_bidder(const size_t bidder_idx)
+{
+ unassigned_bidders_persistence -= get_cost_to_diagonal(bidders[bidder_idx]);
+
+ unassigned_bidders.erase(bidder_idx);
+ never_assigned_bidders.erase(bidder_idx);
+
+#ifdef ORDERED_BY_PERSISTENCE
+ unassigned_bidders_by_persistence.erase(std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx));
+#endif
+
+#ifdef LOG_AUCTION
+ if (is_bidder_diagonal(bidder_idx)) {
+ unassigned_diag_bidders.erase(bidder_idx);
+ } else {
+ unassigned_normal_bidders.erase(bidder_idx);
+ }
+ if (never_assigned_bidders.empty() and not all_assigned_round_found) {
+ all_assigned_round = num_rounds_non_cumulative;
+ all_assigned_round_found = true;
+ }
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::remove_unassigned_item(const size_t item_idx)
+{
+ console_logger->debug("Enter remove_unassigned_item, unassigned_items.size = {0}", unassigned_items.size());
+ unassigned_items_persistence -= get_cost_to_diagonal(items[item_idx]);
+
+ never_assigned_items.erase(item_idx);
+ unassigned_items.erase(item_idx);
+
+#ifdef ORDERED_BY_PERSISTENCE
+ unassigned_items_by_persistence.erase(std::make_pair(items[item_idx].persistence_lp(1.0), item_idx));
+#endif
+
+#ifdef LOG_AUCTION
+ if (is_item_normal(item_idx)) {
+ unassigned_normal_items.erase(item_idx);
+ } else {
+ unassigned_diag_items.erase(item_idx);
+ }
+ if (never_assigned_items.empty() and not all_assigned_round_found) {
+ all_assigned_round = num_rounds_non_cumulative;
+ all_assigned_round_found = true;
+ }
+#endif
+ console_logger->debug("Exit remove_unassigned_item, unassigned_items.size = {0}", unassigned_items.size());
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::decrease_epsilon()
+{
+ auto eps_diff = 1.01 * get_epsilon() * (epsilon_common_ratio - 1.0 ) / epsilon_common_ratio;
+ reverse_oracle.adjust_prices( -eps_diff );
+ set_epsilon( get_epsilon() / epsilon_common_ratio );
+ cumulative_epsilon_factor *= epsilon_common_ratio;
+}
+
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::run_reverse_auction_phase()
+{
+ console_logger->debug("Enter run_reverse_auction_phase");
+ size_t original_unassigned_items = unassigned_items.size();
+// const size_t min_reverse_matching_increment = std::max( static_cast<size_t>(1), static_cast<size_t>(original_unassigned_items / 10));
+ size_t min_reverse_matching_increment = 1;
+
+ while (continue_reverse(original_unassigned_items, min_reverse_matching_increment)) {
+ num_rounds++;
+ num_rounds_non_cumulative++;
+ console_logger->debug("started round = {0}, reverse, unassigned = {1}", num_rounds, unassigned_items.size());
+
+ check_epsilon_css();
+#ifdef LOG_AUCTION
+ if (unassigned_items.size() >= parallel_threshold) {
+ ++num_parallelizable_reverse_rounds;
+ ++num_parallelizable_rounds;
+ }
+ num_reverse_rounds++;
+ num_reverse_rounds_non_cumulative++;
+#endif
+
+ reset_round_stat();
+ // bidding
+#ifdef ORDERED_BY_PERSISTENCE
+ std::vector<size_t> active_items;
+ active_items.reserve(batch_size);
+ for(auto iter = unassigned_items_by_persistence.begin();
+ iter != unassigned_items_by_persistence.end(); ++iter) {
+ active_items.push_back(iter->second);
+ if (active_items.size() >= batch_size) {
+ break;
+ }
+ }
+ run_reverse_bidding_step(active_items);
+#else
+ //if (not never_assigned_items.empty())
+ //run_reverse_bidding_step(never_assigned_items);
+ //else
+ //run_reverse_bidding_step(unassigned_items);
+ run_reverse_bidding_step(unassigned_items);
+#endif
+
+ // assignment phase
+ for(auto bidder_idx : bidders_with_bids ) {
+ assign_to_best_item(bidder_idx);
+ }
+
+ check_epsilon_css();
+
+ console_logger->debug("ended round = {0}, reverse, unassigned = {1}", num_rounds, unassigned_items.size());
+
+#ifdef LOG_AUCTION
+
+ reverse_plot_logger->info("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15} {16} {17} {18} {19} {20} {21} {22}",
+ num_phase,
+ num_rounds,
+ num_reverse_rounds,
+ unassigned_bidders.size(),
+ get_gamma(),
+ partial_cost,
+ reverse_oracle.get_epsilon(),
+ num_normal_reverse_bids_submitted,
+ num_diag_reverse_bids_submitted,
+ num_reverse_diag_to_diag_assignments,
+ num_reverse_diag_to_normal_assignments,
+ num_reverse_normal_to_diag_assignments,
+ num_reverse_normal_to_normal_assignments,
+ num_reverse_diag_from_diag_thefts,
+ num_reverse_diag_from_normal_thefts,
+ num_reverse_normal_from_diag_thefts,
+ num_reverse_normal_from_normal_thefts,
+ unassigned_normal_bidders.size(),
+ unassigned_diag_bidders.size(),
+ unassigned_normal_items.size(),
+ unassigned_diag_items.size(),
+ reverse_oracle.get_heap_top_size(),
+ get_relative_error(false)
+ );
+ sanity_check();
+#endif
+ }
+}
+
+template<class R, class AO>
+template<class Range>
+void AuctionRunnerFR<R, AO>::run_forward_bidding_step(const Range& active_bidders)
+{
+ clear_forward_bid_table();
+ for(const auto bidder_idx : active_bidders) {
+ console_logger->debug("current bidder (forward): {0}, persistence = {1}", bidders[bidder_idx], bidders[bidder_idx].persistence_lp(1.0));
+ submit_forward_bid(bidder_idx, forward_oracle.get_optimal_bid(bidder_idx));
+ if (++num_forward_bids_submitted >= max_bids_per_round) {
+ break;
+ }
+ }
+}
+
+template<class R, class AO>
+template<class Range>
+void AuctionRunnerFR<R, AO>::run_reverse_bidding_step(const Range& active_items)
+{
+ clear_reverse_bid_table();
+
+ assert(bidders_with_bids.empty());
+ assert(std::all_of(reverse_bid_table.begin(), reverse_bid_table.end(),
+ [ki = k_invalid_index, kl = k_lowest_bid_value](const IdxValPairR& b) { return static_cast<size_t>(b.first) == ki and b.second == kl; }));
+
+ for(const auto item_idx : active_items) {
+ console_logger->debug("current bidder (reverse): {0}, persistence = {1}", items[item_idx], items[item_idx].persistence_lp(1.0));
+ submit_reverse_bid(item_idx, reverse_oracle.get_optimal_bid(item_idx));
+ if (++num_reverse_bids_submitted >= max_bids_per_round) {
+ break;
+ }
+ }
+}
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::run_forward_auction_phase()
+{
+ const size_t original_unassigned_bidders = unassigned_bidders.size();
+// const size_t min_forward_matching_increment = std::max( static_cast<size_t>(1), static_cast<size_t>(original_unassigned_bidders / 10));
+ const size_t min_forward_matching_increment = 1;
+ while (continue_forward(original_unassigned_bidders, min_forward_matching_increment)) {
+ console_logger->debug("started round = {0}, forward, unassigned = {1}", num_rounds, unassigned_bidders.size());
+ check_epsilon_css();
+ num_rounds++;
+#ifdef LOG_AUCTION
+ if (unassigned_bidders.size() >= parallel_threshold) {
+ ++num_parallelizable_forward_rounds;
+ ++num_parallelizable_rounds;
+ }
+ num_forward_rounds++;
+ num_forward_rounds_non_cumulative++;
+#endif
+
+ reset_round_stat();
+ // bidding step
+#ifdef ORDERED_BY_PERSISTENCE
+ std::vector<size_t> active_bidders;
+ active_bidders.reserve(batch_size);
+ for(auto iter = unassigned_bidders_by_persistence.begin();
+ iter != unassigned_bidders_by_persistence.end(); ++iter) {
+ active_bidders.push_back(iter->second);
+ if (active_bidders.size() >= batch_size) {
+ break;
+ }
+ }
+ run_forward_bidding_step(active_bidders);
+#else
+
+ //if (not never_assigned_bidders.empty())
+ //run_forward_bidding_step(never_assigned_bidders);
+ //else
+ //run_forward_bidding_step(unassigned_bidders);
+ run_forward_bidding_step(unassigned_bidders);
+#endif
+
+ // assignment step
+ for(auto item_idx : items_with_bids ) {
+ assign_to_best_bidder(item_idx);
+ }
+
+ console_logger->debug("ended round = {0}, forward, unassigned = {1}", num_rounds, unassigned_bidders.size());
+ check_epsilon_css();
+
+#ifdef LOG_AUCTION
+ forward_plot_logger->info("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15} {16} {17} {18} {19} {20} {21} {22}",
+ num_phase,
+ num_rounds,
+ num_forward_rounds,
+ unassigned_bidders.size(),
+ get_gamma(),
+ partial_cost,
+ forward_oracle.get_epsilon(),
+ num_normal_forward_bids_submitted,
+ num_diag_forward_bids_submitted,
+ num_forward_diag_to_diag_assignments,
+ num_forward_diag_to_normal_assignments,
+ num_forward_normal_to_diag_assignments,
+ num_forward_normal_to_normal_assignments,
+ num_forward_diag_from_diag_thefts,
+ num_forward_diag_from_normal_thefts,
+ num_forward_normal_from_diag_thefts,
+ num_forward_normal_from_normal_thefts,
+ unassigned_normal_bidders.size(),
+ unassigned_diag_bidders.size(),
+ unassigned_normal_items.size(),
+ unassigned_diag_items.size(),
+ forward_oracle.get_heap_top_size(),
+ get_relative_error(false)
+ );
+#endif
+ } ;
+
+}
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::assign_diag_to_diag()
+{
+ size_t n_diag_to_diag = std::min(num_diag_bidders, num_diag_items);
+ if (n_diag_to_diag < 2)
+ return;
+ for(size_t i = 0; i < n_diag_to_diag; ++i) {
+ }
+}
+
+template<class R, class AO>
+typename AuctionRunnerFR<R, AO>::Real
+AuctionRunnerFR<R, AO>::get_wasserstein_distance()
+{
+ assert(is_distance_computed);
+ return std::pow(wasserstein_cost, 1.0 / wasserstein_power);
+}
+
+template<class R, class AO>
+typename AuctionRunnerFR<R, AO>::Real
+AuctionRunnerFR<R, AO>::get_wasserstein_cost()
+{
+ assert(is_distance_computed);
+ return wasserstein_cost;
+}
+
+
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::sanity_check()
+{
+#ifdef DEBUG_FR_AUCTION
+ assert(partial_cost >= 0);
+
+
+ assert(num_diag_items == num_normal_bidders);
+ assert(num_diag_bidders == num_normal_items);
+ assert(num_diag_bidders + num_normal_bidders == num_bidders);
+ assert(num_diag_items + num_normal_items == num_items);
+ assert(num_items == num_bidders);
+
+
+ for(size_t b = 0; b < num_bidders; ++b) {
+ assert( is_bidder_diagonal(b) == bidders.at(b).is_diagonal() );
+ assert( is_bidder_normal(b) == bidders.at(b).is_normal() );
+ }
+
+ for(size_t i = 0; i < num_items; ++i) {
+ assert( is_item_diagonal(i) == items.at(i).is_diagonal() );
+ assert( is_item_normal(i) == items.at(i).is_normal() );
+ }
+
+ // check matching consistency
+ assert(bidders_to_items.size() == num_bidders);
+ assert(items_to_bidders.size() == num_bidders);
+
+ assert(std::count(bidders_to_items.begin(), bidders_to_items.end(), k_invalid_index) == std::count(items_to_bidders.begin(), items_to_bidders.end(), k_invalid_index));
+
+ Real true_partial_cost = 0.0;
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (bidders_to_items[bidder_idx] != k_invalid_index) {
+ assert(items_to_bidders.at(bidders_to_items[bidder_idx]) == static_cast<int>(bidder_idx));
+ true_partial_cost += get_item_bidder_cost(bidders_to_items[bidder_idx], bidder_idx);
+ }
+ }
+
+ assert(fabs(partial_cost - true_partial_cost) < 0.00001);
+
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ if (items_to_bidders[item_idx] != k_invalid_index) {
+ assert(bidders_to_items.at(items_to_bidders[item_idx]) == static_cast<int>(item_idx));
+ }
+ }
+
+#ifdef ORDERED_BY_PERSISTENCE
+ assert(unassigned_bidders.size() == unassigned_bidders_by_persistence.size());
+ if (unassigned_items.size() != unassigned_items_by_persistence.size()) {
+ console_logger->error("unassigned_items.size() = {0}, unassigned_items_by_persistence.size() = {1}", unassigned_items.size(),unassigned_items_by_persistence.size());
+ console_logger->error("unassigned_items = {0}, unassigned_items_by_persistence = {1}", format_container_to_log(unassigned_items),format_pair_container_to_log(unassigned_items_by_persistence));
+ }
+ assert(unassigned_items.size() == unassigned_items_by_persistence.size());
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (bidders_to_items[bidder_idx] == k_invalid_index) {
+ assert(unassigned_bidders.count(bidder_idx) == 1);
+ assert(unassigned_bidders_by_persistence.count(std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx)) == 1);
+ } else {
+ assert(unassigned_bidders.count(bidder_idx) == 0);
+ assert(unassigned_bidders_by_persistence.count(std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx)) == 0);
+ }
+ }
+
+ for(size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ if (items_to_bidders[item_idx] == k_invalid_index) {
+ assert(unassigned_items.count(item_idx) == 1);
+ assert(unassigned_items_by_persistence.count(std::make_pair(items[item_idx].persistence_lp(1.0), item_idx)) == 1);
+ } else {
+ assert(unassigned_items.count(item_idx) == 0);
+ assert(unassigned_items_by_persistence.count(std::make_pair(items[item_idx].persistence_lp(1.0), item_idx)) == 0);
+ }
+ }
+#endif
+
+
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::check_epsilon_css()
+{
+#ifdef DEBUG_FR_AUCTION
+ sanity_check();
+
+ std::vector<double> b_prices = reverse_oracle.get_prices();
+ std::vector<double> i_prices = forward_oracle.get_prices();
+ double eps = forward_oracle.get_epsilon();
+
+ for(size_t b = 0; b < num_bidders; ++b) {
+ for(size_t i = 0; i < num_items; ++i) {
+ if(((is_bidder_normal(b) and is_item_diagonal(i)) or (is_bidder_diagonal(b) and is_item_normal(i))) and b != i)
+ continue;
+ if (b_prices[b] + i_prices[i] + eps < -get_item_bidder_cost(i, b) - 0.000001) {
+ console_logger->debug("b = {0}, i = {1}, eps = {2}, b_price = {3}, i_price[i] = {4}, cost = {5}, b_price + i_price + eps = {6}",
+ b,
+ i,
+ eps,
+ b_prices[b],
+ i_prices[i],
+ get_item_bidder_cost(i, b),
+ b_prices[b] + i_prices[i] + eps
+ );
+ }
+ assert(b_prices[b] + i_prices[i] + eps >= -get_item_bidder_cost(i, b) - 0.000001);
+ }
+ }
+
+ for(size_t b = 0; b < num_bidders; ++b) {
+ auto i = bidders_to_items[b];
+ if (i != k_invalid_index) {
+ assert( fabs(b_prices[b] + i_prices[i] + get_item_bidder_cost(i, b)) < 0.000001 );
+ }
+ }
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerFR<R, AO>::print_matching()
+{
+#ifdef DEBUG_FR_AUCTION
+ sanity_check();
+ for(size_t bidder_idx = 0; bidder_idx < bidders_to_items.size(); ++bidder_idx) {
+ if (bidders_to_items[bidder_idx] >= 0) {
+ auto pA = bidders[bidder_idx];
+ auto pB = items[bidders_to_items[bidder_idx]];
+ std::cout << pA << " <-> " << pB << "+" << pow(dist_lp(pA, pB, internal_p), wasserstein_power) << std::endl;
+ } else {
+ assert(false);
+ }
+ }
+#endif
+}
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_gs.h b/geom_matching/wasserstein/include/auction_runner_gs.h
index ce139f1..fc76987 100644
--- a/geom_matching/wasserstein/include/auction_runner_gs.h
+++ b/geom_matching/wasserstein/include/auction_runner_gs.h
@@ -29,98 +29,94 @@ derivative works thereof, in binary and source code form.
#ifndef AUCTION_RUNNER_GS_H
#define AUCTION_RUNNER_GS_H
+#include <memory>
#include <unordered_set>
+#include "spdlog/spdlog.h"
#include "auction_oracle.h"
-//#define KEEP_UNASSIGNED_ORDERED
-// if this symbol is defined,
-// unassigned bidders are processed in a lexicographic order.
-// See LexicogrCompDiagramPoint comparator.
-
-
-namespace geom_ws {
-
-//using AuctionOracle = AuctionOracleLazyHeapRestricted;
-using AuctionOracle = AuctionOracleKDTreeRestricted;
-
-#ifdef KEEP_UNASSIGNED_ORDERED
-using IdxPointPair = std::pair<size_t, DiagramPoint>;
-
-struct LexicogrCompDiagramPoint {
- bool operator ()(const IdxPointPair& a, const IdxPointPair& b) {
- const auto& p1 = a.second;
- const auto& p2 = b.second;
-
- return ( (not p1.isDiagonal() and p2.isDiagonal()) or
- ( p1.isDiagonal() == p2.isDiagonal() and p1.getRealX() < p2.getRealX() ) or
- ( p1.isDiagonal() == p2.isDiagonal() and p1.getRealX() == p2.getRealX() and p1.getRealY() < p2.getRealY() ) or
- ( p1.isDiagonal() == p2.isDiagonal() and p1.getRealX() == p2.getRealX() and p1.getRealY() == p2.getRealY() and a.first < b.first ) );
- }
-};
-
-using OrderedUnassignedKeeper = std::set<IdxPointPair, LexicogrCompDiagramPoint>;
-#endif
-
-// the two parameters that you can tweak in auction algorithm are:
-// 1. epsilonCommonRatio
-// 2. maxIterNum
+namespace hera {
+namespace ws {
+template<class RealType_ = double, class AuctionOracle_ = AuctionOracleKDTreeRestricted<RealType_>, class PointContainer_ = std::vector<DiagramPoint<RealType_>> > // alternatively: AuctionOracleLazyHeap --- TODO
class AuctionRunnerGS {
public:
- AuctionRunnerGS(const std::vector<DiagramPoint>& A,
- const std::vector<DiagramPoint>& B,
- const double q,
- const double _delta,
- const double _internal_p,
- const double _initialEpsilon,
- const double _epsFactor);
- void setEpsilon(double newVal) { assert(epsilon > 0.0); epsilon = newVal; };
- double getEpsilon() const { return epsilon; }
- double getWassersteinDistance();
- double getWassersteinCost();
- double getRelativeError() const { return relativeError; };
- static constexpr int maxIterNum { 35 }; // maximal number of iterations of epsilon-scaling
-private:
+ using Real = RealType_;
+ using AuctionOracle = AuctionOracle_;
+ using DgmPoint = typename AuctionOracle::DiagramPointR;
+ using IdxValPairR = IdxValPair<Real>;
+ using PointContainer = PointContainer_;
+
+
+ AuctionRunnerGS(const PointContainer& A,
+ const PointContainer& B,
+ const AuctionParams<Real>& params,
+ const std::string& _log_filename_prefix = "");
+
+ void set_epsilon(Real new_val) { assert(epsilon > 0.0); epsilon = new_val; };
+ Real get_epsilon() const { return oracle.get_epsilon(); }
+ Real get_wasserstein_cost();
+ Real get_wasserstein_distance();
+ Real get_relative_error() const { return relative_error; };
+ void enable_logging(const char* log_filename, const size_t _max_unassigned_to_log);
+//private:
// private data
- std::vector<DiagramPoint> bidders, items;
- const size_t numBidders;
- const size_t numItems;
- std::vector<IdxType> itemsToBidders;
- std::vector<IdxType> biddersToItems;
- double wassersteinPower;
- double epsilon;
- double delta;
- double internal_p;
- double initialEpsilon;
- double epsilonCommonRatio; // next epsilon = current epsilon / epsilonCommonRatio
- double weightAdjConst;
- double wassersteinDistance;
- double wassersteinCost;
- double relativeError;
+ PointContainer bidders, items;
+ const size_t num_bidders;
+ const size_t num_items;
+ std::vector<IdxType> items_to_bidders;
+ std::vector<IdxType> bidders_to_items;
+ Real wasserstein_power;
+ Real epsilon;
+ Real delta;
+ Real internal_p;
+ Real initial_epsilon;
+ Real epsilon_common_ratio; // next epsilon = current epsilon / epsilon_common_ratio
+ const int max_num_phases; // maximal number of iterations of epsilon-scaling
+ Real weight_adj_const;
+ Real wasserstein_cost;
+ Real relative_error;
+ int dimension;
// to get the 2 best items
- std::unique_ptr<AuctionOracle> oracle;
-#ifdef KEEP_UNASSIGNED_ORDERED
- OrderedUnassignedKeeper unassignedBidders;
-#else
- std::unordered_set<size_t> unassignedBidders;
-#endif
+ AuctionOracle oracle;
+ std::unordered_set<size_t> unassigned_bidders;
// private methods
- void assignItemToBidder(const IdxType bidderIdx, const IdxType itemsIdx);
- void clearBidTable(void);
- void runAuction(void);
- void runAuctionPhase(void);
- void flushAssignment(void);
+ void assign_item_to_bidder(const IdxType bidder_idx, const IdxType items_idx);
+ void run_auction();
+ void run_auction_phases(const int max_num_phases, const Real _initial_epsilon);
+ void run_auction_phase();
+ void flush_assignment();
+ // return 0, if item_idx is invalid
+ Real get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx, const bool tolerate_invalid_idx = false) const;
// for debug only
- void sanityCheck(void);
- void printDebug(void);
- int countUnhappy(void);
- void printMatching(void);
- double getDistanceToQthPowerInternal(void);
- int numRounds { 0 };
+ void sanity_check();
+ void print_debug();
+ int count_unhappy();
+ void print_matching();
+ Real getDistanceToQthPowerInternal();
+ int num_phase { 0 };
+ int num_rounds { 0 };
+ bool is_distance_computed {false};
+#ifdef LOG_AUCTION
+ bool log_auction { false };
+ std::shared_ptr<spdlog::logger> console_logger;
+ std::shared_ptr<spdlog::logger> plot_logger;
+ std::unordered_set<size_t> unassigned_items;
+ size_t max_unassigned_to_log { 0 };
+ const char* logger_name = "auction_detailed_logger"; // the name in spdlog registry; filename is provided as parameter in enable_logging
+ const Real total_items_persistence;
+ const Real total_bidders_persistence;
+ Real partial_cost;
+ Real unassigned_bidders_persistence;
+ Real unassigned_items_persistence;
+#endif
};
-} // end of namespace geom_ws
+} // ws
+} // hera
+
+
+#include "auction_runner_gs.hpp"
#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_gs.hpp b/geom_matching/wasserstein/include/auction_runner_gs.hpp
new file mode 100644
index 0000000..d9f419d
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_gs.hpp
@@ -0,0 +1,486 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+
+#include <assert.h>
+#include <stdexcept>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <chrono>
+#include <numeric>
+
+#include "def_debug_ws.h"
+
+#define PRINT_DETAILED_TIMING
+
+#ifdef FOR_R_TDA
+#include "Rcpp.h"
+#undef DEBUG_AUCTION
+#endif
+
+
+namespace hera {
+namespace ws {
+
+// *****************************
+// AuctionRunnerGS
+// *****************************
+
+template<class R, class AO, class PC>
+AuctionRunnerGS<R, AO, PC>::AuctionRunnerGS(const PC& A,
+ const PC& B,
+ const AuctionParams<Real>& params,
+ const std::string& _log_filename_prefix) :
+ bidders(A),
+ items(B),
+ num_bidders(A.size()),
+ num_items(B.size()),
+ items_to_bidders(B.size(), k_invalid_index),
+ bidders_to_items(A.size(), k_invalid_index),
+ wasserstein_power(params.wasserstein_power),
+ delta(params.delta),
+ internal_p(params.internal_p),
+ initial_epsilon(params.initial_epsilon),
+ epsilon_common_ratio(params.epsilon_common_ratio == 0.0 ? 5.0 : params.epsilon_common_ratio),
+ max_num_phases(params.max_num_phases),
+ dimension(params.dim),
+ oracle(bidders, items, params)
+#ifdef LOG_AUCTION
+ , total_items_persistence(std::accumulate(items.begin(),
+ items.end(),
+ R(0.0),
+ [params](const Real& ps, const DgmPoint& item)
+ { return ps + std::pow(item.persistence_lp(params.internal_p), params.wasserstein_power); }
+ ))
+
+ , total_bidders_persistence(std::accumulate(bidders.begin(),
+ bidders.end(),
+ R(0.0),
+ [params](const Real& ps, const DgmPoint& bidder)
+ { return ps + std::pow(bidder.persistence_lp(params.internal_p), params.wasserstein_power); }
+ ))
+ , partial_cost(0.0)
+ , unassigned_bidders_persistence(0.0)
+ , unassigned_items_persistence(0.0)
+#endif
+
+{
+ assert(initial_epsilon >= 0.0 );
+ assert(epsilon_common_ratio >= 0.0 );
+ assert(A.size() == B.size());
+#ifdef LOG_AUCTION
+
+ unassigned_items_persistence = total_items_persistence;
+ unassigned_bidders_persistence = total_bidders_persistence;
+
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+ console_logger->debug("Gauss-Seidel, num_bidders = {0}", num_bidders);
+
+ plot_logger = spdlog::get("plot_logger");
+ if (not plot_logger) {
+ plot_logger = spdlog::basic_logger_st("plot_logger", "plot_logger.txt");
+ plot_logger->info("New plot starts here");
+ plot_logger->set_pattern("%v");
+ }
+#endif
+
+}
+
+#ifdef LOG_AUCTION
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::enable_logging(const char* log_filename, const size_t _max_unassigned_to_log)
+{
+ log_auction = true;
+ max_unassigned_to_log = _max_unassigned_to_log;
+
+ auto log = spdlog::basic_logger_st(logger_name, log_filename);
+ log->set_pattern("%v");
+}
+#endif
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::assign_item_to_bidder(IdxType item_idx, IdxType bidder_idx)
+{
+ num_rounds++;
+ sanity_check();
+ // only unassigned bidders should submit bids and get items
+ assert(bidders_to_items[bidder_idx] == k_invalid_index);
+ IdxType old_item_owner = items_to_bidders[item_idx];
+
+ // set new owner
+ bidders_to_items[bidder_idx] = item_idx;
+ items_to_bidders[item_idx] = bidder_idx;
+ // remove bidder from the list of unassigned bidders
+ unassigned_bidders.erase(bidder_idx);
+
+ // old owner becomes unassigned
+ if (old_item_owner != k_invalid_index) {
+ bidders_to_items[old_item_owner] = k_invalid_index;
+ unassigned_bidders.insert(old_item_owner);
+ }
+
+
+#ifdef LOG_AUCTION
+
+ partial_cost += get_item_bidder_cost(item_idx, bidder_idx, true);
+ partial_cost -= get_item_bidder_cost(item_idx, old_item_owner, true);
+
+ unassigned_items.erase(item_idx);
+
+ unassigned_bidders_persistence -= std::pow(bidders[bidder_idx].persistence_lp(internal_p), wasserstein_power);
+
+ if (old_item_owner != k_invalid_index) {
+ // item has been assigned to some other bidder,
+ // and he became unassigned
+ unassigned_bidders_persistence += std::pow(bidders[old_item_owner].persistence_lp(internal_p), wasserstein_power);
+ } else {
+ // item was unassigned before
+ unassigned_items_persistence -= std::pow(items[item_idx].persistence_lp(internal_p), wasserstein_power);
+ }
+
+ if (log_auction)
+ plot_logger->info("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}",
+ num_phase,
+ num_rounds,
+ unassigned_bidders.size(),
+ unassigned_items_persistence,
+ unassigned_bidders_persistence,
+ unassigned_items_persistence + unassigned_bidders_persistence,
+ partial_cost,
+ total_bidders_persistence,
+ total_items_persistence,
+ oracle.get_epsilon()
+ );
+
+
+ if (log_auction and unassigned_bidders.size() <= max_unassigned_to_log) {
+ auto logger = spdlog::get(logger_name);
+ if (logger) {
+ auto item = items[item_idx];
+ auto bidder = bidders[bidder_idx];
+ logger->info("{0} # ({1}, {2}) # ({3}, {4}) # {5} # {6} # {7}",
+ num_rounds,
+ item.getRealX(),
+ item.getRealY(),
+ bidder.getRealX(),
+ bidder.getRealY(),
+ format_point_set_to_log(unassigned_bidders, bidders),
+ format_point_set_to_log(unassigned_items, items),
+ oracle.get_epsilon());
+ }
+ }
+#endif
+}
+
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::flush_assignment()
+{
+ for(auto& b2i : bidders_to_items) {
+ b2i = k_invalid_index;
+ }
+ for(auto& i2b : items_to_bidders) {
+ i2b = k_invalid_index;
+ }
+ // we must flush assignment only after we got perfect matching
+ assert(unassigned_bidders.empty());
+ // all bidders become unassigned
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_bidders.insert(bidder_idx);
+ }
+ assert(unassigned_bidders.size() == bidders.size());
+
+#ifdef LOG_AUCTION
+ partial_cost = 0.0;
+ unassigned_bidders_persistence = total_bidders_persistence;
+ unassigned_items_persistence = total_items_persistence;
+
+ for(size_t item_idx = 0; item_idx < items.size(); ++item_idx) {
+ unassigned_items.insert(item_idx);
+ }
+#endif
+
+ oracle.adjust_prices();
+}
+
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::run_auction_phases(const int max_num_phases, const Real _initial_epsilon)
+{
+ relative_error = std::numeric_limits<Real>::max();
+ // choose some initial epsilon
+ oracle.set_epsilon(_initial_epsilon);
+ assert( oracle.get_epsilon() > 0 );
+ for(int phase_num = 0; phase_num < max_num_phases; ++phase_num) {
+ flush_assignment();
+ run_auction_phase();
+ Real current_result = getDistanceToQthPowerInternal();
+ Real denominator = current_result - num_bidders * oracle.get_epsilon();
+ current_result = pow(current_result, 1.0 / wasserstein_power);
+#ifdef LOG_AUCTION
+ console_logger->info("Phase {0} done, num_rounds (cumulative) = {1}, current_result = {2}, epsilon = {3}",
+ phase_num, format_int(num_rounds), current_result,
+ oracle.get_epsilon());
+#endif
+ if ( denominator <= 0 ) {
+#ifdef LOG_AUCTION
+ console_logger->info("Epsilon is too large");
+#endif
+ } else {
+ denominator = pow(denominator, 1.0 / wasserstein_power);
+ Real numerator = current_result - denominator;
+ relative_error = numerator / denominator;
+#ifdef LOG_AUCTION
+ console_logger->info("error = {0} / {1} = {2}",
+ numerator, denominator, relative_error);
+#endif
+ if (relative_error <= delta) {
+ break;
+ }
+ }
+ // decrease epsilon for the next iteration
+ oracle.set_epsilon( oracle.get_epsilon() / epsilon_common_ratio );
+ }
+}
+
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::run_auction()
+{
+
+ if (num_bidders == 1) {
+ assign_item_to_bidder(0, 0);
+ wasserstein_cost = get_item_bidder_cost(0,0);
+ return;
+ }
+
+ double init_eps = ( initial_epsilon > 0.0 ) ? initial_epsilon : oracle.max_val_ / 4.0 ;
+ run_auction_phases(max_num_phases, init_eps);
+ is_distance_computed = true;
+ if (relative_error > delta) {
+#ifndef FOR_R_TDA
+ std::cerr << "Maximum iteration number exceeded, exiting. Current result is: ";
+ std::cerr << pow(wasserstein_cost, 1.0/wasserstein_power) << std::endl;
+#endif
+ throw std::runtime_error("Maximum iteration number exceeded");
+ }
+}
+
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::run_auction_phase()
+{
+ num_phase++;
+ //std::cout << "Entered run_auction_phase" << std::endl;
+ do {
+ size_t bidder_idx = *unassigned_bidders.begin();
+ auto optimal_bid = oracle.get_optimal_bid(bidder_idx);
+ auto optimal_item_idx = optimal_bid.first;
+ auto bid_value = optimal_bid.second;
+ assign_item_to_bidder(optimal_bid.first, bidder_idx);
+ oracle.set_price(optimal_item_idx, bid_value);
+ //print_debug();
+#ifdef FOR_R_TDA
+ if ( num_rounds % 10000 == 0 ) {
+ Rcpp::check_user_interrupt();
+ }
+#endif
+ } while (not unassigned_bidders.empty());
+ //std::cout << "run_auction_phase finished" << std::endl;
+
+#ifdef DEBUG_AUCTION
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if ( bidders_to_items[bidder_idx] < 0 or bidders_to_items[bidder_idx] >= num_bidders) {
+ std::cerr << "After auction terminated bidder " << bidder_idx;
+ std::cerr << " has no items assigned" << std::endl;
+ throw std::runtime_error("Auction did not give a perfect matching");
+ }
+ }
+#endif
+
+}
+
+template<class R, class AO, class PC>
+R AuctionRunnerGS<R, AO, PC>::get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx, const bool tolerate_invalid_idx) const
+{
+ if (item_idx != k_invalid_index and bidder_idx != k_invalid_index) {
+ return std::pow(dist_lp(bidders[bidder_idx], items[item_idx], internal_p, dimension),
+ wasserstein_power);
+ } else {
+ if (tolerate_invalid_idx)
+ return R(0.0);
+ else
+ throw std::runtime_error("Invalid idx in get_item_bidder_cost, item_idx = " + std::to_string(item_idx) + ", bidder_idx = " + std::to_string(bidder_idx));
+ }
+}
+
+template<class R, class AO, class PC>
+R AuctionRunnerGS<R, AO, PC>::getDistanceToQthPowerInternal()
+{
+ sanity_check();
+ Real result = 0.0;
+ //std::cout << "-------------------------------------------------------------------------\n";
+ for(size_t bIdx = 0; bIdx < num_bidders; ++bIdx) {
+ result += get_item_bidder_cost(bidders_to_items[bIdx], bIdx);
+ }
+ //std::cout << "-------------------------------------------------------------------------\n";
+ wasserstein_cost = result;
+ return result;
+}
+
+template<class R, class AO, class PC>
+R AuctionRunnerGS<R, AO, PC>::get_wasserstein_distance()
+{
+ assert(is_distance_computed);
+ return pow(get_wasserstein_cost(), 1.0/wasserstein_power);
+}
+
+template<class R, class AO, class PC>
+R AuctionRunnerGS<R, AO, PC>::get_wasserstein_cost()
+{
+ assert(is_distance_computed);
+ return wasserstein_cost;
+}
+
+
+
+// Debug routines
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::print_debug()
+{
+#ifdef DEBUG_AUCTION
+ sanity_check();
+ std::cout << "**********************" << std::endl;
+ std::cout << "Current assignment:" << std::endl;
+ for(size_t idx = 0; idx < bidders_to_items.size(); ++idx) {
+ std::cout << idx << " <--> " << bidders_to_items[idx] << std::endl;
+ }
+ std::cout << "Weights: " << std::endl;
+ //for(size_t i = 0; i < num_bidders; ++i) {
+ //for(size_t j = 0; j < num_items; ++j) {
+ //std::cout << oracle.weight_matrix[i][j] << " ";
+ //}
+ //std::cout << std::endl;
+ //}
+ std::cout << "Prices: " << std::endl;
+ for(const auto price : oracle.get_prices()) {
+ std::cout << price << std::endl;
+ }
+ std::cout << "**********************" << std::endl;
+#endif
+}
+
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::sanity_check()
+{
+#ifdef DEBUG_AUCTION
+ if (bidders_to_items.size() != num_bidders) {
+ std::cerr << "Wrong size of bidders_to_items, must be " << num_bidders << ", is " << bidders_to_items.size() << std::endl;
+ throw std::runtime_error("Wrong size of bidders_to_items");
+ }
+
+ if (items_to_bidders.size() != num_bidders) {
+ std::cerr << "Wrong size of items_to_bidders, must be " << num_bidders << ", is " << items_to_bidders.size() << std::endl;
+ throw std::runtime_error("Wrong size of items_to_bidders");
+ }
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ assert( bidders_to_items[bidder_idx] == k_invalid_index or ( bidders_to_items[bidder_idx] < num_items and bidders_to_items[bidder_idx] >= 0));
+
+ if ( bidders_to_items[bidder_idx] != k_invalid_index) {
+
+ if ( std::count(bidders_to_items.begin(),
+ bidders_to_items.end(),
+ bidders_to_items[bidder_idx]) > 1 ) {
+ std::cerr << "Item " << bidders_to_items[bidder_idx];
+ std::cerr << " appears in bidders_to_items more than once" << std::endl;
+ throw std::runtime_error("Duplicate in bidders_to_items");
+ }
+
+ if (items_to_bidders.at(bidders_to_items[bidder_idx]) != static_cast<int>(bidder_idx)) {
+ std::cerr << "Inconsitency: bidder_idx = " << bidder_idx;
+ std::cerr << ", item_idx in bidders_to_items = ";
+ std::cerr << bidders_to_items[bidder_idx];
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[bidders_to_items[bidder_idx]] << std::endl;
+ throw std::runtime_error("inconsistent mapping");
+ }
+ }
+ }
+
+ for(IdxType item_idx = 0; item_idx < static_cast<IdxType>(num_bidders); ++item_idx) {
+ assert( items_to_bidders[item_idx] == k_invalid_index or ( items_to_bidders[item_idx] < num_items and items_to_bidders[item_idx] >= 0));
+ if ( items_to_bidders.at(item_idx) != k_invalid_index) {
+
+ // check for uniqueness
+ if ( std::count(items_to_bidders.begin(),
+ items_to_bidders.end(),
+ items_to_bidders[item_idx]) > 1 ) {
+ std::cerr << "Bidder " << items_to_bidders[item_idx];
+ std::cerr << " appears in items_to_bidders more than once" << std::endl;
+ throw std::runtime_error("Duplicate in items_to_bidders");
+ }
+ // check for consistency
+ if (bidders_to_items.at(items_to_bidders.at(item_idx)) != static_cast<int>(item_idx)) {
+ std::cerr << "Inconsitency: item_idx = " << item_idx;
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[item_idx];
+ std::cerr << ", item_idx in bidders_to_items= ";
+ std::cerr << bidders_to_items[items_to_bidders[item_idx]] << std::endl;
+ throw std::runtime_error("inconsistent mapping");
+ }
+ }
+ }
+#endif
+}
+
+template<class R, class AO, class PC>
+void AuctionRunnerGS<R, AO, PC>::print_matching()
+{
+#ifdef DEBUG_AUCTION
+ sanity_check();
+ for(size_t bIdx = 0; bIdx < bidders_to_items.size(); ++bIdx) {
+ if (bidders_to_items[bIdx] != k_invalid_index) {
+ auto pA = bidders[bIdx];
+ auto pB = items[bidders_to_items[bIdx]];
+ std::cout << pA << " <-> " << pB << "+" << pow(dist_lp(pA, pB, internal_p, dimension), wasserstein_power) << std::endl;
+ } else {
+ assert(false);
+ }
+ }
+#endif
+}
+
+} // ws
+} // hera
diff --git a/geom_matching/wasserstein/include/auction_runner_gs_single_diag.h b/geom_matching/wasserstein/include/auction_runner_gs_single_diag.h
new file mode 100644
index 0000000..f32fbbc
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_gs_single_diag.h
@@ -0,0 +1,149 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef AUCTION_RUNNER_GS_SINGLE_DIAG_H
+#define AUCTION_RUNNER_GS_SINGLE_DIAG_H
+
+#include <unordered_set>
+#include <unordered_map>
+
+#include "auction_oracle.h"
+
+namespace hera {
+namespace ws {
+
+// the two parameters that you can tweak in auction algorithm are:
+// 1. epsilon_common_ratio
+// 2. max_iter_num
+
+template<class RealType_, class AuctionOracle_ = AuctionOracleKDTreeSingleDiag<RealType_> > // alternatively: AuctionOracleLazyHeap --- TODO
+class AuctionRunnerGaussSeidelSingleDiag {
+public:
+ using RealType = RealType_;
+ using Real = RealType_;
+ using AuctionOracle = AuctionOracle_;
+ using DgmPoint = DiagramPoint<Real>;
+ using DgmPointVec = std::vector<DgmPoint>;
+ using DiagonalBidR = DiagonalBid<Real>;
+
+
+
+ AuctionRunnerGaussSeidelSingleDiag(const DgmPointVec& A,
+ const DgmPointVec& B,
+ const Real q,
+ const Real _delta,
+ const Real _internal_p,
+ const Real _initial_epsilon,
+ const Real _eps_factor,
+ const int _max_iter_num = std::numeric_limits<int>::max());
+
+ void set_epsilon(Real new_val) { oracle->set_epsilon(new_val); };
+ Real get_epsilon() const { return oracle->get_epsilon(); }
+ Real get_wasserstein_cost();
+ Real get_wasserstein_distance();
+ Real get_relative_error() const { return relative_error; };
+ void enable_logging(const char* log_filename, const size_t _max_unassigned_to_log);
+//private:
+ // private data
+ DgmPointVec bidders, items;
+ const size_t num_bidders;
+ const size_t num_items;
+ size_t num_normal_bidders;
+ size_t num_diag_bidders;
+ size_t num_normal_items;
+ size_t num_diag_items;
+ std::vector<IdxType> items_to_bidders;
+ std::vector<IdxType> bidders_to_items;
+ const Real wasserstein_power;
+ const Real delta;
+ const Real internal_p;
+ const Real initial_epsilon;
+ Real epsilon_common_ratio; // next epsilon = current epsilon / epsilon_common_ratio
+ const int max_iter_num; // maximal number of iterations of epsilon-scaling
+ Real weight_adj_const;
+ Real wasserstein_cost;
+ Real relative_error;
+ // to get the 2 best items we use oracle
+ std::unique_ptr<AuctionOracle> oracle;
+ // unassigned guys
+ std::unordered_set<size_t> unassigned_normal_bidders;
+ std::unordered_set<size_t> unassigned_diag_bidders;
+ // private methods
+ //
+ void process_diagonal_bid(const DiagonalBidR& bid);
+
+ void assign_item_to_bidder(const IdxType item_idx,
+ const IdxType bidder_idx,
+ const IdxType old_owner_idx,
+ const bool item_is_diagonal,
+ const bool bidder_is_diagonal,
+ const bool call_set_prices = false,
+ const Real new_price = std::numeric_limits<Real>::max());
+
+ void run_auction();
+ void run_auction_phases(const int max_num_phases, const Real _initial_epsilon);
+ void run_auction_phase();
+ void flush_assignment();
+ // return 0, if item_idx is invalid
+ Real get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx, const bool tolerate_invalid_idx = false) const;
+
+ bool is_bidder_diagonal(const size_t bidder_idx) const;
+ bool is_bidder_normal(const size_t bidder_idx) const;
+ bool is_item_diagonal(const size_t item_idx) const;
+ bool is_item_normal(const size_t item_idx) const;
+
+ OwnerType get_owner_type(size_t bidder_idx) const;
+
+ // for debug only
+ void sanity_check();
+ void print_debug();
+ int count_unhappy();
+ void print_matching();
+ Real getDistanceToQthPowerInternal();
+ int num_phase { 0 };
+ int num_rounds { 0 };
+#ifdef LOG_AUCTION
+ bool log_auction { false };
+ std::unordered_set<size_t> unassigned_items;
+ size_t max_unassigned_to_log { 0 };
+ const char* logger_name = "auction_detailed_logger"; // the name in spdlog registry; filename is provided as parameter in enable_logging
+ const Real total_items_persistence;
+ const Real total_bidders_persistence;
+ Real partial_cost;
+ Real unassigned_bidders_persistence;
+ Real unassigned_items_persistence;
+#endif
+};
+
+} // ws
+} // hera
+
+
+#include "auction_runner_gs_single_diag.hpp"
+
+#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_gs_single_diag.hpp b/geom_matching/wasserstein/include/auction_runner_gs_single_diag.hpp
new file mode 100644
index 0000000..a3c401e
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_gs_single_diag.hpp
@@ -0,0 +1,738 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+
+#include <assert.h>
+#include <stdexcept>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <chrono>
+
+#include "def_debug_ws.h"
+
+#define PRINT_DETAILED_TIMING
+
+#ifdef FOR_R_TDA
+#include "Rcpp.h"
+#undef DEBUG_AUCTION
+#endif
+
+
+namespace hera {
+namespace ws {
+
+// *****************************
+// AuctionRunnerGaussSeidelSingleDiag
+// *****************************
+
+template<class R, class AO>
+std::ostream& operator<<(std::ostream& s, const AuctionRunnerGaussSeidelSingleDiag<R, AO>& ar)
+{
+ s << "--------------------------------------------------\n";
+ s << "AuctionRunnerGaussSeidelSingleDiag, current assignment, bidders_to_items:" << std::endl;
+ for(size_t idx = 0; idx < ar.bidders_to_items.size(); ++idx) {
+ s << idx << " <--> " << ar.bidders_to_items[idx] << std::endl;
+ }
+ s << "--------------------------------------------------\n";
+ s << "AuctionRunnerGaussSeidelSingleDiag, current assignment, items_to_bidders:" << std::endl;
+ for(size_t idx = 0; idx < ar.items_to_bidders.size(); ++idx) {
+ s << idx << " <--> " << ar.items_to_bidders[idx] << std::endl;
+ }
+ s << "--------------------------------------------------\n";
+ s << "AuctionRunnerGaussSeidelSingleDiag, prices:" << std::endl;
+ for(size_t item_idx = 0; item_idx < ar.num_items; ++item_idx) {
+ s << item_idx << ": " << ar.oracle->get_price(item_idx) << std::endl;
+ }
+ s << "--------------------------------------------------\n";
+ s << "AuctionRunnerGaussSeidelSingleDiag, oracle :" << *(ar.oracle) << std::endl;
+ s << "--------------------------------------------------\n";
+ return s;
+}
+
+
+template<class R, class AO>
+AuctionRunnerGaussSeidelSingleDiag<R, AO>::AuctionRunnerGaussSeidelSingleDiag(const DgmPointVec& A,
+ const DgmPointVec& B,
+ const Real q,
+ const Real _delta,
+ const Real _internal_p,
+ const Real _initial_epsilon,
+ const Real _eps_factor,
+ const int _max_iter_num) :
+ bidders(A),
+ items(B),
+ num_bidders(A.size()),
+ num_items(B.size()),
+ items_to_bidders(B.size(), k_invalid_index),
+ bidders_to_items(A.size(), k_invalid_index),
+ wasserstein_power(q),
+ delta(_delta),
+ internal_p(_internal_p),
+ initial_epsilon(_initial_epsilon),
+ epsilon_common_ratio(_eps_factor == 0.0 ? 5.0 : _eps_factor),
+ max_iter_num(_max_iter_num)
+#ifdef LOG_AUCTION
+ , total_items_persistence(std::accumulate(items.begin(),
+ items.end(),
+ R(0.0),
+ [_internal_p, q](const Real& ps, const DgmPoint& item)
+ { return ps + std::pow(item.persistence_lp(_internal_p), q); }
+ ))
+
+ , total_bidders_persistence(std::accumulate(bidders.begin(),
+ bidders.end(),
+ R(0.0),
+ [_internal_p, q](const Real& ps, const DgmPoint& bidder)
+ { return ps + std::pow(bidder.persistence_lp(_internal_p), q); }
+ ))
+ , partial_cost(0.0)
+ , unassigned_bidders_persistence(0.0)
+ , unassigned_items_persistence(0.0)
+#endif
+
+{
+ assert(initial_epsilon >= 0.0 );
+ assert(epsilon_common_ratio >= 0.0 );
+ assert(A.size() == B.size());
+ oracle = std::unique_ptr<AuctionOracle>(new AuctionOracle(bidders, items, wasserstein_power, internal_p));
+
+ for(num_normal_bidders = 0; num_normal_bidders < num_bidders; ++num_normal_bidders) {
+ if (bidders[num_normal_bidders].is_diagonal())
+ break;
+ }
+
+ num_diag_bidders = num_bidders - num_normal_bidders;
+ num_diag_items = num_normal_bidders;
+ num_normal_items = num_items - num_diag_items;
+
+ for(size_t i = num_normal_bidders; i < num_bidders; ++i) {
+ assert(bidders[i].is_diagonal());
+ }
+
+#ifdef LOG_AUCTION
+
+ unassigned_items_persistence = total_items_persistence;
+ unassigned_bidders_persistence = total_bidders_persistence;
+
+ if (not spdlog::get("plot_logger")) {
+ auto log = spdlog::basic_logger_st("plot_logger", "plot_logger.txt");
+ log->info("New plot starts here");
+ log->set_pattern("%v");
+ }
+#endif
+
+}
+
+#ifdef LOG_AUCTION
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::enable_logging(const char* log_filename, const size_t _max_unassigned_to_log)
+{
+ log_auction = true;
+ max_unassigned_to_log = _max_unassigned_to_log;
+
+ auto log = spdlog::basic_logger_st(logger_name, log_filename);
+ log->set_pattern("%v");
+}
+#endif
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::process_diagonal_bid(const DiagonalBidR& bid)
+{
+
+ //std::cout << "Enter process_diagonal_bid, bid = " << bid << std::endl;
+
+ // increase price of already assigned normal items
+ for(size_t k = 0; k < bid.assigned_normal_items.size(); ++k) {
+ size_t assigned_normal_item_idx = bid.assigned_normal_items[k];
+ Real new_price = bid.assigned_normal_items_bid_values[k];
+ bool item_is_diagonal = false;
+ bool bidder_is_diagonal = true;
+
+ // TODO: SPECIAL PROCEDURE HEER`
+ oracle->set_price(assigned_normal_item_idx, new_price, item_is_diagonal, bidder_is_diagonal, OwnerType::k_diagonal);
+ }
+
+ // set common diag-diag price
+ // if diag_assigned_to_diag_slice_ is empty, it will be
+ // numeric_limits<Real>::max()
+
+ oracle->diag_to_diag_price_ = bid.diag_to_diag_value;
+
+ int unassigned_diag_idx = 0;
+ auto unassigned_diag_item_iter = oracle->diag_unassigned_slice_.begin();
+ auto bid_vec_idx = 0;
+ for(const auto diag_bidder_idx : unassigned_diag_bidders) {
+ if (unassigned_diag_idx < bid.num_from_unassigned_diag) {
+ // take diagonal point from unassigned slice
+
+ //std::cout << "assigning to diag_bidder_idx = " << diag_bidder_idx << std::endl;
+ assert(unassigned_diag_item_iter != oracle->diag_unassigned_slice_.end());
+
+ auto item_idx = *unassigned_diag_item_iter;
+
+ ++unassigned_diag_idx;
+ ++unassigned_diag_item_iter;
+ assign_item_to_bidder(item_idx, diag_bidder_idx, k_invalid_index, true, true, false);
+ } else {
+ // take point from best_item_indices
+ size_t item_idx = bid.best_item_indices[bid_vec_idx];
+ Real new_price = bid.bid_values[bid_vec_idx];
+ bid_vec_idx++;
+
+ auto old_owner_idx = items_to_bidders[item_idx];
+ bool item_is_diagonal = is_item_diagonal(item_idx);
+
+ assign_item_to_bidder(item_idx, diag_bidder_idx, old_owner_idx, item_is_diagonal, true, true, new_price);
+ }
+ }
+
+ // all bids of diagonal bidders are satisfied
+ unassigned_diag_bidders.clear();
+
+ if (oracle->diag_unassigned_slice_.empty()) {
+ oracle->diag_unassigned_price_ = std::numeric_limits<Real>::max();
+ }
+
+ //std::cout << "Exit process_diagonal_bid\n" << *this;
+}
+
+template<class R, class AO>
+bool AuctionRunnerGaussSeidelSingleDiag<R, AO>::is_bidder_diagonal(const size_t bidder_idx) const
+{
+ return bidder_idx >= num_normal_bidders;
+}
+
+template<class R, class AO>
+bool AuctionRunnerGaussSeidelSingleDiag<R, AO>::is_bidder_normal(const size_t bidder_idx) const
+{
+ return bidder_idx < num_normal_bidders;
+}
+
+template<class R, class AO>
+bool AuctionRunnerGaussSeidelSingleDiag<R, AO>::is_item_diagonal(const size_t item_idx) const
+{
+ return item_idx < num_diag_items;
+}
+
+template<class R, class AO>
+bool AuctionRunnerGaussSeidelSingleDiag<R, AO>::is_item_normal(const size_t item_idx) const
+{
+ return item_idx >= num_diag_items;
+}
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::assign_item_to_bidder(const IdxType item_idx,
+ const IdxType bidder_idx,
+ const IdxType old_owner_idx,
+ const bool item_is_diagonal,
+ const bool bidder_is_diagonal,
+ const bool call_set_price,
+ const R new_price)
+{
+ //std::cout << "Enter assign_item_to_bidder, " << std::boolalpha ;
+ //std::cout << "item_idx = " << item_idx << ", bidder_idx = " << bidder_idx << ", old_owner_idx = " << old_owner_idx << ", item_is_diagonal = " << item_is_diagonal << ", bidder_is_diagonal = " << bidder_is_diagonal << std::endl;
+ //std::cout << "################################################################################" << std::endl;
+ //std::cout << *this << std::endl;
+ //std::cout << *(this->oracle) << std::endl;
+ //std::cout << "################################################################################" << std::endl;
+ num_rounds++;
+
+ // for readability
+ const bool item_is_normal = not item_is_diagonal;
+ const bool bidder_is_normal = not bidder_is_diagonal;
+
+ // only unassigned bidders should submit bids and get items
+ assert(bidders_to_items[bidder_idx] == k_invalid_index);
+
+
+ // update matching information
+ bidders_to_items[bidder_idx] = item_idx;
+ items_to_bidders[item_idx] = bidder_idx;
+
+
+ // remove bidder from the list of unassigned bidders
+ // for diagonal bidders we don't need to: in Gauss-Seidel they are all
+ // processed at once, so the set unassigned_diag_bidders will be cleared
+ if (bidder_is_normal) {
+ unassigned_normal_bidders.erase(bidder_idx);
+ }
+
+ OwnerType old_owner_type = get_owner_type(old_owner_idx);
+
+ if (old_owner_type != OwnerType::k_none) {
+ bidders_to_items[old_owner_idx] = k_invalid_index;
+ }
+
+ switch(old_owner_type)
+ {
+ case OwnerType::k_normal : unassigned_normal_bidders.insert(old_owner_idx);
+ break;
+ case OwnerType::k_diagonal : unassigned_diag_bidders.insert(old_owner_idx);
+ break;
+ case OwnerType::k_none : break;
+ }
+
+
+ // update normal_items_assigned_to_diag_
+
+ if (old_owner_type == OwnerType::k_diagonal and item_is_normal and bidder_is_normal) {
+ // normal item was stolen from diagonal, erase
+ assert( oracle->normal_items_assigned_to_diag_.count(item_idx) == 1 );
+ oracle->normal_items_assigned_to_diag_.erase(item_idx);
+ } else if (bidder_is_diagonal and item_is_normal and old_owner_type != OwnerType::k_diagonal) {
+ // diagonal bidder got a new normal item, insert
+ assert(oracle->normal_items_assigned_to_diag_.count(item_idx) == 0);
+ oracle->normal_items_assigned_to_diag_.insert(item_idx);
+ }
+
+
+ // update diag_assigned_to_diag_slice_
+ if (item_is_diagonal and bidder_is_normal and old_owner_type == OwnerType::k_diagonal) {
+ assert( oracle->diag_assigned_to_diag_slice_.count(item_idx) == 1);
+ oracle->diag_assigned_to_diag_slice_.erase(item_idx);
+ } else if (item_is_diagonal and bidder_is_diagonal) {
+ assert( old_owner_type != OwnerType::k_diagonal ); // diagonal does not steal from itself
+ assert( oracle->diag_assigned_to_diag_slice_.count(item_idx) == 0);
+ oracle->diag_assigned_to_diag_slice_.insert(item_idx);
+ }
+
+ // update diag_unassigned_slice_
+ if (item_is_diagonal and old_owner_type == OwnerType::k_none) {
+ oracle->diag_unassigned_slice_.erase(item_idx);
+ }
+
+ if ( not (not call_set_price or new_price != std::numeric_limits<R>::max())) {
+ std::cout << "In the middle of assign_item_to_bidder, " << std::boolalpha ;
+ std::cout << "item_idx = " << item_idx << ", bidder_idx = " << bidder_idx << ", old_owner_idx = " << old_owner_idx << ", item_is_diagonal = " << item_is_diagonal << ", bidder_is_diagonal = " << bidder_is_diagonal << std::endl;
+ std::cout << "################################################################################" << std::endl;
+ std::cout << *this << std::endl;
+ std::cout << "################################################################################" << std::endl;
+ }
+ assert(not call_set_price or new_price != std::numeric_limits<R>::max());
+ if (call_set_price) {
+ oracle->set_price(item_idx, new_price, item_is_diagonal, bidder_is_diagonal, old_owner_type);
+ }
+
+ //std::cout << "Exit assign_item_to_bidder, state\n" << *this << std::endl;
+
+#ifdef LOG_AUCTION
+
+ partial_cost += get_item_bidder_cost(item_idx, bidder_idx, true);
+ partial_cost -= get_item_bidder_cost(item_idx, old_owner_idx, true);
+
+ unassigned_items.erase(item_idx);
+
+ unassigned_bidders_persistence -= std::pow(bidders[bidder_idx].persistence_lp(internal_p), wasserstein_power);
+
+ if (old_owner_type != OwnerType::k_none) {
+ // item has been assigned to some other bidder,
+ // and he became unassigned
+ unassigned_bidders_persistence += std::pow(bidders[old_owner_idx].persistence_lp(internal_p), wasserstein_power);
+ } else {
+ // item was unassigned before
+ unassigned_items_persistence -= std::pow(items[item_idx].persistence_lp(internal_p), wasserstein_power);
+ }
+
+ auto plot_logger = spdlog::get("plot_logger");
+ plot_logger->info("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}",
+ num_phase,
+ num_rounds,
+ unassigned_normal_bidders.size(),
+ unassigned_diag_bidders.size(),
+ unassigned_items_persistence,
+ unassigned_bidders_persistence,
+ unassigned_items_persistence + unassigned_bidders_persistence,
+ partial_cost,
+ total_bidders_persistence,
+ total_items_persistence,
+ oracle->get_epsilon()
+ );
+
+
+ if (log_auction and unassigned_normal_bidders.size() + unassigned_diag_bidders.size() <= max_unassigned_to_log) {
+ auto logger = spdlog::get(logger_name);
+ if (logger) {
+ auto item = items[item_idx];
+ auto bidder = bidders[bidder_idx];
+ logger->info("{0} # ({1}, {2}) # ({3}, {4}) # {5} # {6} # {7} # {8}",
+ num_rounds,
+ item.getRealX(),
+ item.getRealY(),
+ bidder.getRealX(),
+ bidder.getRealY(),
+ format_point_set_to_log(unassigned_diag_bidders, bidders),
+ format_point_set_to_log(unassigned_normal_bidders, bidders),
+ format_point_set_to_log(unassigned_items, items),
+ oracle->get_epsilon());
+ }
+ }
+#endif
+}
+
+
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::flush_assignment()
+{
+ for(auto& b2i : bidders_to_items) {
+ b2i = k_invalid_index;
+ }
+ for(auto& i2b : items_to_bidders) {
+ i2b = k_invalid_index;
+ }
+
+ // we must flush assignment only after we got perfect matching
+ assert(unassigned_normal_bidders.empty() and unassigned_diag_bidders.empty());
+ // all bidders become unassigned
+ for(size_t bidder_idx = 0; bidder_idx < num_normal_bidders; ++bidder_idx) {
+ unassigned_normal_bidders.insert(bidder_idx);
+ }
+ for(size_t bidder_idx = num_normal_bidders; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_diag_bidders.insert(bidder_idx);
+ }
+ assert(unassigned_normal_bidders.size() + unassigned_diag_bidders.size() == bidders.size());
+ assert(unassigned_normal_bidders.size() == num_normal_bidders);
+ assert(unassigned_diag_bidders.size() == num_diag_bidders);
+
+ oracle->flush_assignment();
+ oracle->adjust_prices();
+
+#ifdef LOG_AUCTION
+ partial_cost = 0.0;
+ unassigned_bidders_persistence = total_bidders_persistence;
+ unassigned_items_persistence = total_items_persistence;
+
+ for(size_t item_idx = 0; item_idx < items.size(); ++item_idx) {
+ unassigned_items.insert(item_idx);
+ }
+#endif
+
+}
+
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::run_auction_phases(const int max_num_phases, const Real _initial_epsilon)
+{
+ relative_error = std::numeric_limits<Real>::max();
+ // choose some initial epsilon
+ oracle->set_epsilon(_initial_epsilon);
+ assert( oracle->get_epsilon() > 0 );
+ for(int phase_num = 0; phase_num < max_num_phases; ++phase_num) {
+ flush_assignment();
+ run_auction_phase();
+ phase_num++;
+ //std::cout << "Iteration " << phase_num << " completed. " << std::endl;
+ // result is d^q
+ Real current_result = getDistanceToQthPowerInternal();
+ Real denominator = current_result - num_bidders * oracle->get_epsilon();
+ current_result = pow(current_result, 1.0 / wasserstein_power);
+ //std::cout << "Current result is " << current_result << std::endl;
+ if ( denominator <= 0 ) {
+ //std::cout << "Epsilon is too big." << std::endl;
+ } else {
+ denominator = pow(denominator, 1.0 / wasserstein_power);
+ Real numerator = current_result - denominator;
+ relative_error = numerator / denominator;
+ //std::cout << " numerator: " << numerator << " denominator: " << denominator << std::endl;
+ //std::cout << " error bound: " << numerator / denominator << std::endl;
+ // if relative error is greater than delta, continue
+ if (relative_error <= delta) {
+ break;
+ }
+ }
+ // decrease epsilon for the next iteration
+ oracle->set_epsilon( oracle->get_epsilon() / epsilon_common_ratio );
+ }
+ //print_matching();
+}
+
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::run_auction()
+{
+ double init_eps = ( initial_epsilon > 0.0 ) ? initial_epsilon : oracle->max_val_ / 4.0 ;
+ run_auction_phases(max_iter_num, init_eps);
+ if (relative_error > delta) {
+#ifndef FOR_R_TDA
+ std::cerr << "Maximum iteration number exceeded, exiting. Current result is: ";
+ std::cerr << pow(wasserstein_cost, 1.0/wasserstein_power) << std::endl;
+#endif
+ throw std::runtime_error("Maximum iteration number exceeded");
+ }
+}
+
+template<class R, class AO>
+OwnerType AuctionRunnerGaussSeidelSingleDiag<R, AO>::get_owner_type(size_t bidder_idx) const
+{
+ if (bidder_idx == k_invalid_index) {
+ return OwnerType::k_none;
+ } else if (is_bidder_diagonal(bidder_idx)) {
+ return OwnerType::k_diagonal;
+ } else {
+ return OwnerType::k_normal;
+ }
+}
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::run_auction_phase()
+{
+ num_phase++;
+ //std::cout << "Entered run_auction_phase" << std::endl;
+ do {
+
+ if (not unassigned_diag_bidders.empty()) {
+ // process all unassigned diagonal bidders
+ // easy for Gauss-Seidel: every bidder alwasy gets all he wants
+ //
+ sanity_check();
+ //std::cout << "Current state " << __LINE__ << *this << std::endl;
+ process_diagonal_bid(oracle->get_optimal_bids_for_diagonal( unassigned_diag_bidders.size() ));
+ sanity_check();
+ } else {
+ sanity_check();
+ // process normal unassigned bidder
+ size_t bidder_idx = *(unassigned_normal_bidders.begin());
+ auto optimal_bid = oracle->get_optimal_bid(bidder_idx);
+ auto optimal_item_idx = optimal_bid.first;
+ auto bid_value = optimal_bid.second;
+ bool item_is_diagonal = is_item_diagonal(optimal_item_idx);
+ size_t old_owner_idx = items_to_bidders[optimal_item_idx];
+
+ //OwnerType old_owner_type = get_owner_type(old_owner_idx);
+ //std::cout << "bidder_idx = " << bidder_idx << ", item_idx = " << optimal_item_idx << ", old_owner_type = " << old_owner_type << std::endl;
+
+ assign_item_to_bidder(optimal_item_idx, bidder_idx, old_owner_idx, item_is_diagonal, false, true, bid_value);
+ sanity_check();
+ }
+
+#ifdef FOR_R_TDA
+ if ( num_rounds % 10000 == 0 ) {
+ Rcpp::check_user_interrupt();
+ }
+#endif
+ } while (not (unassigned_diag_bidders.empty() and unassigned_normal_bidders.empty()));
+ //std::cout << "run_auction_phase finished" << std::endl;
+
+#ifdef DEBUG_AUCTION
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if ( bidders_to_items[bidder_idx] < 0 or bidders_to_items[bidder_idx] >= (IdxType)num_bidders) {
+ std::cerr << "After auction terminated bidder " << bidder_idx;
+ std::cerr << " has no items assigned" << std::endl;
+ throw std::runtime_error("Auction did not give a perfect matching");
+ }
+ }
+#endif
+
+}
+
+template<class R, class AO>
+R AuctionRunnerGaussSeidelSingleDiag<R, AO>::get_item_bidder_cost(size_t item_idx, size_t bidder_idx, const bool tolerate_invalid_idx) const
+{
+ if (item_idx != k_invalid_index and bidder_idx != k_invalid_index) {
+ // skew edges are replaced by edges to projection
+ if (is_bidder_diagonal(bidder_idx) and is_item_normal(item_idx)) {
+ bidder_idx = item_idx;
+ } else if (is_bidder_normal(bidder_idx) and is_item_diagonal(item_idx)) {
+ item_idx = bidder_idx;
+ }
+ return std::pow(dist_lp(bidders[bidder_idx], items[item_idx], internal_p),
+ wasserstein_power);
+ } else {
+ if (tolerate_invalid_idx)
+ return R(0.0);
+ else
+ throw std::runtime_error("Invalid idx in get_item_bidder_cost, item_idx = " + std::to_string(item_idx) + ", bidder_idx = " + std::to_string(bidder_idx));
+ }
+}
+
+template<class R, class AO>
+R AuctionRunnerGaussSeidelSingleDiag<R, AO>::getDistanceToQthPowerInternal()
+{
+ sanity_check();
+ Real result = 0.0;
+ for(size_t bIdx = 0; bIdx < num_bidders; ++bIdx) {
+ result += get_item_bidder_cost(bidders_to_items[bIdx], bIdx);
+ }
+ wasserstein_cost = result;
+ return result;
+}
+
+template<class R, class AO>
+R AuctionRunnerGaussSeidelSingleDiag<R, AO>::get_wasserstein_distance()
+{
+ return pow(get_wasserstein_cost(), 1.0/wasserstein_power);
+}
+
+template<class R, class AO>
+R AuctionRunnerGaussSeidelSingleDiag<R, AO>::get_wasserstein_cost()
+{
+ run_auction();
+ return wasserstein_cost;
+}
+
+
+
+// Debug routines
+
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::print_debug()
+{
+#ifdef DEBUG_AUCTION
+ std::cout << "**********************" << std::endl;
+ std::cout << "Current assignment:" << std::endl;
+ for(size_t idx = 0; idx < bidders_to_items.size(); ++idx) {
+ std::cout << idx << " <--> " << bidders_to_items[idx] << std::endl;
+ }
+ std::cout << "Weights: " << std::endl;
+ //for(size_t i = 0; i < num_bidders; ++i) {
+ //for(size_t j = 0; j < num_items; ++j) {
+ //std::cout << oracle->weight_matrix[i][j] << " ";
+ //}
+ //std::cout << std::endl;
+ //}
+ std::cout << "Prices: " << std::endl;
+ for(const auto price : oracle->get_prices()) {
+ std::cout << price << std::endl;
+ }
+ std::cout << "**********************" << std::endl;
+#endif
+}
+
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::sanity_check()
+{
+#ifdef DEBUG_AUCTION
+ if (bidders_to_items.size() != num_bidders) {
+ std::cerr << "Wrong size of bidders_to_items, must be " << num_bidders << ", is " << bidders_to_items.size() << std::endl;
+ throw std::runtime_error("Wrong size of bidders_to_items");
+ }
+
+ if (items_to_bidders.size() != num_bidders) {
+ std::cerr << "Wrong size of items_to_bidders, must be " << num_bidders << ", is " << items_to_bidders.size() << std::endl;
+ throw std::runtime_error("Wrong size of items_to_bidders");
+ }
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ assert( bidders_to_items[bidder_idx] == k_invalid_index or ( bidders_to_items[bidder_idx] < static_cast<IdxType>(num_items) and bidders_to_items[bidder_idx] >= 0));
+
+ if ( bidders_to_items[bidder_idx] != k_invalid_index) {
+
+ if ( std::count(bidders_to_items.begin(),
+ bidders_to_items.end(),
+ bidders_to_items[bidder_idx]) > 1 ) {
+ std::cerr << "Item " << bidders_to_items[bidder_idx];
+ std::cerr << " appears in bidders_to_items more than once" << std::endl;
+ throw std::runtime_error("Duplicate in bidders_to_items");
+ }
+
+ if (items_to_bidders.at(bidders_to_items[bidder_idx]) != static_cast<int>(bidder_idx)) {
+ std::cerr << "Inconsitency: bidder_idx = " << bidder_idx;
+ std::cerr << ", item_idx in bidders_to_items = ";
+ std::cerr << bidders_to_items[bidder_idx];
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[bidders_to_items[bidder_idx]] << std::endl;
+ throw std::runtime_error("inconsistent mapping");
+ }
+ }
+ }
+
+ for(size_t item_idx = 0; item_idx < num_diag_items; ++item_idx) {
+ auto owner = items_to_bidders.at(item_idx);
+ if ( owner == k_invalid_index) {
+ assert((oracle->diag_unassigned_slice_.count(item_idx) == 1 and
+ oracle->diag_items_heap__iters_[item_idx] == oracle->diag_items_heap_.end() and
+ oracle->all_items_heap__iters_[item_idx] == oracle->all_items_heap_.end())
+ or
+ (oracle->diag_unassigned_slice_.count(item_idx) == 0 and
+ oracle->diag_items_heap__iters_[item_idx] != oracle->diag_items_heap_.end() and
+ oracle->all_items_heap__iters_[item_idx] != oracle->all_items_heap_.end()));
+ assert(oracle->diag_assigned_to_diag_slice_.count(item_idx) == 0);
+ } else {
+ if (is_bidder_diagonal(owner)) {
+ assert(oracle->diag_unassigned_slice_.count(item_idx) == 0);
+ assert(oracle->diag_assigned_to_diag_slice_.count(item_idx) == 1);
+ assert(oracle->diag_items_heap__iters_[item_idx] == oracle->diag_items_heap_.end());
+ assert(oracle->all_items_heap__iters_[item_idx] == oracle->all_items_heap_.end());
+ } else {
+ assert(oracle->diag_unassigned_slice_.count(item_idx) == 0);
+ assert(oracle->diag_assigned_to_diag_slice_.count(item_idx) == 0);
+ assert(oracle->diag_items_heap__iters_[item_idx] != oracle->diag_items_heap_.end());
+ assert(oracle->all_items_heap__iters_[item_idx] != oracle->all_items_heap_.end());
+ }
+ }
+ }
+
+ for(IdxType item_idx = 0; item_idx < static_cast<IdxType>(num_bidders); ++item_idx) {
+ assert( items_to_bidders[item_idx] == k_invalid_index or ( items_to_bidders[item_idx] < static_cast<IdxType>(num_items) and items_to_bidders[item_idx] >= 0));
+ if ( items_to_bidders.at(item_idx) != k_invalid_index) {
+
+ // check for uniqueness
+ if ( std::count(items_to_bidders.begin(),
+ items_to_bidders.end(),
+ items_to_bidders[item_idx]) > 1 ) {
+ std::cerr << "Bidder " << items_to_bidders[item_idx];
+ std::cerr << " appears in items_to_bidders more than once" << std::endl;
+ throw std::runtime_error("Duplicate in items_to_bidders");
+ }
+ // check for consistency
+ if (bidders_to_items.at(items_to_bidders.at(item_idx)) != static_cast<int>(item_idx)) {
+ std::cerr << "Inconsitency: item_idx = " << item_idx;
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[item_idx];
+ std::cerr << ", item_idx in bidders_to_items= ";
+ std::cerr << bidders_to_items[items_to_bidders[item_idx]] << std::endl;
+ throw std::runtime_error("inconsistent mapping");
+ }
+ }
+ }
+
+ oracle->sanity_check();
+#endif
+}
+
+template<class R, class AO>
+void AuctionRunnerGaussSeidelSingleDiag<R, AO>::print_matching()
+{
+#ifdef DEBUG_AUCTION
+ sanity_check();
+ for(size_t bIdx = 0; bIdx < bidders_to_items.size(); ++bIdx) {
+ if (bidders_to_items[bIdx] != k_invalid_index) {
+ auto pA = bidders[bIdx];
+ auto pB = items[bidders_to_items[bIdx]];
+ std::cout << pA << " <-> " << pB << "+" << pow(dist_lp(pA, pB, internal_p), wasserstein_power) << std::endl;
+ } else {
+ assert(false);
+ }
+ }
+#endif
+}
+
+} // ws
+} // hera
diff --git a/geom_matching/wasserstein/include/auction_runner_jac.h b/geom_matching/wasserstein/include/auction_runner_jac.h
index d8cada6..252ca32 100644
--- a/geom_matching/wasserstein/include/auction_runner_jac.h
+++ b/geom_matching/wasserstein/include/auction_runner_jac.h
@@ -26,76 +26,205 @@ derivative works thereof, in binary and source code form.
*/
-#ifndef AUCTION_RUNNER_JAK_H
-#define AUCTION_RUNNER_JAK_H
+#ifndef HERA_AUCTION_RUNNER_JAC_H
+#define HERA_AUCTION_RUNNER_JAC_H
-#include <unordered_set>
+#ifdef WASSERSTEIN_PURE_GEOM
+#undef LOG_AUCTION
+#undef ORDERED_BY_PERSISTENCE
+#endif
-#include "auction_oracle.h"
+//#define ORDERED_BY_PERSISTENCE
-namespace geom_ws {
+#include <unordered_set>
-using AuctionOracle = AuctionOracleKDTreeRestricted;
+#include "auction_oracle.h"
-// the two parameters that you can tweak in auction algorithm are:
-// 1. epsilonCommonRatio
-// 2. maxIterNum
+namespace hera {
+namespace ws {
// the two parameters that you can tweak in auction algorithm are:
-// 1. epsilonCommonRatio
-// 2. maxIterNum
+// 1. epsilon_common_ratio
+// 2. max_num_phases
+template<class RealType_ = double, class AuctionOracle_ = AuctionOracleKDTreeRestricted<RealType_>, class PointContainer_ = std::vector<DiagramPoint<RealType_>> > // alternatively: AuctionOracleLazyHeap --- TODO
class AuctionRunnerJac {
public:
- AuctionRunnerJac(const std::vector<DiagramPoint>& A, const std::vector<DiagramPoint>& B, const double q, const double _delta, const double _internal_p);
- void setEpsilon(double newVal) { assert(epsilon > 0.0); epsilon = newVal; };
- double getEpsilon() const { return epsilon; }
- double getWassersteinDistance();
- double getWassersteinCost();
- double getRelativeError() const { return relativeError; };
- static constexpr double epsilonCommonRatio { 5 }; // next epsilon = current epsilon / epsilonCommonRatio
- static constexpr int maxIterNum { 25 }; // maximal number of iterations of epsilon-scaling
-private:
+
+ using Real = RealType_;
+ using AuctionOracle = AuctionOracle_;
+ using DgmPoint = typename AuctionOracle::DiagramPointR;
+ using IdxValPairR = IdxValPair<Real>;
+ using PointContainer = PointContainer_;
+
+ const Real k_lowest_bid_value = -1; // all bid values must be positive
+
+
+ AuctionRunnerJac(const PointContainer& A,
+ const PointContainer& B,
+ const AuctionParams<Real>& params,
+ const std::string& _log_filename_prefix = "");
+
+ void set_epsilon(Real new_val);
+ Real get_epsilon() const { return epsilon; }
+ void run_auction();
+ template<class Range>
+ void run_bidding_step(const Range& r);
+ bool is_done() const;
+ void decrease_epsilon();
+ Real get_wasserstein_distance();
+ Real get_wasserstein_cost();
+ Real get_relative_error(const bool debug_output = false) const;
+//private:
// private data
- std::vector<DiagramPoint> bidders, items;
- const size_t numBidders;
- const size_t numItems;
- std::vector<IdxType> itemsToBidders;
- std::vector<IdxType> biddersToItems;
- double wassersteinPower;
- double epsilon;
- double delta;
- double internal_p;
- double weightAdjConst;
- double wassersteinDistance;
- double wassersteinCost;
- std::vector<IdxValPair> bidTable;
- double relativeError;
+ PointContainer bidders;
+ PointContainer items;
+ const size_t num_bidders;
+ const size_t num_items;
+ std::vector<IdxType> items_to_bidders;
+ std::vector<IdxType> bidders_to_items;
+ Real wasserstein_power;
+ Real epsilon;
+ Real delta;
+ Real internal_p;
+ Real initial_epsilon;
+ const Real epsilon_common_ratio; // next epsilon = current epsilon / epsilon_common_ratio
+ const int max_num_phases; // maximal number of phases of epsilon-scaling
+ Real weight_adj_const;
+ Real wasserstein_cost;
+ std::vector<IdxValPairR> bid_table;
// to get the 2 best items
- std::unique_ptr<AuctionOracle> oracle;
- std::list<size_t> unassignedBidders;
- std::vector< std::list<size_t>::iterator > unassignedBiddersIterators;
- std::vector< short > itemReceivedBidVec;
- std::list<size_t> itemsWithBids;
+ AuctionOracle oracle;
+ std::unordered_set<size_t> unassigned_bidders;
+ std::unordered_set<size_t> items_with_bids;
+ // to imitate Gauss-Seidel
+ const size_t max_bids_per_round;
+ Real partial_cost { 0.0 };
+ bool is_distance_computed { false };
+ int num_rounds { 0 };
+ int num_phase { 0 };
+ int dimension;
+
+ size_t unassigned_threshold; // for experiments
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ std::unordered_set<size_t> unassigned_normal_bidders;
+ std::unordered_set<size_t> unassigned_diag_bidders;
+ bool diag_first {true};
+ size_t batch_size { 1000 };
+#ifdef ORDERED_BY_PERSISTENCE
+ // to process unassigned by persistence
+ using RealIdxPair = std::pair<Real, size_t>;
+ std::set<RealIdxPair, std::greater<RealIdxPair>> unassigned_normal_bidders_by_persistence;
+#endif
+
+ // to stop earlier in the last phase
+ const Real total_items_persistence;
+ const Real total_bidders_persistence;
+ Real unassigned_bidders_persistence;
+ Real unassigned_items_persistence;
+ Real gamma_threshold;
+
+
+ size_t num_diag_items { 0 };
+ size_t num_normal_items { 0 };
+ size_t num_diag_bidders { 0 };
+ size_t num_normal_bidders { 0 };
+
+
+#endif
+
+
+
// private methods
- void assignGoodToBidder(const IdxType bidderIdx, const IdxType itemsIdx);
- void assignToBestBidder(const IdxType itemsIdx);
- void clearBidTable();
- void runAuction();
- void runAuctionPhase();
- void submitBid(IdxType bidderIdx, const IdxValPair& itemsBidValuePair);
- void flushAssignment();
+ void assign_item_to_bidder(const IdxType bidder_idx, const IdxType items_idx);
+ void assign_to_best_bidder(const IdxType items_idx);
+ void clear_bid_table();
+ void run_auction_phases(const int max_num_phases, const Real _initial_epsilon);
+ void run_auction_phase();
+ void submit_bid(IdxType bidder_idx, const IdxValPairR& items_bid_value_pair);
+ void flush_assignment();
+ Real get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx) const;
+#ifndef WASSERSTEIN_PURE_GEOM
+ Real get_cost_to_diagonal(const DgmPoint& pt) const;
+ Real get_gamma() const;
+#endif
+ bool continue_auction_phase() const;
+
+ void add_unassigned_bidder(const size_t bidder_idx);
+ void remove_unassigned_bidder(const size_t bidder_idx);
+ void remove_unassigned_item(const size_t item_idx);
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ bool is_item_diagonal(const size_t item_idx) const { return item_idx < num_diag_items; }
+ bool is_item_normal(const size_t item_idx) const { return not is_item_diagonal(item_idx); }
+ bool is_bidder_diagonal(const size_t bidder_idx) const { return bidder_idx >= num_normal_bidders; }
+ bool is_bidder_normal(const size_t bidder_idx) const { return not is_bidder_diagonal(bidder_idx); }
+#endif
+
+
// for debug only
- void sanityCheck();
- void printDebug();
- int countUnhappy();
- void printMatching();
- double getDistanceToQthPowerInternal();
-};
+ void sanity_check();
+ void print_debug();
+ void print_matching();
+
+ std::string log_filename_prefix;
+ const Real k_max_relative_error = 2.0; // if relative error cannot be estimated or is too large, use this value
+
+#ifdef LOG_AUCTION
+
+ size_t parallel_threshold { 5000 };
+ bool is_step_parallel {false};
+ std::unordered_set<size_t> unassigned_items;
+ std::unordered_set<size_t> unassigned_normal_items;
+ std::unordered_set<size_t> unassigned_diag_items;
+ std::unordered_set<size_t> never_assigned_bidders;
+ size_t all_assigned_round { 0 };
+ size_t all_assigned_round_found { false };
+
+ int num_rounds_non_cumulative { 0 }; // set to 0 in the beginning of each phase
+ int num_diag_assignments { 0 };
+ int num_diag_assignments_non_cumulative { 0 };
+ int num_diag_bids_submitted { 0 };
+ int num_diag_stole_from_diag { 0 };
+ int num_normal_assignments { 0 };
+ int num_normal_assignments_non_cumulative { 0 };
+ int num_normal_bids_submitted { 0 };
+
+ std::vector<std::vector<size_t>> price_change_cnt_vec;
+
+
+ const char* plot_logger_name = "plot_logger";
+ const char* price_state_logger_name = "price_stat_logger";
+ std::string plot_logger_file_name;
+ std::string price_stat_logger_file_name;
+ std::shared_ptr<spdlog::logger> plot_logger;
+ std::shared_ptr<spdlog::logger> price_stat_logger;
+ std::shared_ptr<spdlog::logger> console_logger;
+
+
+ int num_parallel_bids { 0 };
+ int num_total_bids { 0 };
+
+ int num_parallel_diag_bids { 0 };
+ int num_total_diag_bids { 0 };
+
+ int num_parallel_normal_bids { 0 };
+ int num_total_normal_bids { 0 };
+
+ int num_parallel_assignments { 0 };
+ int num_total_assignments { 0 };
+#endif
+
+}; // AuctionRunnerJac
+
+} // ws
+} // hera
+#include "auction_runner_jac.hpp"
-} // end of namespace geom_ws
+#undef ORDERED_BY_PERSISTENCE
#endif
diff --git a/geom_matching/wasserstein/include/auction_runner_jac.hpp b/geom_matching/wasserstein/include/auction_runner_jac.hpp
new file mode 100644
index 0000000..8663bae
--- /dev/null
+++ b/geom_matching/wasserstein/include/auction_runner_jac.hpp
@@ -0,0 +1,873 @@
+/*
+
+Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef AUCTION_RUNNER_JAC_HPP
+#define AUCTION_RUNNER_JAC_HPP
+
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "def_debug_ws.h"
+#include "auction_runner_jac.h"
+
+
+#ifdef FOR_R_TDA
+#include "Rcpp.h"
+#undef DEBUG_AUCTION
+#endif
+
+
+namespace hera {
+namespace ws {
+
+
+// *****************************
+// AuctionRunnerJac
+// *****************************
+
+ template<class R, class AO, class PC>
+ AuctionRunnerJac<R, AO, PC>::AuctionRunnerJac(const PointContainer& A,
+ const PointContainer& B,
+ const AuctionParams<Real>& params,
+ const std::string &_log_filename_prefix
+ ) :
+ bidders(A),
+ items(B),
+ num_bidders(A.size()),
+ num_items(A.size()),
+ items_to_bidders(A.size(), k_invalid_index),
+ bidders_to_items(A.size(), k_invalid_index),
+ wasserstein_power(params.wasserstein_power),
+ delta(params.delta),
+ internal_p(params.internal_p),
+ initial_epsilon(params.initial_epsilon),
+ epsilon_common_ratio(params.epsilon_common_ratio == 0.0 ? 5.0 : params.epsilon_common_ratio),
+ max_num_phases(params.max_num_phases),
+ bid_table(A.size(), std::make_pair(k_invalid_index, k_lowest_bid_value)),
+ oracle(bidders, items, params),
+ max_bids_per_round(params.max_bids_per_round),
+ dimension(params.dim),
+#ifndef WASSERSTEIN_PURE_GEOM
+ total_items_persistence(std::accumulate(items.begin(),
+ items.end(),
+ R(0.0),
+ [params](const Real &ps, const DgmPoint &item) {
+ return ps + std::pow(item.persistence_lp(params.internal_p), params.wasserstein_power);
+ }
+ )),
+ total_bidders_persistence(std::accumulate(bidders.begin(),
+ bidders.end(),
+ R(0.0),
+ [params](const Real &ps, const DgmPoint &bidder) {
+ return ps + std::pow(bidder.persistence_lp(params.internal_p), params.wasserstein_power);
+ }
+ )),
+ unassigned_bidders_persistence(total_bidders_persistence),
+ unassigned_items_persistence(total_items_persistence),
+ gamma_threshold(params.gamma_threshold),
+#endif
+ log_filename_prefix(_log_filename_prefix)
+ {
+ assert(A.size() == B.size());
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ for (const auto &p : bidders) {
+ if (p.is_normal()) {
+ num_normal_bidders++;
+ num_diag_items++;
+ } else {
+ num_normal_items++;
+ num_diag_bidders++;
+ }
+ }
+#endif
+ // for experiments
+ unassigned_threshold = 100;
+
+#ifdef ORDERED_BY_PERSISTENCE
+ batch_size = 1000;
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders_by_persistence.insert(
+ std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx));
+ }
+ }
+#endif
+
+#ifdef LOG_AUCTION
+ parallel_threshold = 16;
+ console_logger = spdlog::get("console");
+ if (not console_logger) {
+ console_logger = spdlog::stdout_logger_st("console");
+ }
+ console_logger->set_pattern("[%H:%M:%S.%e] %v");
+#ifdef ORDERED_BY_PERSISTENCE
+ if (max_bids_per_round == 1) {
+ console_logger->info("Gauss-Seidel imitated by Jacobi runner, q = {0}, max_bids_per_round = {1}, batch_size = {4}, gamma_threshold = {2}, diag_first = {3} ORDERED_BY_PERSISTENCE",
+ wasserstein_power,
+ max_bids_per_round,
+ gamma_threshold,
+ diag_first,
+ batch_size);
+ } else {
+ console_logger->info("Jacobi runner, q = {0}, max_bids_per_round = {1}, batch_size = {4}, gamma_threshold = {2}, diag_first = {3} ORDERED_BY_PERSISTENCE",
+ wasserstein_power,
+ max_bids_per_round,
+ gamma_threshold,
+ diag_first,
+ batch_size);
+ }
+
+#else
+ if (max_bids_per_round == 1) {
+ console_logger->info(
+ "Gauss-Seidel imitated by Jacobi runner, q = {0}, max_bids_per_round = {1}, batch_size = {4}, gamma_threshold = {2}, diag_first = {3}",
+ wasserstein_power,
+ max_bids_per_round,
+ gamma_threshold,
+ diag_first,
+ batch_size);
+ } else {
+ console_logger->info(
+ "Jacobi runner, q = {0}, max_bids_per_round = {1}, batch_size = {4}, gamma_threshold = {2}, diag_first = {3}",
+ wasserstein_power,
+ max_bids_per_round,
+ gamma_threshold,
+ diag_first,
+ batch_size);
+ }
+#endif
+
+ plot_logger_file_name = log_filename_prefix + "_plot.txt";
+ plot_logger = spdlog::get(plot_logger_name);
+ if (not plot_logger) {
+ plot_logger = spdlog::basic_logger_st(plot_logger_name, plot_logger_file_name);
+ }
+ plot_logger->info("New plot starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ plot_logger->set_pattern("%v");
+
+ price_stat_logger_file_name = log_filename_prefix + "_price_change_stat";
+ price_stat_logger = spdlog::get(price_state_logger_name);
+ if (not price_stat_logger) {
+ price_stat_logger = spdlog::basic_logger_st(price_state_logger_name,
+ price_stat_logger_file_name);
+ }
+ price_stat_logger->info(
+ "New price statistics starts here, diagram size = {0}, gamma_threshold = {1}, epsilon_common_ratio = {2}",
+ bidders.size(),
+ gamma_threshold,
+ epsilon_common_ratio);
+ price_stat_logger->set_pattern("%v");
+#endif
+ }
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_cost_to_diagonal(const DgmPoint &pt) const {
+ return std::pow(pt.persistence_lp(internal_p), wasserstein_power);
+ }
+
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_gamma() const {
+ return std::pow(std::fabs(unassigned_items_persistence + unassigned_bidders_persistence),
+ 1.0 / wasserstein_power);
+ }
+#endif
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::assign_item_to_bidder(IdxType item_idx, IdxType bidder_idx)
+ {
+ //sanity_check();
+ // only unassigned bidders submit bids
+ assert(bidders_to_items[bidder_idx] == k_invalid_index);
+
+ IdxType old_item_owner = items_to_bidders[item_idx];
+
+ // set new owner
+ bidders_to_items[bidder_idx] = item_idx;
+ items_to_bidders[item_idx] = bidder_idx;
+
+ // remove bidder and item from the sets of unassigned bidders/items
+ remove_unassigned_bidder(bidder_idx);
+
+ if (k_invalid_index != old_item_owner) {
+ // old owner of item becomes unassigned
+ bidders_to_items[old_item_owner] = k_invalid_index;
+ add_unassigned_bidder(old_item_owner);
+ // existing edge was removed, decrease partial_cost
+ partial_cost -= get_item_bidder_cost(item_idx, old_item_owner);
+ } else {
+ // item was unassigned before
+ remove_unassigned_item(item_idx);
+ }
+
+ // new edge was added to matching, increase partial cost
+ partial_cost += get_item_bidder_cost(item_idx, bidder_idx);
+
+#ifdef LOG_AUCTION
+ if (is_item_diagonal(item_idx)) {
+ num_diag_assignments++;
+ num_diag_assignments_non_cumulative++;
+ } else {
+ num_normal_assignments++;
+ num_normal_assignments_non_cumulative++;
+ }
+
+ if (k_invalid_index != old_item_owner) {
+ if (is_bidder_diagonal(bidder_idx) and is_bidder_diagonal(old_item_owner)) {
+ num_diag_stole_from_diag++;
+ }
+ }
+#endif
+
+ //sanity_check();
+ }
+
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_item_bidder_cost(const size_t item_idx, const size_t bidder_idx) const
+ {
+ return std::pow(dist_lp(bidders[bidder_idx], items[item_idx], internal_p, dimension),
+ wasserstein_power);
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::assign_to_best_bidder(IdxType item_idx) {
+ assert(item_idx >= 0 and item_idx < static_cast<IdxType>(num_items));
+ assert(bid_table[item_idx].first != k_invalid_index);
+ IdxValPairR best_bid{bid_table[item_idx]};
+ assign_item_to_bidder(item_idx, best_bid.first);
+ oracle.set_price(item_idx, best_bid.second);
+#ifdef LOG_AUCTION
+
+ if (is_step_parallel) {
+ num_parallel_assignments++;
+ }
+ num_total_assignments++;
+
+ price_change_cnt_vec.back()[item_idx]++;
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::clear_bid_table() {
+ auto iter = items_with_bids.begin();
+ while (iter != items_with_bids.end()) {
+ auto item_with_bid_idx = *iter;
+ bid_table[item_with_bid_idx].first = k_invalid_index;
+ bid_table[item_with_bid_idx].second = k_lowest_bid_value;
+ iter = items_with_bids.erase(iter);
+ }
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::submit_bid(IdxType bidder_idx, const IdxValPairR &bid) {
+ IdxType item_idx = bid.first;
+ Real bid_value = bid.second;
+ assert(item_idx >= 0);
+ if (bid_table[item_idx].second < bid_value) {
+ bid_table[item_idx].first = bidder_idx;
+ bid_table[item_idx].second = bid_value;
+ }
+ items_with_bids.insert(item_idx);
+
+#ifdef LOG_AUCTION
+
+ num_total_bids++;
+
+
+ if (is_bidder_diagonal(bidder_idx)) {
+ num_diag_bids_submitted++;
+ } else {
+ num_normal_bids_submitted++;
+ }
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::print_debug() {
+#ifdef DEBUG_AUCTION
+ sanity_check();
+ std::cout << "**********************" << std::endl;
+ std::cout << "Current assignment:" << std::endl;
+ for(size_t idx = 0; idx < bidders_to_items.size(); ++idx) {
+ std::cout << idx << " <--> " << bidders_to_items[idx] << std::endl;
+ }
+ std::cout << "Weights: " << std::endl;
+ //for(size_t i = 0; i < num_bidders; ++i) {
+ //for(size_t j = 0; j < num_items; ++j) {
+ //std::cout << oracle.weight_matrix[i][j] << " ";
+ //}
+ //std::cout << std::endl;
+ //}
+ std::cout << "Prices: " << std::endl;
+ for(const auto price : oracle.get_prices()) {
+ std::cout << price << std::endl;
+ }
+ //std::cout << "Value matrix: " << std::endl;
+ //for(size_t i = 0; i < num_bidders; ++i) {
+ //for(size_t j = 0; j < num_items; ++j) {
+ //std::cout << oracle.weight_matrix[i][j] - oracle.prices[j] << " ";
+ //}
+ //std::cout << std::endl;
+ //}
+ std::cout << "**********************" << std::endl;
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_relative_error(const bool debug_output) const
+ {
+ Real result;
+#ifndef WASSERSTEIN_PURE_GEOM
+ Real gamma = get_gamma();
+#else
+ Real gamma = 0.0;
+#endif
+ // cost minus n epsilon
+ Real reduced_cost = partial_cost - num_bidders * get_epsilon();
+ if (reduced_cost < 0) {
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->info("Epsilon too large, reduced_cost = {0}, gamma = {1}", reduced_cost, gamma);
+ }
+#endif
+ result = k_max_relative_error;
+ } else {
+ Real denominator = std::pow(reduced_cost, 1.0 / wasserstein_power) - gamma;
+ if (denominator <= 0) {
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->info("Epsilon too large, reduced_cost = {0}, denominator = {1}, gamma = {2}",
+ reduced_cost, denominator, gamma);
+ }
+#endif
+ result = k_max_relative_error;
+ } else {
+ Real numerator = 2 * gamma +
+ std::pow(partial_cost, 1.0 / wasserstein_power) -
+ std::pow(reduced_cost, 1.0 / wasserstein_power);
+
+ result = numerator / denominator;
+#ifdef LOG_AUCTION
+ if (debug_output) {
+ console_logger->info(
+ "Reduced_cost = {0}, denominator = {1}, numerator {2}, error = {3}, gamma = {4}",
+ reduced_cost,
+ denominator,
+ numerator,
+ result,
+ gamma);
+ }
+#endif
+ }
+ }
+ return result;
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::flush_assignment() {
+ for (auto &b2i : bidders_to_items) {
+ b2i = k_invalid_index;
+ }
+ for (auto &i2b : items_to_bidders) {
+ i2b = k_invalid_index;
+ }
+
+ // all bidders and items become unassigned
+ for (size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ unassigned_bidders.insert(bidder_idx);
+ }
+
+#ifdef ORDERED_BY_PERSISTENCE
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders_by_persistence.insert(
+ std::make_pair(bidders[bidder_idx].persistence_lp(1.0), bidder_idx));
+ }
+ }
+#endif
+ oracle.adjust_prices();
+
+ partial_cost = 0.0;
+
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ for (size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders.insert(bidder_idx);
+ } else {
+ unassigned_diag_bidders.insert(bidder_idx);
+ }
+ }
+
+ unassigned_bidders_persistence = total_bidders_persistence;
+ unassigned_items_persistence = total_items_persistence;
+
+#ifdef LOG_AUCTION
+
+ price_change_cnt_vec.push_back(std::vector<size_t>(num_items, 0));
+
+ never_assigned_bidders = unassigned_bidders;
+
+ for (size_t item_idx = 0; item_idx < items.size(); ++item_idx) {
+ unassigned_items.insert(item_idx);
+ if (is_item_normal(item_idx)) {
+ unassigned_normal_items.insert(item_idx);
+ } else {
+ unassigned_diag_items.insert(item_idx);
+ }
+ }
+
+ num_diag_bids_submitted = 0;
+ num_normal_bids_submitted = 0;
+ num_diag_assignments = 0;
+ num_normal_assignments = 0;
+
+ all_assigned_round = 0;
+ all_assigned_round_found = false;
+ num_rounds_non_cumulative = 0;
+#endif
+#endif
+
+ } // flush_assignment
+
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::set_epsilon(Real new_val) {
+ assert(new_val > 0.0);
+ epsilon = new_val;
+ oracle.set_epsilon(new_val);
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::run_auction_phases(const int max_num_phases, const Real _initial_epsilon) {
+ set_epsilon(_initial_epsilon);
+ assert(oracle.get_epsilon() > 0);
+ for (int phase_num = 0; phase_num < max_num_phases; ++phase_num) {
+ flush_assignment();
+ run_auction_phase();
+
+#ifdef LOG_AUCTION
+ console_logger->info(
+ "Phase {0} done, current_result = {1}, eps = {2}, error = {7}, num_rounds = {3}, num_assignments = {4}, num_bids_submitted = {5}, # unassigned = {6}",
+ num_phase,
+ partial_cost,
+ get_epsilon(),
+ format_int<>(num_rounds),
+ format_int<>(num_normal_assignments + num_diag_assignments),
+ format_int<>(num_normal_bids_submitted + num_diag_bids_submitted),
+ unassigned_bidders.size(),
+ get_relative_error(num_phase == 1)
+ );
+
+// console_logger->info("num_rounds (non-cumulative)= {0}, num_diag_assignments = {1}, num_normal_assignments = {2}, num_diag_bids_submitted = {3}, num_normal_bids_submitted = {4}",
+// format_int<>(num_rounds_non_cumulative),
+// format_int<>(num_diag_assignments),
+// format_int<>(num_normal_assignments),
+// format_int<>(num_diag_bids_submitted),
+// format_int<>(num_normal_bids_submitted)
+// );
+
+ console_logger->info(
+ "num_parallel_bids / num_total_bids = {0} / {1} = {2}, num_parallel_assignments / num_total_aassignments = {3} / {4} = {5}",
+ format_int<>(num_parallel_bids),
+ format_int<>(num_total_bids),
+ static_cast<double>(num_parallel_bids) / static_cast<double>(num_total_bids),
+ format_int<>(num_parallel_assignments),
+ format_int<>(num_total_assignments),
+ static_cast<double>(num_parallel_assignments) / static_cast<double>(num_total_assignments)
+ );
+
+ console_logger->info(
+ "num_parallel_diag_bids / num_total_diag_bids = {0} / {1} = {2}, num_parallel_normal_bids / num_total_normal_bids = {3} / {4} = {5}",
+ format_int<>(num_parallel_diag_bids),
+ format_int<>(num_total_diag_bids),
+ static_cast<double>(num_parallel_diag_bids) / static_cast<double>(num_total_diag_bids),
+ format_int<>(num_parallel_normal_bids),
+ format_int<>(num_total_normal_bids),
+ static_cast<double>(num_parallel_normal_bids) / static_cast<double>(num_total_normal_bids)
+ );
+
+// console_logger->info("num_rounds before all biders assigned = {0}, num_rounds (non-cumulative)= {1}, fraction = {2}",
+// format_int<>(all_assigned_round),
+// format_int<>(num_rounds_non_cumulative),
+// static_cast<double>(all_assigned_round) / static_cast<double>(num_rounds_non_cumulative)
+// );
+
+ for (size_t item_idx = 0; item_idx < num_items; ++item_idx) {
+ price_stat_logger->info("{0} {1} {2} {3} {4}",
+ phase_num,
+ item_idx,
+ items[item_idx][0],
+ items[item_idx][1],
+ price_change_cnt_vec.back()[item_idx]
+ );
+ }
+#endif
+
+
+ if (is_done())
+ break;
+ else
+ decrease_epsilon();
+
+ }
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::decrease_epsilon() {
+ set_epsilon(get_epsilon() / epsilon_common_ratio);
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::run_auction()
+ {
+ if (num_bidders == 1) {
+ assign_item_to_bidder(0, 0);
+ wasserstein_cost = get_item_bidder_cost(0,0);
+ return;
+ }
+ double init_eps = (initial_epsilon > 0.0) ? initial_epsilon : oracle.max_val_ / 4.0;
+ run_auction_phases(max_num_phases, init_eps);
+ is_distance_computed = true;
+ wasserstein_cost = partial_cost;
+ if (not is_done()) {
+#ifndef FOR_R_TDA
+ std::cerr << "Maximum iteration number exceeded, exiting. Current result is: ";
+ std::cerr << get_wasserstein_distance() << std::endl;
+#endif
+ throw std::runtime_error("Maximum iteration number exceeded");
+ }
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::add_unassigned_bidder(const size_t bidder_idx)
+ {
+ unassigned_bidders.insert(bidder_idx);
+
+#ifndef WASSERSTEIN_PURE_GEOM
+ const auto &bidder = bidders[bidder_idx];
+ unassigned_bidders_persistence += get_cost_to_diagonal(bidder);
+
+ if (is_bidder_diagonal(bidder_idx)) {
+ unassigned_diag_bidders.insert(bidder_idx);
+ } else {
+ unassigned_normal_bidders.insert(bidder_idx);
+ }
+#ifdef ORDERED_BY_PERSISTENCE
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders_by_persistence.insert(std::make_pair(bidder.persistence_lp(1.0), bidder_idx));
+ }
+#endif
+
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::remove_unassigned_bidder(const size_t bidder_idx)
+ {
+ unassigned_bidders.erase(bidder_idx);
+#ifndef WASSERSTEIN_PURE_GEOM
+ const auto &bidder = bidders[bidder_idx];
+ unassigned_bidders_persistence -= get_cost_to_diagonal(bidder);
+
+#ifdef ORDERED_BY_PERSISTENCE
+ if (is_bidder_normal(bidder_idx)) {
+ unassigned_normal_bidders_by_persistence.erase(std::make_pair(bidder.persistence_lp(1.0), bidder_idx));
+ }
+#endif
+
+ if (is_bidder_diagonal(bidder_idx)) {
+ unassigned_diag_bidders.erase(bidder_idx);
+ } else {
+ unassigned_normal_bidders.erase(bidder_idx);
+ }
+
+
+#ifdef LOG_AUCTION
+ never_assigned_bidders.erase(bidder_idx);
+ if (never_assigned_bidders.empty() and not all_assigned_round_found) {
+ all_assigned_round = num_rounds_non_cumulative;
+ all_assigned_round_found = true;
+ }
+#endif
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::remove_unassigned_item(const size_t item_idx) {
+#ifndef WASSERSTEIN_PURE_GEOM
+ unassigned_items_persistence -= get_cost_to_diagonal(items[item_idx]);
+
+#ifdef LOG_AUCTION
+ unassigned_items.erase(item_idx);
+
+ if (is_item_normal(item_idx)) {
+ unassigned_normal_items.erase(item_idx);
+ } else {
+ unassigned_diag_items.erase(item_idx);
+ }
+#endif
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ template<class Range>
+ void AuctionRunnerJac<R, AO, PC>::run_bidding_step(const Range &active_bidders)
+ {
+#ifdef LOG_AUCTION
+ is_step_parallel = false;
+ size_t diag_bids_submitted = 0;
+ size_t normal_bids_submitted = 0;
+#endif
+
+ clear_bid_table();
+ size_t bids_submitted = 0;
+ for (const auto bidder_idx : active_bidders) {
+
+ ++bids_submitted;
+
+ submit_bid(bidder_idx, oracle.get_optimal_bid(bidder_idx));
+
+#ifdef LOG_AUCTION
+ if (is_bidder_diagonal(bidder_idx)) {
+ diag_bids_submitted++;
+ } else {
+ normal_bids_submitted++;
+ }
+
+ if (bids_submitted >= parallel_threshold) {
+ is_step_parallel = true;
+ }
+
+ if (bids_submitted >= max_bids_per_round) {
+ break;
+ }
+ if (diag_first and not unassigned_diag_bidders.empty() and
+ diag_bids_submitted >= oracle.get_heap_top_size()) {
+ continue;
+ }
+#endif
+ }
+
+#ifdef LOG_AUCTION
+ num_total_diag_bids += diag_bids_submitted;
+ num_total_normal_bids += normal_bids_submitted;
+ if (is_step_parallel) {
+ num_parallel_bids += bids_submitted;
+ num_parallel_diag_bids += diag_bids_submitted;
+ num_parallel_normal_bids += normal_bids_submitted;
+ }
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ bool AuctionRunnerJac<R, AO, PC>::is_done() const
+ {
+ return get_relative_error() <= delta;
+ }
+
+ template<class R, class AO, class PC>
+ bool AuctionRunnerJac<R, AO, PC>::continue_auction_phase() const
+ {
+ return not unassigned_bidders.empty() and not is_done();
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::run_auction_phase()
+ {
+ num_phase++;
+ //console_logger->debug("Entered run_auction_phase");
+
+ do {
+ num_rounds++;
+#ifdef LOG_AUCTION
+ num_diag_stole_from_diag = 0;
+ num_normal_assignments_non_cumulative = 0;
+ num_diag_assignments_non_cumulative = 0;
+ num_rounds_non_cumulative++;
+#endif
+
+ // bidding
+#ifdef ORDERED_BY_PERSISTENCE
+ if (not unassigned_diag_bidders.empty()) {
+ run_bidding_step(unassigned_diag_bidders);
+ } else {
+ std::vector<size_t> active_bidders;
+ active_bidders.reserve(batch_size);
+ for (auto iter = unassigned_normal_bidders_by_persistence.begin(); iter != unassigned_normal_bidders_by_persistence.end(); ++iter) {
+ active_bidders.push_back(iter->second);
+ if (active_bidders.size() >= batch_size) {
+ break;
+ }
+ }
+ run_bidding_step(active_bidders);
+ }
+#elif defined WASSERSTEIN_PURE_GEOM
+ run_bidding_step(unassigned_bidders);
+#else
+ if (diag_first and not unassigned_diag_bidders.empty()) {
+ run_bidding_step(unassigned_diag_bidders);
+ } else {
+ run_bidding_step(unassigned_bidders);
+ }
+#endif
+
+ // assignment
+ for (auto item_idx : items_with_bids) {
+ assign_to_best_bidder(item_idx);
+ }
+#ifdef LOG_AUCTION
+ plot_logger->info("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14}",
+ num_phase,
+ num_rounds,
+ unassigned_bidders.size(),
+ get_gamma(),
+ partial_cost,
+ oracle.get_epsilon(),
+ unassigned_normal_bidders.size(),
+ unassigned_diag_bidders.size(),
+ unassigned_normal_items.size(),
+ unassigned_diag_items.size(),
+ num_normal_assignments_non_cumulative,
+ num_diag_assignments_non_cumulative,
+ oracle.get_heap_top_size(),
+ get_relative_error(false),
+ num_diag_stole_from_diag
+ );
+#endif
+ //sanity_check();
+ } while (continue_auction_phase());
+ }
+
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_wasserstein_distance()
+ {
+ assert(is_distance_computed);
+ return std::pow(wasserstein_cost, 1.0 / wasserstein_power);
+ }
+
+ template<class R, class AO, class PC>
+ typename AuctionRunnerJac<R, AO, PC>::Real
+ AuctionRunnerJac<R, AO, PC>::get_wasserstein_cost()
+ {
+ assert(is_distance_computed);
+ return wasserstein_cost;
+ }
+
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::sanity_check()
+ {
+#ifdef DEBUG_AUCTION
+ if (bidders_to_items.size() != num_bidders) {
+ std::cerr << "Wrong size of bidders_to_items, must be " << num_bidders << ", is " << bidders_to_items.size() << std::endl;
+ throw "Wrong size of bidders_to_items";
+ }
+
+ if (items_to_bidders.size() != num_bidders) {
+ std::cerr << "Wrong size of items_to_bidders, must be " << num_bidders << ", is " << items_to_bidders.size() << std::endl;
+ throw "Wrong size of items_to_bidders";
+ }
+
+ for(size_t bidder_idx = 0; bidder_idx < num_bidders; ++bidder_idx) {
+ if ( bidders_to_items[bidder_idx] >= 0) {
+
+ if ( std::count(bidders_to_items.begin(),
+ bidders_to_items.end(),
+ bidders_to_items[bidder_idx]) > 1 ) {
+ std::cerr << "Good " << bidders_to_items[bidder_idx];
+ std::cerr << " appears in bidders_to_items more than once" << std::endl;
+ throw "Duplicate in bidders_to_items";
+ }
+
+ if (items_to_bidders.at(bidders_to_items[bidder_idx]) != static_cast<int>(bidder_idx)) {
+ std::cerr << "Inconsitency: bidder_idx = " << bidder_idx;
+ std::cerr << ", item_idx in bidders_to_items = ";
+ std::cerr << bidders_to_items[bidder_idx];
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[bidders_to_items[bidder_idx]] << std::endl;
+ throw "inconsistent mapping";
+ }
+ }
+ }
+
+ for(IdxType item_idx = 0; item_idx < static_cast<IdxType>(num_bidders); ++item_idx) {
+ if ( items_to_bidders[item_idx] >= 0) {
+
+ // check for uniqueness
+ if ( std::count(items_to_bidders.begin(),
+ items_to_bidders.end(),
+ items_to_bidders[item_idx]) > 1 ) {
+ std::cerr << "Bidder " << items_to_bidders[item_idx];
+ std::cerr << " appears in items_to_bidders more than once" << std::endl;
+ throw "Duplicate in items_to_bidders";
+ }
+ // check for consistency
+ if (bidders_to_items.at(items_to_bidders[item_idx]) != static_cast<int>(item_idx)) {
+ std::cerr << "Inconsitency: item_idx = " << item_idx;
+ std::cerr << ", bidder_idx in items_to_bidders = ";
+ std::cerr << items_to_bidders[item_idx];
+ std::cerr << ", item_idx in bidders_to_items= ";
+ std::cerr << bidders_to_items[items_to_bidders[item_idx]] << std::endl;
+ throw "inconsistent mapping";
+ }
+ }
+ }
+#endif
+ }
+
+ template<class R, class AO, class PC>
+ void AuctionRunnerJac<R, AO, PC>::print_matching() {
+#ifdef DEBUG_AUCTION
+ sanity_check();
+ for(size_t bidder_idx = 0; bidder_idx < bidders_to_items.size(); ++bidder_idx) {
+ if (bidders_to_items[bidder_idx] >= 0) {
+ auto pA = bidders[bidder_idx];
+ auto pB = items[bidders_to_items[bidder_idx]];
+ std::cout << pA << " <-> " << pB << "+" << pow(dist_lp(pA, pB, internal_p, dimension), wasserstein_power) << std::endl;
+ } else {
+ assert(false);
+ }
+ }
+#endif
+ }
+
+} // ws
+} // hera
+
+#endif
diff --git a/geom_matching/wasserstein/include/basic_defs_ws.h b/geom_matching/wasserstein/include/basic_defs_ws.h
index db305c0..58d6fd2 100644
--- a/geom_matching/wasserstein/include/basic_defs_ws.h
+++ b/geom_matching/wasserstein/include/basic_defs_ws.h
@@ -29,91 +29,308 @@ derivative works thereof, in binary and source code form.
#define BASIC_DEFS_WS_H
#include <vector>
-#include <cmath>
+#include <math.h>
#include <cstddef>
#include <unordered_map>
#include <unordered_set>
#include <string>
+#include <iomanip>
+#include <locale>
#include <cassert>
+#include <limits>
+#include <ostream>
+#include <typeinfo>
#ifdef _WIN32
#include <ciso646>
#endif
+#ifndef FOR_R_TDA
+#include "spdlog/spdlog.h"
+#include "spdlog/fmt/fmt.h"
+#include "spdlog/fmt/ostr.h"
+#endif
+#include "dnn/geometry/euclidean-dynamic.h"
#include "def_debug_ws.h"
#define MIN_VALID_ID 10
-namespace geom_ws {
+namespace hera
+{
-using IdxType = int;
-using IdxValPair = std::pair<IdxType, double>;
+template<class Real = double>
+bool is_infinity(const Real& x)
+{
+ return x == Real(-1);
+};
+template<class Real = double>
+Real get_infinity()
+{
+ return Real( -1 );
+}
-struct Point {
- double x, y;
- bool operator==(const Point& other) const;
- bool operator!=(const Point& other) const;
- Point(double ax, double ay) : x(ax), y(ay) {}
- Point() : x(0.0), y(0.0) {}
-#ifndef FOR_R_TDA
- friend std::ostream& operator<<(std::ostream& output, const Point p);
-#endif
+template<class Real = double>
+bool is_p_valid_norm(const Real& p)
+{
+ return is_infinity<Real>(p) or p >= Real(1);
+}
+
+template<class Real = double>
+struct AuctionParams
+{
+ Real wasserstein_power { 1.0 };
+ Real delta { 0.01 }; // relative error
+ Real internal_p { get_infinity<Real>() };
+ Real initial_epsilon { 0.0 }; // 0.0 means maxVal / 4.0
+ Real epsilon_common_ratio { 5.0 };
+ Real gamma_threshold { 0.0 }; // for experiments, not in use now
+ int max_num_phases { std::numeric_limits<decltype(max_num_phases)>::max() };
+ size_t max_bids_per_round { 1 }; // imitate Gauss-Seidel is default behaviour
+ unsigned int dim { 2 }; // for pure geometric version only; ignored in persistence diagrams
};
-struct DiagramPoint
+namespace ws
{
- // data members
- // Points above the diagonal have type NORMAL
- // Projections onto the diagonal have type DIAG
- // for DIAG points only x-coordinate is relevant
- enum Type { NORMAL, DIAG};
- double x, y;
- Type type;
- // methods
- DiagramPoint(double xx, double yy, Type ttype);
- bool isDiagonal(void) const { return type == DIAG; }
- bool isNormal(void) const { return type == NORMAL; }
- double getRealX() const; // return the x-coord
- double getRealY() const; // return the y-coord
- double persistenceLp(const double p) const;
+
+ using IdxType = int;
+
+ constexpr size_t k_invalid_index = std::numeric_limits<IdxType>::max();
+
+ template<class Real = double>
+ using IdxValPair = std::pair<IdxType, Real>;
+
+
+
+ template<class R>
+ std::ostream& operator<<(std::ostream& output, const IdxValPair<R> p)
+ {
+ output << fmt::format("({0}, {1})", p.first, p.second);
+ return output;
+ }
+
+ enum class OwnerType { k_none, k_normal, k_diagonal };
+
+ std::ostream& operator<<(std::ostream& s, const OwnerType t)
+ {
+ switch(t)
+ {
+ case OwnerType::k_none : s << "NONE"; break;
+ case OwnerType::k_normal: s << "NORMAL"; break;
+ case OwnerType::k_diagonal: s << "DIAGONAL"; break;
+ }
+ return s;
+ }
+
+ template<class Real = double>
+ struct Point {
+ Real x, y;
+ bool operator==(const Point& other) const;
+ bool operator!=(const Point& other) const;
+ Point(Real _x, Real _y) : x(_x), y(_y) {}
+ Point() : x(0.0), y(0.0) {}
+ };
+
#ifndef FOR_R_TDA
- friend std::ostream& operator<<(std::ostream& output, const DiagramPoint p);
+ template<class Real = double>
+ std::ostream& operator<<(std::ostream& output, const Point<Real> p);
#endif
- struct LexicographicCmp
+ template <class T>
+ inline void hash_combine(std::size_t & seed, const T & v)
{
- bool operator()(const DiagramPoint& p1, const DiagramPoint& p2) const
- { return p1.type < p2.type || (p1.type == p2.type && (p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y))); }
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+
+ template<class Real_ = double>
+ struct DiagramPoint
+ {
+ using Real = Real_;
+ // data members
+ // Points above the diagonal have type NORMAL
+ // Projections onto the diagonal have type DIAG
+ // for DIAG points only x-coordinate is relevant
+ enum Type { NORMAL, DIAG};
+ Real x, y;
+ Type type;
+ // methods
+ DiagramPoint(Real xx, Real yy, Type ttype);
+ bool is_diagonal() const { return type == DIAG; }
+ bool is_normal() const { return type == NORMAL; }
+ Real getRealX() const; // return the x-coord
+ Real getRealY() const; // return the y-coord
+ Real persistence_lp(const Real p) const;
+ struct LexicographicCmp
+ {
+ bool operator()(const DiagramPoint& p1, const DiagramPoint& p2) const
+ { return p1.type < p2.type || (p1.type == p2.type && (p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y))); }
+ };
+
+ const Real& operator[](const int idx) const
+ {
+ switch(idx)
+ {
+ case 0 : return x;
+ break;
+ case 1 : return y;
+ break;
+ default: throw std::out_of_range("DiagramPoint has dimension 2");
+ }
+ }
+
+ Real& operator[](const int idx)
+ {
+ switch(idx)
+ {
+ case 0 : return x;
+ break;
+ case 1 : return y;
+ break;
+ default: throw std::out_of_range("DiagramPoint has dimension 2");
+ }
+ }
+
};
-};
-double sqrDist(const Point& a, const Point& b);
-double dist(const Point& a, const Point& b);
-double distLInf(const DiagramPoint& a, const DiagramPoint& b);
-double distLp(const DiagramPoint& a, const DiagramPoint& b, const double p);
-double persistenceLp(const DiagramPoint& a, const double p);
-template<typename DiagPointContainer>
-double getFurthestDistance3Approx(DiagPointContainer& A, DiagPointContainer& B)
-{
- double result { 0.0 };
- DiagramPoint begA = *(A.begin());
- DiagramPoint optB = *(B.begin());
- for(const auto& pointB : B) {
- if (distLInf(begA, pointB) > result) {
- result = distLInf(begA, pointB);
- optB = pointB;
+ template<class Real>
+ struct DiagramPointHash {
+ size_t operator()(const DiagramPoint<Real> &p) const
+ {
+ std::size_t seed = 0;
+ hash_combine(seed, std::hash<Real>(p.x));
+ hash_combine(seed, std::hash<Real>(p.y));
+ hash_combine(seed, std::hash<bool>(p.is_diagonal()));
+ return seed;
+ }
+ };
+
+
+#ifndef FOR_R_TDA
+ template <class Real = double>
+ std::ostream& operator<<(std::ostream& output, const DiagramPoint<Real> p);
+#endif
+
+ template<class Real>
+ void format_arg(fmt::BasicFormatter<char> &f, const char *&format_str, const DiagramPoint<Real>&p) {
+ if (p.is_diagonal()) {
+ f.writer().write("({0},{1}, DIAG)", p.x, p.y);
+ } else {
+ f.writer().write("({0},{1}, NORM)", p.x, p.y);
}
}
- for(const auto& pointA : A) {
- if (distLInf(pointA, optB) > result) {
- result = distLInf(pointA, optB);
+
+
+ template<class Real, class Pt>
+ struct DistImpl
+ {
+ Real operator()(const Pt& a, const Pt& b, const Real p, const int dim)
+ {
+ Real result = 0.0;
+ if (hera::is_infinity(p)) {
+ for(int d = 0; d < dim; ++d) {
+ result = std::max(result, std::fabs(a[d] - b[d]));
+ }
+ } else if (p == 1.0) {
+ for(int d = 0; d < dim; ++d) {
+ result += std::fabs(a[d] - b[d]);
+ }
+ } else {
+ assert(p > 1.0);
+ for(int d = 0; d < dim; ++d) {
+ result += std::pow(std::fabs(a[d] - b[d]), p);
+ }
+ result = std::pow(result, 1.0 / p);
+ }
+ return result;
}
+ };
+
+ template<class Real>
+ struct DistImpl<Real, DiagramPoint<Real>>
+ {
+ Real operator()(const DiagramPoint<Real>& a, const DiagramPoint<Real>& b, const Real p, const int dim)
+ {
+ Real result = 0.0;
+ if ( a.is_diagonal() and b.is_diagonal()) {
+ return result;
+ } else if (hera::is_infinity(p)) {
+ result = std::max(std::fabs(a.getRealX() - b.getRealX()), std::fabs(a.getRealY() - b.getRealY()));
+ } else if (p == 1.0) {
+ result = std::fabs(a.getRealX() - b.getRealX()) + std::fabs(a.getRealY() - b.getRealY());
+ } else {
+ assert(p > 1.0);
+ result = std::pow(std::pow(std::fabs(a.getRealX() - b.getRealX()), p) + std::pow(std::fabs(a.getRealY() - b.getRealY()), p), 1.0 / p);
+ }
+ return result;
+ }
+ };
+
+ template<class R, class Pt>
+ R dist_lp(const Pt& a, const Pt& b, const R p, const int dim)
+ {
+ return DistImpl<R, Pt>()(a, b, p, dim);
}
- return result;
-}
-} // end of namespace geom_ws
+ // TODO
+ template<class Real, typename DiagPointContainer>
+ double getFurthestDistance3Approx(DiagPointContainer& A, DiagPointContainer& B, const Real p)
+ {
+ int dim = 2;
+ Real result { 0.0 };
+ DiagramPoint<Real> begA = *(A.begin());
+ DiagramPoint<Real> optB = *(B.begin());
+ for(const auto& pointB : B) {
+ if (dist_lp(begA, pointB, p, dim) > result) {
+ result = dist_lp(begA, pointB, p, dim);
+ optB = pointB;
+ }
+ }
+ for(const auto& pointA : A) {
+ if (dist_lp(pointA, optB, p, dim) > result) {
+ result = dist_lp(pointA, optB, p, dim);
+ }
+ }
+ return result;
+ }
+
+ template<class Real>
+ Real getFurthestDistance3Approx_pg(const hera::ws::dnn::DynamicPointVector<Real>& A, const hera::ws::dnn::DynamicPointVector<Real>& B, const Real p, const int dim)
+ {
+ Real result { 0.0 };
+ int opt_b_idx = 0;
+ for(size_t b_idx = 0; b_idx < B.size(); ++b_idx) {
+ if (dist_lp(A[0], B[b_idx], p, dim) > result) {
+ result = dist_lp(A[0], B[b_idx], p, dim);
+ opt_b_idx = b_idx;
+ }
+ }
+
+ for(size_t a_idx = 0; a_idx < A.size(); ++a_idx) {
+ result = std::max(result, dist_lp(A[a_idx], B[opt_b_idx], p, dim));
+ }
+
+ return result;
+ }
+
+
+ template<class Container>
+ std::string format_container_to_log(const Container& cont);
+
+ template<class Real, class IndexContainer>
+ std::string format_point_set_to_log(const IndexContainer& indices, const std::vector<DiagramPoint<Real>>& points);
+
+ template<class T>
+ std::string format_int(T i);
+
+} // ws
+} // hera
+
+
+
+#include "basic_defs_ws.hpp"
+
+
#endif
diff --git a/geom_matching/wasserstein/src/basic_defs.cpp b/geom_matching/wasserstein/include/basic_defs_ws.hpp
index b65ce25..629a2f8 100644
--- a/geom_matching/wasserstein/src/basic_defs.cpp
+++ b/geom_matching/wasserstein/include/basic_defs_ws.hpp
@@ -34,94 +34,66 @@ derivative works thereof, in binary and source code form.
#include <iostream>
#endif
-namespace geom_ws {
+#include <sstream>
+
+namespace hera {
+namespace ws {
// Point
-bool Point::operator==(const Point& other) const
+template <class Real>
+bool Point<Real>::operator==(const Point<Real>& other) const
{
return ((this->x == other.x) and (this->y == other.y));
}
-bool Point::operator!=(const Point& other) const
+template <class Real>
+bool Point<Real>::operator!=(const Point<Real>& other) const
{
return !(*this == other);
}
#ifndef FOR_R_TDA
-std::ostream& operator<<(std::ostream& output, const Point p)
+template <class Real>
+std::ostream& operator<<(std::ostream& output, const Point<Real> p)
{
output << "(" << p.x << ", " << p.y << ")";
return output;
}
#endif
-double sqrDist(const Point& a, const Point& b)
+template <class Real>
+Real sqr_dist(const Point<Real>& a, const Point<Real>& b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
-double dist(const Point& a, const Point& b)
+template <class Real>
+Real dist(const Point<Real>& a, const Point<Real>& b)
{
- return sqrt(sqrDist(a, b));
+ return sqrt(sqr_dist(a, b));
}
-// DiagramPoint
-// compute l-inf distance between two diagram points
-double distLInf(const DiagramPoint& a, const DiagramPoint& b)
+template <class Real>
+Real DiagramPoint<Real>::persistence_lp(const Real p) const
{
- if (a.isDiagonal() and b.isDiagonal()) {
- return 0.0;
- } else {
- return std::max(fabs(a.getRealX() - b.getRealX()), fabs(a.getRealY() - b.getRealY()));
- }
-}
-
-double distLp(const DiagramPoint& a, const DiagramPoint& b, const double p)
-{
- // infinity: special case
- if ( std::isinf(p) )
- return distLInf(a, b);
-
- // check p
- assert( p >= 1.0 );
-
- // avoid calling pow function
- if ( p == 1.0 ) {
- if ( a.isNormal() or b.isNormal() ) {
- // distance between normal points is a usual l-inf distance
- return fabs(a.getRealX() - b.getRealX()) + fabs(a.getRealY() - b.getRealY());
- } else
- return 0.0;
- }
-
- if ( a.isNormal() or b.isNormal() ) {
- // distance between normal points is a usual l-inf distance
- return std::pow(std::pow(fabs(a.getRealX() - b.getRealX()), p) + std::pow(fabs(a.getRealY() - b.getRealY()), p), 1.0/p );
- } else
- return 0.0;
-}
-
-
-double DiagramPoint::persistenceLp(const double p) const
-{
- if (isDiagonal())
+ if (is_diagonal())
return 0.0;
else {
- double u { 0.5 * (getRealY() + getRealX()) };
- DiagramPoint a_proj(u, u, DiagramPoint::DIAG);
- return distLp(*this, a_proj, p);
+ Real u { (getRealY() + getRealX())/2 };
+ int dim = 2;
+ DiagramPoint<Real> a_proj(u, u, DiagramPoint<Real>::DIAG);
+ return dist_lp(*this, a_proj, p, dim);
}
-
-
}
#ifndef FOR_R_TDA
-std::ostream& operator<<(std::ostream& output, const DiagramPoint p)
+template <class Real>
+std::ostream& operator<<(std::ostream& output, const DiagramPoint<Real> p)
{
- if ( p.type == DiagramPoint::DIAG ) {
+ if ( p.type == DiagramPoint<Real>::DIAG ) {
output << "(" << p.x << ", " << p.y << ", " << 0.5 * (p.x + p.y) << " DIAG )";
} else {
output << "(" << p.x << ", " << p.y << ", " << " NORMAL)";
@@ -130,31 +102,92 @@ std::ostream& operator<<(std::ostream& output, const DiagramPoint p)
}
#endif
-DiagramPoint::DiagramPoint(double xx, double yy, Type ttype) :
+template <class Real>
+DiagramPoint<Real>::DiagramPoint(Real xx, Real yy, Type ttype) :
x(xx),
y(yy),
type(ttype)
{
//if ( yy < xx )
//throw "Point is below the diagonal";
- //if ( yy == xx and ttype != DiagramPoint::DIAG)
+ //if ( yy == xx and ttype != DiagramPoint<Real>::DIAG)
//throw "Point on the main diagonal must have DIAG type";
}
-double DiagramPoint::getRealX() const
+template <class Real>
+Real DiagramPoint<Real>::getRealX() const
{
- if (isNormal())
+ if (is_normal())
return x;
else
- return 0.5 * ( x + y);
+ return Real(0.5) * (x + y);
}
-double DiagramPoint::getRealY() const
+template <class Real>
+Real DiagramPoint<Real>::getRealY() const
{
- if (isNormal())
+ if (is_normal())
return y;
else
- return 0.5 * ( x + y);
+ return Real(0.5) * (x + y);
}
-} // end of namespace geom_ws
+template<class Container>
+std::string format_container_to_log(const Container& cont)
+{
+ std::stringstream result;
+ result << "[";
+ for(auto iter = cont.begin(); iter != cont.end(); ++iter) {
+ result << *iter;
+ if (std::next(iter) != cont.end()) {
+ result << ", ";
+ }
+ }
+ result << "]";
+ return result.str();
+}
+
+template<class Container>
+std::string format_pair_container_to_log(const Container& cont)
+{
+ std::stringstream result;
+ result << "[";
+ for(auto iter = cont.begin(); iter != cont.end(); ++iter) {
+ result << "(" << iter->first << ", " << iter->second << ")";
+ if (std::next(iter) != cont.end()) {
+ result << ", ";
+ }
+ }
+ result << "]";
+ return result.str();
+}
+
+
+template<class Real, class IndexContainer>
+std::string format_point_set_to_log(const IndexContainer& indices,
+ const std::vector<DiagramPoint<Real>>& points)
+{
+ std::stringstream result;
+ result << "[";
+ for(auto iter = indices.begin(); iter != indices.end(); ++iter) {
+ DiagramPoint<Real> p = points[*iter];
+ result << "(" << p.getRealX() << ", " << p.getRealY() << ")";
+ if (std::next(iter) != indices.end())
+ result << ", ";
+ }
+ result << "]";
+ return result.str();
+}
+
+template<class T>
+std::string format_int(T i)
+{
+ std::stringstream ss;
+ ss.imbue(std::locale(""));
+ ss << std::fixed << i;
+ return ss.str();
+}
+
+
+} // end of namespace ws
+} // hera
diff --git a/geom_matching/wasserstein/include/catch/catch.hpp b/geom_matching/wasserstein/include/catch/catch.hpp
new file mode 100644
index 0000000..f7681f4
--- /dev/null
+++ b/geom_matching/wasserstein/include/catch/catch.hpp
@@ -0,0 +1,11545 @@
+/*
+ * Catch v1.9.6
+ * Generated: 2017-06-27 12:19:54.557875
+ * ----------------------------------------------------------
+ * 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
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#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"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wparentheses"
+
+# 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
+
+// #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_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+# if __cplusplus >= 201103L
+# define CATCH_CPP11_OR_GREATER
+# endif
+
+# if __cplusplus >= 201402L
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+#endif
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# if defined(CATCH_CPP11_OR_GREATER)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#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 )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+ ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \
+ ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+# define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif
+
+// 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
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+# define CATCH_NULL nullptr
+#else
+# define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+# define CATCH_OVERRIDE override
+#else
+# define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+# define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#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 )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+ struct IConfig;
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ 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 startsWith( std::string const& s, char prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool endsWith( std::string const& s, char 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 );
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SourceLineInfo(SourceLineInfo const& other) = default;
+ 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;
+
+ char const* 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 );
+
+ void seedRng( IConfig const& config );
+ unsigned int rngSeed();
+
+ // 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 );
+
+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( CATCH_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 = CATCH_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() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == CATCH_NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_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
+
+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 std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+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;
+};
+
+void registerTestCase
+ ( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+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 );
+ }
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestCaseName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestCaseName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#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 STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct DecomposedExpression
+ {
+ virtual ~DecomposedExpression() {}
+ virtual bool isBinaryExpression() const {
+ return false;
+ }
+ virtual void reconstructExpression( std::string& dest ) const = 0;
+
+ // Only simple binary comparisons can be decomposed.
+ // If more complex check is required then wrap sub-expressions in parentheses.
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+ private:
+ DecomposedExpression& operator = (DecomposedExpression const&);
+ };
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( char const * _macroName,
+ SourceLineInfo const& _lineInfo,
+ char const * _capturedExpression,
+ ResultDisposition::Flags _resultDisposition,
+ char const * _secondArg = "");
+
+ char const * macroName;
+ SourceLineInfo lineInfo;
+ char const * capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ char const * secondArg;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : decomposedExpression( CATCH_NULL )
+ , resultType( ResultWas::Unknown )
+ , negated( false )
+ , parenthesized( false ) {}
+
+ void negate( bool parenthesize ) {
+ negated = !negated;
+ parenthesized = parenthesize;
+ if( resultType == ResultWas::Ok )
+ resultType = ResultWas::ExpressionFailed;
+ else if( resultType == ResultWas::ExpressionFailed )
+ resultType = ResultWas::Ok;
+ }
+
+ std::string const& reconstructExpression() const {
+ if( decomposedExpression != CATCH_NULL ) {
+ decomposedExpression->reconstructExpression( reconstructedExpression );
+ if( parenthesized ) {
+ reconstructedExpression.insert( 0, 1, '(' );
+ reconstructedExpression.append( 1, ')' );
+ }
+ if( negated ) {
+ reconstructedExpression.insert( 0, 1, '!' );
+ }
+ decomposedExpression = CATCH_NULL;
+ }
+ return reconstructedExpression;
+ }
+
+ mutable DecomposedExpression const* decomposedExpression;
+ mutable std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ bool negated;
+ bool parenthesized;
+ };
+
+ 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;
+ void discardDecomposedExpression() const;
+ void expandDecomposedExpression() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ArgT> struct MatchAllOf;
+ template<typename ArgT> struct MatchAnyOf;
+ template<typename ArgT> struct MatchNotOf;
+
+ class MatcherUntypedBase {
+ public:
+ std::string toString() const {
+ if( m_cachedToString.empty() )
+ m_cachedToString = describe();
+ return m_cachedToString;
+ }
+
+ protected:
+ virtual ~MatcherUntypedBase();
+ virtual std::string describe() const = 0;
+ mutable std::string m_cachedToString;
+ private:
+ MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+ };
+
+ template<typename ObjectT>
+ struct MatcherMethod {
+ virtual bool match( ObjectT const& arg ) const = 0;
+ };
+ template<typename PtrT>
+ struct MatcherMethod<PtrT*> {
+ virtual bool match( PtrT* arg ) const = 0;
+ };
+
+ template<typename ObjectT, typename ComparatorT = ObjectT>
+ struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+
+ MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+ MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+ MatchNotOf<ComparatorT> operator ! () const;
+ };
+
+ template<typename ArgT>
+ struct MatchAllOf : MatcherBase<ArgT> {
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (!m_matchers[i]->match(arg))
+ return false;
+ }
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " and ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+ template<typename ArgT>
+ struct MatchAnyOf : MatcherBase<ArgT> {
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (m_matchers[i]->match(arg))
+ return true;
+ }
+ return false;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " or ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+
+ template<typename ArgT>
+ struct MatchNotOf : MatcherBase<ArgT> {
+
+ MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ return !m_underlyingMatcher.match( arg );
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "not " + m_underlyingMatcher.toString();
+ }
+ MatcherBase<ArgT> const& m_underlyingMatcher;
+ };
+
+ template<typename ObjectT, typename ComparatorT>
+ MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+ return MatchAllOf<ComparatorT>() && *this && other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+ return MatchAnyOf<ComparatorT>() || *this || other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+ return MatchNotOf<ComparatorT>( *this );
+ }
+
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ // - deprecated: prefer ||, && and !
+ template<typename T>
+ inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+ return Impl::MatchNotOf<T>( underlyingMatcher );
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2;
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str(std::string());
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder : public DecomposedExpression {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition,
+ char const* secondArg = "" );
+ ~ResultBuilder();
+
+ 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;
+ }
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+
+ void endExpression( DecomposedExpression const& expr );
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+ AssertionResult build() const;
+ AssertionResult build( DecomposedExpression const& expr ) const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void captureExpectedException( std::string const& expectedMessage );
+ void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+ void handleResult( AssertionResult const& result );
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ template<typename ArgT, typename MatcherT>
+ void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+ void setExceptionGuard();
+ void unsetExceptionGuard();
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+
+ static CopyableStream &m_stream()
+ {
+ static CopyableStream s;
+ return s;
+ }
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ bool m_guardException;
+ };
+
+} // 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
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#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 bool( opCast( lhs ) == opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) != opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) < opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) > opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) >= opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( 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_LONG_LONG
+ // long long to unsigned X
+ template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // unsigned long long to X
+ template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+
+ // pointer to long long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long 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 long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#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( nullptr, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+ }
+#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
+
+#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_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#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 & nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+ extern const std::string unprintableString;
+
+ #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ struct TrueType { char sizer[1]; };
+ struct FalseType { char sizer[2]; };
+
+ 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 ) };
+ };
+#else
+ template<typename T>
+ class IsStreamInsertable {
+ template<typename SS, typename TT>
+ static auto test(int)
+ -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+ template<typename, typename>
+ static auto test(...) -> std::false_type;
+
+ public:
+ static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+ };
+#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 "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 "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 {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+ ExpressionLhs& operator = ( const ExpressionLhs& );
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+ operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+ operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+ operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+ operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+ operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+ operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ m_truthy = m_lhs ? true : false;
+ m_rb
+ .setResultType( m_truthy )
+ .endExpression( *this );
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ dest = Catch::toString( m_lhs );
+ }
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+ return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+ }
+
+ template<Internal::Operator Op>
+ BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+ return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+ bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+ BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+ : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+ BinaryExpression& operator = ( BinaryExpression& );
+
+ void endExpression() const {
+ m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+ .endExpression( *this );
+ }
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string lhs = Catch::toString( m_lhs );
+ std::string rhs = Catch::toString( m_rhs );
+ char delim = lhs.size() + rhs.size() < 40 &&
+ lhs.find('\n') == std::string::npos &&
+ rhs.find('\n') == std::string::npos ? ' ' : '\n';
+ dest.reserve( 7 + lhs.size() + rhs.size() );
+ // 2 for spaces around operator
+ // 2 for operator
+ // 2 for parentheses (conditionally added later)
+ // 1 for negation (conditionally added later)
+ dest = lhs;
+ dest += delim;
+ dest += Internal::OperatorTraits<Op>::getName();
+ dest += delim;
+ dest += rhs;
+ }
+
+private:
+ ResultBuilder& m_rb;
+ LhsT m_lhs;
+ RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+ MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+ : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string matcherAsString = m_matcher.toString();
+ dest = Catch::toString( m_arg );
+ dest += ' ';
+ if( matcherAsString == Detail::unprintableString )
+ dest += m_matcherString;
+ else
+ dest += matcherAsString;
+ }
+
+private:
+ ArgT m_arg;
+ MatcherT m_matcher;
+ char const* m_matcherString;
+};
+
+} // 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 );
+ }
+
+ template<typename ArgT, typename MatcherT>
+ inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+ char const* matcherString ) {
+ MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+ setResultType( matcher.match( arg ) );
+ endExpression( expr );
+ }
+
+} // 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 SectionEndInfo;
+ 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( SectionEndInfo const& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 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 exceptionEarlyReported() = 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(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+# define CATCH_PLATFORM_WINDOWS
+# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINES_NOMINMAX
+# endif
+# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
+#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
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_TRAP() \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" )
+ #else
+ #define CATCH_TRAP() __asm__("int $3\n" : : )
+ #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+ // directly at the location of the failing check instead of breaking inside
+ // raise() called from it, i.e. one stack frame below.
+ #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+ #define CATCH_TRAP() asm volatile ("int $3")
+ #else // Fall back to the generic way.
+ #include <signal.h>
+
+ #define CATCH_TRAP() raise(SIGTRAP)
+ #endif
+#elif defined(_MSC_VER)
+ #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+ #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;
+ };
+}
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+// This can potentially cause false negative, if the test code catches
+// the exception before it propagates back up to the runner.
+#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// 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();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureExpectedException( matcher ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(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( macroName, messageType, resultDisposition, ... ) \
+ 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( macroName, messageType, resultDisposition, 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( macroName, log ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ try { \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ } 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
+
+// #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;
+ };
+}
+
+#include <string>
+
+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;
+ };
+
+ struct SectionEndInfo {
+ SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+ : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo sectionInfo;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef _MSC_VER
+
+namespace Catch {
+ typedef unsigned long long UInt64;
+}
+#else
+#include <stdint.h>
+namespace Catch {
+ typedef uint64_t UInt64;
+}
+#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 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 <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 ) {
+ m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() );
+ 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>
+#include <vector>
+
+// #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 ITagAliasRegistry;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator;
+ typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) 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( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+ try {
+ if( it == itEnd )
+ throw;
+ else
+ return (*it)->translate( it+1, itEnd );
+ }
+ 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_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+ static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_margin( 0.0 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_margin( other.m_margin ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx operator()( T value ) {
+ Approx approx( static_cast<double>(value) );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ explicit Approx( T value ): Approx(static_cast<double>(value))
+ {}
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ auto lhs_v = double(lhs);
+ bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( T lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( Approx const& lhs, T rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( T lhs, Approx const& rhs ) {
+ return double(lhs) < rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value < double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( T lhs, Approx const& rhs ) {
+ return double(lhs) > rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value > double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& epsilon( T newEpsilon ) {
+ m_epsilon = double(newEpsilon);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& margin( T newMargin ) {
+ m_margin = double(newMargin);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& scale( T newScale ) {
+ m_scale = double(newScale);
+ return *this;
+ }
+
+#else
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
+ }
+
+ 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;
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& margin( double newMargin ) {
+ m_margin = newMargin;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+#endif
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_margin;
+ 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_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ struct CasedString
+ {
+ CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+ std::string adjustString( std::string const& str ) const;
+ std::string caseSensitivitySuffix() const;
+
+ CaseSensitive::Choice m_caseSensitivity;
+ std::string m_str;
+ };
+
+ struct StringMatcherBase : MatcherBase<std::string> {
+ StringMatcherBase( std::string const& operation, CasedString const& comparator );
+ virtual std::string describe() const CATCH_OVERRIDE;
+
+ CasedString m_comparator;
+ std::string m_operation;
+ };
+
+ struct EqualsMatcher : StringMatcherBase {
+ EqualsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct ContainsMatcher : StringMatcherBase {
+ ContainsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct StartsWithMatcher : StringMatcherBase {
+ StartsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct EndsWithMatcher : StringMatcherBase {
+ EndsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+
+ } // namespace StdString
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Vector {
+
+ template<typename T>
+ struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ return std::find(v.begin(), v.end(), m_comparator) != v.end();
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ T const& m_comparator;
+ };
+
+ template<typename T>
+ struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+ for (size_t i = 0; i < m_comparator.size(); ++i)
+ if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ std::vector<T> const& m_comparator;
+ };
+
+ template<typename T>
+ struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+ // to using !=. but could be specialised for, e.g. std::vector<T> etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+ for (size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != v[i])
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Equals: " + Catch::toString( m_comparator );
+ }
+ std::vector<T> const& m_comparator;
+ };
+
+ } // namespace Vector
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ template<typename T>
+ Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+ return Vector::ContainsMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+ return Vector::EqualsMatcher<T>( comparator );
+ }
+
+} // 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 const& _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( CATCH_NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_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 = CATCH_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 != CATCH_NULL; }
+ bool none() const { return nullableValue == CATCH_NULL; }
+
+ bool operator !() const { return nullableValue == CATCH_NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T *nullableValue;
+ union {
+ char storage[sizeof(T)];
+
+ // These are here to force alignment for the storage
+ long double dummy1;
+ void (*dummy2)();
+ long double dummy3;
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+ long long dummy4;
+#endif
+ };
+ };
+
+} // 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,
+ NonPortable = 1 << 5
+ };
+
+ 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 );
+
+ friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
+ 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( CATCH_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 {
+
+ struct StringHolder : MatcherBase<NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ virtual bool match( NSString* arg ) const CATCH_OVERRIDE {
+ return false;
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const CATCH_OVERRIDE {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ 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
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+ LeakDetector() {
+ int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flag |= _CRTDBG_LEAK_CHECK_DF;
+ flag |= _CRTDBG_ALLOC_MEM_DF;
+ _CrtSetDbgFlag(flag);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ // Change this to leaking allocation's number to break there
+ _CrtSetBreakAlloc(-1);
+ }
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
+// #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_session.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
+
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <stdexcept>
+
+namespace Catch
+{
+ class WildcardPattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+
+ WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_wildcard( NoWildcard ),
+ m_pattern( adjustCase( pattern ) )
+ {
+ if( startsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~WildcardPattern();
+ virtual bool matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_pattern == adjustCase( str );
+ case WildcardAtStart:
+ return endsWith( adjustCase( str ), m_pattern );
+ case WildcardAtEnd:
+ return startsWith( adjustCase( str ), m_pattern );
+ case WildcardAtBothEnds:
+ return contains( adjustCase( str ), m_pattern );
+ }
+
+#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 adjustCase( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+ }
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard;
+ std::string m_pattern;
+ };
+}
+
+#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 {
+ public:
+ NamePattern( std::string const& name )
+ : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return m_wildcardPattern.matches( toLower( testCase.name ) );
+ }
+ private:
+ WildcardPattern m_wildcardPattern;
+ };
+
+ 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, EscapedName };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ std::vector<std::size_t> m_escapeChars;
+ 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 );
+ m_escapeChars.clear();
+ 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 );
+ case '\\': return escape();
+ 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( c == '\\' )
+ escape();
+ }
+ else if( m_mode == EscapedName )
+ m_mode = Name;
+ 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;
+ }
+ void escape() {
+ if( m_mode == None )
+ m_start = m_pos;
+ m_mode = EscapedName;
+ m_escapeChars.push_back( m_pos );
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ for( size_t i = 0; i < m_escapeChars.size(); ++i )
+ token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+ m_escapeChars.clear();
+ 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 <iosfwd>
+#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
+ }; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
+
+ 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 UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_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 <streambuf>
+#include <ostream>
+#include <fstream>
+#include <memory>
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+
+ struct IStream {
+ virtual ~IStream() CATCH_NOEXCEPT;
+ virtual std::ostream& stream() const = 0;
+ };
+
+ class FileStream : public IStream {
+ mutable std::ofstream m_ofs;
+ public:
+ FileStream( std::string const& filename );
+ virtual ~FileStream() CATCH_NOEXCEPT;
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class CoutStream : public IStream {
+ mutable std::ostream m_os;
+ public:
+ CoutStream();
+ virtual ~CoutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class DebugOutStream : public IStream {
+ CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
+ mutable std::ostream m_os;
+ public:
+ DebugOutStream();
+ virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <stdexcept>
+
+#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 ),
+ listExtraInfo( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ filenamesAsTags( false ),
+ abortAfter( -1 ),
+ rngSeed( 0 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter ),
+ runOrder( RunTests::InDeclarationOrder ),
+ useColour( UseColour::Auto )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+ bool listExtraInfo;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+ bool filenamesAsTags;
+
+ int abortAfter;
+ unsigned int rngSeed;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+ RunTests::InWhatOrder runOrder;
+ UseColour::YesOrNo useColour;
+
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> reporterNames;
+ std::vector<std::string> testsOrTags;
+ std::vector<std::string> sectionsToRun;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_stream( openStream() )
+ {
+ 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() {}
+
+ 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; }
+ bool listExtraInfo() const { return m_data.listExtraInfo; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+ std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+
+ virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+
+ // IConfig interface
+ virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; }
+ virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); }
+ virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+ virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; }
+ virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; }
+ virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; }
+ virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+ virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+ virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+
+ private:
+
+ IStream const* openStream() {
+ if( m_data.outputFilename.empty() )
+ return new CoutStream();
+ else if( m_data.outputFilename[0] == '%' ) {
+ if( m_data.outputFilename == "%debug" )
+ return new DebugOutStream();
+ else
+ throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+ }
+ else
+ return new FileStream( m_data.outputFilename );
+ }
+ ConfigData m_data;
+
+ CATCH_AUTO_PTR( IStream const ) m_stream;
+ 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
+
+// Version 0.0.2.4
+
+// 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>
+#include <algorithm>
+#include <cctype>
+
+// 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 clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+# define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+# define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+# define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
+#include <map>
+#include <stdexcept>
+#include <memory>
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
+// 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;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
+ 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 + "'" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+#endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( CLARA_NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != CLARA_NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) 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 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 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 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 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 bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+ std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+ for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+ args[i] = argv[i];
+
+ return args;
+ }
+
+ class Parser {
+ enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+ Mode mode;
+ std::size_t from;
+ bool inQuotes;
+ public:
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+ void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
+ const std::string doubleDash = "--";
+ for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+ parseIntoTokens( args[i], tokens);
+ }
+
+ void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+ for( std::size_t i = 0; i < arg.size(); ++i ) {
+ char c = arg[i];
+ if( c == '"' )
+ inQuotes = !inQuotes;
+ mode = handleMode( i, c, arg, tokens );
+ }
+ mode = handleMode( arg.size(), '\0', arg, tokens );
+ }
+ Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ switch( mode ) {
+ case None: return handleNone( i, c );
+ case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+ case ShortOpt:
+ case LongOpt:
+ case SlashOpt: return handleOpt( i, c, arg, tokens );
+ case Positional: return handlePositional( i, c, arg, tokens );
+ default: throw std::logic_error( "Unknown mode" );
+ }
+ }
+
+ Mode handleNone( std::size_t i, char c ) {
+ if( inQuotes ) {
+ from = i;
+ return Positional;
+ }
+ switch( c ) {
+ case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+ case '/': from = i+1; return SlashOpt;
+#endif
+ default: from = i; return Positional;
+ }
+ }
+ Mode handleMaybeShortOpt( std::size_t i, char c ) {
+ switch( c ) {
+ case '-': from = i+1; return LongOpt;
+ default: from = i; return ShortOpt;
+ }
+ }
+
+ Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string optName = arg.substr( from, i-from );
+ if( mode == ShortOpt )
+ for( std::size_t j = 0; j < optName.size(); ++j )
+ tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+ else if( mode == SlashOpt && optName.size() == 1 )
+ tokens.push_back( Token( Token::ShortOpt, optName ) );
+ else
+ tokens.push_back( Token( Token::LongOpt, optName ) );
+ return None;
+ }
+ Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string data = arg.substr( from, i-from );
+ tokens.push_back( Token( Token::Positional, data ) );
+ return None;
+ }
+ };
+
+ 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();
+ }
+ };
+
+ typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+
+ 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( std::vector<std::string> const& args ) const {
+ ConfigT config;
+ parseInto( args, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+ std::string processName = args.empty() ? std::string() : args[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( args, 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.set( config, "true" );
+ }
+ 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>
+#include <ctime>
+
+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 addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
+ inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+
+ 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( "Argument 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 setUseColour( ConfigData& config, std::string const& value ) {
+ std::string mode = toLower( value );
+
+ if( mode == "yes" )
+ config.useColour = UseColour::Yes;
+ else if( mode == "no" )
+ config.useColour = UseColour::No;
+ else if( mode == "auto" )
+ config.useColour = UseColour::Auto;
+ else
+ throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+ }
+ inline void forceColour( ConfigData& config ) {
+ config.useColour = UseColour::Yes;
+ }
+ 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, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + 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( &addReporterName, "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" );
+
+ cli["-#"]["--filenames-as-tags"]
+ .describe( "adds a tag for the filename" )
+ .bind( &ConfigData::filenamesAsTags );
+
+ cli["-c"]["--section"]
+ .describe( "specify section to run" )
+ .bind( &addSectionToRun, "section name" );
+
+ // 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-extra-info"]
+ .describe( "list all/matching test cases with more info" )
+ .bind( &ConfigData::listExtraInfo );
+
+ 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 (deprecated)" )
+ .bind( &forceColour );
+
+ cli["--use-colour"]
+ .describe( "should output be colourised" )
+ .bind( &setUseColour, "yes|no" );
+
+ 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 )
+ {}
+
+ 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; }
+
+ 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
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ const std::string wrappableBeforeChars = "[({<\t";
+ const std::string wrappableAfterChars = "])}>-,./|\\";
+ const std::string wrappableInsteadOfChars = " \n\r";
+ std::string indent = _attr.initialIndent != std::string::npos
+ ? std::string( _attr.initialIndent, ' ' )
+ : std::string( _attr.indent, ' ' );
+
+ typedef std::string::const_iterator iterator;
+ iterator it = _str.begin();
+ const iterator strEnd = _str.end();
+
+ while( it != strEnd ) {
+
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+
+ std::string suffix;
+ std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+ iterator itEnd = it+width;
+ iterator itNext = _str.end();
+
+ iterator itNewLine = std::find( it, itEnd, '\n' );
+ if( itNewLine != itEnd )
+ itEnd = itNewLine;
+
+ if( itEnd != strEnd ) {
+ bool foundWrapPoint = false;
+ iterator findIt = itEnd;
+ do {
+ if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+ itEnd = findIt+1;
+ itNext = findIt+1;
+ foundWrapPoint = true;
+ }
+ else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+ itEnd = findIt;
+ itNext = findIt;
+ foundWrapPoint = true;
+ }
+ else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+ itNext = findIt+1;
+ itEnd = findIt;
+ foundWrapPoint = true;
+ }
+ if( findIt == it )
+ break;
+ else
+ --findIt;
+ }
+ while( !foundWrapPoint );
+
+ if( !foundWrapPoint ) {
+ // No good wrap char, so we'll break mid word and add a hyphen
+ --itEnd;
+ itNext = itEnd;
+ suffix = "-";
+ }
+ else {
+ while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+ --itEnd;
+ }
+ }
+ lines.push_back( indent + std::string( it, itEnd ) + suffix );
+
+ if( indent.size() != _attr.indent )
+ indent = std::string( _attr.indent, ' ' );
+ it = itNext;
+ }
+ }
+
+ 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>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig const> 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;
+ };
+
+ class MultipleReporters;
+
+ 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;
+
+ virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+ };
+
+ struct IReporterFactory : IShared {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+ typedef std::vector<Ptr<IReporterFactory> > Listeners;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ virtual Listeners const& getListeners() const = 0;
+ };
+
+ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
+}
+
+#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, descAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ descAttr.setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ 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( config.listExtraInfo() ) {
+ Catch::cout() << " " << testCaseInfo.lineInfo << std::endl;
+ std::string description = testCaseInfo.description;
+ if( description.empty() )
+ description = "(NO DESCRIPTION)";
+ Catch::cout() << Text( description, descAttr ) << 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 = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ if( startsWith( testCaseInfo.name, '#' ) )
+ Catch::cout() << '"' << testCaseInfo.name << '"';
+ else
+ Catch::cout() << testCaseInfo.name;
+ if ( config.listExtraInfo() )
+ Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+ Catch::cout() << 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 = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ 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() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) )
+ 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_run_context.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <algorithm>
+#include <string>
+#include <assert.h>
+#include <vector>
+#include <stdexcept>
+
+CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+ : name( _name ),
+ location( _location )
+ {}
+ };
+
+ struct ITracker : SharedImpl<> {
+ virtual ~ITracker();
+
+ // static queries
+ virtual NameAndLocation const& nameAndLocation() const = 0;
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+ // actions
+ virtual void close() = 0; // Successfully complete
+ virtual void fail() = 0;
+ virtual void markAsNeedingAnotherRun() = 0;
+
+ virtual void addChild( Ptr<ITracker> const& child ) = 0;
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
+ virtual void openChild() = 0;
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+ virtual bool isIndexTracker() const = 0;
+ };
+
+ class TrackerContext {
+
+ enum RunState {
+ NotStarted,
+ Executing,
+ CompletedCycle
+ };
+
+ Ptr<ITracker> m_rootTracker;
+ ITracker* m_currentTracker;
+ RunState m_runState;
+
+ public:
+
+ static TrackerContext& instance() {
+ static TrackerContext s_instance;
+ return s_instance;
+ }
+
+ TrackerContext()
+ : m_currentTracker( CATCH_NULL ),
+ m_runState( NotStarted )
+ {}
+
+ ITracker& startRun();
+
+ void endRun() {
+ m_rootTracker.reset();
+ m_currentTracker = CATCH_NULL;
+ m_runState = NotStarted;
+ }
+
+ void startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
+ void completeCycle() {
+ m_runState = CompletedCycle;
+ }
+
+ bool completedCycle() const {
+ return m_runState == CompletedCycle;
+ }
+ ITracker& currentTracker() {
+ return *m_currentTracker;
+ }
+ void setCurrentTracker( ITracker* tracker ) {
+ m_currentTracker = tracker;
+ }
+ };
+
+ class TrackerBase : public ITracker {
+ protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+ class TrackerHasName {
+ NameAndLocation m_nameAndLocation;
+ public:
+ TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
+ bool operator ()( Ptr<ITracker> const& tracker ) {
+ return
+ tracker->nameAndLocation().name == m_nameAndLocation.name &&
+ tracker->nameAndLocation().location == m_nameAndLocation.location;
+ }
+ };
+ typedef std::vector<Ptr<ITracker> > Children;
+ NameAndLocation m_nameAndLocation;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+ CycleState m_runState;
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : m_nameAndLocation( nameAndLocation ),
+ m_ctx( ctx ),
+ m_parent( parent ),
+ m_runState( NotStarted )
+ {}
+ virtual ~TrackerBase();
+
+ virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+ return m_nameAndLocation;
+ }
+ virtual bool isComplete() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully || m_runState == Failed;
+ }
+ virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully;
+ }
+ virtual bool isOpen() const CATCH_OVERRIDE {
+ return m_runState != NotStarted && !isComplete();
+ }
+ virtual bool hasChildren() const CATCH_OVERRIDE {
+ return !m_children.empty();
+ }
+
+ virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+ m_children.push_back( child );
+ }
+
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+ Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
+ return( it != m_children.end() )
+ ? it->get()
+ : CATCH_NULL;
+ }
+ virtual ITracker& parent() CATCH_OVERRIDE {
+ assert( m_parent ); // Should always be non-null except for root
+ return *m_parent;
+ }
+
+ virtual void openChild() CATCH_OVERRIDE {
+ if( m_runState != ExecutingChildren ) {
+ m_runState = ExecutingChildren;
+ if( m_parent )
+ m_parent->openChild();
+ }
+ }
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
+ void open() {
+ m_runState = Executing;
+ moveToThis();
+ if( m_parent )
+ m_parent->openChild();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+
+ // Close any still open children (e.g. generators)
+ while( &m_ctx.currentTracker() != this )
+ m_ctx.currentTracker().close();
+
+ switch( m_runState ) {
+ case NotStarted:
+ case CompletedSuccessfully:
+ case Failed:
+ throw std::logic_error( "Illogical state" );
+
+ case NeedsAnotherRun:
+ break;;
+
+ case Executing:
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+ if( m_children.empty() || m_children.back()->isComplete() )
+ m_runState = CompletedSuccessfully;
+ break;
+
+ default:
+ throw std::logic_error( "Unexpected state" );
+ }
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void fail() CATCH_OVERRIDE {
+ m_runState = Failed;
+ if( m_parent )
+ m_parent->markAsNeedingAnotherRun();
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+ m_runState = NeedsAnotherRun;
+ }
+ private:
+ void moveToParent() {
+ assert( m_parent );
+ m_ctx.setCurrentTracker( m_parent );
+ }
+ void moveToThis() {
+ m_ctx.setCurrentTracker( this );
+ }
+ };
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent )
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+ parent = &parent->parent();
+
+ SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+ addNextFilters( parentSection.m_filters );
+ }
+ }
+ virtual ~SectionTracker();
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+ SectionTracker* section = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isSectionTracker() );
+ section = static_cast<SectionTracker*>( childTracker );
+ }
+ else {
+ section = new SectionTracker( nameAndLocation, ctx, &currentTracker );
+ currentTracker.addChild( section );
+ }
+ if( !ctx.completedCycle() )
+ section->tryOpen();
+ return *section;
+ }
+
+ void tryOpen() {
+ if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
+ open();
+ }
+
+ void addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+ m_filters.push_back(""); // Root - should never be consulted
+ m_filters.push_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+ m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+ }
+ };
+
+ class IndexTracker : public TrackerBase {
+ int m_size;
+ int m_index;
+ public:
+ IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+ : TrackerBase( nameAndLocation, ctx, parent ),
+ m_size( size ),
+ m_index( -1 )
+ {}
+ virtual ~IndexTracker();
+
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
+ static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+ IndexTracker* tracker = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isIndexTracker() );
+ tracker = static_cast<IndexTracker*>( childTracker );
+ }
+ else {
+ tracker = new IndexTracker( nameAndLocation, ctx, &currentTracker, size );
+ currentTracker.addChild( tracker );
+ }
+
+ if( !ctx.completedCycle() && !tracker->isComplete() ) {
+ if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+ tracker->moveNext();
+ tracker->open();
+ }
+
+ return *tracker;
+ }
+
+ int index() const { return m_index; }
+
+ void moveNext() {
+ m_index++;
+ m_children.clear();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+ TrackerBase::close();
+ if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+ m_runState = Executing;
+ }
+ };
+
+ inline ITracker& TrackerContext::startRun() {
+ m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
+ m_currentTracker = CATCH_NULL;
+ m_runState = Executing;
+ return *m_rootTracker;
+ }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
+
+} // namespace Catch
+
+CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+ // Report the error condition
+ inline void reportFatal( std::string const& message ) {
+ IContext& context = Catch::getCurrentContext();
+ IResultCapture* resultCapture = context.getResultCapture();
+ resultCapture->handleFatalErrorCondition( message );
+ }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_WINDOWS_SEH is defined
+
+namespace Catch {
+
+ struct SignalDefs { DWORD id; const char* name; };
+ extern SignalDefs signalDefs[];
+ // 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 (int 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 Catch to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+ exceptionHandlerHandle = CATCH_NULL;
+ // 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 = CATCH_NULL;
+ 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 = CATCH_NULL;
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_WINDOWS_SEH
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
+#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 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;
+ 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], CATCH_NULL);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, CATCH_NULL);
+ isSet = false;
+ }
+ }
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_POSIX_SIGNALS
+
+#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( CATCH_NULL ),
+ m_config( _config ),
+ m_reporter( reporter ),
+ m_shouldReportUnexpected ( true )
+ {
+ 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() ) );
+ }
+
+ 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;
+
+ do {
+ ITracker& rootTracker = m_trackerContext.startRun();
+ assert( rootTracker.isSectionTracker() );
+ static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
+ do {
+ m_trackerContext.startCycle();
+ m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
+ }
+ // !TBD: deprecated - this will be replaced by indexed trackers
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+ deltaTotals.assertions.failed++;
+ deltaTotals.testCases.passed--;
+ deltaTotals.testCases.failed++;
+ }
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = CATCH_NULL;
+ m_testCaseTracker = CATCH_NULL;
+
+ 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++;
+ }
+
+ // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+ // and should be let to clear themselves out.
+ static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+ // 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
+ )
+ {
+ ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
+ if( !sectionTracker.isOpen() )
+ return false;
+ m_activeSections.push_back( &sectionTracker );
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 )
+ return false;
+ if( !m_config->warnAboutMissingAssertions() )
+ return false;
+ if( m_trackerContext.currentTracker().hasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+ Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( !m_activeSections.empty() ) {
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+ }
+
+ m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+ if( m_unfinishedSections.empty() )
+ m_activeSections.back()->fail();
+ else
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+
+ m_unfinishedSections.push_back( endInfo );
+ }
+
+ 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
+ : std::string();
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ virtual void exceptionEarlyReported() {
+ m_shouldReportUnexpected = false;
+ }
+
+ virtual void handleFatalErrorCondition( std::string const& message ) {
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult;
+ tempResult.resultType = ResultWas::FatalErrorCondition;
+ tempResult.message = message;
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ getResultCapture().assertionEnded(result);
+
+ 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,
+ std::string(),
+ std::string(),
+ false ) );
+ m_totals.testCases.failed++;
+ testGroupEnded( std::string(), 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;
+ m_shouldReportUnexpected = true;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+
+ seedRng( *m_config );
+
+ 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(...) {
+ // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+ // are reported without translation at the point of origin.
+ if (m_shouldReportUnexpected) {
+ makeUnexpectedResultBuilder().useActiveException();
+ }
+ }
+ m_testCaseTracker->close();
+ 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,
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression,
+ 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<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( *it );
+ m_unfinishedSections.clear();
+ }
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ ITracker* m_testCaseTracker;
+ ITracker* m_currentSectionTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<SectionEndInfo> m_unfinishedSections;
+ std::vector<ITracker*> m_activeSections;
+ TrackerContext m_trackerContext;
+ bool m_shouldReportUnexpected;
+ };
+
+ 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 _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber );
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const patchNumber;
+
+ // buildNumber is only used if branchName is not null
+ char const * const branchName;
+ unsigned int const buildNumber;
+
+ friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+ private:
+ void operator=( Version const& );
+ };
+
+ inline Version libraryVersion();
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+ Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+ if( !reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ return reporter;
+ }
+
+ Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+ std::vector<std::string> reporters = config->getReporterNames();
+ if( reporters.empty() )
+ reporters.push_back( "console" );
+
+ Ptr<IStreamingReporter> reporter;
+ for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+ it != itEnd;
+ ++it )
+ reporter = addReporter( reporter, createReporter( *it, config ) );
+ return reporter;
+ }
+ Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+ IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+ for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+ it != itEnd;
+ ++it )
+ reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+ return reporters;
+ }
+
+ Totals runTests( Ptr<Config> const& config ) {
+
+ Ptr<IConfig const> iconfig = config.get();
+
+ Ptr<IStreamingReporter> reporter = makeReporter( config );
+ reporter = addListeners( iconfig, reporter );
+
+ RunContext context( iconfig, reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( config->name(), 1, 1 );
+
+ TestSpec testSpec = config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+ for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+ it != itEnd;
+ ++it ) {
+ if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+ totals += context.runTest( *it );
+ else
+ reporter->skipTest( *it );
+ }
+
+ context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+ return totals;
+ }
+
+ void applyFilenamesAsTags( IConfig const& config ) {
+ std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+ for(std::size_t i = 0; i < tests.size(); ++i ) {
+ TestCase& test = const_cast<TestCase&>( tests[i] );
+ std::set<std::string> tags = test.tags;
+
+ std::string filename = test.lineInfo.file;
+ std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+ if( lastSlash != std::string::npos )
+ filename = filename.substr( lastSlash+1 );
+
+ std::string::size_type lastDot = filename.find_last_of( "." );
+ if( lastDot != std::string::npos )
+ filename = filename.substr( 0, lastDot );
+
+ tags.insert( "#" + filename );
+ setTags( test, tags );
+ }
+ }
+
+ 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() << "\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* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( Clara::argsToVector( 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* const* const argv ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ #if defined(WIN32) && defined(UNICODE)
+ int run( int argc, wchar_t const* const* const argv ) {
+
+ char **utf8Argv = new char *[ argc ];
+
+ for ( int i = 0; i < argc; ++i ) {
+ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+
+ utf8Argv[ i ] = new char[ bufSize ];
+
+ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
+ }
+
+ int returnCode = applyCommandLine( argc, utf8Argv );
+ if( returnCode == 0 )
+ returnCode = run();
+
+ for ( int i = 0; i < argc; ++i )
+ delete [] utf8Argv[ i ];
+
+ delete [] utf8Argv;
+
+ return returnCode;
+ }
+ #endif
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+
+ seedRng( *m_config );
+
+ if( m_configData.filenamesAsTags )
+ applyFilenamesAsTags( *m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runTests( m_config ).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 <algorithm>
+
+namespace Catch {
+
+ struct RandomNumberGenerator {
+ typedef std::ptrdiff_t result_type;
+
+ result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return 1000000; }
+ result_type operator()() const { return std::rand() % max(); }
+#endif
+ template<typename V>
+ static void shuffle( V& vector ) {
+ RandomNumberGenerator rng;
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ std::shuffle( vector.begin(), vector.end(), rng );
+#else
+ std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+ }
+ };
+
+ inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+ std::vector<TestCase> sorted = unsortedTestCases;
+
+ switch( config.runOrder() ) {
+ case RunTests::InLexicographicalOrder:
+ std::sort( sorted.begin(), sorted.end() );
+ break;
+ case RunTests::InRandomOrder:
+ {
+ seedRng( config );
+ RandomNumberGenerator::shuffle( sorted );
+ }
+ break;
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
+ }
+ return sorted;
+ }
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+ return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+ }
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+ std::set<TestCase> seenFunctions;
+ for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+ it != itEnd;
+ ++it ) {
+ std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+ if( !prev.second ) {
+ std::ostringstream ss;
+
+ ss << Colour( Colour::Red )
+ << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
+ << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+ throw std::runtime_error(ss.str());
+ }
+ }
+ }
+
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+ std::vector<TestCase> filtered;
+ filtered.reserve( testCases.size() );
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it )
+ if( matchTest( *it, testSpec, config ) )
+ filtered.push_back( *it );
+ return filtered;
+ }
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+ return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+ }
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ TestRegistry()
+ : m_currentSortOrder( RunTests::InDeclarationOrder ),
+ m_unnamedCount( 0 )
+ {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name.empty() ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+ m_functions.push_back( testCase );
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functions;
+ }
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+ if( m_sortedFunctions.empty() )
+ enforceNoDuplicateTestCases( m_functions );
+
+ if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+ m_sortedFunctions = sortTests( config, m_functions );
+ m_currentSortOrder = config.runOrder();
+ }
+ return m_sortedFunctions;
+ }
+
+ private:
+ std::vector<TestCase> m_functions;
+ mutable RunTests::InWhatOrder m_currentSortOrder;
+ mutable std::vector<TestCase> m_sortedFunctions;
+ size_t m_unnamedCount;
+ std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ 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;
+ }
+
+ void registerTestCase
+ ( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase
+ ( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+ void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCaseFunction( function, lineInfo, nameAndDesc );
+ }
+
+ AutoReg::~AutoReg() {}
+
+} // 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() CATCH_OVERRIDE {}
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return CATCH_NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+ void registerListener( Ptr<IReporterFactory> const& factory ) {
+ m_listeners.push_back( factory );
+ }
+
+ virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
+ return m_factories;
+ }
+ virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+ return m_listeners;
+ }
+
+ private:
+ FactoryMap m_factories;
+ Listeners m_listeners;
+ };
+}
+
+// #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 {
+ return tryTranslators();
+ }
+ @catch (NSException *exception) {
+ return Catch::toString( [exception description] );
+ }
+#else
+ return tryTranslators();
+#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 "Unknown exception";
+ }
+ }
+
+ std::string tryTranslators() const {
+ if( m_translators.empty() )
+ throw;
+ else
+ return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+// #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( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
+ return m_exceptionTranslatorRegistry;
+ }
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE {
+ return m_tagAliasRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerListener( factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE {
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ TagAliasRegistry m_tagAliasRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = CATCH_NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = CATCH_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 <sstream>
+
+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
+
+#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;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ FileStream::FileStream( std::string const& filename ) {
+ m_ofs.open( filename.c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << filename << '\'';
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ std::ostream& FileStream::stream() const {
+ return m_ofs;
+ }
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ DebugOutStream::DebugOutStream()
+ : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+ m_os( m_streamBuf.get() )
+ {}
+
+ std::ostream& DebugOutStream::stream() const {
+ return m_os;
+ }
+
+ // Store the streambuf from cout up-front because
+ // cout may get redirected when running tests
+ CoutStream::CoutStream()
+ : m_os( Catch::cout().rdbuf() )
+ {}
+
+ std::ostream& CoutStream::stream() const {
+ return m_os;
+ }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+ std::ostream& cout() {
+ return std::cout;
+ }
+ std::ostream& cerr() {
+ return std::cerr;
+ }
+#endif
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public:
+ virtual ~Context() {
+ deleteAllValues( m_generatorsByTestName );
+ }
+
+ 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
+ : CATCH_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 = CATCH_NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = CATCH_NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+// #included from: catch_errno_guard.hpp
+#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+#include <cerrno>
+
+namespace Catch {
+
+ class ErrnoGuard {
+ public:
+ ErrnoGuard():m_oldErrno(errno){}
+ ~ErrnoGuard() { errno = m_oldErrno; }
+ private:
+ int m_oldErrno;
+ };
+
+}
+
+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 ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+ originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalForegroundAttributes );
+ 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 | originalBackgroundAttributes );
+ }
+ HANDLE stdoutHandle;
+ WORD originalForegroundAttributes;
+ WORD originalBackgroundAttributes;
+ };
+
+ IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = !isDebuggerActive()
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? &s_instance
+ : NoColourImpl::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() {
+ ErrnoGuard guard;
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? 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 = 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( char const * _macroName,
+ SourceLineInfo const& _lineInfo,
+ char const * _capturedExpression,
+ ResultDisposition::Flags _resultDisposition,
+ char const * _secondArg)
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition ),
+ secondArg( _secondArg )
+ {}
+
+ 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[0] != 0;
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) {
+ return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"')
+ ? capturedExpression
+ : std::string(capturedExpression) + ", " + secondArg;
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return '!' + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ else
+ return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName[0] == 0 )
+ return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ else
+ return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructExpression();
+ }
+
+ 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;
+ }
+
+ void AssertionResult::discardDecomposedExpression() const {
+ m_resultData.decomposedExpression = CATCH_NULL;
+ }
+
+ void AssertionResult::expandDecomposedExpression() const {
+ m_resultData.reconstructExpression();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+#include <cctype>
+
+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 if( tag == "!nonportable" )
+ return TestCaseInfo::NonPortable;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ std::ostringstream ss;
+ ss << Colour(Colour::Red)
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n"
+ << Colour(Colour::FileName)
+ << _lineInfo << '\n';
+ throw std::runtime_error(ss.str());
+ }
+ }
+
+ 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 );
+ }
+
+ void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+ {
+ testCaseInfo.tags = tags;
+ testCaseInfo.lcaseTags.clear();
+
+ 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 );
+ testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+ testCaseInfo.lcaseTags.insert( lcaseTag );
+ }
+ testCaseInfo.tagsAsString = oss.str();
+ }
+
+ 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 ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ setTags( *this, _tags );
+ }
+
+ 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 {
+
+ Version::Version
+ ( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ patchNumber( _patchNumber ),
+ branchName( _branchName ),
+ buildNumber( _buildNumber )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, Version const& version ) {
+ os << version.majorVersion << '.'
+ << version.minorVersion << '.'
+ << version.patchNumber;
+ // branchName is never null -> 0th char is \0 if it is empty
+ if (version.branchName[0]) {
+ os << '-' << version.branchName
+ << '.' << version.buildNumber;
+ }
+ return os;
+ }
+
+ inline Version libraryVersion() {
+ static Version version( 1, 9, 6, "", 0 );
+ return version;
+ }
+
+}
+
+// #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() {
+ if ( !std::uncaught_exception() ){
+ 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
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ 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
+ UInt64 getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,CATCH_NULL);
+ return static_cast<UInt64>( t.tv_sec ) * 1000000ull + static_cast<UInt64>( 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
+
+#include <cstring>
+#include <cctype>
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+ }
+ bool startsWith( std::string const& s, char prefix ) {
+ return !s.empty() && s[0] == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+ }
+ bool endsWith( std::string const& s, char suffix ) {
+ return !s.empty() && s[s.size()-1] == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+ }
+ 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 ) : std::string();
+ }
+
+ 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() : file(""), line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file[0] == '\0';
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+ return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+ }
+
+ void seedRng( IConfig const& config ) {
+ if( config.rngSeed() != 0 )
+ std::srand( config.rngSeed() );
+ }
+ unsigned int rngSeed() {
+ return getCurrentContext().getConfig()->rngSeed();
+ }
+
+ 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();
+ }
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
+#endif
+ Section::~Section() {
+ if( m_sectionIncluded ) {
+ SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ if( std::uncaught_exception() )
+ getResultCapture().sectionEndedEarly( endInfo );
+ else
+ getResultCapture().sectionEnded( endInfo );
+ }
+ }
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+ // 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
+
+#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, CATCH_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(CATCH_PLATFORM_LINUX)
+ #include <fstream>
+ #include <string>
+
+ namespace Catch{
+ // The standard POSIX way of detecting a debugger is to attempt to
+ // ptrace() the process, but this needs to be done from a child and not
+ // this process itself to still allow attaching to this process later
+ // if wanted, so is rather heavy. Under Linux we have the PID of the
+ // "debugger" (which doesn't need to be gdb, of course, it could also
+ // be strace, for example) in /proc/$PID/status, so just get it from
+ // there instead.
+ bool isDebuggerActive(){
+ // Libstdc++ has a bug, where std::ifstream sets errno to 0
+ // This way our users can properly assert over errno values
+ ErrnoGuard guard;
+ std::ifstream in("/proc/self/status");
+ for( std::string line; std::getline(in, line); ) {
+ static const int PREFIX_LEN = 11;
+ if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+ // We're traced if the PID is not 0 and no other PID starts
+ // with 0 digit, so it's enough to check for just a single
+ // character.
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+
+ return false;
+ }
+ } // 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
+
+ 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 {
+
+ const std::string unprintableString = "{?}";
+
+ namespace {
+ const int hexThreshold = 255;
+
+ 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;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << 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 ) {
+ if ( value == '\r' )
+ return "'\\r'";
+ if ( value == '\f' )
+ return "'\\f'";
+ if ( value == '\n' )
+ return "'\\n'";
+ if ( value == '\t' )
+ return "'\\t'";
+ if ( '\0' <= value && value < ' ' )
+ return toString( static_cast<unsigned int>( value ) );
+ char chstr[] = "' '";
+ chstr[1] = value;
+ return chstr;
+}
+
+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_LONG_LONG
+std::string toString( long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+std::string toString( unsigned long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+#endif
+
+#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 & 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,
+ char const* secondArg )
+ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false ),
+ m_guardException( false )
+ {
+ m_stream().oss.str("");
+ }
+
+ ResultBuilder::~ResultBuilder() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ if ( m_guardException ) {
+ m_stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+ captureResult( ResultWas::ThrewException );
+ getCurrentContext().getResultCapture()->exceptionEarlyReported();
+ }
+#endif
+ }
+
+ 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;
+ }
+
+ void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+ AssertionResult result = build( expr );
+ handleResult( result );
+ }
+
+ 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::captureExpectedException( std::string const& expectedMessage ) {
+ if( expectedMessage.empty() )
+ captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
+ else
+ captureExpectedException( Matchers::Equals( expectedMessage ) );
+ }
+
+ void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+
+ assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
+ AssertionResultData data = m_data;
+ data.resultType = ResultWas::Ok;
+ data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+
+ std::string actualMessage = Catch::translateActiveException();
+ if( !matcher.match( actualMessage ) ) {
+ data.resultType = ResultWas::ExpressionFailed;
+ data.reconstructedExpression = actualMessage;
+ }
+ AssertionResult result( m_assertionInfo, data );
+ handleResult( result );
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ handleResult( result );
+ }
+
+ void ResultBuilder::handleResult( AssertionResult const& result )
+ {
+ 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 defined(CATCH_CONFIG_FAST_COMPILE)
+ if (m_shouldDebugBreak) {
+ ///////////////////////////////////////////////////////////////////
+ // To inspect the state during test, you need to go one level up the callstack
+ // To go back to the test and change execution, jump over the throw statement
+ ///////////////////////////////////////////////////////////////////
+ CATCH_BREAK_INTO_DEBUGGER();
+ }
+#endif
+ 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
+ {
+ return build( *this );
+ }
+
+ // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+ // a temporary DecomposedExpression, which in turn holds references to
+ // operands, possibly temporary as well.
+ // It should immediately be passed to handleResult; if the expression
+ // needs to be reported, its string expansion must be composed before
+ // the temporaries are destroyed.
+ AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+ AssertionResultData data = m_data;
+
+ // Flip bool results if FalseTest flag is set
+ if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+ data.negate( expr.isBinaryExpression() );
+ }
+
+ data.message = m_stream().oss.str();
+ data.decomposedExpression = &expr; // for lazy reconstruction
+ return AssertionResult( m_assertionInfo, data );
+ }
+
+ void ResultBuilder::reconstructExpression( std::string& dest ) const {
+ dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+ }
+
+ void ResultBuilder::setExceptionGuard() {
+ m_guardException = true;
+ }
+ void ResultBuilder::unsetExceptionGuard() {
+ m_guardException = false;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+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( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n"
+ << Colour( Colour::FileName )
+ << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at "
+ << Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
+ << Colour( Colour::Red ) << "\tRedefined at "
+ << Colour( Colour::FileName) << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+
+ ITagAliasRegistry const& ITagAliasRegistry::get() {
+ return getRegistryHub().getTagAliasRegistry();
+ }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_str( adjustString( str ) )
+ {}
+ std::string CasedString::adjustString( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? toLower( str )
+ : str;
+ }
+ std::string CasedString::caseSensitivitySuffix() const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? " (case insensitive)"
+ : std::string();
+ }
+
+ StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+ : m_comparator( comparator ),
+ m_operation( operation ) {
+ }
+
+ std::string StringMatcherBase::describe() const {
+ std::string description;
+ description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+ m_comparator.caseSensitivitySuffix().size());
+ description += m_operation;
+ description += ": \"";
+ description += m_comparator.m_str;
+ description += "\"";
+ description += m_comparator.caseSensitivitySuffix();
+ return description;
+ }
+
+ EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+ bool EqualsMatcher::match( std::string const& source ) const {
+ return m_comparator.adjustString( source ) == m_comparator.m_str;
+ }
+
+ ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+ bool ContainsMatcher::match( std::string const& source ) const {
+ return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+ bool StartsWithMatcher::match( std::string const& source ) const {
+ return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+ bool EndsWithMatcher::match( std::string const& source ) const {
+ return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ } // namespace StdString
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+ typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+ Reporters m_reporters;
+
+public:
+ void add( Ptr<IStreamingReporter> const& reporter ) {
+ m_reporters.push_back( reporter );
+ }
+
+public: // IStreamingReporter
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporters[0]->getPreferences();
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->noMatchingTestCases( spec );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunStarting( testRunInfo );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseStarting( testInfo );
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionStarting( sectionInfo );
+ }
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->assertionStarting( assertionInfo );
+ }
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ bool clearBuffer = false;
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ clearBuffer |= (*it)->assertionEnded( assertionStats );
+ return clearBuffer;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionEnded( sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupEnded( testGroupStats );
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunEnded( testRunStats );
+ }
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->skipTest( testInfo );
+ }
+
+ virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+ return this;
+ }
+
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+ Ptr<IStreamingReporter> resultingReporter;
+
+ if( existingReporter ) {
+ MultipleReporters* multi = existingReporter->tryAsMulti();
+ if( !multi ) {
+ multi = new MultipleReporters;
+ resultingReporter = Ptr<IStreamingReporter>( multi );
+ if( existingReporter )
+ multi->add( existingReporter );
+ }
+ else
+ resultingReporter = existingReporter;
+ multi->add( additionalReporter );
+ }
+ else
+ resultingReporter = additionalReporter;
+
+ return resultingReporter;
+}
+
+} // 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>
+#include <cfloat>
+#include <cstdio>
+#include <assert.h>
+
+namespace Catch {
+
+ namespace {
+ // Because formatting using c++ streams is stateful, drop down to C is required
+ // Alternatively we could use stringstream, but its performance is... not good.
+ std::string getFormattedDuration( double duration ) {
+ // Max exponent + 1 is required to represent the whole part
+ // + 1 for decimal point
+ // + 3 for the 3 decimal places
+ // + 1 for null terminator
+ const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+ char buffer[maxDoubleSize];
+
+ // Save previous errno, to prevent sprintf from overwriting it
+ ErrnoGuard guard;
+#ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+#else
+ sprintf(buffer, "%.3f", duration);
+#endif
+ return std::string(buffer);
+ }
+ }
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+
+ virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
+ // Don't do anything with this by default.
+ // It can optionally be overridden in the derived class.
+ }
+
+ Ptr<IConfig const> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+ };
+
+ 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() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+ ~CumulativeReporterBase();
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+ virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ 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& ) CATCH_OVERRIDE {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ // AssertionResult holds a pointer to a temporary DecomposedExpression,
+ // which getExpandedExpression() calls to build the expression string.
+ // Our section stack copy of the assertionResult will likely outlive the
+ // temporary, so it must be expanded or discarded now to avoid calling
+ // a destroyed object later.
+ prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ 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 ) CATCH_OVERRIDE {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ 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& ) CATCH_OVERRIDE {}
+
+ virtual void prepareExpandedExpression( AssertionResult& result ) const {
+ if( result.isOk() )
+ result.discardDecomposedExpression();
+ else
+ result.expandDecomposedExpression();
+ }
+
+ Ptr<IConfig const> 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;
+ ReporterPreferences m_reporterPrefs;
+
+ };
+
+ template<char C>
+ char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ struct TestEventListenerBase : StreamingReporterBase {
+ TestEventListenerBase( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+ virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+ return false;
+ }
+ };
+
+} // 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 SharedImpl<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() );
+ }
+ };
+
+ template<typename T>
+ class ListenerRegistrar {
+
+ class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+ virtual std::string getDescription() const {
+ return std::string();
+ }
+ };
+
+ public:
+
+ ListenerRegistrar() {
+ getMutableRegistryHub().registerListener( new ListenerFactory() );
+ }
+ };
+}
+
+#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 ); }
+
+// Deprecated - use the form without INTERNAL_
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+namespace Catch {
+
+ class XmlEncode {
+ public:
+ enum ForWhat { ForTextNodes, ForAttributes };
+
+ XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+ : m_str( str ),
+ m_forWhat( forWhat )
+ {}
+
+ void encodeTo( std::ostream& os ) const {
+
+ // Apostrophe escaping not necessary if we always use " to write attributes
+ // (see: http://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+ char c = m_str[i];
+ switch( c ) {
+ case '<': os << "&lt;"; break;
+ case '&': os << "&amp;"; break;
+
+ case '>':
+ // See: http://www.w3.org/TR/xml/#syntax
+ if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+ os << "&gt;";
+ else
+ os << c;
+ break;
+
+ case '\"':
+ if( m_forWhat == ForAttributes )
+ os << "&quot;";
+ else
+ os << c;
+ break;
+
+ default:
+ // Escape control chars - based on contribution by @espenalb in PR #465 and
+ // by @mrpi PR #588
+ if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+ // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>( c );
+ }
+ else
+ os << c;
+ }
+ }
+ }
+
+ friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+ xmlEncode.encodeTo( os );
+ return os;
+ }
+
+ private:
+ std::string m_str;
+ ForWhat m_forWhat;
+ };
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = CATCH_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() )
+ {
+ writeDeclaration();
+ }
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( os )
+ {
+ writeDeclaration();
+ }
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ m_os << 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 ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
+ }
+ else {
+ m_os << m_indent << "</" << m_tags.back() << ">";
+ }
+ m_os << std::endl;
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ std::ostringstream oss;
+ oss << attribute;
+ return writeAttribute( name, oss.str() );
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ m_os << m_indent;
+ m_os << XmlEncode( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ m_os << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ void writeStylesheetRef( std::string const& url ) {
+ m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ m_os << '\n';
+ return *this;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << ">" << std::endl;
+ m_tagIsOpen = false;
+ }
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ void writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ m_os << std::endl;
+ m_needsNewline = false;
+ }
+ }
+
+ 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_xml(_config.stream()),
+ m_sectionDepth( 0 )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~XmlReporter() CATCH_OVERRIDE;
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+
+ virtual std::string getStylesheetRef() const {
+ return std::string();
+ }
+
+ void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+ m_xml
+ .writeAttribute( "filename", sourceInfo.file )
+ .writeAttribute( "line", sourceInfo.line );
+ }
+
+ public: // StreamingReporterBase
+
+ virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
+ StreamingReporterBase::noMatchingTestCases( s );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testRunStarting( testInfo );
+ std::string stylesheetRef = getStylesheetRef();
+ if( !stylesheetRef.empty() )
+ m_xml.writeStylesheetRef( stylesheetRef );
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testGroupStarting( groupInfo );
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupInfo.name );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseStarting(testInfo);
+ m_xml.startElement( "TestCase" )
+ .writeAttribute( "name", trim( testInfo.name ) )
+ .writeAttribute( "description", testInfo.description )
+ .writeAttribute( "tags", testInfo.tagsAsString );
+
+ writeSourceInfo( testInfo.lineInfo );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ m_testCaseTimer.start();
+ m_xml.ensureTagClosed();
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::sectionStarting( sectionInfo );
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionInfo.name ) )
+ .writeAttribute( "description", sectionInfo.description );
+ writeSourceInfo( sectionInfo.lineInfo );
+ m_xml.ensureTagClosed();
+ }
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+
+ AssertionResult const& result = assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ if( includeResults ) {
+ // Print any info messages in <Info> tags.
+ 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( !includeResults && result.getResultType() != ResultWas::Warning )
+ return true;
+
+ // Print the expression if there is one.
+ if( result.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", result.succeeded() )
+ .writeAttribute( "type", result.getTestMacroName() );
+
+ writeSourceInfo( result.getSourceInfo() );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( result.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( result.getExpandedExpression() );
+ }
+
+ // And... Print a result applicable to each result type.
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.startElement( "Exception" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::FatalErrorCondition:
+ m_xml.startElement( "FatalErrorCondition" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( result.getMessage() );
+ break;
+ case ResultWas::Warning:
+ // Warning will already have been written
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.startElement( "Failure" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ default:
+ break;
+ }
+
+ if( result.hasExpression() )
+ m_xml.endElement();
+
+ return true;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ 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 ) CATCH_OVERRIDE {
+ 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() );
+
+ if( !testCaseStats.stdOut.empty() )
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+ if( !testCaseStats.stdErr.empty() )
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
+ m_xml.endElement();
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ 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 ) CATCH_OVERRIDE {
+ 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 {
+
+ namespace {
+ std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &rawtime);
+#else
+ std::tm* timeInfo;
+ timeInfo = std::gmtime(&rawtime);
+#endif
+
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+
+ }
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() ),
+ m_okToFail( false )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~JunitReporter() CATCH_OVERRIDE;
+
+ 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*/ ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE {
+ m_okToFail = testCaseInfo.okToFail();
+ }
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() CATCH_OVERRIDE {
+ 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", getCurrentTimestamp() );
+
+ // 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;
+ bool m_okToFail;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cfloat>
+#include <cstdio>
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter() CATCH_OVERRIDE;
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return false;
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, includeResults );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
+ 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_config->showDurations() == ShowDurations::Always ) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ if( m_headerPrinted ) {
+ m_headerPrinted = false;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
+ 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 ) CATCH_OVERRIDE {
+ 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 ";
+ if (_stats.infoMessages.size() == 1)
+ messageLabel += "message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel += "messages";
+ 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() << " 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.back().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.testCases.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 sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE {
+ if (m_config->showDurations() == ShowDurations::Always) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ }
+
+ 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 const& passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << ' ' << passOrFail;
+ }
+ stream << ':';
+ }
+ }
+
+ void printIssue( std::string const& 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 ? std::string() : 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 ) : std::string();
+ 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 {
+ // These are all here to avoid warnings about not having any out of line
+ // virtual methods
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ IStream::~IStream() CATCH_NOEXCEPT {}
+ FileStream::~FileStream() CATCH_NOEXCEPT {}
+ CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+ DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
+ 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() {}
+ WildcardPattern::~WildcardPattern() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+ Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
+
+ void Config::dummy() {}
+
+ namespace TestCaseTracking {
+ ITracker::~ITracker() {}
+ TrackerBase::~TrackerBase() {}
+ SectionTracker::~SectionTracker() {}
+ IndexTracker::~IndexTracker() {}
+ }
+}
+
+#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__
+
+#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+ int result = Catch::Session().run( argc, argv );
+ return ( result < 0xff ? result : 0xff );
+}
+
+#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 < 0xff ? result : 0xff );
+}
+
+#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
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#else
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#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_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __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_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, 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( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+
+#else
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#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 REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __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 REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, 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( std::string(" Given: ") + desc, "" )
+#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string(" 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/geom_matching/wasserstein/include/def_debug_ws.h b/geom_matching/wasserstein/include/def_debug_ws.h
index d4450b2..791ce1d 100644
--- a/geom_matching/wasserstein/include/def_debug_ws.h
+++ b/geom_matching/wasserstein/include/def_debug_ws.h
@@ -36,5 +36,9 @@ derivative works thereof, in binary and source code form.
// for R package TDA, to comply with some CRAN rules
// like no usage of cout, cerr, cin, exit, etc.
//#define FOR_R_TDA
+//
+//#define DEBUG_KDTREE_RESTR_ORACLE
+//#define DEBUG_STUPID_SPARSE_RESTR_ORACLE
+//#define DEBUG_FR_AUCTION
#endif
diff --git a/geom_matching/wasserstein/include/diagonal_heap.h b/geom_matching/wasserstein/include/diagonal_heap.h
new file mode 100644
index 0000000..9ffee70
--- /dev/null
+++ b/geom_matching/wasserstein/include/diagonal_heap.h
@@ -0,0 +1,149 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+ */
+
+#ifndef DIAGONAL_HEAP_H
+#define DIAGONAL_HEAP_H
+
+//#define USE_BOOST_HEAP
+
+#include <map>
+#include <memory>
+#include <set>
+#include <list>
+
+#ifdef USE_BOOST_HEAP
+#include <boost/heap/d_ary_heap.hpp>
+#endif
+
+#include "basic_defs_ws.h"
+
+namespace hera {
+namespace ws {
+
+template <typename T>
+struct CompPairsBySecondLexStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second < b.second or (a.second == b.second and a.first > b.first);
+ }
+};
+
+
+template <typename T>
+struct CompPairsBySecondGreaterStruct {
+ bool operator()(const IdxValPair<T>& a, const IdxValPair<T>& b) const
+ {
+ return a.second > b.second;
+ }
+};
+
+#ifdef USE_BOOST_HEAP
+template <class Real>
+using LossesHeapOld = boost::heap::d_ary_heap<IdxValPair<Real>, boost::heap::arity<2>, boost::heap::mutable_<true>, boost::heap::compare<CompPairsBySecondGreaterStruct<Real>>>;
+#else
+template<typename T, class ComparisonStruct>
+class IdxValHeap {
+public:
+ using InternalKeeper = std::set<IdxValPair<T>, ComparisonStruct>;
+ using handle_type = typename InternalKeeper::iterator;
+ using const_handle_type = typename InternalKeeper::const_iterator;
+ // methods
+ handle_type push(const IdxValPair<T>& val)
+ {
+ auto res_pair = _heap.insert(val);
+ assert(res_pair.second);
+ assert(res_pair.first != _heap.end());
+ return res_pair.first;
+ }
+
+ void decrease(handle_type& handle, const IdxValPair<T>& new_val)
+ {
+ _heap.erase(handle);
+ handle = push(new_val);
+ }
+
+ void increase(handle_type& handle, const IdxValPair<T>& new_val)
+ {
+ _heap.erase(handle);
+ handle = push(new_val);
+ }
+
+ size_t size() const
+ {
+ return _heap.size();
+ }
+
+ handle_type ordered_begin()
+ {
+ return _heap.begin();
+ }
+
+ handle_type ordered_end()
+ {
+ return _heap.end();
+ }
+
+ const_handle_type ordered_begin() const
+ {
+ return _heap.cbegin();
+ }
+
+ const_handle_type ordered_end() const
+ {
+ return _heap.cend();
+ }
+
+
+private:
+ std::set<IdxValPair<T>, ComparisonStruct> _heap;
+};
+
+// if we store losses, the minimal value should come first
+template <class Real>
+using LossesHeapOld = IdxValHeap<Real, CompPairsBySecondLexStruct<Real>>;
+#endif
+
+template <class Real>
+std::string losses_heap_to_string(const LossesHeapOld<Real>& h)
+{
+ std::stringstream result;
+ result << "[";
+ for(auto iter = h.ordered_begin(); iter != h.ordered_end(); ++iter) {
+ result << *iter;
+ if (std::next(iter) != h.ordered_end()) {
+ result << ", ";
+ }
+ }
+ result << "]";
+ return result.str();
+}
+
+} // ws
+} // hera
+
+#endif // DIAGONAL_HEAP_H
diff --git a/geom_matching/wasserstein/include/diagram_reader.h b/geom_matching/wasserstein/include/diagram_reader.h
new file mode 100644
index 0000000..8d09c9b
--- /dev/null
+++ b/geom_matching/wasserstein/include/diagram_reader.h
@@ -0,0 +1,369 @@
+/*
+
+Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
+All rights reserved.
+
+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.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+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 HOLDER 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.
+
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+(Enhancements) to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to copyright holder,
+without imposing a separate written license agreement for such Enhancements,
+then you hereby grant the following license: a non-exclusive, royalty-free
+perpetual license to install, use, modify, prepare derivative works, incorporate
+into other computer software, distribute, and sublicense such enhancements or
+derivative works thereof, in binary and source code form.
+
+*/
+
+#ifndef HERA_DIAGRAM_READER_H
+#define HERA_DIAGRAM_READER_H
+
+#ifndef FOR_R_TDA
+#include <iostream>
+#endif
+
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <fstream>
+#include <string>
+#include <cctype>
+#include <algorithm>
+#include <unordered_map>
+#include <map>
+
+#ifdef WASSERSTEIN_PURE_GEOM
+#include "dnn/geometry/euclidean-dynamic.h"
+#endif
+
+namespace hera {
+
+// cannot choose stod, stof or stold based on RealType,
+// lazy solution: partial specialization
+template<class RealType = double>
+RealType parse_real_from_str(const std::string& s);
+
+template <>
+double parse_real_from_str<double>(const std::string& s)
+{
+ return std::stod(s);
+}
+
+
+template <>
+long double parse_real_from_str<long double>(const std::string& s)
+{
+ return std::stold(s);
+}
+
+
+template <>
+float parse_real_from_str<float>(const std::string& s)
+{
+ return std::stof(s);
+}
+
+
+template<class RealType>
+RealType parse_real_from_str(const std::string& s)
+{
+ static_assert(sizeof(RealType) != sizeof(RealType), "Must be specialized for each type you want to use, see above");
+}
+
+// fill in result with points from file fname
+// return false if file can't be opened
+// or error occurred while reading
+// decPrecision is the maximal decimal precision in the input,
+// it is zero if all coordinates in the input are integers
+template<class RealType = double, class ContType_ = std::vector<std::pair<RealType, RealType>>>
+bool read_diagram_point_set(const char* fname, ContType_& result, int& decPrecision)
+{
+ size_t lineNumber { 0 };
+ result.clear();
+ std::ifstream f(fname);
+ if (!f.good()) {
+#ifndef FOR_R_TDA
+ std::cerr << "Cannot open file " << fname << std::endl;
+#endif
+ return false;
+ }
+ std::locale loc;
+ std::string line;
+ while(std::getline(f, line)) {
+ lineNumber++;
+ // process comments: remove everything after hash
+ auto hashPos = line.find_first_of("#", 0);
+ if( std::string::npos != hashPos) {
+ line = std::string(line.begin(), line.begin() + hashPos);
+ }
+ if (line.empty()) {
+ continue;
+ }
+ // trim whitespaces
+ auto whiteSpaceFront = std::find_if_not(line.begin(),line.end(),isspace);
+ auto whiteSpaceBack = std::find_if_not(line.rbegin(),line.rend(),isspace).base();
+ if (whiteSpaceBack <= whiteSpaceFront) {
+ // line consists of spaces only - move to the next line
+ continue;
+ }
+ line = std::string(whiteSpaceFront,whiteSpaceBack);
+
+ // transform line to lower case
+ // to parse Infinity
+ for(auto& c : line) {
+ c = std::tolower(c, loc);
+ }
+
+ bool fracPart = false;
+ int currDecPrecision = 0;
+ for(auto c : line) {
+ if (c == '.') {
+ fracPart = true;
+ } else if (fracPart) {
+ if (isdigit(c)) {
+ currDecPrecision++;
+ } else {
+ fracPart = false;
+ if (currDecPrecision > decPrecision)
+ decPrecision = currDecPrecision;
+ currDecPrecision = 0;
+ }
+ }
+ }
+
+ RealType x, y;
+ std::string str_x, str_y;
+ std::istringstream iss(line);
+ try {
+ iss >> str_x >> str_y;
+
+ x = parse_real_from_str<RealType>(str_x);
+ y = parse_real_from_str<RealType>(str_y);
+
+ result.push_back(std::make_pair(x, y));
+ }
+ catch (const std::invalid_argument& e) {
+#ifndef FOR_R_TDA
+ std::cerr << "Error in file " << fname << ", line number " << lineNumber << ": cannot parse \"" << line << "\"" << std::endl;
+#endif
+ return false;
+ }
+ catch (const std::out_of_range&) {
+#ifndef FOR_R_TDA
+ std::cerr << "Error while reading file " << fname << ", line number " << lineNumber << ": value too large in \"" << line << "\"" << std::endl;
+#endif
+ return false;
+ }
+ }
+ f.close();
+ return true;
+}
+
+
+// wrappers
+template<class RealType = double, class ContType_ = std::vector<std::pair<RealType, RealType>>>
+bool read_diagram_point_set(const std::string& fname, ContType_& result, int& decPrecision)
+{
+ return read_diagram_point_set<RealType, ContType_>(fname.c_str(), result, decPrecision);
+}
+
+// these two functions are now just wrappers for the previous ones,
+// in case someone needs them; decPrecision is ignored
+template<class RealType = double, class ContType_ = std::vector<std::pair<RealType, RealType>>>
+bool read_diagram_point_set(const char* fname, ContType_& result)
+{
+ int decPrecision;
+ return read_diagram_point_set<RealType, ContType_>(fname, result, decPrecision);
+}
+
+template<class RealType = double, class ContType_ = std::vector<std::pair<RealType, RealType>>>
+bool read_diagram_point_set(const std::string& fname, ContType_& result)
+{
+ int decPrecision;
+ return read_diagram_point_set<RealType, ContType_>(fname.c_str(), result, decPrecision);
+}
+
+
+template<class RealType, class ContType>
+void remove_duplicates(ContType& dgm_A, ContType& dgm_B)
+{
+ std::map<std::pair<RealType, RealType>, int> map_A, map_B;
+ // copy points to maps
+ for(const auto& ptA : dgm_A) {
+ map_A[ptA]++;
+ }
+ for(const auto& ptB : dgm_B) {
+ map_B[ptB]++;
+ }
+ // clear vectors
+ dgm_A.clear();
+ dgm_B.clear();
+ // remove duplicates from maps
+ // loop over the smaller one
+ if (map_A.size() <= map_B.size()) {
+ for(auto& point_multiplicity_pair : map_A) {
+ auto iter_B = map_B.find(point_multiplicity_pair.first);
+ if (iter_B != map_B.end()) {
+ int duplicate_multiplicity = std::min(point_multiplicity_pair.second, iter_B->second);
+ point_multiplicity_pair.second -= duplicate_multiplicity;
+ iter_B->second -= duplicate_multiplicity;
+ }
+ }
+ } else {
+ for(auto& point_multiplicity_pair : map_B) {
+ auto iter_A = map_A.find(point_multiplicity_pair.first);
+ if (iter_A != map_A.end()) {
+ int duplicate_multiplicity = std::min(point_multiplicity_pair.second, iter_A->second);
+ point_multiplicity_pair.second -= duplicate_multiplicity;
+ iter_A->second -= duplicate_multiplicity;
+ }
+ }
+ }
+ // copy points back to vectors
+ for(const auto& pointMultiplicityPairA : map_A) {
+ assert( pointMultiplicityPairA.second >= 0);
+ for(int i = 0; i < pointMultiplicityPairA.second; ++i) {
+ dgm_A.push_back(pointMultiplicityPairA.first);
+ }
+ }
+
+ for(const auto& pointMultiplicityPairB : map_B) {
+ assert( pointMultiplicityPairB.second >= 0);
+ for(int i = 0; i < pointMultiplicityPairB.second; ++i) {
+ dgm_B.push_back(pointMultiplicityPairB.first);
+ }
+ }
+}
+
+
+#ifdef WASSERSTEIN_PURE_GEOM
+
+template<class Real>
+int get_point_dimension(const std::string& line)
+{
+ Real x;
+ int dim = 0;
+ std::istringstream iss(line);
+ while(iss >> x) {
+ dim++;
+ }
+ return dim;
+}
+
+
+template<class RealType = double >
+bool read_point_cloud(const char* fname, hera::ws::dnn::DynamicPointVector<RealType>& result, int& dimension, int& decPrecision)
+{
+ using DynamicPointTraitsR = typename hera::ws::dnn::DynamicPointTraits<RealType>;
+
+ size_t lineNumber { 0 };
+ result.clear();
+ std::ifstream f(fname);
+ if (!f.good()) {
+#ifndef FOR_R_TDA
+ std::cerr << "Cannot open file " << fname << std::endl;
+#endif
+ return false;
+ }
+ std::string line;
+ DynamicPointTraitsR traits;
+ bool dim_computed = false;
+ int point_idx = 0;
+ while(std::getline(f, line)) {
+ lineNumber++;
+ // process comments: remove everything after hash
+ auto hashPos = line.find_first_of("#", 0);
+ if( std::string::npos != hashPos) {
+ line = std::string(line.begin(), line.begin() + hashPos);
+ }
+ if (line.empty()) {
+ continue;
+ }
+ // trim whitespaces
+ auto whiteSpaceFront = std::find_if_not(line.begin(),line.end(),isspace);
+ auto whiteSpaceBack = std::find_if_not(line.rbegin(),line.rend(),isspace).base();
+ if (whiteSpaceBack <= whiteSpaceFront) {
+ // line consists of spaces only - move to the next line
+ continue;
+ }
+
+ line = std::string(whiteSpaceFront,whiteSpaceBack);
+
+ if (not dim_computed) {
+ dimension = get_point_dimension<RealType>(line);
+ traits = hera::ws::dnn::DynamicPointTraits<RealType>(dimension);
+ result = traits.container();
+ result.clear();
+ dim_computed = true;
+ }
+
+ bool fracPart = false;
+ int currDecPrecision = 0;
+ for(auto c : line) {
+ if (c == '.') {
+ fracPart = true;
+ } else if (fracPart) {
+ if (isdigit(c)) {
+ currDecPrecision++;
+ } else {
+ fracPart = false;
+ if (currDecPrecision > decPrecision)
+ decPrecision = currDecPrecision;
+ currDecPrecision = 0;
+ }
+ }
+ }
+
+ result.resize(result.size() + 1);
+ RealType x;
+ std::istringstream iss(line);
+ for(int d = 0; d < dimension; ++d) {
+ if (not(iss >> x)) {
+#ifndef FOR_R_TDA
+ std::cerr << "Error in file " << fname << ", line number " << lineNumber << ": cannot parse \"" << line << "\"" << std::endl;
+#endif
+ return false;
+ }
+ result[point_idx][d] = x;
+ }
+ point_idx++;
+ }
+ f.close();
+ return true;
+}
+
+// wrappers
+template<class RealType = double >
+bool read_point_cloud(const char* fname, hera::ws::dnn::DynamicPointVector<RealType>& result, int& dimension)
+{
+ int dec_precision;
+ return read_point_cloud<RealType>(fname, result, dimension, dec_precision);
+}
+
+template<class RealType = double >
+bool read_point_cloud(std::string fname, hera::ws::dnn::DynamicPointVector<RealType>& result, int& dimension, int& dec_precision)
+{
+ return read_point_cloud<RealType>(fname.c_str(), result, dimension, dec_precision);
+}
+
+template<class RealType = double >
+bool read_point_cloud(std::string fname, hera::ws::dnn::DynamicPointVector<RealType>& result, int& dimension)
+{
+ return read_point_cloud<RealType>(fname.c_str(), result, dimension);
+}
+
+#endif // WASSERSTEIN_PURE_GEOM
+
+} // end namespace hera
+#endif // HERA_DIAGRAM_READER_H
diff --git a/geom_matching/wasserstein/include/dnn/geometry/euclidean-dynamic.h b/geom_matching/wasserstein/include/dnn/geometry/euclidean-dynamic.h
new file mode 100644
index 0000000..4b98309
--- /dev/null
+++ b/geom_matching/wasserstein/include/dnn/geometry/euclidean-dynamic.h
@@ -0,0 +1,248 @@
+#ifndef DNN_GEOMETRY_EUCLIDEAN_DYNAMIC_H
+#define DNN_GEOMETRY_EUCLIDEAN_DYNAMIC_H
+
+#include <vector>
+#include <algorithm>
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/vector.hpp>
+#include <cmath>
+
+namespace hera
+{
+namespace ws
+{
+namespace dnn
+{
+
+template<class Real_>
+class DynamicPointVector
+{
+ public:
+ using Real = Real_;
+ struct PointType
+ {
+ void* p;
+
+ Real& operator[](const int i)
+ {
+ return (static_cast<Real*>(p))[i];
+ }
+
+ const Real& operator[](const int i) const
+ {
+ return (static_cast<Real*>(p))[i];
+ }
+
+ };
+ struct iterator;
+ typedef iterator const_iterator;
+
+ public:
+ DynamicPointVector(size_t point_capacity = 0):
+ point_capacity_(point_capacity) {}
+
+
+ PointType operator[](size_t i) const { return {(void*) &storage_[i*point_capacity_]}; }
+ inline void push_back(PointType p);
+
+ inline iterator begin();
+ inline iterator end();
+ inline const_iterator begin() const;
+ inline const_iterator end() const;
+
+ size_t size() const { return storage_.size() / point_capacity_; }
+
+ void clear() { storage_.clear(); }
+ void swap(DynamicPointVector& other) { storage_.swap(other.storage_); std::swap(point_capacity_, other.point_capacity_); }
+ void reserve(size_t sz) { storage_.reserve(sz * point_capacity_); }
+ void resize(size_t sz) { storage_.resize(sz * point_capacity_); }
+
+ private:
+ size_t point_capacity_;
+ std::vector<char> storage_;
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) { ar & point_capacity_ & storage_; }
+};
+
+template<typename Real>
+struct DynamicPointTraits
+{
+ typedef DynamicPointVector<Real> PointContainer;
+ typedef typename PointContainer::PointType PointType;
+ struct PointHandle
+ {
+ void* p;
+ bool operator==(const PointHandle& other) const { return p == other.p; }
+ bool operator!=(const PointHandle& other) const { return !(*this == other); }
+ bool operator<(const PointHandle& other) const { return p < other.p; }
+ bool operator>(const PointHandle& other) const { return p > other.p; }
+ };
+
+ typedef Real Coordinate;
+ typedef Real DistanceType;
+
+ DynamicPointTraits(unsigned dim = 0):
+ dim_(dim) {}
+
+ DistanceType distance(PointType p1, PointType p2) const { return sqrt(sq_distance(p1,p2)); }
+ DistanceType distance(PointHandle p1, PointHandle p2) const { return distance(PointType({p1.p}), PointType({p2.p})); }
+ DistanceType sq_distance(PointType p1, PointType p2) const { Real res = 0; for (unsigned i = 0; i < dimension(); ++i) { Real c1 = coordinate(p1,i), c2 = coordinate(p2,i); res += (c1 - c2)*(c1 - c2); } return res; }
+ DistanceType sq_distance(PointHandle p1, PointHandle p2) const { return sq_distance(PointType({p1.p}), PointType({p2.p})); }
+ unsigned dimension() const { return dim_; }
+ Real& coordinate(PointType p, unsigned i) const { return ((Real*) p.p)[i]; }
+ Real& coordinate(PointHandle h, unsigned i) const { return ((Real*) h.p)[i]; }
+
+ // it's non-standard to return a reference, but we can rely on it for code that assumes this particular point type
+ size_t& id(PointType p) const { return *((size_t*) ((Real*) p.p + dimension())); }
+ size_t& id(PointHandle h) const { return *((size_t*) ((Real*) h.p + dimension())); }
+ PointHandle handle(PointType p) const { return {p.p}; }
+ PointType point(PointHandle h) const { return {h.p}; }
+
+ void swap(PointType p1, PointType p2) const { std::swap_ranges((char*) p1.p, ((char*) p1.p) + capacity(), (char*) p2.p); }
+ bool cmp(PointType p1, PointType p2) const { return std::lexicographical_compare((Real*) p1.p, ((Real*) p1.p) + dimension(), (Real*) p2.p, ((Real*) p2.p) + dimension()); }
+ bool eq(PointType p1, PointType p2) const { return std::equal((Real*) p1.p, ((Real*) p1.p) + dimension(), (Real*) p2.p); }
+
+ // non-standard, and possibly a weird name
+ size_t capacity() const { return sizeof(Real)*dimension() + sizeof(size_t); }
+
+ PointContainer container(size_t n = 0) const { PointContainer c(capacity()); c.resize(n); return c; }
+ PointContainer container(size_t n, const PointType& p) const;
+
+ typename PointContainer::iterator
+ iterator(PointContainer& c, PointHandle ph) const;
+ typename PointContainer::const_iterator
+ iterator(const PointContainer& c, PointHandle ph) const;
+
+ Real internal_p;
+
+ private:
+ unsigned dim_;
+
+ private:
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int version) { ar & dim_; }
+};
+
+} // dnn
+
+template<class Real>
+struct dnn::DynamicPointVector<Real>::iterator:
+ public boost::iterator_facade<iterator,
+ PointType,
+ std::random_access_iterator_tag,
+ PointType,
+ std::ptrdiff_t>
+{
+ typedef boost::iterator_facade<iterator,
+ PointType,
+ std::random_access_iterator_tag,
+ PointType,
+ std::ptrdiff_t> Parent;
+
+
+ public:
+ typedef typename Parent::value_type value_type;
+ typedef typename Parent::difference_type difference_type;
+ typedef typename Parent::reference reference;
+
+ iterator(size_t point_capacity = 0):
+ point_capacity_(point_capacity) {}
+
+ iterator(void* p, size_t point_capacity):
+ p_(p), point_capacity_(point_capacity) {}
+
+ private:
+ void increment() { p_ = ((char*) p_) + point_capacity_; }
+ void decrement() { p_ = ((char*) p_) - point_capacity_; }
+ void advance(difference_type n) { p_ = ((char*) p_) + n*point_capacity_; }
+ difference_type
+ distance_to(iterator other) const { return (((char*) other.p_) - ((char*) p_))/(int) point_capacity_; }
+ bool equal(const iterator& other) const { return p_ == other.p_; }
+ reference dereference() const { return {p_}; }
+
+ friend class ::boost::iterator_core_access;
+
+ private:
+ void* p_;
+ size_t point_capacity_;
+};
+
+template<class Real>
+void dnn::DynamicPointVector<Real>::push_back(PointType p)
+{
+ if (storage_.capacity() < storage_.size() + point_capacity_)
+ storage_.reserve(1.5*storage_.capacity());
+
+ storage_.resize(storage_.size() + point_capacity_);
+
+ std::copy((char*) p.p, (char*) p.p + point_capacity_, storage_.end() - point_capacity_);
+}
+
+template<class Real>
+typename dnn::DynamicPointVector<Real>::iterator dnn::DynamicPointVector<Real>::begin() { return iterator((void*) &*storage_.begin(), point_capacity_); }
+
+template<class Real>
+typename dnn::DynamicPointVector<Real>::iterator dnn::DynamicPointVector<Real>::end() { return iterator((void*) &*storage_.end(), point_capacity_); }
+
+template<class Real>
+typename dnn::DynamicPointVector<Real>::const_iterator dnn::DynamicPointVector<Real>::begin() const { return const_iterator((void*) &*storage_.begin(), point_capacity_); }
+
+template<class Real>
+typename dnn::DynamicPointVector<Real>::const_iterator dnn::DynamicPointVector<Real>::end() const { return const_iterator((void*) &*storage_.end(), point_capacity_); }
+
+template<typename R>
+typename dnn::DynamicPointTraits<R>::PointContainer
+dnn::DynamicPointTraits<R>::container(size_t n, const PointType& p) const
+{
+ PointContainer c = container(n);
+ for (auto x : c)
+ std::copy((char*) p.p, (char*) p.p + capacity(), (char*) x.p);
+ return c;
+}
+
+template<typename R>
+typename dnn::DynamicPointTraits<R>::PointContainer::iterator
+dnn::DynamicPointTraits<R>::iterator(PointContainer& c, PointHandle ph) const
+{ return typename PointContainer::iterator(ph.p, capacity()); }
+
+template<typename R>
+typename dnn::DynamicPointTraits<R>::PointContainer::const_iterator
+dnn::DynamicPointTraits<R>::iterator(const PointContainer& c, PointHandle ph) const
+{ return typename PointContainer::const_iterator(ph.p, capacity()); }
+
+} // ws
+} // hera
+
+namespace std {
+ template<>
+ struct hash<typename hera::ws::dnn::DynamicPointTraits<double>::PointHandle>
+ {
+ using PointHandle = typename hera::ws::dnn::DynamicPointTraits<double>::PointHandle;
+ size_t operator()(const PointHandle& ph) const
+ {
+ return std::hash<void*>()(ph.p);
+ }
+ };
+
+ template<>
+ struct hash<typename hera::ws::dnn::DynamicPointTraits<float>::PointHandle>
+ {
+ using PointHandle = typename hera::ws::dnn::DynamicPointTraits<float>::PointHandle;
+ size_t operator()(const PointHandle& ph) const
+ {
+ return std::hash<void*>()(ph.p);
+ }
+ };
+
+
+} // std
+
+
+#endif
diff --git a/geom_matching/wasserstein/include/dnn/geometry/euclidean-fixed.h b/geom_matching/wasserstein/include/dnn/geometry/euclidean-fixed.h
index e2c5b44..3e38baf 100644
--- a/geom_matching/wasserstein/include/dnn/geometry/euclidean-fixed.h
+++ b/geom_matching/wasserstein/include/dnn/geometry/euclidean-fixed.h
@@ -1,5 +1,5 @@
-#ifndef DNN_GEOMETRY_EUCLIDEAN_FIXED_H
-#define DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+#ifndef HERA_WS_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
+#define HERA_WS_DNN_GEOMETRY_EUCLIDEAN_FIXED_H
#include <boost/operators.hpp>
#include <boost/array.hpp>
@@ -15,6 +15,10 @@
#include "../parallel/tbb.h" // for dnn::vector<...>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
// TODO: wrap in another namespace (e.g., euclidean)
@@ -107,7 +111,7 @@ namespace dnn
template<class Point>
struct PointTraits; // intentionally undefined; should be specialized for each type
-
+
template<size_t D, typename Real>
struct PointTraits< Point<D, Real> > // specialization for dnn::Point
{
@@ -119,11 +123,11 @@ namespace dnn
typedef typename PointType::DistanceType DistanceType;
- static DistanceType
- distance(const PointType& p1, const PointType& p2) { if (std::isinf(internal_p)) return p1.distance(p2); else return p1.p_distance(p2, internal_p); }
+ static DistanceType
+ distance(const PointType& p1, const PointType& p2) { if (hera::is_infinity(internal_p)) return p1.distance(p2); else return p1.p_distance(p2, internal_p); }
- static DistanceType
- distance(PointHandle p1, PointHandle p2) { return distance(*p1,*p2); }
+ static DistanceType
+ distance(PointHandle p1, PointHandle p2) { return distance(*p1,*p2); }
static size_t dimension() { return D; }
static Real coordinate(const PointType& p, size_t i) { return p[i]; }
@@ -163,8 +167,8 @@ namespace dnn
};
template<size_t D, typename Real>
- Real PointTraits< Point<D, Real> >::internal_p = std::numeric_limits<Real>::infinity();
-
+ Real PointTraits< Point<D, Real> >::internal_p = hera::get_infinity<Real>();
+
template<class PointContainer>
void read_points(const std::string& filename, PointContainer& points)
@@ -185,6 +189,8 @@ namespace dnn
points.back()[i++] = x;
}
}
-}
+} // dnn
+} // ws
+} // hera
#endif
diff --git a/geom_matching/wasserstein/include/dnn/local/kd-tree.h b/geom_matching/wasserstein/include/dnn/local/kd-tree.h
index 13eaf27..8e52a5c 100644
--- a/geom_matching/wasserstein/include/dnn/local/kd-tree.h
+++ b/geom_matching/wasserstein/include/dnn/local/kd-tree.h
@@ -1,5 +1,5 @@
-#ifndef DNN_LOCAL_KD_TREE_H
-#define DNN_LOCAL_KD_TREE_H
+#ifndef HERA_WS_DNN_LOCAL_KD_TREE_H
+#define HERA_WS_DNN_LOCAL_KD_TREE_H
#include "../utils.h"
#include "search-functors.h"
@@ -13,6 +13,10 @@
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
// Weighted KDTree
@@ -48,8 +52,9 @@ namespace dnn
template<class Range>
void init(const Range& range);
- DistanceType weight(PointHandle p) { return weights_[indices_[p]]; }
+ DistanceType weight(PointHandle p) { return weights_[indices_[p]]; }
void change_weight(PointHandle p, DistanceType w);
+ void adjust_weights(DistanceType delta); // subtract delta from all weights
HandleDistance find(PointHandle q) const;
Result findR(PointHandle q, DistanceType r) const; // all neighbors within r
@@ -83,7 +88,9 @@ namespace dnn
HandleMap indices_;
double wassersteinPower;
};
-}
+} // dnn
+} // ws
+} // hera
#include "kd-tree.hpp"
diff --git a/geom_matching/wasserstein/include/dnn/local/kd-tree.hpp b/geom_matching/wasserstein/include/dnn/local/kd-tree.hpp
index 22108aa..3a4f0eb 100644
--- a/geom_matching/wasserstein/include/dnn/local/kd-tree.hpp
+++ b/geom_matching/wasserstein/include/dnn/local/kd-tree.hpp
@@ -9,14 +9,14 @@
#include "def_debug_ws.h"
template<class T>
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
KDTree(const Traits& traits, HandleContainer&& handles, double _wassersteinPower):
traits_(traits), tree_(std::move(handles)), wassersteinPower(_wassersteinPower)
{ assert(wassersteinPower >= 1.0); init(); }
template<class T>
template<class Range>
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
KDTree(const Traits& traits, const Range& range, double _wassersteinPower):
traits_(traits), wassersteinPower(_wassersteinPower)
{
@@ -27,7 +27,7 @@ KDTree(const Traits& traits, const Range& range, double _wassersteinPower):
template<class T>
template<class Range>
void
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
init(const Range& range)
{
size_t sz = std::distance(std::begin(range), std::end(range));
@@ -41,7 +41,7 @@ init(const Range& range)
template<class T>
void
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
init()
{
if (tree_.empty())
@@ -61,7 +61,7 @@ init()
template<class T>
struct
-dnn::KDTree<T>::OrderTree
+hera::ws::dnn::KDTree<T>::OrderTree
{
OrderTree(HCIterator b_, HCIterator e_, size_t i_, const Traits& traits_):
b(b_), e(e_), i(i_), traits(traits_) {}
@@ -114,7 +114,7 @@ dnn::KDTree<T>::OrderTree
template<class T>
template<class ResultsFunctor>
void
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
search(PointHandle q, ResultsFunctor& rf) const
{
typedef typename HandleContainer::const_iterator HCIterator;
@@ -123,7 +123,7 @@ search(PointHandle q, ResultsFunctor& rf) const
if (tree_.empty())
return;
- DistanceType D = std::numeric_limits<DistanceType>::infinity();
+ DistanceType D = std::numeric_limits<DistanceType>::max();
// TODO: use tbb::scalable_allocator for the queue
std::queue<KDTreeNode> nodes;
@@ -140,14 +140,16 @@ search(PointHandle q, ResultsFunctor& rf) const
i = (i + 1) % traits().dimension();
HCIterator m = b + (e - b)/2;
- DistanceType dist = pow(traits().distance(q, *m), wassersteinPower) + weights_[m - tree_.begin()];
+
+ DistanceType dist = (wassersteinPower == 1.0) ? traits().distance(q, *m) + weights_[m - tree_.begin()] : std::pow(traits().distance(q, *m), wassersteinPower) + weights_[m - tree_.begin()];
D = rf(*m, dist);
// we are really searching w.r.t L_\infty ball; could prune better with an L_2 ball
Coordinate diff = cmp.diff(q, *m); // diff returns signed distance
- DistanceType diffToWasserPower = (diff > 0 ? 1.0 : -1.0) * pow(fabs(diff), wassersteinPower);
+
+ DistanceType diffToWasserPower = (wassersteinPower == 1.0) ? diff : ((diff > 0 ? 1.0 : -1.0) * std::pow(fabs(diff), wassersteinPower));
size_t lm = m + 1 + (e - (m+1))/2 - tree_.begin();
if (e > m + 1 && diffToWasserPower - subtree_weights_[lm] >= -D) {
@@ -163,7 +165,20 @@ search(PointHandle q, ResultsFunctor& rf) const
template<class T>
void
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
+adjust_weights(DistanceType delta)
+{
+ for(auto& w : weights_)
+ w -= delta;
+
+ for(auto& sw : subtree_weights_)
+ sw -= delta;
+}
+
+
+template<class T>
+void
+hera::ws::dnn::KDTree<T>::
change_weight(PointHandle p, DistanceType w)
{
size_t idx = indices_[p];
@@ -246,32 +261,32 @@ change_weight(PointHandle p, DistanceType w)
}
template<class T>
-typename dnn::KDTree<T>::HandleDistance
-dnn::KDTree<T>::
+typename hera::ws::dnn::KDTree<T>::HandleDistance
+hera::ws::dnn::KDTree<T>::
find(PointHandle q) const
{
- dnn::NNRecord<HandleDistance> nn;
+ hera::ws::dnn::NNRecord<HandleDistance> nn;
search(q, nn);
return nn.result;
}
template<class T>
-typename dnn::KDTree<T>::Result
-dnn::KDTree<T>::
+typename hera::ws::dnn::KDTree<T>::Result
+hera::ws::dnn::KDTree<T>::
findR(PointHandle q, DistanceType r) const
{
- dnn::rNNRecord<HandleDistance> rnn(r);
+ hera::ws::dnn::rNNRecord<HandleDistance> rnn(r);
search(q, rnn);
std::sort(rnn.result.begin(), rnn.result.end());
return rnn.result;
}
template<class T>
-typename dnn::KDTree<T>::Result
-dnn::KDTree<T>::
+typename hera::ws::dnn::KDTree<T>::Result
+hera::ws::dnn::KDTree<T>::
findK(PointHandle q, size_t k) const
{
- dnn::kNNRecord<HandleDistance> knn(k);
+ hera::ws::dnn::kNNRecord<HandleDistance> knn(k);
search(q, knn);
std::sort(knn.result.begin(), knn.result.end());
return knn.result;
@@ -279,7 +294,7 @@ findK(PointHandle q, size_t k) const
template<class T>
-struct dnn::KDTree<T>::CoordinateComparison
+struct hera::ws::dnn::KDTree<T>::CoordinateComparison
{
CoordinateComparison(size_t i, const Traits& traits):
i_(i), traits_(traits) {}
@@ -297,7 +312,7 @@ struct dnn::KDTree<T>::CoordinateComparison
template<class T>
void
-dnn::KDTree<T>::
+hera::ws::dnn::KDTree<T>::
printWeights(void)
{
#ifndef FOR_R_TDA
diff --git a/geom_matching/wasserstein/include/dnn/local/search-functors.h b/geom_matching/wasserstein/include/dnn/local/search-functors.h
index f257d0c..1419f22 100644
--- a/geom_matching/wasserstein/include/dnn/local/search-functors.h
+++ b/geom_matching/wasserstein/include/dnn/local/search-functors.h
@@ -1,8 +1,12 @@
-#ifndef DNN_LOCAL_SEARCH_FUNCTORS_H
-#define DNN_LOCAL_SEARCH_FUNCTORS_H
+#ifndef HERA_WS_DNN_LOCAL_SEARCH_FUNCTORS_H
+#define HERA_WS_DNN_LOCAL_SEARCH_FUNCTORS_H
#include <boost/range/algorithm/heap_algorithm.hpp>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
@@ -28,7 +32,7 @@ struct NNRecord
typedef typename HandleDistance::PointHandle PointHandle;
typedef typename HandleDistance::DistanceType DistanceType;
- NNRecord() { result.d = std::numeric_limits<DistanceType>::infinity(); }
+ NNRecord() { result.d = std::numeric_limits<DistanceType>::max(); }
DistanceType operator()(PointHandle p, DistanceType d) { if (d < result.d) { result.p = p; result.d = d; } return result.d; }
HandleDistance result;
};
@@ -67,7 +71,7 @@ struct kNNRecord
result.push_back(HandleDistance(p,d));
boost::push_heap(result);
if (result.size() < k)
- return std::numeric_limits<DistanceType>::infinity();
+ return std::numeric_limits<DistanceType>::max();
} else if (d < result[0].d)
{
boost::pop_heap(result);
@@ -84,6 +88,8 @@ struct kNNRecord
HDContainer result;
};
-}
+} // dnn
+} // ws
+} // hera
#endif // DNN_LOCAL_SEARCH_FUNCTORS_H
diff --git a/geom_matching/wasserstein/include/dnn/parallel/tbb.h b/geom_matching/wasserstein/include/dnn/parallel/tbb.h
index 64c59e0..3f811d6 100644
--- a/geom_matching/wasserstein/include/dnn/parallel/tbb.h
+++ b/geom_matching/wasserstein/include/dnn/parallel/tbb.h
@@ -1,7 +1,6 @@
-#ifndef PARALLEL_H
-#define PARALLEL_H
+#ifndef HERA_WS_PARALLEL_H
+#define HERA_WS_PARALLEL_H
-//#include <iostream>
#include <vector>
#include <boost/range.hpp>
@@ -18,6 +17,10 @@
#include <boost/serialization/collections_load_imp.hpp>
#include <boost/serialization/collections_save_imp.hpp>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
using tbb::mutex;
@@ -87,7 +90,9 @@ namespace dnn
tbb::tick_count start;
};
-}
+} // dnn
+} // ws
+} // hera
// Serialization for tbb::concurrent_vector<...>
namespace boost
@@ -132,6 +137,10 @@ namespace boost
#include <map>
#include <boost/progress.hpp>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
template<class T>
@@ -207,14 +216,22 @@ namespace dnn
};
using boost::progress_timer;
-}
+} // dnn
+} // ws
+} // hera
#endif // TBB
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
template<class Range, class F>
void do_foreach(const Range& range, const F& f) { do_foreach(boost::begin(range), boost::end(range), f); }
-}
+} // dnn
+} // ws
+} // hera
#endif
diff --git a/geom_matching/wasserstein/include/dnn/parallel/utils.h b/geom_matching/wasserstein/include/dnn/parallel/utils.h
index ba73814..7104ec3 100644
--- a/geom_matching/wasserstein/include/dnn/parallel/utils.h
+++ b/geom_matching/wasserstein/include/dnn/parallel/utils.h
@@ -1,8 +1,12 @@
-#ifndef PARALLEL_UTILS_H
-#define PARALLEL_UTILS_H
+#ifndef HERA_WS_PARALLEL_UTILS_H
+#define HERA_WS_PARALLEL_UTILS_H
#include "../utils.h"
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
// Assumes rng is synchronized across ranks
@@ -15,11 +19,13 @@ namespace dnn
typedef decltype(data[0]) T;
shuffle(world, data, rng, [](T& x, T& y) { std::swap(x,y); });
}
-}
+} // dnn
+} // ws
+} // hera
template<class DataVector, class RNGType, class SwapFunctor>
void
-dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty)
+hera::ws::dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const SwapFunctor& swap, DataVector empty)
{
// This is not a perfect shuffle: it dishes out data in chunks of 1/size.
// (It can be interpreted as generating a bistochastic matrix by taking the
@@ -42,7 +48,7 @@ dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const Swa
RNGType local_rng(seed);
// Shuffle local data
- dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+ hera::ws::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
// Decide how much of our data goes to i-th processor
std::vector<size_t> out_counts(size);
@@ -50,7 +56,7 @@ dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const Swa
boost::counting_iterator<int>(size));
for (size_t i = 0; i < size; ++i)
{
- dnn::random_shuffle(ranks.begin(), ranks.end(), rng);
+ hera::ws::dnn::random_shuffle(ranks.begin(), ranks.end(), rng);
++out_counts[ranks[rank]];
}
@@ -87,7 +93,7 @@ dnn::shuffle(mpi::communicator& world, DataVector& data, RNGType& rng, const Swa
for(const DataVector& vec : incoming)
for (size_t i = 0; i < vec.size(); ++i)
data.push_back(vec[i]);
- dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
+ hera::ws::dnn::random_shuffle(data.begin(), data.end(), local_rng, swap);
// XXX: the final shuffle is irrelevant for our purposes. But it's also cheap.
}
diff --git a/geom_matching/wasserstein/include/dnn/utils.h b/geom_matching/wasserstein/include/dnn/utils.h
index 83c2865..bbce793 100644
--- a/geom_matching/wasserstein/include/dnn/utils.h
+++ b/geom_matching/wasserstein/include/dnn/utils.h
@@ -1,10 +1,14 @@
-#ifndef DNN_UTILS_H
-#define DNN_UTILS_H
+#ifndef HERA_WS_DNN_UTILS_H
+#define HERA_WS_DNN_UTILS_H
#include <boost/random/uniform_int.hpp>
#include <boost/foreach.hpp>
#include <boost/typeof/typeof.hpp>
+namespace hera
+{
+namespace ws
+{
namespace dnn
{
@@ -36,6 +40,8 @@ void random_shuffle(RandomIt first, RandomIt last, UniformRandomNumberGenerator&
random_shuffle(first, last, g, [](T& x, T& y) { std::swap(x,y); });
}
-}
+} // dnn
+} // ws
+} // hera
#endif
diff --git a/geom_matching/wasserstein/include/spdlog/async_logger.h b/geom_matching/wasserstein/include/spdlog/async_logger.h
new file mode 100644
index 0000000..9d7e08f
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/async_logger.h
@@ -0,0 +1,82 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Very fast asynchronous logger (millions of logs per second on an average desktop)
+// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
+// Creates a single back thread to pop messages from the queue and log them.
+//
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message
+// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
+// 3. will throw spdlog_ex upon log exceptions
+// Upon destruction, logs all remaining messages in the queue before destructing..
+
+#include "spdlog/common.h"
+#include "spdlog/logger.h"
+
+#include <chrono>
+#include <functional>
+#include <string>
+#include <memory>
+
+namespace spdlog
+{
+
+namespace details
+{
+class async_log_helper;
+}
+
+class async_logger SPDLOG_FINAL :public logger
+{
+public:
+ template<class It>
+ async_logger(const std::string& name,
+ const It& begin,
+ const It& end,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
+ const std::function<void()>& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
+
+ async_logger(const std::string& logger_name,
+ sinks_init_list sinks,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
+ const std::function<void()>& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
+
+ async_logger(const std::string& logger_name,
+ sink_ptr single_sink,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
+ const std::function<void()>& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
+
+ //Wait for the queue to be empty, and flush synchronously
+ //Warning: this can potentially last forever as we wait it to complete
+ void flush() override;
+
+ // Error handler
+ virtual void set_error_handler(log_err_handler) override;
+ virtual log_err_handler error_handler() override;
+
+protected:
+ void _sink_it(details::log_msg& msg) override;
+ void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
+ void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
+
+private:
+ std::unique_ptr<details::async_log_helper> _async_log_helper;
+};
+}
+
+
+#include "spdlog/details/async_logger_impl.h"
diff --git a/geom_matching/wasserstein/include/spdlog/common.h b/geom_matching/wasserstein/include/spdlog/common.h
new file mode 100644
index 0000000..7e352fa
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/common.h
@@ -0,0 +1,160 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include <string>
+#include <initializer_list>
+#include <chrono>
+#include <memory>
+#include <atomic>
+#include <exception>
+#include<functional>
+
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+#include <codecvt>
+#include <locale>
+#endif
+
+#include "spdlog/details/null_mutex.h"
+
+//visual studio upto 2013 does not support noexcept nor constexpr
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#define SPDLOG_NOEXCEPT throw()
+#define SPDLOG_CONSTEXPR
+#else
+#define SPDLOG_NOEXCEPT noexcept
+#define SPDLOG_CONSTEXPR constexpr
+#endif
+
+// See tweakme.h
+#if !defined(SPDLOG_FINAL)
+#define SPDLOG_FINAL
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define SPDLOG_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#define SPDLOG_DEPRECATED __declspec(deprecated)
+#else
+#define SPDLOG_DEPRECATED
+#endif
+
+
+#include "spdlog/fmt/fmt.h"
+
+namespace spdlog
+{
+
+class formatter;
+
+namespace sinks
+{
+class sink;
+}
+
+using log_clock = std::chrono::system_clock;
+using sink_ptr = std::shared_ptr < sinks::sink >;
+using sinks_init_list = std::initializer_list < sink_ptr >;
+using formatter_ptr = std::shared_ptr<spdlog::formatter>;
+#if defined(SPDLOG_NO_ATOMIC_LEVELS)
+using level_t = details::null_atomic_int;
+#else
+using level_t = std::atomic<int>;
+#endif
+
+using log_err_handler = std::function<void(const std::string &err_msg)>;
+
+//Log level enum
+namespace level
+{
+typedef enum
+{
+ trace = 0,
+ debug = 1,
+ info = 2,
+ warn = 3,
+ err = 4,
+ critical = 5,
+ off = 6
+} level_enum;
+
+#if !defined(SPDLOG_LEVEL_NAMES)
+#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" };
+#endif
+static const char* level_names[] SPDLOG_LEVEL_NAMES
+
+static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
+
+inline const char* to_str(spdlog::level::level_enum l)
+{
+ return level_names[l];
+}
+
+inline const char* to_short_str(spdlog::level::level_enum l)
+{
+ return short_level_names[l];
+}
+} //level
+
+
+//
+// Async overflow policy - block by default.
+//
+enum class async_overflow_policy
+{
+ block_retry, // Block / yield / sleep until message can be enqueued
+ discard_log_msg // Discard the message it enqueue fails
+};
+
+//
+// Pattern time - specific time getting to use for pattern_formatter.
+// local time by default
+//
+enum class pattern_time_type
+{
+ local, // log localtime
+ utc // log utc
+};
+
+//
+// Log exception
+//
+namespace details
+{
+namespace os
+{
+std::string errno_str(int err_num);
+}
+}
+class spdlog_ex: public std::exception
+{
+public:
+ spdlog_ex(const std::string& msg):_msg(msg)
+ {}
+ spdlog_ex(const std::string& msg, int last_errno)
+ {
+ _msg = msg + ": " + details::os::errno_str(last_errno);
+ }
+ const char* what() const SPDLOG_NOEXCEPT override
+ {
+ return _msg.c_str();
+ }
+private:
+ std::string _msg;
+
+};
+
+//
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+//
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+using filename_t = std::wstring;
+#else
+using filename_t = std::string;
+#endif
+
+
+} //spdlog
diff --git a/geom_matching/wasserstein/include/spdlog/details/async_log_helper.h b/geom_matching/wasserstein/include/spdlog/details/async_log_helper.h
new file mode 100644
index 0000000..6145dfa
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/async_log_helper.h
@@ -0,0 +1,399 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+// async log helper :
+// Process logs asynchronously using a back thread.
+//
+// If the internal queue of log messages reaches its max size,
+// then the client call will block until there is more room.
+//
+
+#pragma once
+
+#include "spdlog/common.h"
+#include "spdlog/sinks/sink.h"
+#include "spdlog/details/mpmc_bounded_q.h"
+#include "spdlog/details/log_msg.h"
+#include "spdlog/details/os.h"
+#include "spdlog/formatter.h"
+
+#include <chrono>
+#include <exception>
+#include <functional>
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace spdlog
+{
+namespace details
+{
+
+class async_log_helper
+{
+ // Async msg to move to/from the queue
+ // Movable only. should never be copied
+ enum class async_msg_type
+ {
+ log,
+ flush,
+ terminate
+ };
+ struct async_msg
+ {
+ std::string logger_name;
+ level::level_enum level;
+ log_clock::time_point time;
+ size_t thread_id;
+ std::string txt;
+ async_msg_type msg_type;
+ size_t msg_id;
+
+ async_msg() = default;
+ ~async_msg() = default;
+
+
+async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
+ logger_name(std::move(other.logger_name)),
+ level(std::move(other.level)),
+ time(std::move(other.time)),
+ thread_id(other.thread_id),
+ txt(std::move(other.txt)),
+ msg_type(std::move(other.msg_type)),
+ msg_id(other.msg_id)
+ {}
+
+ async_msg(async_msg_type m_type):
+ level(level::info),
+ thread_id(0),
+ msg_type(m_type),
+ msg_id(0)
+ {}
+
+ async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
+ {
+ logger_name = std::move(other.logger_name);
+ level = other.level;
+ time = std::move(other.time);
+ thread_id = other.thread_id;
+ txt = std::move(other.txt);
+ msg_type = other.msg_type;
+ msg_id = other.msg_id;
+ return *this;
+ }
+
+ // never copy or assign. should only be moved..
+ async_msg(const async_msg&) = delete;
+ async_msg& operator=(const async_msg& other) = delete;
+
+ // construct from log_msg
+ async_msg(const details::log_msg& m):
+ level(m.level),
+ time(m.time),
+ thread_id(m.thread_id),
+ txt(m.raw.data(), m.raw.size()),
+ msg_type(async_msg_type::log),
+ msg_id(m.msg_id)
+ {
+#ifndef SPDLOG_NO_NAME
+ logger_name = *m.logger_name;
+#endif
+ }
+
+
+ // copy into log_msg
+ void fill_log_msg(log_msg &msg)
+ {
+ msg.logger_name = &logger_name;
+ msg.level = level;
+ msg.time = time;
+ msg.thread_id = thread_id;
+ msg.raw << txt;
+ msg.msg_id = msg_id;
+ }
+ };
+
+public:
+
+ using item_type = async_msg;
+ using q_type = details::mpmc_bounded_queue<item_type>;
+
+ using clock = std::chrono::steady_clock;
+
+
+ async_log_helper(formatter_ptr formatter,
+ const std::vector<sink_ptr>& sinks,
+ size_t queue_size,
+ const log_err_handler err_handler,
+ const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
+ const std::function<void()>& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
+ const std::function<void()>& worker_teardown_cb = nullptr);
+
+ void log(const details::log_msg& msg);
+
+ // stop logging and join the back thread
+ ~async_log_helper();
+
+ void set_formatter(formatter_ptr);
+
+ void flush(bool wait_for_q);
+
+ void set_error_handler(spdlog::log_err_handler err_handler);
+
+private:
+ formatter_ptr _formatter;
+ std::vector<std::shared_ptr<sinks::sink>> _sinks;
+
+ // queue of messages to log
+ q_type _q;
+
+ log_err_handler _err_handler;
+
+ bool _flush_requested;
+
+ bool _terminate_requested;
+
+
+ // overflow policy
+ const async_overflow_policy _overflow_policy;
+
+ // worker thread warmup callback - one can set thread priority, affinity, etc
+ const std::function<void()> _worker_warmup_cb;
+
+ // auto periodic sink flush parameter
+ const std::chrono::milliseconds _flush_interval_ms;
+
+ // worker thread teardown callback
+ const std::function<void()> _worker_teardown_cb;
+
+ // worker thread
+ std::thread _worker_thread;
+
+ void push_msg(async_msg&& new_msg);
+
+ // worker thread main loop
+ void worker_loop();
+
+ // pop next message from the queue and process it. will set the last_pop to the pop time
+ // return false if termination of the queue is required
+ bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
+
+ void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
+
+ // sleep,yield or return immediately using the time passed since last message as a hint
+ static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
+
+ // wait until the queue is empty
+ void wait_empty_q();
+
+};
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// async_sink class implementation
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::details::async_log_helper::async_log_helper(
+ formatter_ptr formatter,
+ const std::vector<sink_ptr>& sinks,
+ size_t queue_size,
+ log_err_handler err_handler,
+ const async_overflow_policy overflow_policy,
+ const std::function<void()>& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb):
+ _formatter(formatter),
+ _sinks(sinks),
+ _q(queue_size),
+ _err_handler(err_handler),
+ _flush_requested(false),
+ _terminate_requested(false),
+ _overflow_policy(overflow_policy),
+ _worker_warmup_cb(worker_warmup_cb),
+ _flush_interval_ms(flush_interval_ms),
+ _worker_teardown_cb(worker_teardown_cb),
+ _worker_thread(&async_log_helper::worker_loop, this)
+{}
+
+// Send to the worker thread termination message(level=off)
+// and wait for it to finish gracefully
+inline spdlog::details::async_log_helper::~async_log_helper()
+{
+ try
+ {
+ push_msg(async_msg(async_msg_type::terminate));
+ _worker_thread.join();
+ }
+ catch (...) // don't crash in destructor
+ {
+ }
+}
+
+
+//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
+inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
+{
+ push_msg(async_msg(msg));
+}
+
+inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
+{
+ if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
+ {
+ auto last_op_time = details::os::now();
+ auto now = last_op_time;
+ do
+ {
+ now = details::os::now();
+ sleep_or_yield(now, last_op_time);
+ }
+ while (!_q.enqueue(std::move(new_msg)));
+ }
+}
+
+// optionally wait for the queue be empty and request flush from the sinks
+inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
+{
+ push_msg(async_msg(async_msg_type::flush));
+ if (wait_for_q)
+ wait_empty_q(); //return only make after the above flush message was processed
+}
+
+inline void spdlog::details::async_log_helper::worker_loop()
+{
+ if (_worker_warmup_cb) _worker_warmup_cb();
+ auto last_pop = details::os::now();
+ auto last_flush = last_pop;
+ auto active = true;
+ while (active)
+ {
+ try
+ {
+ active = process_next_msg(last_pop, last_flush);
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception");
+ }
+ }
+ if (_worker_teardown_cb) _worker_teardown_cb();
+
+
+}
+
+// process next message in the queue
+// return true if this thread should still be active (while no terminate msg was received)
+inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
+{
+ async_msg incoming_async_msg;
+
+ if (_q.dequeue(incoming_async_msg))
+ {
+ last_pop = details::os::now();
+ switch (incoming_async_msg.msg_type)
+ {
+ case async_msg_type::flush:
+ _flush_requested = true;
+ break;
+
+ case async_msg_type::terminate:
+ _flush_requested = true;
+ _terminate_requested = true;
+ break;
+
+ default:
+ log_msg incoming_log_msg;
+ incoming_async_msg.fill_log_msg(incoming_log_msg);
+ _formatter->format(incoming_log_msg);
+ for (auto &s : _sinks)
+ {
+ if (s->should_log(incoming_log_msg.level))
+ {
+ s->log(incoming_log_msg);
+ }
+ }
+ }
+ return true;
+ }
+
+ // Handle empty queue..
+ // This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
+ else
+ {
+ auto now = details::os::now();
+ handle_flush_interval(now, last_flush);
+ sleep_or_yield(now, last_pop);
+ return !_terminate_requested;
+ }
+}
+
+// flush all sinks if _flush_interval_ms has expired
+inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
+{
+ auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
+ if (should_flush)
+ {
+ for (auto &s : _sinks)
+ s->flush();
+ now = last_flush = details::os::now();
+ _flush_requested = false;
+ }
+}
+
+inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
+{
+ _formatter = msg_formatter;
+}
+
+
+// spin, yield or sleep. use the time passed since last message as a hint
+inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
+{
+ using namespace std::this_thread;
+ using std::chrono::milliseconds;
+ using std::chrono::microseconds;
+
+ auto time_since_op = now - last_op_time;
+
+ // spin upto 50 micros
+ if (time_since_op <= microseconds(50))
+ return;
+
+ // yield upto 150 micros
+ if (time_since_op <= microseconds(100))
+ return std::this_thread::yield();
+
+ // sleep for 20 ms upto 200 ms
+ if (time_since_op <= milliseconds(200))
+ return sleep_for(milliseconds(20));
+
+ // sleep for 200 ms
+ return sleep_for(milliseconds(200));
+}
+
+// wait for the queue to be empty
+inline void spdlog::details::async_log_helper::wait_empty_q()
+{
+ auto last_op = details::os::now();
+ while (_q.approx_size() > 0)
+ {
+ sleep_or_yield(details::os::now(), last_op);
+ }
+}
+
+inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = err_handler;
+}
+
+
+
diff --git a/geom_matching/wasserstein/include/spdlog/details/async_logger_impl.h b/geom_matching/wasserstein/include/spdlog/details/async_logger_impl.h
new file mode 100644
index 0000000..33486c2
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/async_logger_impl.h
@@ -0,0 +1,105 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Async Logger implementation
+// Use an async_sink (queue per logger) to perform the logging in a worker thread
+
+#include "spdlog/details/async_log_helper.h"
+#include "spdlog/async_logger.h"
+
+#include <string>
+#include <functional>
+#include <chrono>
+#include <memory>
+
+template<class It>
+inline spdlog::async_logger::async_logger(const std::string& logger_name,
+ const It& begin,
+ const It& end,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy,
+ const std::function<void()>& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
+ logger(logger_name, begin, end),
+ _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
+{
+}
+
+inline spdlog::async_logger::async_logger(const std::string& logger_name,
+ sinks_init_list sinks_list,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy,
+ const std::function<void()>& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
+ async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
+
+inline spdlog::async_logger::async_logger(const std::string& logger_name,
+ sink_ptr single_sink,
+ size_t queue_size,
+ const async_overflow_policy overflow_policy,
+ const std::function<void()>& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms,
+ const std::function<void()>& worker_teardown_cb) :
+ async_logger(logger_name,
+{
+ single_sink
+}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
+
+
+inline void spdlog::async_logger::flush()
+{
+ _async_log_helper->flush(true);
+}
+
+// Error handler
+inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = err_handler;
+ _async_log_helper->set_error_handler(err_handler);
+
+}
+inline spdlog::log_err_handler spdlog::async_logger::error_handler()
+{
+ return _err_handler;
+}
+
+
+inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
+{
+ _formatter = msg_formatter;
+ _async_log_helper->set_formatter(_formatter);
+}
+
+inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
+{
+ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
+ _async_log_helper->set_formatter(_formatter);
+}
+
+
+inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
+{
+ try
+ {
+#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
+ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
+#endif
+ _async_log_helper->log(msg);
+ if (_should_flush_on(msg))
+ _async_log_helper->flush(false); // do async flush
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception");
+ }
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/file_helper.h b/geom_matching/wasserstein/include/spdlog/details/file_helper.h
new file mode 100644
index 0000000..d0d730e
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/file_helper.h
@@ -0,0 +1,117 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Helper class for file sink
+// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
+// Throw spdlog_ex exception on errors
+
+#include "spdlog/details/os.h"
+#include "spdlog/details/log_msg.h"
+
+#include <chrono>
+#include <cstdio>
+#include <string>
+#include <thread>
+#include <cerrno>
+
+namespace spdlog
+{
+namespace details
+{
+
+class file_helper
+{
+
+public:
+ const int open_tries = 5;
+ const int open_interval = 10;
+
+ explicit file_helper() :
+ _fd(nullptr)
+ {}
+
+ file_helper(const file_helper&) = delete;
+ file_helper& operator=(const file_helper&) = delete;
+
+ ~file_helper()
+ {
+ close();
+ }
+
+
+ void open(const filename_t& fname, bool truncate = false)
+ {
+
+ close();
+ auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
+ _filename = fname;
+ for (int tries = 0; tries < open_tries; ++tries)
+ {
+ if (!os::fopen_s(&_fd, fname, mode))
+ return;
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
+ }
+
+ throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
+ }
+
+ void reopen(bool truncate)
+ {
+ if (_filename.empty())
+ throw spdlog_ex("Failed re opening file - was not opened before");
+ open(_filename, truncate);
+
+ }
+
+ void flush()
+ {
+ std::fflush(_fd);
+ }
+
+ void close()
+ {
+ if (_fd)
+ {
+ std::fclose(_fd);
+ _fd = nullptr;
+ }
+ }
+
+ void write(const log_msg& msg)
+ {
+
+ size_t msg_size = msg.formatted.size();
+ auto data = msg.formatted.data();
+ if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
+ throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
+ }
+
+ size_t size()
+ {
+ if (!_fd)
+ throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
+ return os::filesize(_fd);
+ }
+
+ const filename_t& filename() const
+ {
+ return _filename;
+ }
+
+ static bool file_exists(const filename_t& name)
+ {
+
+ return os::file_exists(name);
+ }
+
+private:
+ FILE* _fd;
+ filename_t _filename;
+};
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/log_msg.h b/geom_matching/wasserstein/include/spdlog/details/log_msg.h
new file mode 100644
index 0000000..0d7ce4b
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/log_msg.h
@@ -0,0 +1,50 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/common.h"
+#include "spdlog/details/os.h"
+
+
+#include <string>
+#include <utility>
+
+namespace spdlog
+{
+namespace details
+{
+struct log_msg
+{
+ log_msg() = default;
+ log_msg(const std::string *loggers_name, level::level_enum lvl) :
+ logger_name(loggers_name),
+ level(lvl),
+ msg_id(0)
+ {
+#ifndef SPDLOG_NO_DATETIME
+ time = os::now();
+#endif
+
+#ifndef SPDLOG_NO_THREAD_ID
+ thread_id = os::thread_id();
+#endif
+ }
+
+ log_msg(const log_msg& other) = delete;
+ log_msg& operator=(log_msg&& other) = delete;
+ log_msg(log_msg&& other) = delete;
+
+
+ const std::string *logger_name;
+ level::level_enum level;
+ log_clock::time_point time;
+ size_t thread_id;
+ fmt::MemoryWriter raw;
+ fmt::MemoryWriter formatted;
+ size_t msg_id;
+};
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/logger_impl.h b/geom_matching/wasserstein/include/spdlog/details/logger_impl.h
new file mode 100644
index 0000000..79c4ef6
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/logger_impl.h
@@ -0,0 +1,563 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/logger.h"
+#include "spdlog/sinks/stdout_sinks.h"
+
+#include <memory>
+#include <string>
+
+
+// create logger with given name, sinks and the default pattern formatter
+// all other ctors will call this one
+template<class It>
+inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
+ _name(logger_name),
+ _sinks(begin, end),
+ _formatter(std::make_shared<pattern_formatter>("%+")),
+ _level(level::info),
+ _flush_level(level::off),
+ _last_err_time(0),
+ _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
+{
+ _err_handler = [this](const std::string &msg)
+ {
+ this->_default_err_handler(msg);
+ };
+}
+
+// ctor with sinks as init list
+inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
+ logger(logger_name, sinks_list.begin(), sinks_list.end())
+{}
+
+
+// ctor with single sink
+inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
+ logger(logger_name,
+{
+ single_sink
+})
+{}
+
+
+inline spdlog::logger::~logger() = default;
+
+
+inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
+{
+ _set_formatter(msg_formatter);
+}
+
+inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
+{
+ _set_pattern(pattern, pattern_time);
+}
+
+
+template <typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
+{
+ if (!should_log(lvl)) return;
+
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+ log_msg.raw.write(fmt, args...);
+ _sink_it(log_msg);
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception");
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
+{
+ if (!should_log(lvl)) return;
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+ log_msg.raw << msg;
+ _sink_it(log_msg);
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception");
+ }
+
+}
+
+template<typename T>
+inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
+{
+ if (!should_log(lvl)) return;
+ try
+ {
+ details::log_msg log_msg(&_name, lvl);
+ log_msg.raw << msg;
+ _sink_it(log_msg);
+ }
+ catch (const std::exception &ex)
+ {
+ _err_handler(ex.what());
+ }
+ catch (...)
+ {
+ _err_handler("Unknown exception");
+ }
+}
+
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::trace, fmt, arg1, args...);
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::debug, fmt, arg1, args...);
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::info, fmt, arg1, args...);
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::warn, fmt, arg1, args...);
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::err, fmt, arg1, args...);
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ log(level::critical, fmt, arg1, args...);
+}
+
+template <typename... Args>
+inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg)
+{
+ if (flag)
+ {
+ log(lvl, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg)
+{
+ if (flag)
+ {
+ log(lvl, msg);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::trace, fmt, arg1, args...);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::debug, fmt, arg1, args...);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::info, fmt, arg1, args...);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::warn, fmt, arg1, args...);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::err, fmt, arg1, args...);
+ }
+}
+
+template <typename Arg1, typename... Args>
+inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::critical, fmt, arg1, args...);
+ }
+}
+
+
+template<typename T>
+inline void spdlog::logger::trace(const T& msg)
+{
+ log(level::trace, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::debug(const T& msg)
+{
+ log(level::debug, msg);
+}
+
+
+template<typename T>
+inline void spdlog::logger::info(const T& msg)
+{
+ log(level::info, msg);
+}
+
+
+template<typename T>
+inline void spdlog::logger::warn(const T& msg)
+{
+ log(level::warn, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::error(const T& msg)
+{
+ log(level::err, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::critical(const T& msg)
+{
+ log(level::critical, msg);
+}
+
+template<typename T>
+inline void spdlog::logger::trace_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::trace, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::debug_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::debug, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::info_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::info, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::warn_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::warn, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::error_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::err, msg);
+ }
+}
+
+template<typename T>
+inline void spdlog::logger::critical_if(const bool flag, const T& msg)
+{
+ if (flag)
+ {
+ log(level::critical, msg);
+ }
+}
+
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+#include <codecvt>
+
+template <typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
+
+ log(lvl, conv.to_bytes(msg));
+}
+
+template <typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args)
+{
+ fmt::WMemoryWriter wWriter;
+
+ wWriter.write(fmt, args...);
+ log(lvl, wWriter.c_str());
+}
+
+template <typename... Args>
+inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
+{
+ log(level::trace, fmt, args...);
+}
+
+template <typename... Args>
+inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args)
+{
+ log(level::debug, fmt, args...);
+}
+
+template <typename... Args>
+inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
+{
+ log(level::info, fmt, args...);
+}
+
+
+template <typename... Args>
+inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
+{
+ log(level::warn, fmt, args...);
+}
+
+template <typename... Args>
+inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args)
+{
+ log(level::err, fmt, args...);
+}
+
+template <typename... Args>
+inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
+{
+ log(level::critical, fmt, args...);
+}
+
+//
+// conditional logging
+//
+
+template <typename... Args>
+inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
+{
+ if (flag)
+ {
+ log(lvl, msg);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(lvl, fmt, args);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::trace, fmt, args...);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::debug, fmt, args...);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::info, fmt, args...);
+ }
+}
+
+
+template <typename... Args>
+inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::warn, fmt, args...);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::err, fmt, args...);
+ }
+}
+
+template <typename... Args>
+inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
+{
+ if (flag)
+ {
+ log(level::critical, fmt, args...);
+ }
+}
+
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+
+
+//
+// name and level
+//
+inline const std::string& spdlog::logger::name() const
+{
+ return _name;
+}
+
+inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
+{
+ _level.store(log_level);
+}
+
+inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
+{
+ _err_handler = err_handler;
+}
+
+inline spdlog::log_err_handler spdlog::logger::error_handler()
+{
+ return _err_handler;
+}
+
+
+inline void spdlog::logger::flush_on(level::level_enum log_level)
+{
+ _flush_level.store(log_level);
+}
+
+inline spdlog::level::level_enum spdlog::logger::level() const
+{
+ return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
+}
+
+inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
+{
+ return msg_level >= _level.load(std::memory_order_relaxed);
+}
+
+//
+// protected virtual called at end of each user log call (if enabled) by the line_logger
+//
+inline void spdlog::logger::_sink_it(details::log_msg& msg)
+{
+#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
+ msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
+#endif
+ _formatter->format(msg);
+ for (auto &sink : _sinks)
+ {
+ if( sink->should_log( msg.level))
+ {
+ sink->log(msg);
+ }
+ }
+
+ if(_should_flush_on(msg))
+ flush();
+}
+
+inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
+{
+ _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
+}
+inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
+{
+ _formatter = msg_formatter;
+}
+
+inline void spdlog::logger::flush()
+{
+ for (auto& sink : _sinks)
+ sink->flush();
+}
+
+inline void spdlog::logger::_default_err_handler(const std::string &msg)
+{
+ auto now = time(nullptr);
+ if (now - _last_err_time < 60)
+ return;
+ auto tm_time = details::os::localtime(now);
+ char date_buf[100];
+ std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
+ details::log_msg err_msg;
+ err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
+ sinks::stderr_sink_mt::instance()->log(err_msg);
+ _last_err_time = now;
+}
+
+inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
+{
+ const auto flush_level = _flush_level.load(std::memory_order_relaxed);
+ return (msg.level >= flush_level) && (msg.level != level::off);
+}
+
+inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
+{
+ return _sinks;
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/mpmc_bounded_q.h b/geom_matching/wasserstein/include/spdlog/details/mpmc_bounded_q.h
new file mode 100644
index 0000000..afd4c88
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/mpmc_bounded_q.h
@@ -0,0 +1,172 @@
+/*
+A modified version of Bounded MPMC queue by Dmitry Vyukov.
+
+Original code from:
+http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
+
+licensed by Dmitry Vyukov under the terms below:
+
+Simplified BSD license
+
+Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
+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 DMITRY VYUKOV "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 DMITRY VYUKOV 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.
+
+The views and conclusions contained in the software and documentation are those of the authors and
+should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
+*/
+
+/*
+The code in its current form adds the license below:
+
+Copyright(c) 2015 Gabi Melman.
+Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+*/
+
+#pragma once
+
+#include "spdlog/common.h"
+
+#include <atomic>
+#include <utility>
+
+namespace spdlog
+{
+namespace details
+{
+
+template<typename T>
+class mpmc_bounded_queue
+{
+public:
+
+ using item_type = T;
+ mpmc_bounded_queue(size_t buffer_size)
+ :max_size_(buffer_size),
+ buffer_(new cell_t [buffer_size]),
+ buffer_mask_(buffer_size - 1)
+ {
+ //queue size must be power of two
+ if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
+ throw spdlog_ex("async logger queue size must be power of two");
+
+ for (size_t i = 0; i != buffer_size; i += 1)
+ buffer_[i].sequence_.store(i, std::memory_order_relaxed);
+ enqueue_pos_.store(0, std::memory_order_relaxed);
+ dequeue_pos_.store(0, std::memory_order_relaxed);
+ }
+
+ ~mpmc_bounded_queue()
+ {
+ delete [] buffer_;
+ }
+
+
+ bool enqueue(T&& data)
+ {
+ cell_t* cell;
+ size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
+ for (;;)
+ {
+ cell = &buffer_[pos & buffer_mask_];
+ size_t seq = cell->sequence_.load(std::memory_order_acquire);
+ intptr_t dif = (intptr_t)seq - (intptr_t)pos;
+ if (dif == 0)
+ {
+ if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
+ break;
+ }
+ else if (dif < 0)
+ {
+ return false;
+ }
+ else
+ {
+ pos = enqueue_pos_.load(std::memory_order_relaxed);
+ }
+ }
+ cell->data_ = std::move(data);
+ cell->sequence_.store(pos + 1, std::memory_order_release);
+ return true;
+ }
+
+ bool dequeue(T& data)
+ {
+ cell_t* cell;
+ size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
+ for (;;)
+ {
+ cell = &buffer_[pos & buffer_mask_];
+ size_t seq =
+ cell->sequence_.load(std::memory_order_acquire);
+ intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
+ if (dif == 0)
+ {
+ if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
+ break;
+ }
+ else if (dif < 0)
+ return false;
+ else
+ pos = dequeue_pos_.load(std::memory_order_relaxed);
+ }
+ data = std::move(cell->data_);
+ cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
+ return true;
+ }
+
+ size_t approx_size()
+ {
+ size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
+ size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
+ if (last_pos <= first_pos)
+ return 0;
+ auto size = last_pos - first_pos;
+ return size < max_size_ ? size : max_size_;
+ }
+
+private:
+ struct cell_t
+ {
+ std::atomic<size_t> sequence_;
+ T data_;
+ };
+
+ size_t const max_size_;
+
+ static size_t const cacheline_size = 64;
+ typedef char cacheline_pad_t [cacheline_size];
+
+ cacheline_pad_t pad0_;
+ cell_t* const buffer_;
+ size_t const buffer_mask_;
+ cacheline_pad_t pad1_;
+ std::atomic<size_t> enqueue_pos_;
+ cacheline_pad_t pad2_;
+ std::atomic<size_t> dequeue_pos_;
+ cacheline_pad_t pad3_;
+
+ mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
+ void operator= (mpmc_bounded_queue const&) = delete;
+};
+
+} // ns details
+} // ns spdlog
diff --git a/geom_matching/wasserstein/include/spdlog/details/null_mutex.h b/geom_matching/wasserstein/include/spdlog/details/null_mutex.h
new file mode 100644
index 0000000..67b0aee
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/null_mutex.h
@@ -0,0 +1,45 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include <atomic>
+// null, no cost dummy "mutex" and dummy "atomic" int
+
+namespace spdlog
+{
+namespace details
+{
+struct null_mutex
+{
+ void lock() {}
+ void unlock() {}
+ bool try_lock()
+ {
+ return true;
+ }
+};
+
+struct null_atomic_int
+{
+ int value;
+ null_atomic_int() = default;
+
+ null_atomic_int(int val):value(val)
+ {}
+
+ int load(std::memory_order) const
+ {
+ return value;
+ }
+
+ void store(int val)
+ {
+ value = val;
+ }
+};
+
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/os.h b/geom_matching/wasserstein/include/spdlog/details/os.h
new file mode 100644
index 0000000..735f601
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/os.h
@@ -0,0 +1,469 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+#pragma once
+
+#include "spdlog/common.h"
+
+#include <cstdio>
+#include <ctime>
+#include <functional>
+#include <string>
+#include <chrono>
+#include <thread>
+#include <algorithm>
+#include <cstring>
+#include <cstdlib>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+
+#ifndef NOMINMAX
+#define NOMINMAX //prevent windows redefining min/max
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <process.h> // _get_pid support
+#include <io.h> // _get_osfhandle and _isatty support
+
+#ifdef __MINGW32__
+#include <share.h>
+#endif
+
+#else // unix
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef __linux__
+#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
+
+#elif __FreeBSD__
+#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
+#endif
+
+#endif //unix
+
+#ifndef __has_feature // Clang - feature checking macros.
+#define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+
+namespace spdlog
+{
+namespace details
+{
+namespace os
+{
+
+inline spdlog::log_clock::time_point now()
+{
+
+#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+ return std::chrono::time_point<log_clock, typename log_clock::duration>(
+ std::chrono::duration_cast<typename log_clock::duration>(
+ std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
+
+
+#else
+ return log_clock::now();
+#endif
+
+}
+inline std::tm localtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ localtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ localtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm localtime()
+{
+ std::time_t now_t = time(nullptr);
+ return localtime(now_t);
+}
+
+
+inline std::tm gmtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ gmtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ gmtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm gmtime()
+{
+ std::time_t now_t = time(nullptr);
+ return gmtime(now_t);
+}
+inline bool operator==(const std::tm& tm1, const std::tm& tm2)
+{
+ return (tm1.tm_sec == tm2.tm_sec &&
+ tm1.tm_min == tm2.tm_min &&
+ tm1.tm_hour == tm2.tm_hour &&
+ tm1.tm_mday == tm2.tm_mday &&
+ tm1.tm_mon == tm2.tm_mon &&
+ tm1.tm_year == tm2.tm_year &&
+ tm1.tm_isdst == tm2.tm_isdst);
+}
+
+inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
+{
+ return !(tm1 == tm2);
+}
+
+// eol definition
+#if !defined (SPDLOG_EOL)
+#ifdef _WIN32
+#define SPDLOG_EOL "\r\n"
+#else
+#define SPDLOG_EOL "\n"
+#endif
+#endif
+
+SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
+SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
+
+inline void prevent_child_fd(FILE *f)
+{
+#ifdef _WIN32
+ auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
+ if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
+ throw spdlog_ex("SetHandleInformation failed", errno);
+#else
+ auto fd = fileno(f);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
+#endif
+}
+
+
+//fopen_s on non windows for writing
+inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
+{
+#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+ *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#else
+ *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
+#endif
+#else //unix
+ *fp = fopen((filename.c_str()), mode.c_str());
+#endif
+
+#ifdef SPDLOG_PREVENT_CHILD_FD
+ if (*fp != nullptr)
+ prevent_child_fd(*fp);
+#endif
+ return *fp == nullptr;
+}
+
+
+inline int remove(const filename_t &filename)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wremove(filename.c_str());
+#else
+ return std::remove(filename.c_str());
+#endif
+}
+
+inline int rename(const filename_t& filename1, const filename_t& filename2)
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return _wrename(filename1.c_str(), filename2.c_str());
+#else
+ return std::rename(filename1.c_str(), filename2.c_str());
+#endif
+}
+
+
+//Return if file exists
+inline bool file_exists(const filename_t& filename)
+{
+#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+ auto attribs = GetFileAttributesW(filename.c_str());
+#else
+ auto attribs = GetFileAttributesA(filename.c_str());
+#endif
+ return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
+#else //common linux/unix all have the stat system call
+ struct stat buffer;
+ return (stat(filename.c_str(), &buffer) == 0);
+#endif
+}
+
+
+
+
+//Return file size according to open FILE* object
+inline size_t filesize(FILE *f)
+{
+ if (f == nullptr)
+ throw spdlog_ex("Failed getting file size. fd is null");
+#ifdef _WIN32
+ int fd = _fileno(f);
+#if _WIN64 //64 bits
+ struct _stat64 st;
+ if (_fstat64(fd, &st) == 0)
+ return st.st_size;
+
+#else //windows 32 bits
+ long ret = _filelength(fd);
+ if (ret >= 0)
+ return static_cast<size_t>(ret);
+#endif
+
+#else // unix
+ int fd = fileno(f);
+ //64 bits(but not in osx, where fstat64 is deprecated)
+#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
+ struct stat64 st;
+ if (fstat64(fd, &st) == 0)
+ return static_cast<size_t>(st.st_size);
+#else // unix 32 bits or osx
+ struct stat st;
+ if (fstat(fd, &st) == 0)
+ return static_cast<size_t>(st.st_size);
+#endif
+#endif
+ throw spdlog_ex("Failed getting file size from fd", errno);
+}
+
+
+
+
+//Return utc offset in minutes or throw spdlog_ex on failure
+inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
+{
+
+#ifdef _WIN32
+#if _WIN32_WINNT < _WIN32_WINNT_WS08
+ TIME_ZONE_INFORMATION tzinfo;
+ auto rv = GetTimeZoneInformation(&tzinfo);
+#else
+ DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+ auto rv = GetDynamicTimeZoneInformation(&tzinfo);
+#endif
+ if (rv == TIME_ZONE_ID_INVALID)
+ throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
+
+ int offset = -tzinfo.Bias;
+ if (tm.tm_isdst)
+ offset -= tzinfo.DaylightBias;
+ else
+ offset -= tzinfo.StandardBias;
+ return offset;
+#else
+
+#if defined(sun) || defined(__sun)
+ // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
+ struct helper
+ {
+ static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
+ {
+ int local_year = localtm.tm_year + (1900 - 1);
+ int gmt_year = gmtm.tm_year + (1900 - 1);
+
+ long int days = (
+ // difference in day of year
+ localtm.tm_yday - gmtm.tm_yday
+
+ // + intervening leap days
+ + ((local_year >> 2) - (gmt_year >> 2))
+ - (local_year / 100 - gmt_year / 100)
+ + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+
+ // + difference in years * 365 */
+ + (long int)(local_year - gmt_year) * 365
+ );
+
+ long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
+ long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
+ long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
+
+ return secs;
+ }
+ };
+
+ long int offset_seconds = helper::calculate_gmt_offset(tm);
+#else
+ long int offset_seconds = tm.tm_gmtoff;
+#endif
+
+ return static_cast<int>(offset_seconds / 60);
+#endif
+}
+
+//Return current thread id as size_t
+//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
+inline size_t _thread_id()
+{
+#ifdef _WIN32
+ return static_cast<size_t>(::GetCurrentThreadId());
+#elif __linux__
+# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
+# define SYS_gettid __NR_gettid
+# endif
+ return static_cast<size_t>(syscall(SYS_gettid));
+#elif __FreeBSD__
+ long tid;
+ thr_self(&tid);
+ return static_cast<size_t>(tid);
+#elif __APPLE__
+ uint64_t tid;
+ pthread_threadid_np(nullptr, &tid);
+ return static_cast<size_t>(tid);
+#else //Default to standard C++11 (other Unix)
+ return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
+#endif
+}
+
+//Return current thread id as size_t (from thread local storage)
+inline size_t thread_id()
+{
+#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
+ return _thread_id();
+#else
+ static thread_local const size_t tid = _thread_id();
+ return tid;
+#endif
+}
+
+
+
+
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+#define SPDLOG_FILENAME_T(s) L ## s
+inline std::string filename_to_str(const filename_t& filename)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
+ return c.to_bytes(filename);
+}
+#else
+#define SPDLOG_FILENAME_T(s) s
+inline std::string filename_to_str(const filename_t& filename)
+{
+ return filename;
+}
+#endif
+
+inline std::string errno_to_string(char[256], char* res)
+{
+ return std::string(res);
+}
+
+inline std::string errno_to_string(char buf[256], int res)
+{
+ if (res == 0)
+ {
+ return std::string(buf);
+ }
+ else
+ {
+ return "Unknown error";
+ }
+}
+
+// Return errno string (thread safe)
+inline std::string errno_str(int err_num)
+{
+ char buf[256];
+ SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
+
+#ifdef _WIN32
+ if (strerror_s(buf, buf_size, err_num) == 0)
+ return std::string(buf);
+ else
+ return "Unknown error";
+
+#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
+ ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
+
+ if (strerror_r(err_num, buf, buf_size) == 0)
+ return std::string(buf);
+ else
+ return "Unknown error";
+
+#else // gnu version (might not use the given buf, so its retval pointer must be used)
+ auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
+ return errno_to_string(buf, err); // use overloading to select correct stringify function
+#endif
+}
+
+inline int pid()
+{
+
+#ifdef _WIN32
+ return ::_getpid();
+#else
+ return static_cast<int>(::getpid());
+#endif
+
+}
+
+
+// Detrmine if the terminal supports colors
+// Source: https://github.com/agauniyal/rang/
+inline bool is_color_terminal()
+{
+#ifdef _WIN32
+ return true;
+#else
+ static constexpr const char* Terms[] =
+ {
+ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
+ "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
+ };
+
+ const char *env_p = std::getenv("TERM");
+ if (env_p == nullptr)
+ {
+ return false;
+ }
+
+ static const bool result = std::any_of(
+ std::begin(Terms), std::end(Terms), [&](const char* term)
+ {
+ return std::strstr(env_p, term) != nullptr;
+ });
+ return result;
+#endif
+}
+
+
+// Detrmine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+inline bool in_terminal(FILE* file)
+{
+
+#ifdef _WIN32
+ return _isatty(_fileno(file)) ? true : false;
+#else
+ return isatty(fileno(file)) ? true : false;
+#endif
+}
+} //os
+} //details
+} //spdlog
diff --git a/geom_matching/wasserstein/include/spdlog/details/pattern_formatter_impl.h b/geom_matching/wasserstein/include/spdlog/details/pattern_formatter_impl.h
new file mode 100644
index 0000000..c1ce719
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/pattern_formatter_impl.h
@@ -0,0 +1,690 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/formatter.h"
+#include "spdlog/details/log_msg.h"
+#include "spdlog/details/os.h"
+#include "spdlog/fmt/fmt.h"
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+#include <array>
+
+namespace spdlog
+{
+namespace details
+{
+class flag_formatter
+{
+public:
+ virtual ~flag_formatter()
+ {}
+ virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////
+// name & level pattern appenders
+///////////////////////////////////////////////////////////////////////
+namespace
+{
+class name_formatter:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << *msg.logger_name;
+ }
+};
+}
+
+// log level appender
+class level_formatter:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << level::to_str(msg.level);
+ }
+};
+
+// short log level appender
+class short_level_formatter:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << level::to_short_str(msg.level);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////
+// Date time pattern appenders
+///////////////////////////////////////////////////////////////////////
+
+static const char* ampm(const tm& t)
+{
+ return t.tm_hour >= 12 ? "PM" : "AM";
+}
+
+static int to12h(const tm& t)
+{
+ return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
+}
+
+//Abbreviated weekday name
+using days_array = std::array<std::string, 7>;
+static const days_array& days()
+{
+ static const days_array arr{ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } };
+ return arr;
+}
+class a_formatter:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << days()[tm_time.tm_wday];
+ }
+};
+// message counter formatter
+class i_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << '#' << msg.msg_id;
+ }
+};
+//Full weekday name
+static const days_array& full_days()
+{
+ static const days_array arr{ { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } };
+ return arr;
+}
+class A_formatter SPDLOG_FINAL :public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << full_days()[tm_time.tm_wday];
+ }
+};
+
+//Abbreviated month
+using months_array = std::array<std::string, 12>;
+static const months_array& months()
+{
+ static const months_array arr{ { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" } };
+ return arr;
+}
+class b_formatter:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << months()[tm_time.tm_mon];
+ }
+};
+
+//Full month name
+static const months_array& full_months()
+{
+ static const months_array arr{ { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } };
+ return arr;
+}
+class B_formatter SPDLOG_FINAL :public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << full_months()[tm_time.tm_mon];
+ }
+};
+
+
+//write 2 ints seperated by sep with padding of 2
+static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
+{
+ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
+ return w;
+}
+
+//write 3 ints seperated by sep with padding of 2
+static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
+{
+ w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
+ return w;
+}
+
+
+//Date and time representation (Thu Aug 23 15:35:46 2014)
+class c_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << days()[tm_time.tm_wday] << ' ' << months()[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
+ }
+};
+
+
+// year - 2 digit
+class C_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
+ }
+};
+
+
+
+// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
+class D_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
+ }
+};
+
+
+// year - 4 digit
+class Y_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << tm_time.tm_year + 1900;
+ }
+};
+
+// month 1-12
+class m_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
+ }
+};
+
+// day of month 1-31
+class d_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
+ }
+};
+
+// hours in 24 format 0-23
+class H_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
+ }
+};
+
+// hours in 12 format 1-12
+class I_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
+ }
+};
+
+// minutes 0-59
+class M_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
+ }
+};
+
+// seconds 0-59
+class S_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
+ }
+};
+
+// milliseconds
+class e_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+ msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
+ }
+};
+
+// microseconds
+class f_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
+ msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
+ }
+};
+
+// nanoseconds
+class F_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
+ msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
+ }
+};
+
+// AM/PM
+class p_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ msg.formatted << ampm(tm_time);
+ }
+};
+
+
+// 12 hour clock 02:55:02 pm
+class r_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
+ }
+};
+
+// 24-hour HH:MM time, equivalent to %H:%M
+class R_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
+ }
+};
+
+// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
+class T_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+ pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
+ }
+};
+
+// ISO 8601 offset from UTC in timezone (+-HH:MM)
+class z_formatter SPDLOG_FINAL:public flag_formatter
+{
+public:
+ const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
+
+ z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
+ {}
+ z_formatter(const z_formatter&) = delete;
+ z_formatter& operator=(const z_formatter&) = delete;
+
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+#ifdef _WIN32
+ int total_minutes = get_cached_offset(msg, tm_time);
+#else
+ // No need to chache under gcc,
+ // it is very fast (already stored in tm.tm_gmtoff)
+ int total_minutes = os::utc_minutes_offset(tm_time);
+#endif
+ bool is_negative = total_minutes < 0;
+ char sign;
+ if (is_negative)
+ {
+ total_minutes = -total_minutes;
+ sign = '-';
+ }
+ else
+ {
+ sign = '+';
+ }
+
+ int h = total_minutes / 60;
+ int m = total_minutes % 60;
+ msg.formatted << sign;
+ pad_n_join(msg.formatted, h, m, ':');
+ }
+private:
+ log_clock::time_point _last_update;
+ int _offset_minutes;
+ std::mutex _mutex;
+
+ int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
+ {
+ using namespace std::chrono;
+ std::lock_guard<std::mutex> l(_mutex);
+ if (msg.time - _last_update >= cache_refresh)
+ {
+ _offset_minutes = os::utc_minutes_offset(tm_time);
+ _last_update = msg.time;
+ }
+ return _offset_minutes;
+ }
+};
+
+
+
+// Thread id
+class t_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << msg.thread_id;
+ }
+};
+
+// Current pid
+class pid_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << details::os::pid();
+ }
+};
+
+
+class v_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
+ }
+};
+
+class ch_formatter SPDLOG_FINAL:public flag_formatter
+{
+public:
+ explicit ch_formatter(char ch): _ch(ch)
+ {}
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << _ch;
+ }
+private:
+ char _ch;
+};
+
+
+//aggregate user chars to display as is
+class aggregate_formatter SPDLOG_FINAL:public flag_formatter
+{
+public:
+ aggregate_formatter()
+ {}
+ void add_ch(char ch)
+ {
+ _str += ch;
+ }
+ void format(details::log_msg& msg, const std::tm&) override
+ {
+ msg.formatted << _str;
+ }
+private:
+ std::string _str;
+};
+
+// Full info formatter
+// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
+class full_formatter SPDLOG_FINAL:public flag_formatter
+{
+ void format(details::log_msg& msg, const std::tm& tm_time) override
+ {
+#ifndef SPDLOG_NO_DATETIME
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+
+ /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
+ msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
+ tm_time.tm_year + 1900,
+ tm_time.tm_mon + 1,
+ tm_time.tm_mday,
+ tm_time.tm_hour,
+ tm_time.tm_min,
+ tm_time.tm_sec,
+ static_cast<int>(millis),
+ msg.logger_name,
+ level::to_str(msg.level),
+ msg.raw.str());*/
+
+
+ // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
+ msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
+ << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
+ << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
+
+ //no datetime needed
+#else
+ (void)tm_time;
+#endif
+
+#ifndef SPDLOG_NO_NAME
+ msg.formatted << '[' << *msg.logger_name << "] ";
+#endif
+
+ msg.formatted << '[' << level::to_str(msg.level) << "] ";
+ msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
+ }
+};
+
+
+
+}
+}
+///////////////////////////////////////////////////////////////////////////////
+// pattern_formatter inline impl
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
+ : _pattern_time(pattern_time)
+{
+ compile_pattern(pattern);
+}
+
+inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
+{
+ auto end = pattern.end();
+ std::unique_ptr<details::aggregate_formatter> user_chars;
+ for (auto it = pattern.begin(); it != end; ++it)
+ {
+ if (*it == '%')
+ {
+ if (user_chars) //append user chars found so far
+ _formatters.push_back(std::move(user_chars));
+
+ if (++it != end)
+ handle_flag(*it);
+ else
+ break;
+ }
+ else // chars not following the % sign should be displayed as is
+ {
+ if (!user_chars)
+ user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
+ user_chars->add_ch(*it);
+ }
+ }
+ if (user_chars) //append raw chars found so far
+ {
+ _formatters.push_back(std::move(user_chars));
+ }
+
+}
+inline void spdlog::pattern_formatter::handle_flag(char flag)
+{
+ switch (flag)
+ {
+ // logger name
+ case 'n':
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
+ break;
+
+ case 'l':
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
+ break;
+
+ case 'L':
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
+ break;
+
+ case('t'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
+ break;
+
+ case('v'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
+ break;
+
+ case('a'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
+ break;
+
+ case('A'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
+ break;
+
+ case('b'):
+ case('h'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
+ break;
+
+ case('B'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
+ break;
+ case('c'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
+ break;
+
+ case('C'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
+ break;
+
+ case('Y'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
+ break;
+
+ case('D'):
+ case('x'):
+
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
+ break;
+
+ case('m'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
+ break;
+
+ case('d'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
+ break;
+
+ case('H'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
+ break;
+
+ case('I'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
+ break;
+
+ case('M'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
+ break;
+
+ case('S'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
+ break;
+
+ case('e'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
+ break;
+
+ case('f'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
+ break;
+ case('F'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
+ break;
+
+ case('p'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
+ break;
+
+ case('r'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
+ break;
+
+ case('R'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
+ break;
+
+ case('T'):
+ case('X'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
+ break;
+
+ case('z'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
+ break;
+
+ case ('+'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
+ break;
+
+ case ('P'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
+ break;
+
+#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
+ case ('i'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
+ break;
+#endif
+
+ default: //Unknown flag appears as is
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
+ break;
+ }
+}
+
+inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
+{
+ if (_pattern_time == pattern_time_type::local)
+ return details::os::localtime(log_clock::to_time_t(msg.time));
+ else
+ return details::os::gmtime(log_clock::to_time_t(msg.time));
+}
+
+inline void spdlog::pattern_formatter::format(details::log_msg& msg)
+{
+
+#ifndef SPDLOG_NO_DATETIME
+ auto tm_time = get_time(msg);
+#else
+ std::tm tm_time;
+#endif
+ for (auto &f : _formatters)
+ {
+ f->format(msg, tm_time);
+ }
+ //write eol
+ msg.formatted.write(details::os::eol, details::os::eol_size);
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/registry.h b/geom_matching/wasserstein/include/spdlog/details/registry.h
new file mode 100644
index 0000000..b518990
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/registry.h
@@ -0,0 +1,214 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Loggers registy of unique name->logger pointer
+// An attempt to create a logger with an already existing name will be ignored
+// If user requests a non existing logger, nullptr will be returned
+// This class is thread safe
+
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/logger.h"
+#include "spdlog/async_logger.h"
+#include "spdlog/common.h"
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+namespace spdlog
+{
+namespace details
+{
+template <class Mutex> class registry_t
+{
+public:
+
+ void register_logger(std::shared_ptr<logger> logger)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ auto logger_name = logger->name();
+ throw_if_exists(logger_name);
+ _loggers[logger_name] = logger;
+ }
+
+
+ std::shared_ptr<logger> get(const std::string& logger_name)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ auto found = _loggers.find(logger_name);
+ return found == _loggers.end() ? nullptr : found->second;
+ }
+
+ template<class It>
+ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ throw_if_exists(logger_name);
+ std::shared_ptr<logger> new_logger;
+ if (_async_mode)
+ new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
+ else
+ new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
+
+ if (_formatter)
+ new_logger->set_formatter(_formatter);
+
+ if (_err_handler)
+ new_logger->set_error_handler(_err_handler);
+
+ new_logger->set_level(_level);
+
+
+ //Add to registry
+ _loggers[logger_name] = new_logger;
+ return new_logger;
+ }
+
+ template<class It>
+ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ throw_if_exists(logger_name);
+ auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
+
+ if (_formatter)
+ new_logger->set_formatter(_formatter);
+
+ if (_err_handler)
+ new_logger->set_error_handler(_err_handler);
+
+ new_logger->set_level(_level);
+
+ //Add to registry
+ _loggers[logger_name] = new_logger;
+ return new_logger;
+ }
+
+ void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ for (auto &l : _loggers)
+ fun(l.second);
+ }
+
+ void drop(const std::string& logger_name)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _loggers.erase(logger_name);
+ }
+
+ void drop_all()
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _loggers.clear();
+ }
+ std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
+ {
+ return create(logger_name, sinks.begin(), sinks.end());
+ }
+
+ std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
+ {
+ return create(logger_name, { sink });
+ }
+
+ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
+ {
+ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
+ }
+
+ std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
+ {
+ return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
+ }
+
+ void formatter(formatter_ptr f)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _formatter = f;
+ for (auto& l : _loggers)
+ l.second->set_formatter(_formatter);
+ }
+
+ void set_pattern(const std::string& pattern)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _formatter = std::make_shared<pattern_formatter>(pattern);
+ for (auto& l : _loggers)
+ l.second->set_formatter(_formatter);
+ }
+
+ void set_level(level::level_enum log_level)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ for (auto& l : _loggers)
+ l.second->set_level(log_level);
+ _level = log_level;
+ }
+
+ void set_error_handler(log_err_handler handler)
+ {
+ for (auto& l : _loggers)
+ l.second->set_error_handler(handler);
+ _err_handler = handler;
+ }
+
+ void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _async_mode = true;
+ _async_q_size = q_size;
+ _overflow_policy = overflow_policy;
+ _worker_warmup_cb = worker_warmup_cb;
+ _flush_interval_ms = flush_interval_ms;
+ _worker_teardown_cb = worker_teardown_cb;
+ }
+
+ void set_sync_mode()
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _async_mode = false;
+ }
+
+ static registry_t<Mutex>& instance()
+ {
+ static registry_t<Mutex> s_instance;
+ return s_instance;
+ }
+
+private:
+ registry_t<Mutex>() {}
+ registry_t<Mutex>(const registry_t<Mutex>&) = delete;
+ registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
+
+ void throw_if_exists(const std::string &logger_name)
+ {
+ if (_loggers.find(logger_name) != _loggers.end())
+ throw spdlog_ex("logger with name '" + logger_name + "' already exists");
+ }
+ Mutex _mutex;
+ std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
+ formatter_ptr _formatter;
+ level::level_enum _level = level::info;
+ log_err_handler _err_handler;
+ bool _async_mode = false;
+ size_t _async_q_size = 0;
+ async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
+ std::function<void()> _worker_warmup_cb = nullptr;
+ std::chrono::milliseconds _flush_interval_ms;
+ std::function<void()> _worker_teardown_cb = nullptr;
+};
+#ifdef SPDLOG_NO_REGISTRY_MUTEX
+typedef registry_t<spdlog::details::null_mutex> registry;
+#else
+typedef registry_t<std::mutex> registry;
+#endif
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/details/spdlog_impl.h b/geom_matching/wasserstein/include/spdlog/details/spdlog_impl.h
new file mode 100644
index 0000000..7fe9ab4
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/details/spdlog_impl.h
@@ -0,0 +1,263 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+//
+// Global registry functions
+//
+#include "spdlog/spdlog.h"
+#include "spdlog/details/registry.h"
+#include "spdlog/sinks/file_sinks.h"
+#include "spdlog/sinks/stdout_sinks.h"
+#ifdef SPDLOG_ENABLE_SYSLOG
+#include "spdlog/sinks/syslog_sink.h"
+#endif
+
+#ifdef _WIN32
+#include "spdlog/sinks/wincolor_sink.h"
+#else
+#include "spdlog/sinks/ansicolor_sink.h"
+#endif
+
+
+#ifdef __ANDROID__
+#include "spdlog/sinks/android_sink.h"
+#endif
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+
+inline void spdlog::register_logger(std::shared_ptr<logger> logger)
+{
+ return details::registry::instance().register_logger(logger);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
+{
+ return details::registry::instance().get(name);
+}
+
+inline void spdlog::drop(const std::string &name)
+{
+ details::registry::instance().drop(name);
+}
+
+// Create multi/single threaded simple file logger
+inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
+{
+ return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
+{
+ return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
+}
+
+// Create multi/single threaded rotating file logger
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
+{
+ return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
+{
+ return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
+}
+
+// Create file logger which creates new file at midnight):
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
+{
+ return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
+{
+ return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
+}
+
+
+//
+// stdout/stderr loggers
+//
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
+{
+ return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
+}
+
+//
+// stdout/stderr color loggers
+//
+#ifdef _WIN32
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+#else //ansi terminal colors
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
+{
+ auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
+ return spdlog::details::registry::instance().create(logger_name, sink);
+}
+#endif
+
+#ifdef SPDLOG_ENABLE_SYSLOG
+// Create syslog logger
+inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
+{
+ return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
+}
+#endif
+
+#ifdef __ANDROID__
+inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
+{
+ return create<spdlog::sinks::android_sink>(logger_name, tag);
+}
+#endif
+
+// Create and register a logger a single sink
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
+{
+ return details::registry::instance().create(logger_name, sink);
+}
+
+//Create logger with multiple sinks
+
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
+{
+ return details::registry::instance().create(logger_name, sinks);
+}
+
+
+template <typename Sink, typename... Args>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
+{
+ sink_ptr sink = std::make_shared<Sink>(args...);
+ return details::registry::instance().create(logger_name, { sink });
+}
+
+
+template<class It>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
+{
+ return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
+}
+
+// Create and register an async logger with a single sink
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
+{
+ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
+}
+
+// Create and register an async logger with multiple sinks
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
+{
+ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
+}
+
+template<class It>
+inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
+{
+ return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
+}
+
+inline void spdlog::set_formatter(spdlog::formatter_ptr f)
+{
+ details::registry::instance().formatter(f);
+}
+
+inline void spdlog::set_pattern(const std::string& format_string)
+{
+ return details::registry::instance().set_pattern(format_string);
+}
+
+inline void spdlog::set_level(level::level_enum log_level)
+{
+ return details::registry::instance().set_level(log_level);
+}
+
+inline void spdlog::set_error_handler(log_err_handler handler)
+{
+ return details::registry::instance().set_error_handler(handler);
+}
+
+
+inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
+{
+ details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
+}
+
+inline void spdlog::set_sync_mode()
+{
+ details::registry::instance().set_sync_mode();
+}
+
+inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
+{
+ details::registry::instance().apply_all(fun);
+}
+
+inline void spdlog::drop_all()
+{
+ details::registry::instance().drop_all();
+}
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.cc b/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.cc
new file mode 100644
index 0000000..2bd774e
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.cc
@@ -0,0 +1,940 @@
+/*
+ Formatting library for C++
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ 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.
+ */
+
+#include "format.h"
+
+#include <string.h>
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstddef> // for std::ptrdiff_t
+
+#if defined(_WIN32) && defined(__MINGW32__)
+# include <cstring>
+#endif
+
+#if FMT_USE_WINDOWS_H
+# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
+# include <windows.h>
+# else
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# endif
+#endif
+
+using fmt::internal::Arg;
+
+#if FMT_EXCEPTIONS
+# define FMT_TRY try
+# define FMT_CATCH(x) catch (x)
+#else
+# define FMT_TRY if (true)
+# define FMT_CATCH(x) if (false)
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4127) // conditional expression is constant
+# pragma warning(disable: 4702) // unreachable code
+// Disable deprecation warning for strerror. The latter is not called but
+// MSVC fails to detect it.
+# pragma warning(disable: 4996)
+#endif
+
+// Dummy implementations of strerror_r and strerror_s called if corresponding
+// system functions are not available.
+static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
+ return fmt::internal::Null<>();
+}
+static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
+ return fmt::internal::Null<>();
+}
+
+namespace fmt {
+
+FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {}
+FMT_FUNC FormatError::~FormatError() throw() {}
+FMT_FUNC SystemError::~SystemError() throw() {}
+
+namespace {
+
+#ifndef _MSC_VER
+# define FMT_SNPRINTF snprintf
+#else // _MSC_VER
+inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
+ va_end(args);
+ return result;
+}
+# define FMT_SNPRINTF fmt_snprintf
+#endif // _MSC_VER
+
+#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
+# define FMT_SWPRINTF snwprintf
+#else
+# define FMT_SWPRINTF swprintf
+#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
+
+// Checks if a value fits in int - used to avoid warnings about comparing
+// signed and unsigned integers.
+template <bool IsSigned>
+struct IntChecker {
+ template <typename T>
+ static bool fits_in_int(T value) {
+ unsigned max = INT_MAX;
+ return value <= max;
+ }
+ static bool fits_in_int(bool) { return true; }
+};
+
+template <>
+struct IntChecker<true> {
+ template <typename T>
+ static bool fits_in_int(T value) {
+ return value >= INT_MIN && value <= INT_MAX;
+ }
+ static bool fits_in_int(int) { return true; }
+};
+
+const char RESET_COLOR[] = "\x1b[0m";
+
+typedef void (*FormatFunc)(Writer &, int, StringRef);
+
+// Portable thread-safe version of strerror.
+// Sets buffer to point to a string describing the error code.
+// This can be either a pointer to a string stored in buffer,
+// or a pointer to some static immutable string.
+// Returns one of the following values:
+// 0 - success
+// ERANGE - buffer is not large enough to store the error message
+// other - failure
+// Buffer should be at least of size 1.
+int safe_strerror(
+ int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
+ FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
+
+ class StrError {
+ private:
+ int error_code_;
+ char *&buffer_;
+ std::size_t buffer_size_;
+
+ // A noop assignment operator to avoid bogus warnings.
+ void operator=(const StrError &) {}
+
+ // Handle the result of XSI-compliant version of strerror_r.
+ int handle(int result) {
+ // glibc versions before 2.13 return result in errno.
+ return result == -1 ? errno : result;
+ }
+
+ // Handle the result of GNU-specific version of strerror_r.
+ int handle(char *message) {
+ // If the buffer is full then the message is probably truncated.
+ if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
+ return ERANGE;
+ buffer_ = message;
+ return 0;
+ }
+
+ // Handle the case when strerror_r is not available.
+ int handle(internal::Null<>) {
+ return fallback(strerror_s(buffer_, buffer_size_, error_code_));
+ }
+
+ // Fallback to strerror_s when strerror_r is not available.
+ int fallback(int result) {
+ // If the buffer is full then the message is probably truncated.
+ return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
+ ERANGE : result;
+ }
+
+ // Fallback to strerror if strerror_r and strerror_s are not available.
+ int fallback(internal::Null<>) {
+ errno = 0;
+ buffer_ = strerror(error_code_);
+ return errno;
+ }
+
+ public:
+ StrError(int err_code, char *&buf, std::size_t buf_size)
+ : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
+
+ int run() {
+ strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
+ return handle(strerror_r(error_code_, buffer_, buffer_size_));
+ }
+ };
+ return StrError(error_code, buffer, buffer_size).run();
+}
+
+void format_error_code(Writer &out, int error_code,
+ StringRef message) FMT_NOEXCEPT {
+ // Report error code making sure that the output fits into
+ // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
+ // bad_alloc.
+ out.clear();
+ static const char SEP[] = ": ";
+ static const char ERROR_STR[] = "error ";
+ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+ std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+ typedef internal::IntTraits<int>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(error_code);
+ if (internal::is_negative(error_code)) {
+ abs_value = 0 - abs_value;
+ ++error_code_size;
+ }
+ error_code_size += internal::count_digits(abs_value);
+ if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
+ out << message << SEP;
+ out << ERROR_STR << error_code;
+ assert(out.size() <= internal::INLINE_BUFFER_SIZE);
+}
+
+void report_error(FormatFunc func, int error_code,
+ StringRef message) FMT_NOEXCEPT {
+ MemoryWriter full_message;
+ func(full_message, error_code, message);
+ // Use Writer::data instead of Writer::c_str to avoid potential memory
+ // allocation.
+ std::fwrite(full_message.data(), full_message.size(), 1, stderr);
+ std::fputc('\n', stderr);
+}
+
+// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
+class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
+ public:
+ template <typename T>
+ bool visit_any_int(T value) { return value == 0; }
+};
+
+// Checks if an argument is a valid printf width specifier and sets
+// left alignment if it is negative.
+class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
+ private:
+ FormatSpec &spec_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
+
+ public:
+ explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
+
+ void report_unhandled_arg() {
+ FMT_THROW(FormatError("width is not integer"));
+ }
+
+ template <typename T>
+ unsigned visit_any_int(T value) {
+ typedef typename internal::IntTraits<T>::MainType UnsignedType;
+ UnsignedType width = static_cast<UnsignedType>(value);
+ if (internal::is_negative(value)) {
+ spec_.align_ = ALIGN_LEFT;
+ width = 0 - width;
+ }
+ if (width > INT_MAX)
+ FMT_THROW(FormatError("number is too big"));
+ return static_cast<unsigned>(width);
+ }
+};
+
+class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
+ public:
+ void report_unhandled_arg() {
+ FMT_THROW(FormatError("precision is not integer"));
+ }
+
+ template <typename T>
+ int visit_any_int(T value) {
+ if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+ FMT_THROW(FormatError("number is too big"));
+ return static_cast<int>(value);
+ }
+};
+
+template <typename T, typename U>
+struct is_same {
+ enum { value = 0 };
+};
+
+template <typename T>
+struct is_same<T, T> {
+ enum { value = 1 };
+};
+
+// An argument visitor that converts an integer argument to T for printf,
+// if T is an integral type. If T is void, the argument is converted to
+// corresponding signed or unsigned type depending on the type specifier:
+// 'd' and 'i' - signed, other - unsigned)
+template <typename T = void>
+class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
+ private:
+ internal::Arg &arg_;
+ wchar_t type_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
+
+ public:
+ ArgConverter(internal::Arg &arg, wchar_t type)
+ : arg_(arg), type_(type) {}
+
+ void visit_bool(bool value) {
+ if (type_ != 's')
+ visit_any_int(value);
+ }
+
+ template <typename U>
+ void visit_any_int(U value) {
+ bool is_signed = type_ == 'd' || type_ == 'i';
+ using internal::Arg;
+ typedef typename internal::Conditional<
+ is_same<T, void>::value, U, T>::type TargetType;
+ if (sizeof(TargetType) <= sizeof(int)) {
+ // Extra casts are used to silence warnings.
+ if (is_signed) {
+ arg_.type = Arg::INT;
+ arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
+ } else {
+ arg_.type = Arg::UINT;
+ typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
+ arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
+ }
+ } else {
+ if (is_signed) {
+ arg_.type = Arg::LONG_LONG;
+ // glibc's printf doesn't sign extend arguments of smaller types:
+ // std::printf("%lld", -42); // prints "4294967254"
+ // but we don't have to do the same because it's a UB.
+ arg_.long_long_value = static_cast<LongLong>(value);
+ } else {
+ arg_.type = Arg::ULONG_LONG;
+ arg_.ulong_long_value =
+ static_cast<typename internal::MakeUnsigned<U>::Type>(value);
+ }
+ }
+ }
+};
+
+// Converts an integer argument to char for printf.
+class CharConverter : public ArgVisitor<CharConverter, void> {
+ private:
+ internal::Arg &arg_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
+
+ public:
+ explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
+
+ template <typename T>
+ void visit_any_int(T value) {
+ arg_.type = internal::Arg::CHAR;
+ arg_.int_value = static_cast<char>(value);
+ }
+};
+} // namespace
+
+namespace internal {
+
+template <typename Char>
+class PrintfArgFormatter :
+ public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
+
+ void write_null_pointer() {
+ this->spec().type_ = 0;
+ this->write("(nil)");
+ }
+
+ typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
+
+ public:
+ PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
+ : ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
+
+ void visit_bool(bool value) {
+ FormatSpec &fmt_spec = this->spec();
+ if (fmt_spec.type_ != 's')
+ return this->visit_any_int(value);
+ fmt_spec.type_ = 0;
+ this->write(value);
+ }
+
+ void visit_char(int value) {
+ const FormatSpec &fmt_spec = this->spec();
+ BasicWriter<Char> &w = this->writer();
+ if (fmt_spec.type_ && fmt_spec.type_ != 'c')
+ w.write_int(value, fmt_spec);
+ typedef typename BasicWriter<Char>::CharPtr CharPtr;
+ CharPtr out = CharPtr();
+ if (fmt_spec.width_ > 1) {
+ Char fill = ' ';
+ out = w.grow_buffer(fmt_spec.width_);
+ if (fmt_spec.align_ != ALIGN_LEFT) {
+ std::fill_n(out, fmt_spec.width_ - 1, fill);
+ out += fmt_spec.width_ - 1;
+ } else {
+ std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
+ }
+ } else {
+ out = w.grow_buffer(1);
+ }
+ *out = static_cast<Char>(value);
+ }
+
+ void visit_cstring(const char *value) {
+ if (value)
+ Base::visit_cstring(value);
+ else if (this->spec().type_ == 'p')
+ write_null_pointer();
+ else
+ this->write("(null)");
+ }
+
+ void visit_pointer(const void *value) {
+ if (value)
+ return Base::visit_pointer(value);
+ this->spec().type_ = 0;
+ write_null_pointer();
+ }
+
+ void visit_custom(Arg::CustomValue c) {
+ BasicFormatter<Char> formatter(ArgList(), this->writer());
+ const Char format_str[] = {'}', 0};
+ const Char *format = format_str;
+ c.format(&formatter, c.value, &format);
+ }
+};
+} // namespace internal
+} // namespace fmt
+
+FMT_FUNC void fmt::SystemError::init(
+ int err_code, CStringRef format_str, ArgList args) {
+ error_code_ = err_code;
+ MemoryWriter w;
+ internal::format_system_error(w, err_code, format(format_str, args));
+ std::runtime_error &base = *this;
+ base = std::runtime_error(w.str());
+}
+
+template <typename T>
+int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, T value) {
+ if (width == 0) {
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, value) :
+ FMT_SNPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, width, value) :
+ FMT_SNPRINTF(buffer, size, format, width, precision, value);
+}
+
+template <typename T>
+int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, T value) {
+ if (width == 0) {
+ return precision < 0 ?
+ FMT_SWPRINTF(buffer, size, format, value) :
+ FMT_SWPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SWPRINTF(buffer, size, format, width, value) :
+ FMT_SWPRINTF(buffer, size, format, width, precision, value);
+}
+
+template <typename T>
+const char fmt::internal::BasicData<T>::DIGITS[] =
+ "0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+
+#define FMT_POWERS_OF_10(factor) \
+ factor * 10, \
+ factor * 100, \
+ factor * 1000, \
+ factor * 10000, \
+ factor * 100000, \
+ factor * 1000000, \
+ factor * 10000000, \
+ factor * 100000000, \
+ factor * 1000000000
+
+template <typename T>
+const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
+ 0, FMT_POWERS_OF_10(1)
+};
+
+template <typename T>
+const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
+ 0,
+ FMT_POWERS_OF_10(1),
+ FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
+ // Multiply several constants instead of using a single long long constant
+ // to avoid warnings about C++98 not supporting long long.
+ fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
+};
+
+FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
+ (void)type;
+ if (std::isprint(static_cast<unsigned char>(code))) {
+ FMT_THROW(fmt::FormatError(
+ fmt::format("unknown format code '{}' for {}", code, type)));
+ }
+ FMT_THROW(fmt::FormatError(
+ fmt::format("unknown format code '\\x{:02x}' for {}",
+ static_cast<unsigned>(code), type)));
+}
+
+#if FMT_USE_WINDOWS_H
+
+FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
+ static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
+ if (s.size() > INT_MAX)
+ FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
+ int s_size = static_cast<int>(s.size());
+ int length = MultiByteToWideChar(
+ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
+ if (length == 0)
+ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
+ buffer_.resize(length + 1);
+ length = MultiByteToWideChar(
+ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
+ if (length == 0)
+ FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
+ buffer_[length] = 0;
+}
+
+FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
+ if (int error_code = convert(s)) {
+ FMT_THROW(WindowsError(error_code,
+ "cannot convert string from UTF-16 to UTF-8"));
+ }
+}
+
+FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
+ if (s.size() > INT_MAX)
+ return ERROR_INVALID_PARAMETER;
+ int s_size = static_cast<int>(s.size());
+ int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
+ if (length == 0)
+ return GetLastError();
+ buffer_.resize(length + 1);
+ length = WideCharToMultiByte(
+ CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
+ if (length == 0)
+ return GetLastError();
+ buffer_[length] = 0;
+ return 0;
+}
+
+FMT_FUNC void fmt::WindowsError::init(
+ int err_code, CStringRef format_str, ArgList args) {
+ error_code_ = err_code;
+ MemoryWriter w;
+ internal::format_windows_error(w, err_code, format(format_str, args));
+ std::runtime_error &base = *this;
+ base = std::runtime_error(w.str());
+}
+
+FMT_FUNC void fmt::internal::format_windows_error(
+ fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT {
+ FMT_TRY {
+ MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
+ buffer.resize(INLINE_BUFFER_SIZE);
+ for (;;) {
+ wchar_t *system_message = &buffer[0];
+ int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ system_message, static_cast<uint32_t>(buffer.size()), 0);
+ if (result != 0) {
+ UTF16ToUTF8 utf8_message;
+ if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
+ out << message << ": " << utf8_message;
+ return;
+ }
+ break;
+ }
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ break; // Can't get error message, report error code instead.
+ buffer.resize(buffer.size() * 2);
+ }
+ } FMT_CATCH(...) {}
+ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
+}
+
+#endif // FMT_USE_WINDOWS_H
+
+FMT_FUNC void fmt::internal::format_system_error(
+ fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT {
+ FMT_TRY {
+ MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
+ buffer.resize(INLINE_BUFFER_SIZE);
+ for (;;) {
+ char *system_message = &buffer[0];
+ int result = safe_strerror(error_code, system_message, buffer.size());
+ if (result == 0) {
+ out << message << ": " << system_message;
+ return;
+ }
+ if (result != ERANGE)
+ break; // Can't get error message, report error code instead.
+ buffer.resize(buffer.size() * 2);
+ }
+ } FMT_CATCH(...) {}
+ fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
+}
+
+template <typename Char>
+void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
+ if (!map_.empty())
+ return;
+ typedef internal::NamedArg<Char> NamedArg;
+ const NamedArg *named_arg = 0;
+ bool use_values =
+ args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
+ if (use_values) {
+ for (unsigned i = 0;/*nothing*/; ++i) {
+ internal::Arg::Type arg_type = args.type(i);
+ switch (arg_type) {
+ case internal::Arg::NONE:
+ return;
+ case internal::Arg::NAMED_ARG:
+ named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
+ }
+ return;
+ }
+ for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
+ internal::Arg::Type arg_type = args.type(i);
+ if (arg_type == internal::Arg::NAMED_ARG) {
+ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ }
+ }
+ for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
+ switch (args.args_[i].type) {
+ case internal::Arg::NONE:
+ return;
+ case internal::Arg::NAMED_ARG:
+ named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
+ map_.push_back(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
+ }
+}
+
+template <typename Char>
+void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
+ FMT_THROW(std::runtime_error("buffer overflow"));
+}
+
+FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
+ unsigned arg_index, const char *&error) {
+ Arg arg = args_[arg_index];
+ switch (arg.type) {
+ case Arg::NONE:
+ error = "argument index out of range";
+ break;
+ case Arg::NAMED_ARG:
+ arg = *static_cast<const internal::Arg*>(arg.pointer);
+ break;
+ default:
+ /*nothing*/;
+ }
+ return arg;
+}
+
+template <typename Char>
+void fmt::internal::PrintfFormatter<Char>::parse_flags(
+ FormatSpec &spec, const Char *&s) {
+ for (;;) {
+ switch (*s++) {
+ case '-':
+ spec.align_ = ALIGN_LEFT;
+ break;
+ case '+':
+ spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
+ break;
+ case '0':
+ spec.fill_ = '0';
+ break;
+ case ' ':
+ spec.flags_ |= SIGN_FLAG;
+ break;
+ case '#':
+ spec.flags_ |= HASH_FLAG;
+ break;
+ default:
+ --s;
+ return;
+ }
+ }
+}
+
+template <typename Char>
+Arg fmt::internal::PrintfFormatter<Char>::get_arg(
+ const Char *s, unsigned arg_index) {
+ (void)s;
+ const char *error = 0;
+ Arg arg = arg_index == UINT_MAX ?
+ next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
+ if (error)
+ FMT_THROW(FormatError(!*s ? "invalid format string" : error));
+ return arg;
+}
+
+template <typename Char>
+unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
+ const Char *&s, FormatSpec &spec) {
+ unsigned arg_index = UINT_MAX;
+ Char c = *s;
+ if (c >= '0' && c <= '9') {
+ // Parse an argument index (if followed by '$') or a width possibly
+ // preceded with '0' flag(s).
+ unsigned value = parse_nonnegative_int(s);
+ if (*s == '$') { // value is an argument index
+ ++s;
+ arg_index = value;
+ } else {
+ if (c == '0')
+ spec.fill_ = '0';
+ if (value != 0) {
+ // Nonzero value means that we parsed width and don't need to
+ // parse it or flags again, so return now.
+ spec.width_ = value;
+ return arg_index;
+ }
+ }
+ }
+ parse_flags(spec, s);
+ // Parse width.
+ if (*s >= '0' && *s <= '9') {
+ spec.width_ = parse_nonnegative_int(s);
+ } else if (*s == '*') {
+ ++s;
+ spec.width_ = WidthHandler(spec).visit(get_arg(s));
+ }
+ return arg_index;
+}
+
+template <typename Char>
+void fmt::internal::PrintfFormatter<Char>::format(
+ BasicWriter<Char> &writer, BasicCStringRef<Char> format_str) {
+ const Char *start = format_str.c_str();
+ const Char *s = start;
+ while (*s) {
+ Char c = *s++;
+ if (c != '%') continue;
+ if (*s == c) {
+ write(writer, start, s);
+ start = ++s;
+ continue;
+ }
+ write(writer, start, s - 1);
+
+ FormatSpec spec;
+ spec.align_ = ALIGN_RIGHT;
+
+ // Parse argument index, flags and width.
+ unsigned arg_index = parse_header(s, spec);
+
+ // Parse precision.
+ if (*s == '.') {
+ ++s;
+ if ('0' <= *s && *s <= '9') {
+ spec.precision_ = static_cast<int>(parse_nonnegative_int(s));
+ } else if (*s == '*') {
+ ++s;
+ spec.precision_ = PrecisionHandler().visit(get_arg(s));
+ }
+ }
+
+ Arg arg = get_arg(s, arg_index);
+ if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
+ spec.flags_ &= ~to_unsigned<int>(HASH_FLAG);
+ if (spec.fill_ == '0') {
+ if (arg.type <= Arg::LAST_NUMERIC_TYPE)
+ spec.align_ = ALIGN_NUMERIC;
+ else
+ spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
+ }
+
+ // Parse length and convert the argument to the required type.
+ switch (*s++) {
+ case 'h':
+ if (*s == 'h')
+ ArgConverter<signed char>(arg, *++s).visit(arg);
+ else
+ ArgConverter<short>(arg, *s).visit(arg);
+ break;
+ case 'l':
+ if (*s == 'l')
+ ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
+ else
+ ArgConverter<long>(arg, *s).visit(arg);
+ break;
+ case 'j':
+ ArgConverter<intmax_t>(arg, *s).visit(arg);
+ break;
+ case 'z':
+ ArgConverter<std::size_t>(arg, *s).visit(arg);
+ break;
+ case 't':
+ ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
+ break;
+ case 'L':
+ // printf produces garbage when 'L' is omitted for long double, no
+ // need to do the same.
+ break;
+ default:
+ --s;
+ ArgConverter<void>(arg, *s).visit(arg);
+ }
+
+ // Parse type.
+ if (!*s)
+ FMT_THROW(FormatError("invalid format string"));
+ spec.type_ = static_cast<char>(*s++);
+ if (arg.type <= Arg::LAST_INTEGER_TYPE) {
+ // Normalize type.
+ switch (spec.type_) {
+ case 'i': case 'u':
+ spec.type_ = 'd';
+ break;
+ case 'c':
+ // TODO: handle wchar_t
+ CharConverter(arg).visit(arg);
+ break;
+ }
+ }
+
+ start = s;
+
+ // Format argument.
+ internal::PrintfArgFormatter<Char>(writer, spec).visit(arg);
+ }
+ write(writer, start, s);
+}
+
+FMT_FUNC void fmt::report_system_error(
+ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
+ // 'fmt::' is for bcc32.
+ fmt::report_error(internal::format_system_error, error_code, message);
+}
+
+#if FMT_USE_WINDOWS_H
+FMT_FUNC void fmt::report_windows_error(
+ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
+ // 'fmt::' is for bcc32.
+ fmt::report_error(internal::format_windows_error, error_code, message);
+}
+#endif
+
+FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) {
+ MemoryWriter w;
+ w.write(format_str, args);
+ std::fwrite(w.data(), 1, w.size(), f);
+}
+
+FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
+ print(stdout, format_str, args);
+}
+
+FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
+ char escape[] = "\x1b[30m";
+ escape[3] = static_cast<char>('0' + c);
+ std::fputs(escape, stdout);
+ print(format, args);
+ std::fputs(RESET_COLOR, stdout);
+}
+
+FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
+ MemoryWriter w;
+ printf(w, format, args);
+ std::size_t size = w.size();
+ return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
+}
+
+#ifndef FMT_HEADER_ONLY
+
+template struct fmt::internal::BasicData<void>;
+
+// Explicit instantiations for char.
+
+template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
+
+template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
+
+template void fmt::internal::PrintfFormatter<char>::format(
+ BasicWriter<char> &writer, CStringRef format);
+
+template int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, double value);
+
+template int fmt::internal::CharTraits<char>::format_float(
+ char *buffer, std::size_t size, const char *format,
+ unsigned width, int precision, long double value);
+
+// Explicit instantiations for wchar_t.
+
+template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
+
+template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
+
+template void fmt::internal::PrintfFormatter<wchar_t>::format(
+ BasicWriter<wchar_t> &writer, WCStringRef format);
+
+template int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, double value);
+
+template int fmt::internal::CharTraits<wchar_t>::format_float(
+ wchar_t *buffer, std::size_t size, const wchar_t *format,
+ unsigned width, int precision, long double value);
+
+#endif // FMT_HEADER_ONLY
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.h b/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.h
new file mode 100644
index 0000000..64c949b
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/format.h
@@ -0,0 +1,4501 @@
+/*
+ Formatting library for C++
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ 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.
+ */
+
+#ifndef FMT_FORMAT_H_
+#define FMT_FORMAT_H_
+
+#include <cassert>
+#include <clocale>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+
+#ifdef _SECURE_SCL
+# define FMT_SECURE_SCL _SECURE_SCL
+#else
+# define FMT_SECURE_SCL 0
+#endif
+
+#if FMT_SECURE_SCL
+# include <iterator>
+#endif
+
+#ifdef _MSC_VER
+# define FMT_MSC_VER _MSC_VER
+#else
+# define FMT_MSC_VER 0
+#endif
+
+#if FMT_MSC_VER && FMT_MSC_VER <= 1500
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int64 intmax_t;
+#else
+#include <stdint.h>
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+# ifdef FMT_EXPORT
+# define FMT_API __declspec(dllexport)
+# elif defined(FMT_SHARED)
+# define FMT_API __declspec(dllimport)
+# endif
+#endif
+#ifndef FMT_API
+# define FMT_API
+#endif
+
+#ifdef __GNUC__
+# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# define FMT_GCC_EXTENSION __extension__
+# if FMT_GCC_VERSION >= 406
+# pragma GCC diagnostic push
+// Disable the warning about "long long" which is sometimes reported even
+// when using __extension__.
+# pragma GCC diagnostic ignored "-Wlong-long"
+// Disable the warning about declaration shadowing because it affects too
+// many valid cases.
+# pragma GCC diagnostic ignored "-Wshadow"
+// Disable the warning about implicit conversions that may change the sign of
+// an integer; silencing it otherwise would require many explicit casts.
+# pragma GCC diagnostic ignored "-Wsign-conversion"
+# endif
+# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__
+# define FMT_HAS_GXX_CXX11 1
+# endif
+#else
+# define FMT_GCC_EXTENSION
+#endif
+
+#if defined(__INTEL_COMPILER)
+# define FMT_ICC_VERSION __INTEL_COMPILER
+#elif defined(__ICL)
+# define FMT_ICC_VERSION __ICL
+#endif
+
+#if defined(__clang__) && !defined(FMT_ICC_VERSION)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+# pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#ifdef __GNUC_LIBSTD__
+# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__)
+#endif
+
+#ifdef __has_feature
+# define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+# define FMT_HAS_FEATURE(x) 0
+#endif
+
+#ifdef __has_builtin
+# define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+# define FMT_HAS_BUILTIN(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+# define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#ifndef FMT_USE_VARIADIC_TEMPLATES
+// Variadic templates are available in GCC since version 4.4
+// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++
+// since version 2013.
+# define FMT_USE_VARIADIC_TEMPLATES \
+ (FMT_HAS_FEATURE(cxx_variadic_templates) || \
+ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800)
+#endif
+
+#ifndef FMT_USE_RVALUE_REFERENCES
+// Don't use rvalue references when compiling with clang and an old libstdc++
+// as the latter doesn't provide std::move.
+# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402
+# define FMT_USE_RVALUE_REFERENCES 0
+# else
+# define FMT_USE_RVALUE_REFERENCES \
+ (FMT_HAS_FEATURE(cxx_rvalue_references) || \
+ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600)
+# endif
+#endif
+
+#if FMT_USE_RVALUE_REFERENCES
+# include <utility> // for std::move
+#endif
+
+// Check if exceptions are disabled.
+#if defined(__GNUC__) && !defined(__EXCEPTIONS)
+# define FMT_EXCEPTIONS 0
+#endif
+#if FMT_MSC_VER && !_HAS_EXCEPTIONS
+# define FMT_EXCEPTIONS 0
+#endif
+#ifndef FMT_EXCEPTIONS
+# define FMT_EXCEPTIONS 1
+#endif
+
+#ifndef FMT_THROW
+# if FMT_EXCEPTIONS
+# define FMT_THROW(x) throw x
+# else
+# define FMT_THROW(x) assert(false)
+# endif
+#endif
+
+// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
+#ifndef FMT_USE_NOEXCEPT
+# define FMT_USE_NOEXCEPT 0
+#endif
+
+#ifndef FMT_NOEXCEPT
+# if FMT_EXCEPTIONS
+# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
+ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
+ FMT_MSC_VER >= 1900
+# define FMT_NOEXCEPT noexcept
+# else
+# define FMT_NOEXCEPT throw()
+# endif
+# else
+# define FMT_NOEXCEPT
+# endif
+#endif
+
+#ifndef FMT_OVERRIDE
+# if FMT_USE_OVERRIDE || FMT_HAS_FEATURE(cxx_override) || \
+ (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
+ FMT_MSC_VER >= 1900
+# define FMT_OVERRIDE override
+# else
+# define FMT_OVERRIDE
+# endif
+#endif
+
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#ifndef FMT_USE_DELETED_FUNCTIONS
+# define FMT_USE_DELETED_FUNCTIONS 0
+#endif
+
+#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \
+ (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800
+# define FMT_DELETED_OR_UNDEFINED = delete
+# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ TypeName& operator=(const TypeName&) = delete
+#else
+# define FMT_DELETED_OR_UNDEFINED
+# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ TypeName& operator=(const TypeName&)
+#endif
+
+#ifndef FMT_USE_USER_DEFINED_LITERALS
+// All compilers which support UDLs also support variadic templates. This
+// makes the fmt::literals implementation easier. However, an explicit check
+// for variadic templates is added here just in case.
+// For Intel's compiler both it and the system gcc/msc must support UDLs.
+# define FMT_USE_USER_DEFINED_LITERALS \
+ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \
+ (FMT_HAS_FEATURE(cxx_user_literals) || \
+ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \
+ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500)
+#endif
+
+#ifndef FMT_ASSERT
+# define FMT_ASSERT(condition, message) assert((condition) && message)
+#endif
+
+#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
+# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#endif
+
+#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
+# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#endif
+
+// Some compilers masquerade as both MSVC and GCC-likes or
+// otherwise support __builtin_clz and __builtin_clzll, so
+// only define FMT_BUILTIN_CLZ using the MSVC intrinsics
+// if the clz and clzll builtins are not available.
+#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL)
+# include <intrin.h> // _BitScanReverse, _BitScanReverse64
+
+namespace fmt
+{
+namespace internal
+{
+# pragma intrinsic(_BitScanReverse)
+inline uint32_t clz(uint32_t x)
+{
+ unsigned long r = 0;
+ _BitScanReverse(&r, x);
+
+ assert(x != 0);
+ // Static analysis complains about using uninitialized data
+ // "r", but the only way that can happen is if "x" is 0,
+ // which the callers guarantee to not happen.
+# pragma warning(suppress: 6102)
+ return 31 - r;
+}
+# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
+
+# ifdef _WIN64
+# pragma intrinsic(_BitScanReverse64)
+# endif
+
+inline uint32_t clzll(uint64_t x)
+{
+ unsigned long r = 0;
+# ifdef _WIN64
+ _BitScanReverse64(&r, x);
+# else
+ // Scan the high 32 bits.
+ if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+ return 63 - (r + 32);
+
+ // Scan the low 32 bits.
+ _BitScanReverse(&r, static_cast<uint32_t>(x));
+# endif
+
+ assert(x != 0);
+ // Static analysis complains about using uninitialized data
+ // "r", but the only way that can happen is if "x" is 0,
+ // which the callers guarantee to not happen.
+# pragma warning(suppress: 6102)
+ return 63 - r;
+}
+# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
+}
+}
+#endif
+
+namespace fmt
+{
+namespace internal
+{
+struct DummyInt
+{
+ int data[2];
+ operator int() const
+ {
+ return 0;
+ }
+};
+typedef std::numeric_limits<fmt::internal::DummyInt> FPUtil;
+
+// Dummy implementations of system functions such as signbit and ecvt called
+// if the latter are not available.
+inline DummyInt signbit(...)
+{
+ return DummyInt();
+}
+inline DummyInt _ecvt_s(...)
+{
+ return DummyInt();
+}
+inline DummyInt isinf(...)
+{
+ return DummyInt();
+}
+inline DummyInt _finite(...)
+{
+ return DummyInt();
+}
+inline DummyInt isnan(...)
+{
+ return DummyInt();
+}
+inline DummyInt _isnan(...)
+{
+ return DummyInt();
+}
+
+// A helper function to suppress bogus "conditional expression is constant"
+// warnings.
+template <typename T>
+inline T const_check(T value)
+{
+ return value;
+}
+}
+} // namespace fmt
+
+namespace std
+{
+// Standard permits specialization of std::numeric_limits. This specialization
+// is used to resolve ambiguity between isinf and std::isinf in glibc:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891
+// and the same for isnan and signbit.
+template <>
+class numeric_limits<fmt::internal::DummyInt> :
+ public std::numeric_limits<int>
+{
+public:
+ // Portable version of isinf.
+ template <typename T>
+ static bool isinfinity(T x)
+ {
+ using namespace fmt::internal;
+ // The resolution "priority" is:
+ // isinf macro > std::isinf > ::isinf > fmt::internal::isinf
+ if (const_check(sizeof(isinf(x)) == sizeof(bool) ||
+ sizeof(isinf(x)) == sizeof(int)))
+ {
+ return isinf(x) != 0;
+ }
+ return !_finite(static_cast<double>(x));
+ }
+
+ // Portable version of isnan.
+ template <typename T>
+ static bool isnotanumber(T x)
+ {
+ using namespace fmt::internal;
+ if (const_check(sizeof(isnan(x)) == sizeof(bool) ||
+ sizeof(isnan(x)) == sizeof(int)))
+ {
+ return isnan(x) != 0;
+ }
+ return _isnan(static_cast<double>(x)) != 0;
+ }
+
+ // Portable version of signbit.
+ static bool isnegative(double x)
+ {
+ using namespace fmt::internal;
+ if (const_check(sizeof(signbit(x)) == sizeof(int)))
+ return signbit(x) != 0;
+ if (x < 0) return true;
+ if (!isnotanumber(x)) return false;
+ int dec = 0, sign = 0;
+ char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
+ _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign);
+ return sign != 0;
+ }
+};
+} // namespace std
+
+namespace fmt
+{
+
+// Fix the warning about long long on older versions of GCC
+// that don't support the diagnostic pragma.
+FMT_GCC_EXTENSION typedef long long LongLong;
+FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
+
+#if FMT_USE_RVALUE_REFERENCES
+using std::move;
+#endif
+
+template <typename Char>
+class BasicWriter;
+
+typedef BasicWriter<char> Writer;
+typedef BasicWriter<wchar_t> WWriter;
+
+template <typename Char>
+class ArgFormatter;
+
+template <typename CharType,
+ typename ArgFormatter = fmt::ArgFormatter<CharType> >
+class BasicFormatter;
+
+/**
+ \rst
+ A string reference. It can be constructed from a C string or ``std::string``.
+
+ You can use one of the following typedefs for common character types:
+
+ +------------+-------------------------+
+ | Type | Definition |
+ +============+=========================+
+ | StringRef | BasicStringRef<char> |
+ +------------+-------------------------+
+ | WStringRef | BasicStringRef<wchar_t> |
+ +------------+-------------------------+
+
+ This class is most useful as a parameter type to allow passing
+ different types of strings to a function, for example::
+
+ template <typename... Args>
+ std::string format(StringRef format_str, const Args & ... args);
+
+ format("{}", 42);
+ format(std::string("{}"), 42);
+ \endrst
+ */
+template <typename Char>
+class BasicStringRef
+{
+private:
+ const Char *data_;
+ std::size_t size_;
+
+public:
+ /** Constructs a string reference object from a C string and a size. */
+ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {}
+
+ /**
+ \rst
+ Constructs a string reference object from a C string computing
+ the size with ``std::char_traits<Char>::length``.
+ \endrst
+ */
+ BasicStringRef(const Char *s)
+ : data_(s), size_(std::char_traits<Char>::length(s)) {}
+
+ /**
+ \rst
+ Constructs a string reference from an ``std::string`` object.
+ \endrst
+ */
+ BasicStringRef(const std::basic_string<Char> &s)
+ : data_(s.c_str()), size_(s.size()) {}
+
+ /**
+ \rst
+ Converts a string reference to an ``std::string`` object.
+ \endrst
+ */
+ std::basic_string<Char> to_string() const
+ {
+ return std::basic_string<Char>(data_, size_);
+ }
+
+ /** Returns a pointer to the string data. */
+ const Char *data() const
+ {
+ return data_;
+ }
+
+ /** Returns the string size. */
+ std::size_t size() const
+ {
+ return size_;
+ }
+
+ // Lexicographically compare this string reference to other.
+ int compare(BasicStringRef other) const
+ {
+ std::size_t size = size_ < other.size_ ? size_ : other.size_;
+ int result = std::char_traits<Char>::compare(data_, other.data_, size);
+ if (result == 0)
+ result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+ return result;
+ }
+
+ friend bool operator==(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ friend bool operator<(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+ friend bool operator>(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) > 0;
+ }
+ friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs)
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+};
+
+typedef BasicStringRef<char> StringRef;
+typedef BasicStringRef<wchar_t> WStringRef;
+
+/**
+ \rst
+ A reference to a null terminated string. It can be constructed from a C
+ string or ``std::string``.
+
+ You can use one of the following typedefs for common character types:
+
+ +-------------+--------------------------+
+ | Type | Definition |
+ +=============+==========================+
+ | CStringRef | BasicCStringRef<char> |
+ +-------------+--------------------------+
+ | WCStringRef | BasicCStringRef<wchar_t> |
+ +-------------+--------------------------+
+
+ This class is most useful as a parameter type to allow passing
+ different types of strings to a function, for example::
+
+ template <typename... Args>
+ std::string format(CStringRef format_str, const Args & ... args);
+
+ format("{}", 42);
+ format(std::string("{}"), 42);
+ \endrst
+ */
+template <typename Char>
+class BasicCStringRef
+{
+private:
+ const Char *data_;
+
+public:
+ /** Constructs a string reference object from a C string. */
+ BasicCStringRef(const Char *s) : data_(s) {}
+
+ /**
+ \rst
+ Constructs a string reference from an ``std::string`` object.
+ \endrst
+ */
+ BasicCStringRef(const std::basic_string<Char> &s) : data_(s.c_str()) {}
+
+ /** Returns the pointer to a C string. */
+ const Char *c_str() const
+ {
+ return data_;
+ }
+};
+
+typedef BasicCStringRef<char> CStringRef;
+typedef BasicCStringRef<wchar_t> WCStringRef;
+
+/** A formatting error such as invalid format string. */
+class FormatError : public std::runtime_error
+{
+public:
+ explicit FormatError(CStringRef message)
+ : std::runtime_error(message.c_str()) {}
+ ~FormatError() throw();
+};
+
+namespace internal
+{
+
+// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T.
+template <typename T>
+struct MakeUnsigned
+{
+ typedef T Type;
+};
+
+#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \
+ template <> \
+ struct MakeUnsigned<T> { typedef U Type; }
+
+FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char);
+FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char);
+FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short);
+FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned);
+FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long);
+FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong);
+
+// Casts nonnegative integer to unsigned.
+template <typename Int>
+inline typename MakeUnsigned<Int>::Type to_unsigned(Int value)
+{
+ FMT_ASSERT(value >= 0, "negative value");
+ return static_cast<typename MakeUnsigned<Int>::Type>(value);
+}
+
+// The number of characters to store in the MemoryBuffer object itself
+// to avoid dynamic memory allocation.
+enum { INLINE_BUFFER_SIZE = 500 };
+
+#if FMT_SECURE_SCL
+// Use checked iterator to avoid warnings on MSVC.
+template <typename T>
+inline stdext::checked_array_iterator<T*> make_ptr(T *ptr, std::size_t size)
+{
+ return stdext::checked_array_iterator<T*>(ptr, size);
+}
+#else
+template <typename T>
+inline T *make_ptr(T *ptr, std::size_t)
+{
+ return ptr;
+}
+#endif
+} // namespace internal
+
+/**
+ \rst
+ A buffer supporting a subset of ``std::vector``'s operations.
+ \endrst
+ */
+template <typename T>
+class Buffer
+{
+private:
+ FMT_DISALLOW_COPY_AND_ASSIGN(Buffer);
+
+protected:
+ T *ptr_;
+ std::size_t size_;
+ std::size_t capacity_;
+
+ Buffer(T *ptr = 0, std::size_t capacity = 0)
+ : ptr_(ptr), size_(0), capacity_(capacity) {}
+
+ /**
+ \rst
+ Increases the buffer capacity to hold at least *size* elements updating
+ ``ptr_`` and ``capacity_``.
+ \endrst
+ */
+ virtual void grow(std::size_t size) = 0;
+
+public:
+ virtual ~Buffer() {}
+
+ /** Returns the size of this buffer. */
+ std::size_t size() const
+ {
+ return size_;
+ }
+
+ /** Returns the capacity of this buffer. */
+ std::size_t capacity() const
+ {
+ return capacity_;
+ }
+
+ /**
+ Resizes the buffer. If T is a POD type new elements may not be initialized.
+ */
+ void resize(std::size_t new_size)
+ {
+ if (new_size > capacity_)
+ grow(new_size);
+ size_ = new_size;
+ }
+
+ /**
+ \rst
+ Reserves space to store at least *capacity* elements.
+ \endrst
+ */
+ void reserve(std::size_t capacity)
+ {
+ if (capacity > capacity_)
+ grow(capacity);
+ }
+
+ void clear() FMT_NOEXCEPT { size_ = 0; }
+
+ void push_back(const T &value)
+ {
+ if (size_ == capacity_)
+ grow(size_ + 1);
+ ptr_[size_++] = value;
+ }
+
+ /** Appends data to the end of the buffer. */
+ template <typename U>
+ void append(const U *begin, const U *end);
+
+ T &operator[](std::size_t index)
+ {
+ return ptr_[index];
+ }
+ const T &operator[](std::size_t index) const
+ {
+ return ptr_[index];
+ }
+};
+
+template <typename T>
+template <typename U>
+void Buffer<T>::append(const U *begin, const U *end)
+{
+ std::size_t new_size = size_ + internal::to_unsigned(end - begin);
+ if (new_size > capacity_)
+ grow(new_size);
+ std::uninitialized_copy(begin, end,
+ internal::make_ptr(ptr_, capacity_) + size_);
+ size_ = new_size;
+}
+
+namespace internal
+{
+
+// A memory buffer for trivially copyable/constructible types with the first
+// SIZE elements stored in the object itself.
+template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >
+class MemoryBuffer : private Allocator, public Buffer<T>
+{
+private:
+ T data_[SIZE];
+
+ // Deallocate memory allocated by the buffer.
+ void deallocate()
+ {
+ if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_);
+ }
+
+protected:
+ void grow(std::size_t size) FMT_OVERRIDE;
+
+public:
+ explicit MemoryBuffer(const Allocator &alloc = Allocator())
+ : Allocator(alloc), Buffer<T>(data_, SIZE) {}
+ ~MemoryBuffer()
+ {
+ deallocate();
+ }
+
+#if FMT_USE_RVALUE_REFERENCES
+private:
+ // Move data from other to this buffer.
+ void move(MemoryBuffer &other)
+ {
+ Allocator &this_alloc = *this, &other_alloc = other;
+ this_alloc = std::move(other_alloc);
+ this->size_ = other.size_;
+ this->capacity_ = other.capacity_;
+ if (other.ptr_ == other.data_)
+ {
+ this->ptr_ = data_;
+ std::uninitialized_copy(other.data_, other.data_ + this->size_,
+ make_ptr(data_, this->capacity_));
+ }
+ else
+ {
+ this->ptr_ = other.ptr_;
+ // Set pointer to the inline array so that delete is not called
+ // when deallocating.
+ other.ptr_ = other.data_;
+ }
+ }
+
+public:
+ MemoryBuffer(MemoryBuffer &&other)
+ {
+ move(other);
+ }
+
+ MemoryBuffer &operator=(MemoryBuffer &&other)
+ {
+ assert(this != &other);
+ deallocate();
+ move(other);
+ return *this;
+ }
+#endif
+
+ // Returns a copy of the allocator associated with this buffer.
+ Allocator get_allocator() const
+ {
+ return *this;
+ }
+};
+
+template <typename T, std::size_t SIZE, typename Allocator>
+void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size)
+{
+ std::size_t new_capacity = this->capacity_ + this->capacity_ / 2;
+ if (size > new_capacity)
+ new_capacity = size;
+ T *new_ptr = this->allocate(new_capacity);
+ // The following code doesn't throw, so the raw pointer above doesn't leak.
+ std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_,
+ make_ptr(new_ptr, new_capacity));
+ std::size_t old_capacity = this->capacity_;
+ T *old_ptr = this->ptr_;
+ this->capacity_ = new_capacity;
+ this->ptr_ = new_ptr;
+ // deallocate may throw (at least in principle), but it doesn't matter since
+ // the buffer already uses the new storage and will deallocate it in case
+ // of exception.
+ if (old_ptr != data_)
+ Allocator::deallocate(old_ptr, old_capacity);
+}
+
+// A fixed-size buffer.
+template <typename Char>
+class FixedBuffer : public fmt::Buffer<Char>
+{
+public:
+ FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}
+
+protected:
+ FMT_API void grow(std::size_t size);
+};
+
+template <typename Char>
+class BasicCharTraits
+{
+public:
+#if FMT_SECURE_SCL
+ typedef stdext::checked_array_iterator<Char*> CharPtr;
+#else
+ typedef Char *CharPtr;
+#endif
+ static Char cast(int value)
+ {
+ return static_cast<Char>(value);
+ }
+};
+
+template <typename Char>
+class CharTraits;
+
+template <>
+class CharTraits<char> : public BasicCharTraits<char>
+{
+private:
+ // Conversion from wchar_t to char is not allowed.
+ static char convert(wchar_t);
+
+public:
+ static char convert(char value)
+ {
+ return value;
+ }
+
+ // Formats a floating-point number.
+ template <typename T>
+ FMT_API static int format_float(char *buffer, std::size_t size,
+ const char *format, unsigned width, int precision, T value);
+};
+
+template <>
+class CharTraits<wchar_t> : public BasicCharTraits<wchar_t>
+{
+public:
+ static wchar_t convert(char value)
+ {
+ return value;
+ }
+ static wchar_t convert(wchar_t value)
+ {
+ return value;
+ }
+
+ template <typename T>
+ FMT_API static int format_float(wchar_t *buffer, std::size_t size,
+ const wchar_t *format, unsigned width, int precision, T value);
+};
+
+// Checks if a number is negative - used to avoid warnings.
+template <bool IsSigned>
+struct SignChecker
+{
+ template <typename T>
+ static bool is_negative(T value)
+ {
+ return value < 0;
+ }
+};
+
+template <>
+struct SignChecker<false>
+{
+ template <typename T>
+ static bool is_negative(T)
+ {
+ return false;
+ }
+};
+
+// Returns true if value is negative, false otherwise.
+// Same as (value < 0) but doesn't produce warnings if T is an unsigned type.
+template <typename T>
+inline bool is_negative(T value)
+{
+ return SignChecker<std::numeric_limits<T>::is_signed>::is_negative(value);
+}
+
+// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise.
+template <bool FitsIn32Bits>
+struct TypeSelector
+{
+ typedef uint32_t Type;
+};
+
+template <>
+struct TypeSelector<false>
+{
+ typedef uint64_t Type;
+};
+
+template <typename T>
+struct IntTraits
+{
+ // Smallest of uint32_t and uint64_t that is large enough to represent
+ // all values of T.
+ typedef typename
+ TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType;
+};
+
+FMT_API void report_unknown_type(char code, const char *type);
+
+// Static data is placed in this class template to allow header-only
+// configuration.
+template <typename T = void>
+struct FMT_API BasicData
+{
+ static const uint32_t POWERS_OF_10_32[];
+ static const uint64_t POWERS_OF_10_64[];
+ static const char DIGITS[];
+};
+
+#ifndef FMT_USE_EXTERN_TEMPLATES
+// Clang doesn't have a feature check for extern templates so we check
+// for variadic templates which were introduced in the same version.
+# define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES)
+#endif
+
+#if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY)
+extern template struct BasicData<void>;
+#endif
+
+typedef BasicData<> Data;
+
+#ifdef FMT_BUILTIN_CLZLL
+// Returns the number of decimal digits in n. Leading zeros are not counted
+// except for n == 0 in which case count_digits returns 1.
+inline unsigned count_digits(uint64_t n)
+{
+ // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+ // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
+ int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
+ return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1;
+}
+#else
+// Fallback version of count_digits used when __builtin_clz is not available.
+inline unsigned count_digits(uint64_t n)
+{
+ unsigned count = 1;
+ for (;;)
+ {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000u;
+ count += 4;
+ }
+}
+#endif
+
+#ifdef FMT_BUILTIN_CLZ
+// Optional version of count_digits for better performance on 32-bit platforms.
+inline unsigned count_digits(uint32_t n)
+{
+ int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
+ return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1;
+}
+#endif
+
+// A functor that doesn't add a thousands separator.
+struct NoThousandsSep
+{
+ template <typename Char>
+ void operator()(Char *) {}
+};
+
+// A functor that adds a thousands separator.
+class ThousandsSep
+{
+private:
+ fmt::StringRef sep_;
+
+ // Index of a decimal digit with the least significant digit having index 0.
+ unsigned digit_index_;
+
+public:
+ explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {}
+
+ template <typename Char>
+ void operator()(Char *&buffer)
+ {
+ if (++digit_index_ % 3 != 0)
+ return;
+ buffer -= sep_.size();
+ std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(),
+ internal::make_ptr(buffer, sep_.size()));
+ }
+};
+
+// Formats a decimal unsigned integer value writing into buffer.
+// thousands_sep is a functor that is called after writing each char to
+// add a thousands separator if necessary.
+template <typename UInt, typename Char, typename ThousandsSep>
+inline void format_decimal(Char *buffer, UInt value, unsigned num_digits,
+ ThousandsSep thousands_sep)
+{
+ buffer += num_digits;
+ while (value >= 100)
+ {
+ // Integer division is slow so do it for a group of two digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ unsigned index = static_cast<unsigned>((value % 100) * 2);
+ value /= 100;
+ *--buffer = Data::DIGITS[index + 1];
+ thousands_sep(buffer);
+ *--buffer = Data::DIGITS[index];
+ thousands_sep(buffer);
+ }
+ if (value < 10)
+ {
+ *--buffer = static_cast<char>('0' + value);
+ return;
+ }
+ unsigned index = static_cast<unsigned>(value * 2);
+ *--buffer = Data::DIGITS[index + 1];
+ thousands_sep(buffer);
+ *--buffer = Data::DIGITS[index];
+}
+
+template <typename UInt, typename Char>
+inline void format_decimal(Char *buffer, UInt value, unsigned num_digits)
+{
+ return format_decimal(buffer, value, num_digits, NoThousandsSep());
+}
+
+#ifndef _WIN32
+# define FMT_USE_WINDOWS_H 0
+#elif !defined(FMT_USE_WINDOWS_H)
+# define FMT_USE_WINDOWS_H 1
+#endif
+
+// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h.
+// All the functionality that relies on it will be disabled too.
+#if FMT_USE_WINDOWS_H
+// A converter from UTF-8 to UTF-16.
+// It is only provided for Windows since other systems support UTF-8 natively.
+class UTF8ToUTF16
+{
+private:
+ MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer_;
+
+public:
+ FMT_API explicit UTF8ToUTF16(StringRef s);
+ operator WStringRef() const
+ {
+ return WStringRef(&buffer_[0], size());
+ }
+ size_t size() const
+ {
+ return buffer_.size() - 1;
+ }
+ const wchar_t *c_str() const
+ {
+ return &buffer_[0];
+ }
+ std::wstring str() const
+ {
+ return std::wstring(&buffer_[0], size());
+ }
+};
+
+// A converter from UTF-16 to UTF-8.
+// It is only provided for Windows since other systems support UTF-8 natively.
+class UTF16ToUTF8
+{
+private:
+ MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer_;
+
+public:
+ UTF16ToUTF8() {}
+ FMT_API explicit UTF16ToUTF8(WStringRef s);
+ operator StringRef() const
+ {
+ return StringRef(&buffer_[0], size());
+ }
+ size_t size() const
+ {
+ return buffer_.size() - 1;
+ }
+ const char *c_str() const
+ {
+ return &buffer_[0];
+ }
+ std::string str() const
+ {
+ return std::string(&buffer_[0], size());
+ }
+
+ // Performs conversion returning a system error code instead of
+ // throwing exception on conversion error. This method may still throw
+ // in case of memory allocation error.
+ FMT_API int convert(WStringRef s);
+};
+
+FMT_API void format_windows_error(fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT;
+#endif
+
+FMT_API void format_system_error(fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT;
+
+// A formatting argument value.
+struct Value
+{
+ template <typename Char>
+ struct StringValue
+ {
+ const Char *value;
+ std::size_t size;
+ };
+
+ typedef void (*FormatFunc)(
+ void *formatter, const void *arg, void *format_str_ptr);
+
+ struct CustomValue
+ {
+ const void *value;
+ FormatFunc format;
+ };
+
+ union
+ {
+ int int_value;
+ unsigned uint_value;
+ LongLong long_long_value;
+ ULongLong ulong_long_value;
+ double double_value;
+ long double long_double_value;
+ const void *pointer;
+ StringValue<char> string;
+ StringValue<signed char> sstring;
+ StringValue<unsigned char> ustring;
+ StringValue<wchar_t> wstring;
+ CustomValue custom;
+ };
+
+ enum Type
+ {
+ NONE, NAMED_ARG,
+ // Integer types should go first,
+ INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR,
+ // followed by floating-point types.
+ DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE,
+ CSTRING, STRING, WSTRING, POINTER, CUSTOM
+ };
+};
+
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in internal::MemoryBuffer.
+struct Arg : Value
+{
+ Type type;
+};
+
+template <typename Char>
+struct NamedArg;
+
+template <typename T = void>
+struct Null {};
+
+// A helper class template to enable or disable overloads taking wide
+// characters and strings in MakeValue.
+template <typename T, typename Char>
+struct WCharHelper
+{
+ typedef Null<T> Supported;
+ typedef T Unsupported;
+};
+
+template <typename T>
+struct WCharHelper<T, wchar_t>
+{
+ typedef T Supported;
+ typedef Null<T> Unsupported;
+};
+
+typedef char Yes[1];
+typedef char No[2];
+
+template <typename T>
+T &get();
+
+// These are non-members to workaround an overload resolution bug in bcc32.
+Yes &convert(fmt::ULongLong);
+No &convert(...);
+
+template<typename T, bool ENABLE_CONVERSION>
+struct ConvertToIntImpl
+{
+ enum { value = ENABLE_CONVERSION };
+};
+
+template<typename T, bool ENABLE_CONVERSION>
+struct ConvertToIntImpl2
+{
+ enum { value = false };
+};
+
+template<typename T>
+struct ConvertToIntImpl2<T, true>
+{
+ enum
+ {
+ // Don't convert numeric types.
+ value = ConvertToIntImpl<T, !std::numeric_limits<T>::is_specialized>::value
+ };
+};
+
+template<typename T>
+struct ConvertToInt
+{
+ enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
+ enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
+};
+
+#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
+ template <> \
+ struct ConvertToInt<Type> { enum { value = 0 }; }
+
+// Silence warnings about convering float to int.
+FMT_DISABLE_CONVERSION_TO_INT(float);
+FMT_DISABLE_CONVERSION_TO_INT(double);
+FMT_DISABLE_CONVERSION_TO_INT(long double);
+
+template<bool B, class T = void>
+struct EnableIf {};
+
+template<class T>
+struct EnableIf<true, T>
+{
+ typedef T type;
+};
+
+template<bool B, class T, class F>
+struct Conditional
+{
+ typedef T type;
+};
+
+template<class T, class F>
+struct Conditional<false, T, F>
+{
+ typedef F type;
+};
+
+// For bcc32 which doesn't understand ! in template arguments.
+template<bool>
+struct Not
+{
+ enum { value = 0 };
+};
+
+template<>
+struct Not<false>
+{
+ enum { value = 1 };
+};
+
+template<typename T, T> struct LConvCheck
+{
+ LConvCheck(int) {}
+};
+
+// Returns the thousands separator for the current locale.
+// We check if ``lconv`` contains ``thousands_sep`` because on Android
+// ``lconv`` is stubbed as an empty struct.
+template <typename LConv>
+inline StringRef thousands_sep(
+ LConv *lc, LConvCheck<char *LConv::*, &LConv::thousands_sep> = 0)
+{
+ return lc->thousands_sep;
+}
+
+inline fmt::StringRef thousands_sep(...)
+{
+ return "";
+}
+
+// Makes an Arg object from any type.
+template <typename Formatter>
+class MakeValue : public Arg
+{
+public:
+ typedef typename Formatter::Char Char;
+
+private:
+ // The following two methods are private to disallow formatting of
+ // arbitrary pointers. If you want to output a pointer cast it to
+ // "void *" or "const void *". In particular, this forbids formatting
+ // of "[const] volatile char *" which is printed as bool by iostreams.
+ // Do not implement!
+ template <typename T>
+ MakeValue(const T *value);
+ template <typename T>
+ MakeValue(T *value);
+
+ // The following methods are private to disallow formatting of wide
+ // characters and strings into narrow strings as in
+ // fmt::format("{}", L"test");
+ // To fix this, use a wide format string: fmt::format(L"{}", L"test").
+#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED)
+ MakeValue(typename WCharHelper<wchar_t, Char>::Unsupported);
+#endif
+ MakeValue(typename WCharHelper<wchar_t *, Char>::Unsupported);
+ MakeValue(typename WCharHelper<const wchar_t *, Char>::Unsupported);
+ MakeValue(typename WCharHelper<const std::wstring &, Char>::Unsupported);
+ MakeValue(typename WCharHelper<WStringRef, Char>::Unsupported);
+
+ void set_string(StringRef str)
+ {
+ string.value = str.data();
+ string.size = str.size();
+ }
+
+ void set_string(WStringRef str)
+ {
+ wstring.value = str.data();
+ wstring.size = str.size();
+ }
+
+ // Formats an argument of a custom type, such as a user-defined class.
+ template <typename T>
+ static void format_custom_arg(
+ void *formatter, const void *arg, void *format_str_ptr)
+ {
+ format(*static_cast<Formatter*>(formatter),
+ *static_cast<const Char**>(format_str_ptr),
+ *static_cast<const T*>(arg));
+ }
+
+public:
+ MakeValue() {}
+
+#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \
+ MakeValue(Type value) { field = rhs; } \
+ static uint64_t type(Type) { return Arg::TYPE; }
+
+#define FMT_MAKE_VALUE(Type, field, TYPE) \
+ FMT_MAKE_VALUE_(Type, field, TYPE, value)
+
+ FMT_MAKE_VALUE(bool, int_value, BOOL)
+ FMT_MAKE_VALUE(short, int_value, INT)
+ FMT_MAKE_VALUE(unsigned short, uint_value, UINT)
+ FMT_MAKE_VALUE(int, int_value, INT)
+ FMT_MAKE_VALUE(unsigned, uint_value, UINT)
+
+ MakeValue(long value)
+ {
+ // To minimize the number of types we need to deal with, long is
+ // translated either to int or to long long depending on its size.
+ if (const_check(sizeof(long) == sizeof(int)))
+ int_value = static_cast<int>(value);
+ else
+ long_long_value = value;
+ }
+ static uint64_t type(long)
+ {
+ return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG;
+ }
+
+ MakeValue(unsigned long value)
+ {
+ if (const_check(sizeof(unsigned long) == sizeof(unsigned)))
+ uint_value = static_cast<unsigned>(value);
+ else
+ ulong_long_value = value;
+ }
+ static uint64_t type(unsigned long)
+ {
+ return sizeof(unsigned long) == sizeof(unsigned) ?
+ Arg::UINT : Arg::ULONG_LONG;
+ }
+
+ FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG)
+ FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG)
+ FMT_MAKE_VALUE(float, double_value, DOUBLE)
+ FMT_MAKE_VALUE(double, double_value, DOUBLE)
+ FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE)
+ FMT_MAKE_VALUE(signed char, int_value, INT)
+ FMT_MAKE_VALUE(unsigned char, uint_value, UINT)
+ FMT_MAKE_VALUE(char, int_value, CHAR)
+
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+ MakeValue(typename WCharHelper<wchar_t, Char>::Supported value)
+ {
+ int_value = value;
+ }
+ static uint64_t type(wchar_t)
+ {
+ return Arg::CHAR;
+ }
+#endif
+
+#define FMT_MAKE_STR_VALUE(Type, TYPE) \
+ MakeValue(Type value) { set_string(value); } \
+ static uint64_t type(Type) { return Arg::TYPE; }
+
+ FMT_MAKE_VALUE(char *, string.value, CSTRING)
+ FMT_MAKE_VALUE(const char *, string.value, CSTRING)
+ FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING)
+ FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING)
+ FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING)
+ FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING)
+ FMT_MAKE_STR_VALUE(const std::string &, STRING)
+ FMT_MAKE_STR_VALUE(StringRef, STRING)
+ FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str())
+
+#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \
+ MakeValue(typename WCharHelper<Type, Char>::Supported value) { \
+ set_string(value); \
+ } \
+ static uint64_t type(Type) { return Arg::TYPE; }
+
+ FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING)
+ FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING)
+ FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING)
+ FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING)
+
+ FMT_MAKE_VALUE(void *, pointer, POINTER)
+ FMT_MAKE_VALUE(const void *, pointer, POINTER)
+
+ template <typename T>
+ MakeValue(const T &value,
+ typename EnableIf<Not<
+ ConvertToInt<T>::value>::value, int>::type = 0)
+ {
+ custom.value = &value;
+ custom.format = &format_custom_arg<T>;
+ }
+
+ template <typename T>
+ MakeValue(const T &value,
+ typename EnableIf<ConvertToInt<T>::value, int>::type = 0)
+ {
+ int_value = value;
+ }
+
+ template <typename T>
+ static uint64_t type(const T &)
+ {
+ return ConvertToInt<T>::value ? Arg::INT : Arg::CUSTOM;
+ }
+
+ // Additional template param `Char_` is needed here because make_type always
+ // uses char.
+ template <typename Char_>
+ MakeValue(const NamedArg<Char_> &value)
+ {
+ pointer = &value;
+ }
+
+ template <typename Char_>
+ static uint64_t type(const NamedArg<Char_> &)
+ {
+ return Arg::NAMED_ARG;
+ }
+};
+
+template <typename Formatter>
+class MakeArg : public Arg
+{
+public:
+ MakeArg()
+ {
+ type = Arg::NONE;
+ }
+
+ template <typename T>
+ MakeArg(const T &value)
+ : Arg(MakeValue<Formatter>(value))
+ {
+ type = static_cast<Arg::Type>(MakeValue<Formatter>::type(value));
+ }
+};
+
+template <typename Char>
+struct NamedArg : Arg
+{
+ BasicStringRef<Char> name;
+
+ template <typename T>
+ NamedArg(BasicStringRef<Char> argname, const T &value)
+ : Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname) {}
+};
+
+class RuntimeError : public std::runtime_error
+{
+protected:
+ RuntimeError() : std::runtime_error("") {}
+ ~RuntimeError() throw();
+};
+
+template <typename Char>
+class PrintfArgFormatter;
+
+template <typename Char>
+class ArgMap;
+} // namespace internal
+
+/** An argument list. */
+class ArgList
+{
+private:
+ // To reduce compiled code size per formatting function call, types of first
+ // MAX_PACKED_ARGS arguments are passed in the types_ field.
+ uint64_t types_;
+ union
+ {
+ // If the number of arguments is less than MAX_PACKED_ARGS, the argument
+ // values are stored in values_, otherwise they are stored in args_.
+ // This is done to reduce compiled code size as storing larger objects
+ // may require more code (at least on x86-64) even if the same amount of
+ // data is actually copied to stack. It saves ~10% on the bloat test.
+ const internal::Value *values_;
+ const internal::Arg *args_;
+ };
+
+ internal::Arg::Type type(unsigned index) const
+ {
+ unsigned shift = index * 4;
+ uint64_t mask = 0xf;
+ return static_cast<internal::Arg::Type>(
+ (types_ & (mask << shift)) >> shift);
+ }
+
+ template <typename Char>
+ friend class internal::ArgMap;
+
+public:
+ // Maximum number of arguments with packed types.
+ enum { MAX_PACKED_ARGS = 16 };
+
+ ArgList() : types_(0) {}
+
+ ArgList(ULongLong types, const internal::Value *values)
+ : types_(types), values_(values) {}
+ ArgList(ULongLong types, const internal::Arg *args)
+ : types_(types), args_(args) {}
+
+ /** Returns the argument at specified index. */
+ internal::Arg operator[](unsigned index) const
+ {
+ using internal::Arg;
+ Arg arg;
+ bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE;
+ if (index < MAX_PACKED_ARGS)
+ {
+ Arg::Type arg_type = type(index);
+ internal::Value &val = arg;
+ if (arg_type != Arg::NONE)
+ val = use_values ? values_[index] : args_[index];
+ arg.type = arg_type;
+ return arg;
+ }
+ if (use_values)
+ {
+ // The index is greater than the number of arguments that can be stored
+ // in values, so return a "none" argument.
+ arg.type = Arg::NONE;
+ return arg;
+ }
+ for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i)
+ {
+ if (args_[i].type == Arg::NONE)
+ return args_[i];
+ }
+ return args_[index];
+ }
+};
+
+#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
+
+/**
+ \rst
+ An argument visitor based on the `curiously recurring template pattern
+ <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
+
+ To use `~fmt::ArgVisitor` define a subclass that implements some or all of the
+ visit methods with the same signatures as the methods in `~fmt::ArgVisitor`,
+ for example, `~fmt::ArgVisitor::visit_int()`.
+ Pass the subclass as the *Impl* template parameter. Then calling
+ `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method
+ specific to the argument type. For example, if the argument type is
+ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
+ will be called. If the subclass doesn't contain a method with this signature,
+ then a corresponding method of `~fmt::ArgVisitor` will be called.
+
+ **Example**::
+
+ class MyArgVisitor : public fmt::ArgVisitor<MyArgVisitor, void> {
+ public:
+ void visit_int(int value) { fmt::print("{}", value); }
+ void visit_double(double value) { fmt::print("{}", value ); }
+ };
+ \endrst
+ */
+template <typename Impl, typename Result>
+class ArgVisitor
+{
+private:
+ typedef internal::Arg Arg;
+
+public:
+ void report_unhandled_arg() {}
+
+ Result visit_unhandled_arg()
+ {
+ FMT_DISPATCH(report_unhandled_arg());
+ return Result();
+ }
+
+ /** Visits an ``int`` argument. **/
+ Result visit_int(int value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits a ``long long`` argument. **/
+ Result visit_long_long(LongLong value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits an ``unsigned`` argument. **/
+ Result visit_uint(unsigned value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits an ``unsigned long long`` argument. **/
+ Result visit_ulong_long(ULongLong value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits a ``bool`` argument. **/
+ Result visit_bool(bool value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits a ``char`` or ``wchar_t`` argument. **/
+ Result visit_char(int value)
+ {
+ return FMT_DISPATCH(visit_any_int(value));
+ }
+
+ /** Visits an argument of any integral type. **/
+ template <typename T>
+ Result visit_any_int(T)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits a ``double`` argument. **/
+ Result visit_double(double value)
+ {
+ return FMT_DISPATCH(visit_any_double(value));
+ }
+
+ /** Visits a ``long double`` argument. **/
+ Result visit_long_double(long double value)
+ {
+ return FMT_DISPATCH(visit_any_double(value));
+ }
+
+ /** Visits a ``double`` or ``long double`` argument. **/
+ template <typename T>
+ Result visit_any_double(T)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits a null-terminated C string (``const char *``) argument. **/
+ Result visit_cstring(const char *)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits a string argument. **/
+ Result visit_string(Arg::StringValue<char>)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits a wide string argument. **/
+ Result visit_wstring(Arg::StringValue<wchar_t>)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits a pointer argument. **/
+ Result visit_pointer(const void *)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /** Visits an argument of a custom (user-defined) type. **/
+ Result visit_custom(Arg::CustomValue)
+ {
+ return FMT_DISPATCH(visit_unhandled_arg());
+ }
+
+ /**
+ \rst
+ Visits an argument dispatching to the appropriate visit method based on
+ the argument type. For example, if the argument type is ``double`` then
+ the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be
+ called.
+ \endrst
+ */
+ Result visit(const Arg &arg)
+ {
+ switch (arg.type)
+ {
+ case Arg::NONE:
+ case Arg::NAMED_ARG:
+ FMT_ASSERT(false, "invalid argument type");
+ break;
+ case Arg::INT:
+ return FMT_DISPATCH(visit_int(arg.int_value));
+ case Arg::UINT:
+ return FMT_DISPATCH(visit_uint(arg.uint_value));
+ case Arg::LONG_LONG:
+ return FMT_DISPATCH(visit_long_long(arg.long_long_value));
+ case Arg::ULONG_LONG:
+ return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value));
+ case Arg::BOOL:
+ return FMT_DISPATCH(visit_bool(arg.int_value != 0));
+ case Arg::CHAR:
+ return FMT_DISPATCH(visit_char(arg.int_value));
+ case Arg::DOUBLE:
+ return FMT_DISPATCH(visit_double(arg.double_value));
+ case Arg::LONG_DOUBLE:
+ return FMT_DISPATCH(visit_long_double(arg.long_double_value));
+ case Arg::CSTRING:
+ return FMT_DISPATCH(visit_cstring(arg.string.value));
+ case Arg::STRING:
+ return FMT_DISPATCH(visit_string(arg.string));
+ case Arg::WSTRING:
+ return FMT_DISPATCH(visit_wstring(arg.wstring));
+ case Arg::POINTER:
+ return FMT_DISPATCH(visit_pointer(arg.pointer));
+ case Arg::CUSTOM:
+ return FMT_DISPATCH(visit_custom(arg.custom));
+ }
+ return Result();
+ }
+};
+
+enum Alignment
+{
+ ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
+};
+
+// Flags.
+enum
+{
+ SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8,
+ CHAR_FLAG = 0x10 // Argument has char type - used in error reporting.
+};
+
+// An empty format specifier.
+struct EmptySpec {};
+
+// A type specifier.
+template <char TYPE>
+struct TypeSpec : EmptySpec
+{
+ Alignment align() const
+ {
+ return ALIGN_DEFAULT;
+ }
+ unsigned width() const
+ {
+ return 0;
+ }
+ int precision() const
+ {
+ return -1;
+ }
+ bool flag(unsigned) const
+ {
+ return false;
+ }
+ char type() const
+ {
+ return TYPE;
+ }
+ char fill() const
+ {
+ return ' ';
+ }
+};
+
+// A width specifier.
+struct WidthSpec
+{
+ unsigned width_;
+ // Fill is always wchar_t and cast to char if necessary to avoid having
+ // two specialization of WidthSpec and its subclasses.
+ wchar_t fill_;
+
+ WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {}
+
+ unsigned width() const
+ {
+ return width_;
+ }
+ wchar_t fill() const
+ {
+ return fill_;
+ }
+};
+
+// An alignment specifier.
+struct AlignSpec : WidthSpec
+{
+ Alignment align_;
+
+ AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT)
+ : WidthSpec(width, fill), align_(align) {}
+
+ Alignment align() const
+ {
+ return align_;
+ }
+
+ int precision() const
+ {
+ return -1;
+ }
+};
+
+// An alignment and type specifier.
+template <char TYPE>
+struct AlignTypeSpec : AlignSpec
+{
+ AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {}
+
+ bool flag(unsigned) const
+ {
+ return false;
+ }
+ char type() const
+ {
+ return TYPE;
+ }
+};
+
+// A full format specifier.
+struct FormatSpec : AlignSpec
+{
+ unsigned flags_;
+ int precision_;
+ char type_;
+
+ FormatSpec(
+ unsigned width = 0, char type = 0, wchar_t fill = ' ')
+ : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {}
+
+ bool flag(unsigned f) const
+ {
+ return (flags_ & f) != 0;
+ }
+ int precision() const
+ {
+ return precision_;
+ }
+ char type() const
+ {
+ return type_;
+ }
+};
+
+// An integer format specifier.
+template <typename T, typename SpecT = TypeSpec<0>, typename Char = char>
+class IntFormatSpec : public SpecT
+{
+private:
+ T value_;
+
+public:
+ IntFormatSpec(T val, const SpecT &spec = SpecT())
+ : SpecT(spec), value_(val) {}
+
+ T value() const
+ {
+ return value_;
+ }
+};
+
+// A string format specifier.
+template <typename Char>
+class StrFormatSpec : public AlignSpec
+{
+private:
+ const Char *str_;
+
+public:
+ template <typename FillChar>
+ StrFormatSpec(const Char *str, unsigned width, FillChar fill)
+ : AlignSpec(width, fill), str_(str)
+ {
+ internal::CharTraits<Char>::convert(FillChar());
+ }
+
+ const Char *str() const
+ {
+ return str_;
+ }
+};
+
+/**
+ Returns an integer format specifier to format the value in base 2.
+ */
+IntFormatSpec<int, TypeSpec<'b'> > bin(int value);
+
+/**
+ Returns an integer format specifier to format the value in base 8.
+ */
+IntFormatSpec<int, TypeSpec<'o'> > oct(int value);
+
+/**
+ Returns an integer format specifier to format the value in base 16 using
+ lower-case letters for the digits above 9.
+ */
+IntFormatSpec<int, TypeSpec<'x'> > hex(int value);
+
+/**
+ Returns an integer formatter format specifier to format in base 16 using
+ upper-case letters for the digits above 9.
+ */
+IntFormatSpec<int, TypeSpec<'X'> > hexu(int value);
+
+/**
+ \rst
+ Returns an integer format specifier to pad the formatted argument with the
+ fill character to the specified width using the default (right) numeric
+ alignment.
+
+ **Example**::
+
+ MemoryWriter out;
+ out << pad(hex(0xcafe), 8, '0');
+ // out.str() == "0000cafe"
+
+ \endrst
+ */
+template <char TYPE_CODE, typename Char>
+IntFormatSpec<int, AlignTypeSpec<TYPE_CODE>, Char> pad(
+ int value, unsigned width, Char fill = ' ');
+
+#define FMT_DEFINE_INT_FORMATTERS(TYPE) \
+inline IntFormatSpec<TYPE, TypeSpec<'b'> > bin(TYPE value) { \
+ return IntFormatSpec<TYPE, TypeSpec<'b'> >(value, TypeSpec<'b'>()); \
+} \
+ \
+inline IntFormatSpec<TYPE, TypeSpec<'o'> > oct(TYPE value) { \
+ return IntFormatSpec<TYPE, TypeSpec<'o'> >(value, TypeSpec<'o'>()); \
+} \
+ \
+inline IntFormatSpec<TYPE, TypeSpec<'x'> > hex(TYPE value) { \
+ return IntFormatSpec<TYPE, TypeSpec<'x'> >(value, TypeSpec<'x'>()); \
+} \
+ \
+inline IntFormatSpec<TYPE, TypeSpec<'X'> > hexu(TYPE value) { \
+ return IntFormatSpec<TYPE, TypeSpec<'X'> >(value, TypeSpec<'X'>()); \
+} \
+ \
+template <char TYPE_CODE> \
+inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> > pad( \
+ IntFormatSpec<TYPE, TypeSpec<TYPE_CODE> > f, unsigned width) { \
+ return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE> >( \
+ f.value(), AlignTypeSpec<TYPE_CODE>(width, ' ')); \
+} \
+ \
+/* For compatibility with older compilers we provide two overloads for pad, */ \
+/* one that takes a fill character and one that doesn't. In the future this */ \
+/* can be replaced with one overload making the template argument Char */ \
+/* default to char (C++11). */ \
+template <char TYPE_CODE, typename Char> \
+inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char> pad( \
+ IntFormatSpec<TYPE, TypeSpec<TYPE_CODE>, Char> f, \
+ unsigned width, Char fill) { \
+ return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char>( \
+ f.value(), AlignTypeSpec<TYPE_CODE>(width, fill)); \
+} \
+ \
+inline IntFormatSpec<TYPE, AlignTypeSpec<0> > pad( \
+ TYPE value, unsigned width) { \
+ return IntFormatSpec<TYPE, AlignTypeSpec<0> >( \
+ value, AlignTypeSpec<0>(width, ' ')); \
+} \
+ \
+template <typename Char> \
+inline IntFormatSpec<TYPE, AlignTypeSpec<0>, Char> pad( \
+ TYPE value, unsigned width, Char fill) { \
+ return IntFormatSpec<TYPE, AlignTypeSpec<0>, Char>( \
+ value, AlignTypeSpec<0>(width, fill)); \
+}
+
+FMT_DEFINE_INT_FORMATTERS(int)
+FMT_DEFINE_INT_FORMATTERS(long)
+FMT_DEFINE_INT_FORMATTERS(unsigned)
+FMT_DEFINE_INT_FORMATTERS(unsigned long)
+FMT_DEFINE_INT_FORMATTERS(LongLong)
+FMT_DEFINE_INT_FORMATTERS(ULongLong)
+
+/**
+ \rst
+ Returns a string formatter that pads the formatted argument with the fill
+ character to the specified width using the default (left) string alignment.
+
+ **Example**::
+
+ std::string s = str(MemoryWriter() << pad("abc", 8));
+ // s == "abc "
+
+ \endrst
+ */
+template <typename Char>
+inline StrFormatSpec<Char> pad(
+ const Char *str, unsigned width, Char fill = ' ')
+{
+ return StrFormatSpec<Char>(str, width, fill);
+}
+
+inline StrFormatSpec<wchar_t> pad(
+ const wchar_t *str, unsigned width, char fill = ' ')
+{
+ return StrFormatSpec<wchar_t>(str, width, fill);
+}
+
+namespace internal
+{
+
+template <typename Char>
+class ArgMap
+{
+private:
+ typedef std::vector<
+ std::pair<fmt::BasicStringRef<Char>, internal::Arg> > MapType;
+ typedef typename MapType::value_type Pair;
+
+ MapType map_;
+
+public:
+ FMT_API void init(const ArgList &args);
+
+ const internal::Arg* find(const fmt::BasicStringRef<Char> &name) const
+ {
+ // The list is unsorted, so just return the first matching name.
+ for (typename MapType::const_iterator it = map_.begin(), end = map_.end();
+ it != end; ++it)
+ {
+ if (it->first == name)
+ return &it->second;
+ }
+ return 0;
+ }
+};
+
+template <typename Impl, typename Char>
+class ArgFormatterBase : public ArgVisitor<Impl, void>
+{
+private:
+ BasicWriter<Char> &writer_;
+ FormatSpec &spec_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase);
+
+ void write_pointer(const void *p)
+ {
+ spec_.flags_ = HASH_FLAG;
+ spec_.type_ = 'x';
+ writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
+ }
+
+protected:
+ BasicWriter<Char> &writer()
+ {
+ return writer_;
+ }
+ FormatSpec &spec()
+ {
+ return spec_;
+ }
+
+ void write(bool value)
+ {
+ const char *str_value = value ? "true" : "false";
+ Arg::StringValue<char> str = { str_value, std::strlen(str_value) };
+ writer_.write_str(str, spec_);
+ }
+
+ void write(const char *value)
+ {
+ Arg::StringValue<char> str = {value, value != 0 ? std::strlen(value) : 0};
+ writer_.write_str(str, spec_);
+ }
+
+public:
+ ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s)
+ : writer_(w), spec_(s) {}
+
+ template <typename T>
+ void visit_any_int(T value)
+ {
+ writer_.write_int(value, spec_);
+ }
+
+ template <typename T>
+ void visit_any_double(T value)
+ {
+ writer_.write_double(value, spec_);
+ }
+
+ void visit_bool(bool value)
+ {
+ if (spec_.type_)
+ return visit_any_int(value);
+ write(value);
+ }
+
+ void visit_char(int value)
+ {
+ if (spec_.type_ && spec_.type_ != 'c')
+ {
+ spec_.flags_ |= CHAR_FLAG;
+ writer_.write_int(value, spec_);
+ return;
+ }
+ if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
+ FMT_THROW(FormatError("invalid format specifier for char"));
+ typedef typename BasicWriter<Char>::CharPtr CharPtr;
+ Char fill = internal::CharTraits<Char>::cast(spec_.fill());
+ CharPtr out = CharPtr();
+ const unsigned CHAR_SIZE = 1;
+ if (spec_.width_ > CHAR_SIZE)
+ {
+ out = writer_.grow_buffer(spec_.width_);
+ if (spec_.align_ == ALIGN_RIGHT)
+ {
+ std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill);
+ out += spec_.width_ - CHAR_SIZE;
+ }
+ else if (spec_.align_ == ALIGN_CENTER)
+ {
+ out = writer_.fill_padding(out, spec_.width_,
+ internal::const_check(CHAR_SIZE), fill);
+ }
+ else
+ {
+ std::uninitialized_fill_n(out + CHAR_SIZE,
+ spec_.width_ - CHAR_SIZE, fill);
+ }
+ }
+ else
+ {
+ out = writer_.grow_buffer(CHAR_SIZE);
+ }
+ *out = internal::CharTraits<Char>::cast(value);
+ }
+
+ void visit_cstring(const char *value)
+ {
+ if (spec_.type_ == 'p')
+ return write_pointer(value);
+ write(value);
+ }
+
+ void visit_string(Arg::StringValue<char> value)
+ {
+ writer_.write_str(value, spec_);
+ }
+
+ using ArgVisitor<Impl, void>::visit_wstring;
+
+ void visit_wstring(Arg::StringValue<Char> value)
+ {
+ writer_.write_str(value, spec_);
+ }
+
+ void visit_pointer(const void *value)
+ {
+ if (spec_.type_ && spec_.type_ != 'p')
+ report_unknown_type(spec_.type_, "pointer");
+ write_pointer(value);
+ }
+};
+
+class FormatterBase
+{
+private:
+ ArgList args_;
+ int next_arg_index_;
+
+ // Returns the argument with specified index.
+ FMT_API Arg do_get_arg(unsigned arg_index, const char *&error);
+
+protected:
+ const ArgList &args() const
+ {
+ return args_;
+ }
+
+ explicit FormatterBase(const ArgList &args)
+ {
+ args_ = args;
+ next_arg_index_ = 0;
+ }
+
+ // Returns the next argument.
+ Arg next_arg(const char *&error)
+ {
+ if (next_arg_index_ >= 0)
+ return do_get_arg(internal::to_unsigned(next_arg_index_++), error);
+ error = "cannot switch from manual to automatic argument indexing";
+ return Arg();
+ }
+
+ // Checks if manual indexing is used and returns the argument with
+ // specified index.
+ Arg get_arg(unsigned arg_index, const char *&error)
+ {
+ return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg();
+ }
+
+ bool check_no_auto_index(const char *&error)
+ {
+ if (next_arg_index_ > 0)
+ {
+ error = "cannot switch from automatic to manual argument indexing";
+ return false;
+ }
+ next_arg_index_ = -1;
+ return true;
+ }
+
+ template <typename Char>
+ void write(BasicWriter<Char> &w, const Char *start, const Char *end)
+ {
+ if (start != end)
+ w << BasicStringRef<Char>(start, internal::to_unsigned(end - start));
+ }
+};
+
+// A printf formatter.
+template <typename Char>
+class PrintfFormatter : private FormatterBase
+{
+private:
+ void parse_flags(FormatSpec &spec, const Char *&s);
+
+ // Returns the argument with specified index or, if arg_index is equal
+ // to the maximum unsigned value, the next argument.
+ Arg get_arg(const Char *s,
+ unsigned arg_index = (std::numeric_limits<unsigned>::max)());
+
+ // Parses argument index, flags and width and returns the argument index.
+ unsigned parse_header(const Char *&s, FormatSpec &spec);
+
+public:
+ explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {}
+ FMT_API void format(BasicWriter<Char> &writer,
+ BasicCStringRef<Char> format_str);
+};
+} // namespace internal
+
+/**
+ \rst
+ An argument formatter based on the `curiously recurring template pattern
+ <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
+
+ To use `~fmt::BasicArgFormatter` define a subclass that implements some or
+ all of the visit methods with the same signatures as the methods in
+ `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
+ Pass the subclass as the *Impl* template parameter. When a formatting
+ function processes an argument, it will dispatch to a visit method
+ specific to the argument type. For example, if the argument type is
+ ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
+ will be called. If the subclass doesn't contain a method with this signature,
+ then a corresponding method of `~fmt::BasicArgFormatter` or its superclass
+ will be called.
+ \endrst
+ */
+template <typename Impl, typename Char>
+class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char>
+{
+private:
+ BasicFormatter<Char, Impl> &formatter_;
+ const Char *format_;
+
+public:
+ /**
+ \rst
+ Constructs an argument formatter object.
+ *formatter* is a reference to the main formatter object, *spec* contains
+ format specifier information for standard argument types, and *fmt* points
+ to the part of the format string being parsed for custom argument types.
+ \endrst
+ */
+ BasicArgFormatter(BasicFormatter<Char, Impl> &formatter,
+ FormatSpec &spec, const Char *fmt)
+ : internal::ArgFormatterBase<Impl, Char>(formatter.writer(), spec),
+ formatter_(formatter), format_(fmt) {}
+
+ /** Formats argument of a custom (user-defined) type. */
+ void visit_custom(internal::Arg::CustomValue c)
+ {
+ c.format(&formatter_, c.value, &format_);
+ }
+};
+
+/** The default argument formatter. */
+template <typename Char>
+class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char>
+{
+public:
+ /** Constructs an argument formatter object. */
+ ArgFormatter(BasicFormatter<Char> &formatter,
+ FormatSpec &spec, const Char *fmt)
+ : BasicArgFormatter<ArgFormatter<Char>, Char>(formatter, spec, fmt) {}
+};
+
+/** This template formats data and writes the output to a writer. */
+template <typename CharType, typename ArgFormatter>
+class BasicFormatter : private internal::FormatterBase
+{
+public:
+ /** The character type for the output. */
+ typedef CharType Char;
+
+private:
+ BasicWriter<Char> &writer_;
+ internal::ArgMap<Char> map_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter);
+
+ using internal::FormatterBase::get_arg;
+
+ // Checks if manual indexing is used and returns the argument with
+ // specified name.
+ internal::Arg get_arg(BasicStringRef<Char> arg_name, const char *&error);
+
+ // Parses argument index and returns corresponding argument.
+ internal::Arg parse_arg_index(const Char *&s);
+
+ // Parses argument name and returns corresponding argument.
+ internal::Arg parse_arg_name(const Char *&s);
+
+public:
+ /**
+ \rst
+ Constructs a ``BasicFormatter`` object. References to the arguments and
+ the writer are stored in the formatter object so make sure they have
+ appropriate lifetimes.
+ \endrst
+ */
+ BasicFormatter(const ArgList &args, BasicWriter<Char> &w)
+ : internal::FormatterBase(args), writer_(w) {}
+
+ /** Returns a reference to the writer associated with this formatter. */
+ BasicWriter<Char> &writer()
+ {
+ return writer_;
+ }
+
+ /** Formats stored arguments and writes the output to the writer. */
+ void format(BasicCStringRef<Char> format_str);
+
+ // Formats a single argument and advances format_str, a format string pointer.
+ const Char *format(const Char *&format_str, const internal::Arg &arg);
+};
+
+// Generates a comma-separated list with results of applying f to
+// numbers 0..n-1.
+# define FMT_GEN(n, f) FMT_GEN##n(f)
+# define FMT_GEN1(f) f(0)
+# define FMT_GEN2(f) FMT_GEN1(f), f(1)
+# define FMT_GEN3(f) FMT_GEN2(f), f(2)
+# define FMT_GEN4(f) FMT_GEN3(f), f(3)
+# define FMT_GEN5(f) FMT_GEN4(f), f(4)
+# define FMT_GEN6(f) FMT_GEN5(f), f(5)
+# define FMT_GEN7(f) FMT_GEN6(f), f(6)
+# define FMT_GEN8(f) FMT_GEN7(f), f(7)
+# define FMT_GEN9(f) FMT_GEN8(f), f(8)
+# define FMT_GEN10(f) FMT_GEN9(f), f(9)
+# define FMT_GEN11(f) FMT_GEN10(f), f(10)
+# define FMT_GEN12(f) FMT_GEN11(f), f(11)
+# define FMT_GEN13(f) FMT_GEN12(f), f(12)
+# define FMT_GEN14(f) FMT_GEN13(f), f(13)
+# define FMT_GEN15(f) FMT_GEN14(f), f(14)
+
+namespace internal
+{
+inline uint64_t make_type()
+{
+ return 0;
+}
+
+template <typename T>
+inline uint64_t make_type(const T &arg)
+{
+ return MakeValue< BasicFormatter<char> >::type(arg);
+}
+
+template <unsigned N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
+ struct ArgArray;
+
+template <unsigned N>
+struct ArgArray<N, true/*IsPacked*/>
+{
+ typedef Value Type[N > 0 ? N : 1];
+
+template <typename Formatter, typename T>
+static Value make(const T &value)
+{
+#ifdef __clang__
+ Value result = MakeValue<Formatter>(value);
+ // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang:
+ // https://github.com/fmtlib/fmt/issues/276
+ (void)result.custom.format;
+ return result;
+#else
+ return MakeValue<Formatter>(value);
+#endif
+}
+ };
+
+template <unsigned N>
+struct ArgArray<N, false/*IsPacked*/>
+{
+ typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE
+
+ template <typename Formatter, typename T>
+ static Arg make(const T &value)
+ {
+ return MakeArg<Formatter>(value);
+ }
+};
+
+#if FMT_USE_VARIADIC_TEMPLATES
+template <typename Arg, typename... Args>
+inline uint64_t make_type(const Arg &first, const Args & ... tail)
+{
+ return make_type(first) | (make_type(tail...) << 4);
+}
+
+#else
+
+struct ArgType
+{
+ uint64_t type;
+
+ ArgType() : type(0) {}
+
+ template <typename T>
+ ArgType(const T &arg) : type(make_type(arg)) {}
+};
+
+# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType()
+
+inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT))
+{
+ return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) |
+ (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) |
+ (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) |
+ (t12.type << 48) | (t13.type << 52) | (t14.type << 56);
+}
+#endif
+} // namespace internal
+
+# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n
+# define FMT_MAKE_ARG_TYPE(n) T##n
+# define FMT_MAKE_ARG(n) const T##n &v##n
+# define FMT_ASSIGN_char(n) \
+ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter<char> >(v##n)
+# define FMT_ASSIGN_wchar_t(n) \
+ arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter<wchar_t> >(v##n)
+
+#if FMT_USE_VARIADIC_TEMPLATES
+// Defines a variadic function returning void.
+# define FMT_VARIADIC_VOID(func, arg_type) \
+ template <typename... Args> \
+ void func(arg_type arg0, const Args & ... args) { \
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
+ func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \
+ }
+
+// Defines a variadic constructor.
+# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
+ template <typename... Args> \
+ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
+ func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \
+ }
+
+#else
+
+# define FMT_MAKE_REF(n) \
+ fmt::internal::MakeValue< fmt::BasicFormatter<Char> >(v##n)
+# define FMT_MAKE_REF2(n) v##n
+
+// Defines a wrapper for a function taking one argument of type arg_type
+// and n additional arguments of arbitrary types.
+# define FMT_WRAP1(func, arg_type, n) \
+ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
+ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
+ const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
+ func(arg1, fmt::ArgList( \
+ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
+ }
+
+// Emulates a variadic function returning void on a pre-C++11 compiler.
+# define FMT_VARIADIC_VOID(func, arg_type) \
+ inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \
+ FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \
+ FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \
+ FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \
+ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \
+ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10)
+
+# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \
+ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
+ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
+ const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \
+ func(arg0, arg1, fmt::ArgList( \
+ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \
+ }
+
+// Emulates a variadic constructor on a pre-C++11 compiler.
+# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \
+ FMT_CTOR(ctor, func, arg0_type, arg1_type, 10)
+#endif
+
+// Generates a comma-separated list with results of applying f to pairs
+// (argument, index).
+#define FMT_FOR_EACH1(f, x0) f(x0, 0)
+#define FMT_FOR_EACH2(f, x0, x1) \
+ FMT_FOR_EACH1(f, x0), f(x1, 1)
+#define FMT_FOR_EACH3(f, x0, x1, x2) \
+ FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2)
+#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \
+ FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3)
+#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \
+ FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4)
+#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \
+ FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5)
+#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \
+ FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6)
+#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \
+ FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7)
+#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \
+ FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8)
+#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \
+ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9)
+
+/**
+ An error returned by an operating system or a language runtime,
+ for example a file opening error.
+*/
+class SystemError : public internal::RuntimeError
+{
+private:
+ void init(int err_code, CStringRef format_str, ArgList args);
+
+protected:
+ int error_code_;
+
+ typedef char Char; // For FMT_VARIADIC_CTOR.
+
+ SystemError() {}
+
+public:
+ /**
+ \rst
+ Constructs a :class:`fmt::SystemError` object with the description
+ of the form
+
+ .. parsed-literal::
+ *<message>*: *<system-message>*
+
+ where *<message>* is the formatted message and *<system-message>* is
+ the system message corresponding to the error code.
+ *error_code* is a system error code as given by ``errno``.
+ If *error_code* is not a valid error code such as -1, the system message
+ may look like "Unknown error -1" and is platform-dependent.
+
+ **Example**::
+
+ // This throws a SystemError with the description
+ // cannot open file 'madeup': No such file or directory
+ // or similar (system message may vary).
+ const char *filename = "madeup";
+ std::FILE *file = std::fopen(filename, "r");
+ if (!file)
+ throw fmt::SystemError(errno, "cannot open file '{}'", filename);
+ \endrst
+ */
+ SystemError(int error_code, CStringRef message)
+ {
+ init(error_code, message, ArgList());
+ }
+ FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef)
+
+ ~SystemError() throw();
+
+ int error_code() const
+ {
+ return error_code_;
+ }
+};
+
+/**
+ \rst
+ This template provides operations for formatting and writing data into
+ a character stream. The output is stored in a buffer provided by a subclass
+ such as :class:`fmt::BasicMemoryWriter`.
+
+ You can use one of the following typedefs for common character types:
+
+ +---------+----------------------+
+ | Type | Definition |
+ +=========+======================+
+ | Writer | BasicWriter<char> |
+ +---------+----------------------+
+ | WWriter | BasicWriter<wchar_t> |
+ +---------+----------------------+
+
+ \endrst
+ */
+template <typename Char>
+class BasicWriter
+{
+private:
+ // Output buffer.
+ Buffer<Char> &buffer_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter);
+
+ typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
+
+#if FMT_SECURE_SCL
+ // Returns pointer value.
+ static Char *get(CharPtr p)
+ {
+ return p.base();
+ }
+#else
+ static Char *get(Char *p)
+ {
+ return p;
+ }
+#endif
+
+ // Fills the padding around the content and returns the pointer to the
+ // content area.
+ static CharPtr fill_padding(CharPtr buffer,
+ unsigned total_size, std::size_t content_size, wchar_t fill);
+
+ // Grows the buffer by n characters and returns a pointer to the newly
+ // allocated area.
+ CharPtr grow_buffer(std::size_t n)
+ {
+ std::size_t size = buffer_.size();
+ buffer_.resize(size + n);
+ return internal::make_ptr(&buffer_[size], n);
+ }
+
+ // Writes an unsigned decimal integer.
+ template <typename UInt>
+ Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0)
+ {
+ unsigned num_digits = internal::count_digits(value);
+ Char *ptr = get(grow_buffer(prefix_size + num_digits));
+ internal::format_decimal(ptr + prefix_size, value, num_digits);
+ return ptr;
+ }
+
+ // Writes a decimal integer.
+ template <typename Int>
+ void write_decimal(Int value)
+ {
+ typedef typename internal::IntTraits<Int>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(value);
+ if (internal::is_negative(value))
+ {
+ abs_value = 0 - abs_value;
+ *write_unsigned_decimal(abs_value, 1) = '-';
+ }
+ else
+ {
+ write_unsigned_decimal(abs_value, 0);
+ }
+ }
+
+ // Prepare a buffer for integer formatting.
+ CharPtr prepare_int_buffer(unsigned num_digits,
+ const EmptySpec &, const char *prefix, unsigned prefix_size)
+ {
+ unsigned size = prefix_size + num_digits;
+ CharPtr p = grow_buffer(size);
+ std::uninitialized_copy(prefix, prefix + prefix_size, p);
+ return p + size - 1;
+ }
+
+ template <typename Spec>
+ CharPtr prepare_int_buffer(unsigned num_digits,
+ const Spec &spec, const char *prefix, unsigned prefix_size);
+
+ // Formats an integer.
+ template <typename T, typename Spec>
+ void write_int(T value, Spec spec);
+
+ // Formats a floating-point number (double or long double).
+ template <typename T>
+ void write_double(T value, const FormatSpec &spec);
+
+ // Writes a formatted string.
+ template <typename StrChar>
+ CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec);
+
+ template <typename StrChar>
+ void write_str(const internal::Arg::StringValue<StrChar> &str,
+ const FormatSpec &spec);
+
+ // This following methods are private to disallow writing wide characters
+ // and strings to a char stream. If you want to print a wide string as a
+ // pointer as std::ostream does, cast it to const void*.
+ // Do not implement!
+ void operator<<(typename internal::WCharHelper<wchar_t, Char>::Unsupported);
+ void operator<<(
+ typename internal::WCharHelper<const wchar_t *, Char>::Unsupported);
+
+ // Appends floating-point length specifier to the format string.
+ // The second argument is only used for overload resolution.
+ void append_float_length(Char *&format_ptr, long double)
+ {
+ *format_ptr++ = 'L';
+ }
+
+ template<typename T>
+ void append_float_length(Char *&, T) {}
+
+ template <typename Impl, typename Char_>
+ friend class internal::ArgFormatterBase;
+
+ friend class internal::PrintfArgFormatter<Char>;
+
+protected:
+ /**
+ Constructs a ``BasicWriter`` object.
+ */
+ explicit BasicWriter(Buffer<Char> &b) : buffer_(b) {}
+
+public:
+ /**
+ \rst
+ Destroys a ``BasicWriter`` object.
+ \endrst
+ */
+ virtual ~BasicWriter() {}
+
+ /**
+ Returns the total number of characters written.
+ */
+ std::size_t size() const
+ {
+ return buffer_.size();
+ }
+
+ /**
+ Returns a pointer to the output buffer content. No terminating null
+ character is appended.
+ */
+ const Char *data() const FMT_NOEXCEPT
+ {
+ return &buffer_[0];
+ }
+
+ /**
+ Returns a pointer to the output buffer content with terminating null
+ character appended.
+ */
+ const Char *c_str() const
+ {
+ std::size_t size = buffer_.size();
+ buffer_.reserve(size + 1);
+ buffer_[size] = '\0';
+ return &buffer_[0];
+ }
+
+ /**
+ \rst
+ Returns the content of the output buffer as an `std::string`.
+ \endrst
+ */
+ std::basic_string<Char> str() const
+ {
+ return std::basic_string<Char>(&buffer_[0], buffer_.size());
+ }
+
+ /**
+ \rst
+ Writes formatted data.
+
+ *args* is an argument list representing arbitrary arguments.
+
+ **Example**::
+
+ MemoryWriter out;
+ out.write("Current point:\n");
+ out.write("({:+f}, {:+f})", -3.14, 3.14);
+
+ This will write the following output to the ``out`` object:
+
+ .. code-block:: none
+
+ Current point:
+ (-3.140000, +3.140000)
+
+ The output can be accessed using :func:`data()`, :func:`c_str` or
+ :func:`str` methods.
+
+ See also :ref:`syntax`.
+ \endrst
+ */
+ void write(BasicCStringRef<Char> format, ArgList args)
+ {
+ BasicFormatter<Char>(args, *this).format(format);
+ }
+ FMT_VARIADIC_VOID(write, BasicCStringRef<Char>)
+
+ BasicWriter &operator<<(int value)
+ {
+ write_decimal(value);
+ return *this;
+ }
+ BasicWriter &operator<<(unsigned value)
+ {
+ return *this << IntFormatSpec<unsigned>(value);
+ }
+ BasicWriter &operator<<(long value)
+ {
+ write_decimal(value);
+ return *this;
+ }
+ BasicWriter &operator<<(unsigned long value)
+ {
+ return *this << IntFormatSpec<unsigned long>(value);
+ }
+ BasicWriter &operator<<(LongLong value)
+ {
+ write_decimal(value);
+ return *this;
+ }
+
+ /**
+ \rst
+ Formats *value* and writes it to the stream.
+ \endrst
+ */
+ BasicWriter &operator<<(ULongLong value)
+ {
+ return *this << IntFormatSpec<ULongLong>(value);
+ }
+
+ BasicWriter &operator<<(double value)
+ {
+ write_double(value, FormatSpec());
+ return *this;
+ }
+
+ /**
+ \rst
+ Formats *value* using the general format for floating-point numbers
+ (``'g'``) and writes it to the stream.
+ \endrst
+ */
+ BasicWriter &operator<<(long double value)
+ {
+ write_double(value, FormatSpec());
+ return *this;
+ }
+
+ /**
+ Writes a character to the stream.
+ */
+ BasicWriter &operator<<(char value)
+ {
+ buffer_.push_back(value);
+ return *this;
+ }
+
+ BasicWriter &operator<<(
+ typename internal::WCharHelper<wchar_t, Char>::Supported value)
+ {
+ buffer_.push_back(value);
+ return *this;
+ }
+
+ /**
+ \rst
+ Writes *value* to the stream.
+ \endrst
+ */
+ BasicWriter &operator<<(fmt::BasicStringRef<Char> value)
+ {
+ const Char *str = value.data();
+ buffer_.append(str, str + value.size());
+ return *this;
+ }
+
+ BasicWriter &operator<<(
+ typename internal::WCharHelper<StringRef, Char>::Supported value)
+ {
+ const char *str = value.data();
+ buffer_.append(str, str + value.size());
+ return *this;
+ }
+
+ template <typename T, typename Spec, typename FillChar>
+ BasicWriter &operator<<(IntFormatSpec<T, Spec, FillChar> spec)
+ {
+ internal::CharTraits<Char>::convert(FillChar());
+ write_int(spec.value(), spec);
+ return *this;
+ }
+
+ template <typename StrChar>
+ BasicWriter &operator<<(const StrFormatSpec<StrChar> &spec)
+ {
+ const StrChar *s = spec.str();
+ write_str(s, std::char_traits<Char>::length(s), spec);
+ return *this;
+ }
+
+ void clear() FMT_NOEXCEPT { buffer_.clear(); }
+
+ Buffer<Char> &buffer() FMT_NOEXCEPT { return buffer_; }
+};
+
+template <typename Char>
+template <typename StrChar>
+typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
+ const StrChar *s, std::size_t size, const AlignSpec &spec)
+{
+ CharPtr out = CharPtr();
+ if (spec.width() > size)
+ {
+ out = grow_buffer(spec.width());
+ Char fill = internal::CharTraits<Char>::cast(spec.fill());
+ if (spec.align() == ALIGN_RIGHT)
+ {
+ std::uninitialized_fill_n(out, spec.width() - size, fill);
+ out += spec.width() - size;
+ }
+ else if (spec.align() == ALIGN_CENTER)
+ {
+ out = fill_padding(out, spec.width(), size, fill);
+ }
+ else
+ {
+ std::uninitialized_fill_n(out + size, spec.width() - size, fill);
+ }
+ }
+ else
+ {
+ out = grow_buffer(size);
+ }
+ std::uninitialized_copy(s, s + size, out);
+ return out;
+}
+
+template <typename Char>
+template <typename StrChar>
+void BasicWriter<Char>::write_str(
+ const internal::Arg::StringValue<StrChar> &s, const FormatSpec &spec)
+{
+ // Check if StrChar is convertible to Char.
+ internal::CharTraits<Char>::convert(StrChar());
+ if (spec.type_ && spec.type_ != 's')
+ internal::report_unknown_type(spec.type_, "string");
+ const StrChar *str_value = s.value;
+ std::size_t str_size = s.size;
+ if (str_size == 0)
+ {
+ if (!str_value)
+ {
+ FMT_THROW(FormatError("string pointer is null"));
+ }
+ }
+ std::size_t precision = static_cast<std::size_t>(spec.precision_);
+ if (spec.precision_ >= 0 && precision < str_size)
+ str_size = precision;
+ write_str(str_value, str_size, spec);
+}
+
+template <typename Char>
+typename BasicWriter<Char>::CharPtr
+BasicWriter<Char>::fill_padding(
+ CharPtr buffer, unsigned total_size,
+ std::size_t content_size, wchar_t fill)
+{
+ std::size_t padding = total_size - content_size;
+ std::size_t left_padding = padding / 2;
+ Char fill_char = internal::CharTraits<Char>::cast(fill);
+ std::uninitialized_fill_n(buffer, left_padding, fill_char);
+ buffer += left_padding;
+ CharPtr content = buffer;
+ std::uninitialized_fill_n(buffer + content_size,
+ padding - left_padding, fill_char);
+ return content;
+}
+
+template <typename Char>
+template <typename Spec>
+typename BasicWriter<Char>::CharPtr
+BasicWriter<Char>::prepare_int_buffer(
+ unsigned num_digits, const Spec &spec,
+ const char *prefix, unsigned prefix_size)
+{
+ unsigned width = spec.width();
+ Alignment align = spec.align();
+ Char fill = internal::CharTraits<Char>::cast(spec.fill());
+ if (spec.precision() > static_cast<int>(num_digits))
+ {
+ // Octal prefix '0' is counted as a digit, so ignore it if precision
+ // is specified.
+ if (prefix_size > 0 && prefix[prefix_size - 1] == '0')
+ --prefix_size;
+ unsigned number_size =
+ prefix_size + internal::to_unsigned(spec.precision());
+ AlignSpec subspec(number_size, '0', ALIGN_NUMERIC);
+ if (number_size >= width)
+ return prepare_int_buffer(num_digits, subspec, prefix, prefix_size);
+ buffer_.reserve(width);
+ unsigned fill_size = width - number_size;
+ if (align != ALIGN_LEFT)
+ {
+ CharPtr p = grow_buffer(fill_size);
+ std::uninitialized_fill(p, p + fill_size, fill);
+ }
+ CharPtr result = prepare_int_buffer(
+ num_digits, subspec, prefix, prefix_size);
+ if (align == ALIGN_LEFT)
+ {
+ CharPtr p = grow_buffer(fill_size);
+ std::uninitialized_fill(p, p + fill_size, fill);
+ }
+ return result;
+ }
+ unsigned size = prefix_size + num_digits;
+ if (width <= size)
+ {
+ CharPtr p = grow_buffer(size);
+ std::uninitialized_copy(prefix, prefix + prefix_size, p);
+ return p + size - 1;
+ }
+ CharPtr p = grow_buffer(width);
+ CharPtr end = p + width;
+ if (align == ALIGN_LEFT)
+ {
+ std::uninitialized_copy(prefix, prefix + prefix_size, p);
+ p += size;
+ std::uninitialized_fill(p, end, fill);
+ }
+ else if (align == ALIGN_CENTER)
+ {
+ p = fill_padding(p, width, size, fill);
+ std::uninitialized_copy(prefix, prefix + prefix_size, p);
+ p += size;
+ }
+ else
+ {
+ if (align == ALIGN_NUMERIC)
+ {
+ if (prefix_size != 0)
+ {
+ p = std::uninitialized_copy(prefix, prefix + prefix_size, p);
+ size -= prefix_size;
+ }
+ }
+ else
+ {
+ std::uninitialized_copy(prefix, prefix + prefix_size, end - size);
+ }
+ std::uninitialized_fill(p, end - size, fill);
+ p = end;
+ }
+ return p - 1;
+}
+
+template <typename Char>
+template <typename T, typename Spec>
+void BasicWriter<Char>::write_int(T value, Spec spec)
+{
+ unsigned prefix_size = 0;
+ typedef typename internal::IntTraits<T>::MainType UnsignedType;
+ UnsignedType abs_value = static_cast<UnsignedType>(value);
+ char prefix[4] = "";
+ if (internal::is_negative(value))
+ {
+ prefix[0] = '-';
+ ++prefix_size;
+ abs_value = 0 - abs_value;
+ }
+ else if (spec.flag(SIGN_FLAG))
+ {
+ prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
+ ++prefix_size;
+ }
+ switch (spec.type())
+ {
+ case 0:
+ case 'd':
+ {
+ unsigned num_digits = internal::count_digits(abs_value);
+ CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1;
+ internal::format_decimal(get(p), abs_value, 0);
+ break;
+ }
+ case 'x':
+ case 'X':
+ {
+ UnsignedType n = abs_value;
+ if (spec.flag(HASH_FLAG))
+ {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = spec.type();
+ }
+ unsigned num_digits = 0;
+ do
+ {
+ ++num_digits;
+ }
+ while ((n >>= 4) != 0);
+ Char *p = get(prepare_int_buffer(
+ num_digits, spec, prefix, prefix_size));
+ n = abs_value;
+ const char *digits = spec.type() == 'x' ?
+ "0123456789abcdef" : "0123456789ABCDEF";
+ do
+ {
+ *p-- = digits[n & 0xf];
+ }
+ while ((n >>= 4) != 0);
+ break;
+ }
+ case 'b':
+ case 'B':
+ {
+ UnsignedType n = abs_value;
+ if (spec.flag(HASH_FLAG))
+ {
+ prefix[prefix_size++] = '0';
+ prefix[prefix_size++] = spec.type();
+ }
+ unsigned num_digits = 0;
+ do
+ {
+ ++num_digits;
+ }
+ while ((n >>= 1) != 0);
+ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size));
+ n = abs_value;
+ do
+ {
+ *p-- = static_cast<Char>('0' + (n & 1));
+ }
+ while ((n >>= 1) != 0);
+ break;
+ }
+ case 'o':
+ {
+ UnsignedType n = abs_value;
+ if (spec.flag(HASH_FLAG))
+ prefix[prefix_size++] = '0';
+ unsigned num_digits = 0;
+ do
+ {
+ ++num_digits;
+ }
+ while ((n >>= 3) != 0);
+ Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size));
+ n = abs_value;
+ do
+ {
+ *p-- = static_cast<Char>('0' + (n & 7));
+ }
+ while ((n >>= 3) != 0);
+ break;
+ }
+ case 'n':
+ {
+ unsigned num_digits = internal::count_digits(abs_value);
+ fmt::StringRef sep = "";
+#ifndef ANDROID
+ sep = internal::thousands_sep(std::localeconv());
+#endif
+ unsigned size = static_cast<unsigned>(
+ num_digits + sep.size() * ((num_digits - 1) / 3));
+ CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
+ internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep));
+ break;
+ }
+ default:
+ internal::report_unknown_type(
+ spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer");
+ break;
+ }
+}
+
+template <typename Char>
+template <typename T>
+void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
+{
+ // Check type.
+ char type = spec.type();
+ bool upper = false;
+ switch (type)
+ {
+ case 0:
+ type = 'g';
+ break;
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'a':
+ break;
+ case 'F':
+#if FMT_MSC_VER
+ // MSVC's printf doesn't support 'F'.
+ type = 'f';
+#endif
+ // Fall through.
+ case 'E':
+ case 'G':
+ case 'A':
+ upper = true;
+ break;
+ default:
+ internal::report_unknown_type(type, "double");
+ break;
+ }
+
+ char sign = 0;
+ // Use isnegative instead of value < 0 because the latter is always
+ // false for NaN.
+ if (internal::FPUtil::isnegative(static_cast<double>(value)))
+ {
+ sign = '-';
+ value = -value;
+ }
+ else if (spec.flag(SIGN_FLAG))
+ {
+ sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
+ }
+
+ if (internal::FPUtil::isnotanumber(value))
+ {
+ // Format NaN ourselves because sprintf's output is not consistent
+ // across platforms.
+ std::size_t nan_size = 4;
+ const char *nan = upper ? " NAN" : " nan";
+ if (!sign)
+ {
+ --nan_size;
+ ++nan;
+ }
+ CharPtr out = write_str(nan, nan_size, spec);
+ if (sign)
+ *out = sign;
+ return;
+ }
+
+ if (internal::FPUtil::isinfinity(value))
+ {
+ // Format infinity ourselves because sprintf's output is not consistent
+ // across platforms.
+ std::size_t inf_size = 4;
+ const char *inf = upper ? " INF" : " inf";
+ if (!sign)
+ {
+ --inf_size;
+ ++inf;
+ }
+ CharPtr out = write_str(inf, inf_size, spec);
+ if (sign)
+ *out = sign;
+ return;
+ }
+
+ std::size_t offset = buffer_.size();
+ unsigned width = spec.width();
+ if (sign)
+ {
+ buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u));
+ if (width > 0)
+ --width;
+ ++offset;
+ }
+
+ // Build format string.
+ enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
+ Char format[MAX_FORMAT_SIZE];
+ Char *format_ptr = format;
+ *format_ptr++ = '%';
+ unsigned width_for_sprintf = width;
+ if (spec.flag(HASH_FLAG))
+ *format_ptr++ = '#';
+ if (spec.align() == ALIGN_CENTER)
+ {
+ width_for_sprintf = 0;
+ }
+ else
+ {
+ if (spec.align() == ALIGN_LEFT)
+ *format_ptr++ = '-';
+ if (width != 0)
+ *format_ptr++ = '*';
+ }
+ if (spec.precision() >= 0)
+ {
+ *format_ptr++ = '.';
+ *format_ptr++ = '*';
+ }
+
+ append_float_length(format_ptr, value);
+ *format_ptr++ = type;
+ *format_ptr = '\0';
+
+ // Format using snprintf.
+ Char fill = internal::CharTraits<Char>::cast(spec.fill());
+ unsigned n = 0;
+ Char *start = 0;
+ for (;;)
+ {
+ std::size_t buffer_size = buffer_.capacity() - offset;
+#if FMT_MSC_VER
+ // MSVC's vsnprintf_s doesn't work with zero size, so reserve
+ // space for at least one extra character to make the size non-zero.
+ // Note that the buffer's capacity will increase by more than 1.
+ if (buffer_size == 0)
+ {
+ buffer_.reserve(offset + 1);
+ buffer_size = buffer_.capacity() - offset;
+ }
+#endif
+ start = &buffer_[offset];
+ int result = internal::CharTraits<Char>::format_float(
+ start, buffer_size, format, width_for_sprintf, spec.precision(), value);
+ if (result >= 0)
+ {
+ n = internal::to_unsigned(result);
+ if (offset + n < buffer_.capacity())
+ break; // The buffer is large enough - continue with formatting.
+ buffer_.reserve(offset + n + 1);
+ }
+ else
+ {
+ // If result is negative we ask to increase the capacity by at least 1,
+ // but as std::vector, the buffer grows exponentially.
+ buffer_.reserve(buffer_.capacity() + 1);
+ }
+ }
+ if (sign)
+ {
+ if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
+ *start != ' ')
+ {
+ *(start - 1) = sign;
+ sign = 0;
+ }
+ else
+ {
+ *(start - 1) = fill;
+ }
+ ++n;
+ }
+ if (spec.align() == ALIGN_CENTER && spec.width() > n)
+ {
+ width = spec.width();
+ CharPtr p = grow_buffer(width);
+ std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char));
+ fill_padding(p, spec.width(), n, fill);
+ return;
+ }
+ if (spec.fill() != ' ' || sign)
+ {
+ while (*start == ' ')
+ *start++ = fill;
+ if (sign)
+ *(start - 1) = sign;
+ }
+ grow_buffer(n);
+}
+
+/**
+ \rst
+ This class template provides operations for formatting and writing data
+ into a character stream. The output is stored in a memory buffer that grows
+ dynamically.
+
+ You can use one of the following typedefs for common character types
+ and the standard allocator:
+
+ +---------------+-----------------------------------------------------+
+ | Type | Definition |
+ +===============+=====================================================+
+ | MemoryWriter | BasicMemoryWriter<char, std::allocator<char>> |
+ +---------------+-----------------------------------------------------+
+ | WMemoryWriter | BasicMemoryWriter<wchar_t, std::allocator<wchar_t>> |
+ +---------------+-----------------------------------------------------+
+
+ **Example**::
+
+ MemoryWriter out;
+ out << "The answer is " << 42 << "\n";
+ out.write("({:+f}, {:+f})", -3.14, 3.14);
+
+ This will write the following output to the ``out`` object:
+
+ .. code-block:: none
+
+ The answer is 42
+ (-3.140000, +3.140000)
+
+ The output can be converted to an ``std::string`` with ``out.str()`` or
+ accessed as a C string with ``out.c_str()``.
+ \endrst
+ */
+template <typename Char, typename Allocator = std::allocator<Char> >
+class BasicMemoryWriter : public BasicWriter<Char>
+{
+private:
+ internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE, Allocator> buffer_;
+
+public:
+ explicit BasicMemoryWriter(const Allocator& alloc = Allocator())
+ : BasicWriter<Char>(buffer_), buffer_(alloc) {}
+
+#if FMT_USE_RVALUE_REFERENCES
+ /**
+ \rst
+ Constructs a :class:`fmt::BasicMemoryWriter` object moving the content
+ of the other object to it.
+ \endrst
+ */
+ BasicMemoryWriter(BasicMemoryWriter &&other)
+ : BasicWriter<Char>(buffer_), buffer_(std::move(other.buffer_))
+ {
+ }
+
+ /**
+ \rst
+ Moves the content of the other ``BasicMemoryWriter`` object to this one.
+ \endrst
+ */
+ BasicMemoryWriter &operator=(BasicMemoryWriter &&other)
+ {
+ buffer_ = std::move(other.buffer_);
+ return *this;
+ }
+#endif
+};
+
+typedef BasicMemoryWriter<char> MemoryWriter;
+typedef BasicMemoryWriter<wchar_t> WMemoryWriter;
+
+/**
+ \rst
+ This class template provides operations for formatting and writing data
+ into a fixed-size array. For writing into a dynamically growing buffer
+ use :class:`fmt::BasicMemoryWriter`.
+
+ Any write method will throw ``std::runtime_error`` if the output doesn't fit
+ into the array.
+
+ You can use one of the following typedefs for common character types:
+
+ +--------------+---------------------------+
+ | Type | Definition |
+ +==============+===========================+
+ | ArrayWriter | BasicArrayWriter<char> |
+ +--------------+---------------------------+
+ | WArrayWriter | BasicArrayWriter<wchar_t> |
+ +--------------+---------------------------+
+ \endrst
+ */
+template <typename Char>
+class BasicArrayWriter : public BasicWriter<Char>
+{
+private:
+ internal::FixedBuffer<Char> buffer_;
+
+public:
+ /**
+ \rst
+ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the
+ given size.
+ \endrst
+ */
+ BasicArrayWriter(Char *array, std::size_t size)
+ : BasicWriter<Char>(buffer_), buffer_(array, size) {}
+
+ /**
+ \rst
+ Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the
+ size known at compile time.
+ \endrst
+ */
+ template <std::size_t SIZE>
+ explicit BasicArrayWriter(Char (&array)[SIZE])
+ : BasicWriter<Char>(buffer_), buffer_(array, SIZE) {}
+};
+
+typedef BasicArrayWriter<char> ArrayWriter;
+typedef BasicArrayWriter<wchar_t> WArrayWriter;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code,
+ StringRef message) FMT_NOEXCEPT;
+
+#if FMT_USE_WINDOWS_H
+
+/** A Windows error. */
+class WindowsError : public SystemError
+{
+private:
+ FMT_API void init(int error_code, CStringRef format_str, ArgList args);
+
+public:
+ /**
+ \rst
+ Constructs a :class:`fmt::WindowsError` object with the description
+ of the form
+
+ .. parsed-literal::
+ *<message>*: *<system-message>*
+
+ where *<message>* is the formatted message and *<system-message>* is the
+ system message corresponding to the error code.
+ *error_code* is a Windows error code as given by ``GetLastError``.
+ If *error_code* is not a valid error code such as -1, the system message
+ will look like "error -1".
+
+ **Example**::
+
+ // This throws a WindowsError with the description
+ // cannot open file 'madeup': The system cannot find the file specified.
+ // or similar (system message may vary).
+ const char *filename = "madeup";
+ LPOFSTRUCT of = LPOFSTRUCT();
+ HFILE file = OpenFile(filename, &of, OF_READ);
+ if (file == HFILE_ERROR) {
+ throw fmt::WindowsError(GetLastError(),
+ "cannot open file '{}'", filename);
+ }
+ \endrst
+ */
+ WindowsError(int error_code, CStringRef message)
+ {
+ init(error_code, message, ArgList());
+ }
+ FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef)
+};
+
+// Reports a Windows error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_windows_error(int error_code,
+ StringRef message) FMT_NOEXCEPT;
+
+#endif
+
+enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
+
+/**
+ Formats a string and prints it to stdout using ANSI escape sequences
+ to specify color (experimental).
+ Example:
+ print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
+ */
+FMT_API void print_colored(Color c, CStringRef format, ArgList args);
+
+/**
+ \rst
+ Formats arguments and returns the result as a string.
+
+ **Example**::
+
+ std::string message = format("The answer is {}", 42);
+ \endrst
+*/
+inline std::string format(CStringRef format_str, ArgList args)
+{
+ MemoryWriter w;
+ w.write(format_str, args);
+ return w.str();
+}
+
+inline std::wstring format(WCStringRef format_str, ArgList args)
+{
+ WMemoryWriter w;
+ w.write(format_str, args);
+ return w.str();
+}
+
+/**
+ \rst
+ Prints formatted data to the file *f*.
+
+ **Example**::
+
+ print(stderr, "Don't {}!", "panic");
+ \endrst
+ */
+FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args);
+
+/**
+ \rst
+ Prints formatted data to ``stdout``.
+
+ **Example**::
+
+ print("Elapsed time: {0:.2f} seconds", 1.23);
+ \endrst
+ */
+FMT_API void print(CStringRef format_str, ArgList args);
+
+template <typename Char>
+void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
+{
+ internal::PrintfFormatter<Char>(args).format(w, format);
+}
+
+/**
+ \rst
+ Formats arguments and returns the result as a string.
+
+ **Example**::
+
+ std::string message = fmt::sprintf("The answer is %d", 42);
+ \endrst
+*/
+inline std::string sprintf(CStringRef format, ArgList args)
+{
+ MemoryWriter w;
+ printf(w, format, args);
+ return w.str();
+}
+
+inline std::wstring sprintf(WCStringRef format, ArgList args)
+{
+ WMemoryWriter w;
+ printf(w, format, args);
+ return w.str();
+}
+
+/**
+ \rst
+ Prints formatted data to the file *f*.
+
+ **Example**::
+
+ fmt::fprintf(stderr, "Don't %s!", "panic");
+ \endrst
+ */
+FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
+
+/**
+ \rst
+ Prints formatted data to ``stdout``.
+
+ **Example**::
+
+ fmt::printf("Elapsed time: %.2f seconds", 1.23);
+ \endrst
+ */
+inline int printf(CStringRef format, ArgList args)
+{
+ return fprintf(stdout, format, args);
+}
+
+/**
+ Fast integer formatter.
+ */
+class FormatInt
+{
+private:
+ // Buffer should be large enough to hold all digits (digits10 + 1),
+ // a sign and a null character.
+ enum {BUFFER_SIZE = std::numeric_limits<ULongLong>::digits10 + 3};
+ mutable char buffer_[BUFFER_SIZE];
+ char *str_;
+
+ // Formats value in reverse and returns the number of digits.
+ char *format_decimal(ULongLong value)
+ {
+ char *buffer_end = buffer_ + BUFFER_SIZE - 1;
+ while (value >= 100)
+ {
+ // Integer division is slow so do it for a group of two digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ unsigned index = static_cast<unsigned>((value % 100) * 2);
+ value /= 100;
+ *--buffer_end = internal::Data::DIGITS[index + 1];
+ *--buffer_end = internal::Data::DIGITS[index];
+ }
+ if (value < 10)
+ {
+ *--buffer_end = static_cast<char>('0' + value);
+ return buffer_end;
+ }
+ unsigned index = static_cast<unsigned>(value * 2);
+ *--buffer_end = internal::Data::DIGITS[index + 1];
+ *--buffer_end = internal::Data::DIGITS[index];
+ return buffer_end;
+ }
+
+ void FormatSigned(LongLong value)
+ {
+ ULongLong abs_value = static_cast<ULongLong>(value);
+ bool negative = value < 0;
+ if (negative)
+ abs_value = 0 - abs_value;
+ str_ = format_decimal(abs_value);
+ if (negative)
+ *--str_ = '-';
+ }
+
+public:
+ explicit FormatInt(int value)
+ {
+ FormatSigned(value);
+ }
+ explicit FormatInt(long value)
+ {
+ FormatSigned(value);
+ }
+ explicit FormatInt(LongLong value)
+ {
+ FormatSigned(value);
+ }
+ explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
+ explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
+ explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {}
+
+ /** Returns the number of characters written to the output buffer. */
+ std::size_t size() const
+ {
+ return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1);
+ }
+
+ /**
+ Returns a pointer to the output buffer content. No terminating null
+ character is appended.
+ */
+ const char *data() const
+ {
+ return str_;
+ }
+
+ /**
+ Returns a pointer to the output buffer content with terminating null
+ character appended.
+ */
+ const char *c_str() const
+ {
+ buffer_[BUFFER_SIZE - 1] = '\0';
+ return str_;
+ }
+
+ /**
+ \rst
+ Returns the content of the output buffer as an ``std::string``.
+ \endrst
+ */
+ std::string str() const
+ {
+ return std::string(str_, size());
+ }
+};
+
+// Formats a decimal integer value writing into buffer and returns
+// a pointer to the end of the formatted string. This function doesn't
+// write a terminating null character.
+template <typename T>
+inline void format_decimal(char *&buffer, T value)
+{
+ typedef typename internal::IntTraits<T>::MainType MainType;
+ MainType abs_value = static_cast<MainType>(value);
+ if (internal::is_negative(value))
+ {
+ *buffer++ = '-';
+ abs_value = 0 - abs_value;
+ }
+ if (abs_value < 100)
+ {
+ if (abs_value < 10)
+ {
+ *buffer++ = static_cast<char>('0' + abs_value);
+ return;
+ }
+ unsigned index = static_cast<unsigned>(abs_value * 2);
+ *buffer++ = internal::Data::DIGITS[index];
+ *buffer++ = internal::Data::DIGITS[index + 1];
+ return;
+ }
+ unsigned num_digits = internal::count_digits(abs_value);
+ internal::format_decimal(buffer, abs_value, num_digits);
+ buffer += num_digits;
+}
+
+/**
+ \rst
+ Returns a named argument for formatting functions.
+
+ **Example**::
+
+ print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
+
+ \endrst
+ */
+template <typename T>
+inline internal::NamedArg<char> arg(StringRef name, const T &arg)
+{
+ return internal::NamedArg<char>(name, arg);
+}
+
+template <typename T>
+inline internal::NamedArg<wchar_t> arg(WStringRef name, const T &arg)
+{
+ return internal::NamedArg<wchar_t>(name, arg);
+}
+
+// The following two functions are deleted intentionally to disable
+// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
+template <typename Char>
+void arg(StringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
+template <typename Char>
+void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
+}
+
+#if FMT_GCC_VERSION
+// Use the system_header pragma to suppress warnings about variadic macros
+// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't
+// work. It is used at the end because we want to suppress as little warnings
+// as possible.
+# pragma GCC system_header
+#endif
+
+// This is used to work around VC++ bugs in handling variadic macros.
+#define FMT_EXPAND(args) args
+
+// Returns the number of arguments.
+// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s.
+#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N())
+#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__))
+#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define FMT_CONCAT(a, b) a##b
+#define FMT_FOR_EACH_(N, f, ...) \
+ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__))
+#define FMT_FOR_EACH(f, ...) \
+ FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__))
+
+#define FMT_ADD_ARG_NAME(type, index) type arg##index
+#define FMT_GET_ARG_NAME(type, index) arg##index
+
+#if FMT_USE_VARIADIC_TEMPLATES
+# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
+ template <typename... Args> \
+ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
+ const Args & ... args) { \
+ typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray; \
+ typename ArgArray::Type array{ \
+ ArgArray::template make<fmt::BasicFormatter<Char> >(args)...}; \
+ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
+ fmt::ArgList(fmt::internal::make_type(args...), array)); \
+ }
+#else
+// Defines a wrapper for a function taking __VA_ARGS__ arguments
+// and n additional arguments of arbitrary types.
+# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \
+ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
+ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
+ FMT_GEN(n, FMT_MAKE_ARG)) { \
+ fmt::internal::ArgArray<n>::Type arr; \
+ FMT_GEN(n, FMT_ASSIGN_##Char); \
+ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \
+ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \
+ }
+
+# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
+ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \
+ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \
+ } \
+ FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \
+ FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__)
+#endif // FMT_USE_VARIADIC_TEMPLATES
+
+/**
+ \rst
+ Defines a variadic function with the specified return type, function name
+ and argument types passed as variable arguments to this macro.
+
+ **Example**::
+
+ void print_error(const char *file, int line, const char *format,
+ fmt::ArgList args) {
+ fmt::print("{}: {}: ", file, line);
+ fmt::print(format, args);
+ }
+ FMT_VARIADIC(void, print_error, const char *, int, const char *)
+
+ ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that
+ don't implement variadic templates. You don't have to use this macro if
+ you don't need legacy compiler support and can use variadic templates
+ directly::
+
+ template <typename... Args>
+ void print_error(const char *file, int line, const char *format,
+ const Args & ... args) {
+ fmt::print("{}: {}: ", file, line);
+ fmt::print(format, args...);
+ }
+ \endrst
+ */
+#define FMT_VARIADIC(ReturnType, func, ...) \
+ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__)
+
+#define FMT_VARIADIC_W(ReturnType, func, ...) \
+ FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__)
+
+#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id)
+
+#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id)
+
+/**
+ \rst
+ Convenient macro to capture the arguments' names and values into several
+ ``fmt::arg(name, value)``.
+
+ **Example**::
+
+ int x = 1, y = 2;
+ print("point: ({x}, {y})", FMT_CAPTURE(x, y));
+ // same as:
+ // print("point: ({x}, {y})", arg("x", x), arg("y", y));
+
+ \endrst
+ */
+#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__)
+
+#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__)
+
+namespace fmt
+{
+FMT_VARIADIC(std::string, format, CStringRef)
+FMT_VARIADIC_W(std::wstring, format, WCStringRef)
+FMT_VARIADIC(void, print, CStringRef)
+FMT_VARIADIC(void, print, std::FILE *, CStringRef)
+
+FMT_VARIADIC(void, print_colored, Color, CStringRef)
+FMT_VARIADIC(std::string, sprintf, CStringRef)
+FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
+FMT_VARIADIC(int, printf, CStringRef)
+FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
+
+namespace internal
+{
+template <typename Char>
+inline bool is_name_start(Char c)
+{
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+}
+
+// Parses an unsigned integer advancing s to the end of the parsed input.
+// This function assumes that the first character of s is a digit.
+template <typename Char>
+unsigned parse_nonnegative_int(const Char *&s)
+{
+ assert('0' <= *s && *s <= '9');
+ unsigned value = 0;
+ do
+ {
+ unsigned new_value = value * 10 + (*s++ - '0');
+ // Check if value wrapped around.
+ if (new_value < value)
+ {
+ value = (std::numeric_limits<unsigned>::max)();
+ break;
+ }
+ value = new_value;
+ }
+ while ('0' <= *s && *s <= '9');
+ // Convert to unsigned to prevent a warning.
+ unsigned max_int = (std::numeric_limits<int>::max)();
+ if (value > max_int)
+ FMT_THROW(FormatError("number is too big"));
+ return value;
+}
+
+inline void require_numeric_argument(const Arg &arg, char spec)
+{
+ if (arg.type > Arg::LAST_NUMERIC_TYPE)
+ {
+ std::string message =
+ fmt::format("format specifier '{}' requires numeric argument", spec);
+ FMT_THROW(fmt::FormatError(message));
+ }
+}
+
+template <typename Char>
+void check_sign(const Char *&s, const Arg &arg)
+{
+ char sign = static_cast<char>(*s);
+ require_numeric_argument(arg, sign);
+ if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG)
+ {
+ FMT_THROW(FormatError(fmt::format(
+ "format specifier '{}' requires signed argument", sign)));
+ }
+ ++s;
+}
+} // namespace internal
+
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::get_arg(
+ BasicStringRef<Char> arg_name, const char *&error)
+{
+ if (check_no_auto_index(error))
+ {
+ map_.init(args());
+ const internal::Arg *arg = map_.find(arg_name);
+ if (arg)
+ return *arg;
+ error = "argument not found";
+ }
+ return internal::Arg();
+}
+
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s)
+{
+ const char *error = 0;
+ internal::Arg arg = *s < '0' || *s > '9' ?
+ next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error);
+ if (error)
+ {
+ FMT_THROW(FormatError(
+ *s != '}' && *s != ':' ? "invalid format string" : error));
+ }
+ return arg;
+}
+
+template <typename Char, typename AF>
+inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s)
+{
+ assert(internal::is_name_start(*s));
+ const Char *start = s;
+ Char c;
+ do
+ {
+ c = *++s;
+ }
+ while (internal::is_name_start(c) || ('0' <= c && c <= '9'));
+ const char *error = 0;
+ internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error);
+ if (error)
+ FMT_THROW(FormatError(error));
+ return arg;
+}
+
+template <typename Char, typename ArgFormatter>
+const Char *BasicFormatter<Char, ArgFormatter>::format(
+ const Char *&format_str, const internal::Arg &arg)
+{
+ using internal::Arg;
+ const Char *s = format_str;
+ FormatSpec spec;
+ if (*s == ':')
+ {
+ if (arg.type == Arg::CUSTOM)
+ {
+ arg.custom.format(this, arg.custom.value, &s);
+ return s;
+ }
+ ++s;
+ // Parse fill and alignment.
+ if (Char c = *s)
+ {
+ const Char *p = s + 1;
+ spec.align_ = ALIGN_DEFAULT;
+ do
+ {
+ switch (*p)
+ {
+ case '<':
+ spec.align_ = ALIGN_LEFT;
+ break;
+ case '>':
+ spec.align_ = ALIGN_RIGHT;
+ break;
+ case '=':
+ spec.align_ = ALIGN_NUMERIC;
+ break;
+ case '^':
+ spec.align_ = ALIGN_CENTER;
+ break;
+ }
+ if (spec.align_ != ALIGN_DEFAULT)
+ {
+ if (p != s)
+ {
+ if (c == '}') break;
+ if (c == '{')
+ FMT_THROW(FormatError("invalid fill character '{'"));
+ s += 2;
+ spec.fill_ = c;
+ }
+ else ++s;
+ if (spec.align_ == ALIGN_NUMERIC)
+ require_numeric_argument(arg, '=');
+ break;
+ }
+ }
+ while (--p >= s);
+ }
+
+ // Parse sign.
+ switch (*s)
+ {
+ case '+':
+ check_sign(s, arg);
+ spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
+ break;
+ case '-':
+ check_sign(s, arg);
+ spec.flags_ |= MINUS_FLAG;
+ break;
+ case ' ':
+ check_sign(s, arg);
+ spec.flags_ |= SIGN_FLAG;
+ break;
+ }
+
+ if (*s == '#')
+ {
+ require_numeric_argument(arg, '#');
+ spec.flags_ |= HASH_FLAG;
+ ++s;
+ }
+
+ // Parse zero flag.
+ if (*s == '0')
+ {
+ require_numeric_argument(arg, '0');
+ spec.align_ = ALIGN_NUMERIC;
+ spec.fill_ = '0';
+ ++s;
+ }
+
+ // Parse width.
+ if ('0' <= *s && *s <= '9')
+ {
+ spec.width_ = internal::parse_nonnegative_int(s);
+ }
+ else if (*s == '{')
+ {
+ ++s;
+ Arg width_arg = internal::is_name_start(*s) ?
+ parse_arg_name(s) : parse_arg_index(s);
+ if (*s++ != '}')
+ FMT_THROW(FormatError("invalid format string"));
+ ULongLong value = 0;
+ switch (width_arg.type)
+ {
+ case Arg::INT:
+ if (width_arg.int_value < 0)
+ FMT_THROW(FormatError("negative width"));
+ value = width_arg.int_value;
+ break;
+ case Arg::UINT:
+ value = width_arg.uint_value;
+ break;
+ case Arg::LONG_LONG:
+ if (width_arg.long_long_value < 0)
+ FMT_THROW(FormatError("negative width"));
+ value = width_arg.long_long_value;
+ break;
+ case Arg::ULONG_LONG:
+ value = width_arg.ulong_long_value;
+ break;
+ default:
+ FMT_THROW(FormatError("width is not integer"));
+ }
+ if (value > (std::numeric_limits<int>::max)())
+ FMT_THROW(FormatError("number is too big"));
+ spec.width_ = static_cast<int>(value);
+ }
+
+ // Parse precision.
+ if (*s == '.')
+ {
+ ++s;
+ spec.precision_ = 0;
+ if ('0' <= *s && *s <= '9')
+ {
+ spec.precision_ = internal::parse_nonnegative_int(s);
+ }
+ else if (*s == '{')
+ {
+ ++s;
+ Arg precision_arg = internal::is_name_start(*s) ?
+ parse_arg_name(s) : parse_arg_index(s);
+ if (*s++ != '}')
+ FMT_THROW(FormatError("invalid format string"));
+ ULongLong value = 0;
+ switch (precision_arg.type)
+ {
+ case Arg::INT:
+ if (precision_arg.int_value < 0)
+ FMT_THROW(FormatError("negative precision"));
+ value = precision_arg.int_value;
+ break;
+ case Arg::UINT:
+ value = precision_arg.uint_value;
+ break;
+ case Arg::LONG_LONG:
+ if (precision_arg.long_long_value < 0)
+ FMT_THROW(FormatError("negative precision"));
+ value = precision_arg.long_long_value;
+ break;
+ case Arg::ULONG_LONG:
+ value = precision_arg.ulong_long_value;
+ break;
+ default:
+ FMT_THROW(FormatError("precision is not integer"));
+ }
+ if (value > (std::numeric_limits<int>::max)())
+ FMT_THROW(FormatError("number is too big"));
+ spec.precision_ = static_cast<int>(value);
+ }
+ else
+ {
+ FMT_THROW(FormatError("missing precision specifier"));
+ }
+ if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER)
+ {
+ FMT_THROW(FormatError(
+ fmt::format("precision not allowed in {} format specifier",
+ arg.type == Arg::POINTER ? "pointer" : "integer")));
+ }
+ }
+
+ // Parse type.
+ if (*s != '}' && *s)
+ spec.type_ = static_cast<char>(*s++);
+ }
+
+ if (*s++ != '}')
+ FMT_THROW(FormatError("missing '}' in format string"));
+
+ // Format argument.
+ ArgFormatter(*this, spec, s - 1).visit(arg);
+ return s;
+}
+
+template <typename Char, typename AF>
+void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
+{
+ const Char *s = format_str.c_str();
+ const Char *start = s;
+ while (*s)
+ {
+ Char c = *s++;
+ if (c != '{' && c != '}') continue;
+ if (*s == c)
+ {
+ write(writer_, start, s);
+ start = ++s;
+ continue;
+ }
+ if (c == '}')
+ FMT_THROW(FormatError("unmatched '}' in format string"));
+ write(writer_, start, s - 1);
+ internal::Arg arg = internal::is_name_start(*s) ?
+ parse_arg_name(s) : parse_arg_index(s);
+ start = s = format(s, arg);
+ }
+ write(writer_, start, s);
+}
+} // namespace fmt
+
+#if FMT_USE_USER_DEFINED_LITERALS
+namespace fmt
+{
+namespace internal
+{
+
+template <typename Char>
+struct UdlFormat
+{
+ const Char *str;
+
+ template <typename... Args>
+ auto operator()(Args && ... args) const
+ -> decltype(format(str, std::forward<Args>(args)...))
+ {
+ return format(str, std::forward<Args>(args)...);
+ }
+};
+
+template <typename Char>
+struct UdlArg
+{
+ const Char *str;
+
+ template <typename T>
+ NamedArg<Char> operator=(T &&value) const
+ {
+ return {str, std::forward<T>(value)};
+ }
+};
+
+} // namespace internal
+
+inline namespace literals
+{
+
+/**
+ \rst
+ C++11 literal equivalent of :func:`fmt::format`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ std::string message = "The answer is {}"_format(42);
+ \endrst
+ */
+inline internal::UdlFormat<char>
+operator"" _format(const char *s, std::size_t)
+{
+ return {s};
+}
+inline internal::UdlFormat<wchar_t>
+operator"" _format(const wchar_t *s, std::size_t)
+{
+ return {s};
+}
+
+/**
+ \rst
+ C++11 literal equivalent of :func:`fmt::arg`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+ \endrst
+ */
+inline internal::UdlArg<char>
+operator"" _a(const char *s, std::size_t)
+{
+ return {s};
+}
+inline internal::UdlArg<wchar_t>
+operator"" _a(const wchar_t *s, std::size_t)
+{
+ return {s};
+}
+
+} // inline namespace literals
+} // namespace fmt
+#endif // FMT_USE_USER_DEFINED_LITERALS
+
+// Restore warnings.
+#if FMT_GCC_VERSION >= 406
+# pragma GCC diagnostic pop
+#endif
+
+#if defined(__clang__) && !defined(FMT_ICC_VERSION)
+# pragma clang diagnostic pop
+#endif
+
+#ifdef FMT_HEADER_ONLY
+# define FMT_FUNC inline
+# include "format.cc"
+#else
+# define FMT_FUNC
+#endif
+
+#endif // FMT_FORMAT_H_
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.cc b/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.cc
new file mode 100644
index 0000000..bcb67fe
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.cc
@@ -0,0 +1,43 @@
+/*
+ Formatting library for C++ - std::ostream support
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ For the license information refer to format.h.
+ */
+
+#include "ostream.h"
+
+namespace fmt {
+
+namespace {
+// Write the content of w to os.
+void write(std::ostream &os, Writer &w) {
+ const char *data = w.data();
+ typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
+ UnsignedStreamSize size = w.size();
+ UnsignedStreamSize max_size =
+ internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
+ do {
+ UnsignedStreamSize n = size <= max_size ? size : max_size;
+ os.write(data, static_cast<std::streamsize>(n));
+ data += n;
+ size -= n;
+ } while (size != 0);
+}
+}
+
+FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
+ MemoryWriter w;
+ w.write(format_str, args);
+ write(os, w);
+}
+
+FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
+ MemoryWriter w;
+ printf(w, format, args);
+ write(os, w);
+ return static_cast<int>(w.size());
+}
+} // namespace fmt
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.h b/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.h
new file mode 100644
index 0000000..c52646d
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/ostream.h
@@ -0,0 +1,126 @@
+/*
+ Formatting library for C++ - std::ostream support
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ For the license information refer to format.h.
+ */
+
+#ifndef FMT_OSTREAM_H_
+#define FMT_OSTREAM_H_
+
+#include "format.h"
+#include <ostream>
+
+namespace fmt
+{
+
+namespace internal
+{
+
+template <class Char>
+class FormatBuf : public std::basic_streambuf<Char>
+{
+private:
+ typedef typename std::basic_streambuf<Char>::int_type int_type;
+ typedef typename std::basic_streambuf<Char>::traits_type traits_type;
+
+ Buffer<Char> &buffer_;
+ Char *start_;
+
+public:
+ FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
+ {
+ this->setp(start_, start_ + buffer_.capacity());
+ }
+
+ int_type overflow(int_type ch = traits_type::eof())
+ {
+ if (!traits_type::eq_int_type(ch, traits_type::eof()))
+ {
+ size_t buf_size = size();
+ buffer_.resize(buf_size);
+ buffer_.reserve(buf_size * 2);
+
+ start_ = &buffer_[0];
+ start_[buf_size] = traits_type::to_char_type(ch);
+ this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
+ }
+ return ch;
+ }
+
+ size_t size() const
+ {
+ return to_unsigned(this->pptr() - start_);
+ }
+};
+
+Yes &convert(std::ostream &);
+
+struct DummyStream : std::ostream
+{
+ DummyStream(); // Suppress a bogus warning in MSVC.
+ // Hide all operator<< overloads from std::ostream.
+ void operator<<(Null<>);
+};
+
+No &operator<<(std::ostream &, int);
+
+template<typename T>
+struct ConvertToIntImpl<T, true>
+{
+ // Convert to int only if T doesn't have an overloaded operator<<.
+ enum
+ {
+ value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
+ };
+};
+} // namespace internal
+
+// Formats a value.
+template <typename Char, typename ArgFormatter, typename T>
+void format(BasicFormatter<Char, ArgFormatter> &f,
+ const Char *&format_str, const T &value)
+{
+ internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
+
+ internal::FormatBuf<Char> format_buf(buffer);
+ std::basic_ostream<Char> output(&format_buf);
+ output << value;
+
+ BasicStringRef<Char> str(&buffer[0], format_buf.size());
+ typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
+ format_str = f.format(format_str, MakeArg(str));
+}
+
+/**
+ \rst
+ Prints formatted data to the stream *os*.
+
+ **Example**::
+
+ print(cerr, "Don't {}!", "panic");
+ \endrst
+ */
+FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
+FMT_VARIADIC(void, print, std::ostream &, CStringRef)
+
+/**
+ \rst
+ Prints formatted data to the stream *os*.
+
+ **Example**::
+
+ fprintf(cerr, "Don't %s!", "panic");
+ \endrst
+ */
+FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
+FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
+} // namespace fmt
+
+#ifdef FMT_HEADER_ONLY
+# include "ostream.cc"
+#endif
+
+#endif // FMT_OSTREAM_H_
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.cc b/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.cc
new file mode 100644
index 0000000..76eb7f0
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.cc
@@ -0,0 +1,238 @@
+/*
+ A C++ interface to POSIX functions.
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ For the license information refer to format.h.
+ */
+
+// Disable bogus MSVC warnings.
+#ifndef _CRT_SECURE_NO_WARNINGS
+# define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "posix.h"
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include <windows.h>
+# include <io.h>
+
+# define O_CREAT _O_CREAT
+# define O_TRUNC _O_TRUNC
+
+# ifndef S_IRUSR
+# define S_IRUSR _S_IREAD
+# endif
+
+# ifndef S_IWUSR
+# define S_IWUSR _S_IWRITE
+# endif
+
+# ifdef __MINGW32__
+# define _SH_DENYNO 0x40
+# endif
+
+#endif // _WIN32
+
+#ifdef fileno
+# undef fileno
+#endif
+
+namespace {
+#ifdef _WIN32
+// Return type of read and write functions.
+typedef int RWResult;
+
+// On Windows the count argument to read and write is unsigned, so convert
+// it from size_t preventing integer overflow.
+inline unsigned convert_rwcount(std::size_t count) {
+ return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
+}
+#else
+// Return type of read and write functions.
+typedef ssize_t RWResult;
+
+inline std::size_t convert_rwcount(std::size_t count) { return count; }
+#endif
+}
+
+fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
+ if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
+ fmt::report_system_error(errno, "cannot close file");
+}
+
+fmt::BufferedFile::BufferedFile(
+ fmt::CStringRef filename, fmt::CStringRef mode) {
+ FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
+ if (!file_)
+ FMT_THROW(SystemError(errno, "cannot open file {}", filename));
+}
+
+void fmt::BufferedFile::close() {
+ if (!file_)
+ return;
+ int result = FMT_SYSTEM(fclose(file_));
+ file_ = 0;
+ if (result != 0)
+ FMT_THROW(SystemError(errno, "cannot close file"));
+}
+
+// A macro used to prevent expansion of fileno on broken versions of MinGW.
+#define FMT_ARGS
+
+int fmt::BufferedFile::fileno() const {
+ int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
+ if (fd == -1)
+ FMT_THROW(SystemError(errno, "cannot get file descriptor"));
+ return fd;
+}
+
+fmt::File::File(fmt::CStringRef path, int oflag) {
+ int mode = S_IRUSR | S_IWUSR;
+#if defined(_WIN32) && !defined(__MINGW32__)
+ fd_ = -1;
+ FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
+#else
+ FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
+#endif
+ if (fd_ == -1)
+ FMT_THROW(SystemError(errno, "cannot open file {}", path));
+}
+
+fmt::File::~File() FMT_NOEXCEPT {
+ // Don't retry close in case of EINTR!
+ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+ if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
+ fmt::report_system_error(errno, "cannot close file");
+}
+
+void fmt::File::close() {
+ if (fd_ == -1)
+ return;
+ // Don't retry close in case of EINTR!
+ // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+ int result = FMT_POSIX_CALL(close(fd_));
+ fd_ = -1;
+ if (result != 0)
+ FMT_THROW(SystemError(errno, "cannot close file"));
+}
+
+fmt::LongLong fmt::File::size() const {
+#ifdef _WIN32
+ // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
+ // is less than 0x0500 as is the case with some default MinGW builds.
+ // Both functions support large file sizes.
+ DWORD size_upper = 0;
+ HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
+ DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
+ if (size_lower == INVALID_FILE_SIZE) {
+ DWORD error = GetLastError();
+ if (error != NO_ERROR)
+ FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
+ }
+ fmt::ULongLong long_size = size_upper;
+ return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
+#else
+ typedef struct stat Stat;
+ Stat file_stat = Stat();
+ if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
+ FMT_THROW(SystemError(errno, "cannot get file attributes"));
+ FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
+ "return type of File::size is not large enough");
+ return file_stat.st_size;
+#endif
+}
+
+std::size_t fmt::File::read(void *buffer, std::size_t count) {
+ RWResult result = 0;
+ FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
+ if (result < 0)
+ FMT_THROW(SystemError(errno, "cannot read from file"));
+ return internal::to_unsigned(result);
+}
+
+std::size_t fmt::File::write(const void *buffer, std::size_t count) {
+ RWResult result = 0;
+ FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
+ if (result < 0)
+ FMT_THROW(SystemError(errno, "cannot write to file"));
+ return internal::to_unsigned(result);
+}
+
+fmt::File fmt::File::dup(int fd) {
+ // Don't retry as dup doesn't return EINTR.
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
+ int new_fd = FMT_POSIX_CALL(dup(fd));
+ if (new_fd == -1)
+ FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
+ return File(new_fd);
+}
+
+void fmt::File::dup2(int fd) {
+ int result = 0;
+ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
+ if (result == -1) {
+ FMT_THROW(SystemError(errno,
+ "cannot duplicate file descriptor {} to {}", fd_, fd));
+ }
+}
+
+void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
+ int result = 0;
+ FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
+ if (result == -1)
+ ec = ErrorCode(errno);
+}
+
+void fmt::File::pipe(File &read_end, File &write_end) {
+ // Close the descriptors first to make sure that assignments don't throw
+ // and there are no leaks.
+ read_end.close();
+ write_end.close();
+ int fds[2] = {};
+#ifdef _WIN32
+ // Make the default pipe capacity same as on Linux 2.6.11+.
+ enum { DEFAULT_CAPACITY = 65536 };
+ int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
+#else
+ // Don't retry as the pipe function doesn't return EINTR.
+ // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
+ int result = FMT_POSIX_CALL(pipe(fds));
+#endif
+ if (result != 0)
+ FMT_THROW(SystemError(errno, "cannot create pipe"));
+ // The following assignments don't throw because read_fd and write_fd
+ // are closed.
+ read_end = File(fds[0]);
+ write_end = File(fds[1]);
+}
+
+fmt::BufferedFile fmt::File::fdopen(const char *mode) {
+ // Don't retry as fdopen doesn't return EINTR.
+ FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
+ if (!f)
+ FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
+ BufferedFile file(f);
+ fd_ = -1;
+ return file;
+}
+
+long fmt::getpagesize() {
+#ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+#else
+ long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
+ if (size < 0)
+ FMT_THROW(SystemError(errno, "cannot get memory page size"));
+ return size;
+#endif
+}
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.h b/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.h
new file mode 100644
index 0000000..859fcaa
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/posix.h
@@ -0,0 +1,443 @@
+/*
+ A C++ interface to POSIX functions.
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ For the license information refer to format.h.
+ */
+
+#ifndef FMT_POSIX_H_
+#define FMT_POSIX_H_
+
+#if defined(__MINGW32__) || defined(__CYGWIN__)
+// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
+# undef __STRICT_ANSI__
+#endif
+
+#include <errno.h>
+#include <fcntl.h> // for O_RDONLY
+#include <locale.h> // for locale_t
+#include <stdio.h>
+#include <stdlib.h> // for strtod_l
+
+#include <cstddef>
+
+#if defined __APPLE__ || defined(__FreeBSD__)
+# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
+#endif
+
+#include "format.h"
+
+#ifndef FMT_POSIX
+# if defined(_WIN32) && !defined(__MINGW32__)
+// Fix warnings about deprecated symbols.
+# define FMT_POSIX(call) _##call
+# else
+# define FMT_POSIX(call) call
+# endif
+#endif
+
+// Calls to system functions are wrapped in FMT_SYSTEM for testability.
+#ifdef FMT_SYSTEM
+# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
+#else
+# define FMT_SYSTEM(call) call
+# ifdef _WIN32
+// Fix warnings about deprecated symbols.
+# define FMT_POSIX_CALL(call) ::_##call
+# else
+# define FMT_POSIX_CALL(call) ::call
+# endif
+#endif
+
+#if FMT_GCC_VERSION >= 407
+# define FMT_UNUSED __attribute__((unused))
+#else
+# define FMT_UNUSED
+#endif
+
+#ifndef FMT_USE_STATIC_ASSERT
+# define FMT_USE_STATIC_ASSERT 0
+#endif
+
+#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
+ (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
+# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
+#else
+# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
+# define FMT_STATIC_ASSERT(cond, message) \
+ typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
+#endif
+
+// Retries the expression while it evaluates to error_result and errno
+// equals to EINTR.
+#ifndef _WIN32
+# define FMT_RETRY_VAL(result, expression, error_result) \
+ do { \
+ result = (expression); \
+ } while (result == error_result && errno == EINTR)
+#else
+# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
+#endif
+
+#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
+
+namespace fmt
+{
+
+// An error code.
+class ErrorCode
+{
+private:
+ int value_;
+
+public:
+explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
+ value_(value) {}
+
+ int get() const FMT_NOEXCEPT
+ {
+ return value_;
+ }
+};
+
+// A buffered file.
+class BufferedFile
+{
+private:
+ FILE *file_;
+
+ friend class File;
+
+ explicit BufferedFile(FILE *f) : file_(f) {}
+
+public:
+ // Constructs a BufferedFile object which doesn't represent any file.
+BufferedFile() FMT_NOEXCEPT :
+ file_(0) {}
+
+ // Destroys the object closing the file it represents if any.
+ ~BufferedFile() FMT_NOEXCEPT;
+
+#if !FMT_USE_RVALUE_REFERENCES
+ // Emulate a move constructor and a move assignment operator if rvalue
+ // references are not supported.
+
+private:
+ // A proxy object to emulate a move constructor.
+ // It is private to make it impossible call operator Proxy directly.
+ struct Proxy
+ {
+ FILE *file;
+ };
+
+public:
+ // A "move constructor" for moving from a temporary.
+BufferedFile(Proxy p) FMT_NOEXCEPT :
+ file_(p.file) {}
+
+ // A "move constructor" for moving from an lvalue.
+BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
+ file_(f.file_)
+ {
+ f.file_ = 0;
+ }
+
+ // A "move assignment operator" for moving from a temporary.
+ BufferedFile &operator=(Proxy p)
+ {
+ close();
+ file_ = p.file;
+ return *this;
+ }
+
+ // A "move assignment operator" for moving from an lvalue.
+ BufferedFile &operator=(BufferedFile &other)
+ {
+ close();
+ file_ = other.file_;
+ other.file_ = 0;
+ return *this;
+ }
+
+ // Returns a proxy object for moving from a temporary:
+ // BufferedFile file = BufferedFile(...);
+ operator Proxy() FMT_NOEXCEPT
+ {
+ Proxy p = {file_};
+ file_ = 0;
+ return p;
+ }
+
+#else
+private:
+ FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
+
+public:
+BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
+ file_(other.file_)
+ {
+ other.file_ = 0;
+ }
+
+ BufferedFile& operator=(BufferedFile &&other)
+ {
+ close();
+ file_ = other.file_;
+ other.file_ = 0;
+ return *this;
+ }
+#endif
+
+ // Opens a file.
+ BufferedFile(CStringRef filename, CStringRef mode);
+
+ // Closes the file.
+ void close();
+
+ // Returns the pointer to a FILE object representing this file.
+ FILE *get() const FMT_NOEXCEPT
+ {
+ return file_;
+ }
+
+ // We place parentheses around fileno to workaround a bug in some versions
+ // of MinGW that define fileno as a macro.
+ int (fileno)() const;
+
+ void print(CStringRef format_str, const ArgList &args)
+ {
+ fmt::print(file_, format_str, args);
+ }
+ FMT_VARIADIC(void, print, CStringRef)
+};
+
+// A file. Closed file is represented by a File object with descriptor -1.
+// Methods that are not declared with FMT_NOEXCEPT may throw
+// fmt::SystemError in case of failure. Note that some errors such as
+// closing the file multiple times will cause a crash on Windows rather
+// than an exception. You can get standard behavior by overriding the
+// invalid parameter handler with _set_invalid_parameter_handler.
+class File
+{
+private:
+ int fd_; // File descriptor.
+
+ // Constructs a File object with a given descriptor.
+ explicit File(int fd) : fd_(fd) {}
+
+public:
+ // Possible values for the oflag argument to the constructor.
+ enum
+ {
+ RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
+ WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
+ RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
+ };
+
+ // Constructs a File object which doesn't represent any file.
+File() FMT_NOEXCEPT :
+ fd_(-1) {}
+
+ // Opens a file and constructs a File object representing this file.
+ File(CStringRef path, int oflag);
+
+#if !FMT_USE_RVALUE_REFERENCES
+ // Emulate a move constructor and a move assignment operator if rvalue
+ // references are not supported.
+
+private:
+ // A proxy object to emulate a move constructor.
+ // It is private to make it impossible call operator Proxy directly.
+ struct Proxy
+ {
+ int fd;
+ };
+
+public:
+ // A "move constructor" for moving from a temporary.
+File(Proxy p) FMT_NOEXCEPT :
+ fd_(p.fd) {}
+
+ // A "move constructor" for moving from an lvalue.
+File(File &other) FMT_NOEXCEPT :
+ fd_(other.fd_)
+ {
+ other.fd_ = -1;
+ }
+
+ // A "move assignment operator" for moving from a temporary.
+ File &operator=(Proxy p)
+ {
+ close();
+ fd_ = p.fd;
+ return *this;
+ }
+
+ // A "move assignment operator" for moving from an lvalue.
+ File &operator=(File &other)
+ {
+ close();
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+ }
+
+ // Returns a proxy object for moving from a temporary:
+ // File file = File(...);
+ operator Proxy() FMT_NOEXCEPT
+ {
+ Proxy p = {fd_};
+ fd_ = -1;
+ return p;
+ }
+
+#else
+private:
+ FMT_DISALLOW_COPY_AND_ASSIGN(File);
+
+public:
+File(File &&other) FMT_NOEXCEPT :
+ fd_(other.fd_)
+ {
+ other.fd_ = -1;
+ }
+
+ File& operator=(File &&other)
+ {
+ close();
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+ }
+#endif
+
+ // Destroys the object closing the file it represents if any.
+ ~File() FMT_NOEXCEPT;
+
+ // Returns the file descriptor.
+ int descriptor() const FMT_NOEXCEPT
+ {
+ return fd_;
+ }
+
+ // Closes the file.
+ void close();
+
+ // Returns the file size. The size has signed type for consistency with
+ // stat::st_size.
+ LongLong size() const;
+
+ // Attempts to read count bytes from the file into the specified buffer.
+ std::size_t read(void *buffer, std::size_t count);
+
+ // Attempts to write count bytes from the specified buffer to the file.
+ std::size_t write(const void *buffer, std::size_t count);
+
+ // Duplicates a file descriptor with the dup function and returns
+ // the duplicate as a file object.
+ static File dup(int fd);
+
+ // Makes fd be the copy of this file descriptor, closing fd first if
+ // necessary.
+ void dup2(int fd);
+
+ // Makes fd be the copy of this file descriptor, closing fd first if
+ // necessary.
+ void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
+
+ // Creates a pipe setting up read_end and write_end file objects for reading
+ // and writing respectively.
+ static void pipe(File &read_end, File &write_end);
+
+ // Creates a BufferedFile object associated with this file and detaches
+ // this File object from the file.
+ BufferedFile fdopen(const char *mode);
+};
+
+// Returns the memory page size.
+long getpagesize();
+
+#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
+ !defined(__ANDROID__) && !defined(__CYGWIN__)
+# define FMT_LOCALE
+#endif
+
+#ifdef FMT_LOCALE
+// A "C" numeric locale.
+class Locale
+{
+private:
+# ifdef _MSC_VER
+ typedef _locale_t locale_t;
+
+ enum { LC_NUMERIC_MASK = LC_NUMERIC };
+
+ static locale_t newlocale(int category_mask, const char *locale, locale_t)
+ {
+ return _create_locale(category_mask, locale);
+ }
+
+ static void freelocale(locale_t locale)
+ {
+ _free_locale(locale);
+ }
+
+ static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
+ {
+ return _strtod_l(nptr, endptr, locale);
+ }
+# endif
+
+ locale_t locale_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
+
+public:
+ typedef locale_t Type;
+
+ Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL))
+ {
+ if (!locale_)
+ FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
+ }
+ ~Locale()
+ {
+ freelocale(locale_);
+ }
+
+ Type get() const
+ {
+ return locale_;
+ }
+
+ // Converts string to floating-point number and advances str past the end
+ // of the parsed input.
+ double strtod(const char *&str) const
+ {
+ char *end = 0;
+ double result = strtod_l(str, &end, locale_);
+ str = end;
+ return result;
+ }
+};
+#endif // FMT_LOCALE
+} // namespace fmt
+
+#if !FMT_USE_RVALUE_REFERENCES
+namespace std
+{
+// For compatibility with C++98.
+inline fmt::BufferedFile &move(fmt::BufferedFile &f)
+{
+ return f;
+}
+inline fmt::File &move(fmt::File &f)
+{
+ return f;
+}
+}
+#endif
+
+#endif // FMT_POSIX_H_
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/bundled/time.h b/geom_matching/wasserstein/include/spdlog/fmt/bundled/time.h
new file mode 100644
index 0000000..10c6cfc
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/bundled/time.h
@@ -0,0 +1,58 @@
+/*
+ Formatting library for C++ - time formatting
+
+ Copyright (c) 2012 - 2016, Victor Zverovich
+ All rights reserved.
+
+ For the license information refer to format.h.
+ */
+
+#ifndef FMT_TIME_H_
+#define FMT_TIME_H_
+
+#include "format.h"
+#include <ctime>
+
+namespace fmt
+{
+template <typename ArgFormatter>
+void format(BasicFormatter<char, ArgFormatter> &f,
+ const char *&format_str, const std::tm &tm)
+{
+ if (*format_str == ':')
+ ++format_str;
+ const char *end = format_str;
+ while (*end && *end != '}')
+ ++end;
+ if (*end != '}')
+ FMT_THROW(FormatError("missing '}' in format string"));
+ internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
+ format.append(format_str, end + 1);
+ format[format.size() - 1] = '\0';
+ Buffer<char> &buffer = f.writer().buffer();
+ std::size_t start = buffer.size();
+ for (;;)
+ {
+ std::size_t size = buffer.capacity() - start;
+ std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
+ if (count != 0)
+ {
+ buffer.resize(start + count);
+ break;
+ }
+ if (size >= format.size() * 256)
+ {
+ // If the buffer is 256 times larger than the format string, assume
+ // that `strftime` gives an empty result. There doesn't seem to be a
+ // better way to distinguish the two cases:
+ // https://github.com/fmtlib/fmt/issues/367
+ break;
+ }
+ const std::size_t MIN_GROWTH = 10;
+ buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
+ }
+ format_str = end + 1;
+}
+}
+
+#endif // FMT_TIME_H_
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/fmt.h b/geom_matching/wasserstein/include/spdlog/fmt/fmt.h
new file mode 100644
index 0000000..a4ee467
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/fmt.h
@@ -0,0 +1,28 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+//
+// Include a bundled header-only copy of fmtlib or an external one.
+// By default spdlog include its own copy.
+//
+
+#if !defined(SPDLOG_FMT_EXTERNAL)
+
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#ifndef FMT_USE_WINDOWS_H
+#define FMT_USE_WINDOWS_H 0
+#endif
+#include "spdlog/fmt/bundled/format.h"
+
+#else //external fmtlib
+
+#include <fmt/format.h>
+
+#endif
+
diff --git a/geom_matching/wasserstein/include/spdlog/fmt/ostr.h b/geom_matching/wasserstein/include/spdlog/fmt/ostr.h
new file mode 100644
index 0000000..49b5e98
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/fmt/ostr.h
@@ -0,0 +1,17 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// include external or bundled copy of fmtlib's ostream support
+//
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#include "spdlog/fmt/fmt.h"
+#include "spdlog/fmt/bundled/ostream.h"
+#else
+#include <fmt/ostream.h>
+#endif
+
+
diff --git a/geom_matching/wasserstein/include/spdlog/formatter.h b/geom_matching/wasserstein/include/spdlog/formatter.h
new file mode 100644
index 0000000..6bba902
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/formatter.h
@@ -0,0 +1,47 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/details/log_msg.h"
+
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace spdlog
+{
+namespace details
+{
+class flag_formatter;
+}
+
+class formatter
+{
+public:
+ virtual ~formatter() {}
+ virtual void format(details::log_msg& msg) = 0;
+};
+
+class pattern_formatter SPDLOG_FINAL : public formatter
+{
+
+public:
+ explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local);
+ pattern_formatter(const pattern_formatter&) = delete;
+ pattern_formatter& operator=(const pattern_formatter&) = delete;
+ void format(details::log_msg& msg) override;
+private:
+ const std::string _pattern;
+ const pattern_time_type _pattern_time;
+ std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
+ std::tm get_time(details::log_msg& msg);
+ void handle_flag(char flag);
+ void compile_pattern(const std::string& pattern);
+};
+}
+
+#include "spdlog/details/pattern_formatter_impl.h"
+
diff --git a/geom_matching/wasserstein/include/spdlog/logger.h b/geom_matching/wasserstein/include/spdlog/logger.h
new file mode 100644
index 0000000..642208e
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/logger.h
@@ -0,0 +1,132 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
+// Has name, log level, vector of std::shared sink pointers and formatter
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message
+// 2. Format the message using the formatter function
+// 3. Pass the formatted message to its sinks to performa the actual logging
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/common.h"
+
+#include <vector>
+#include <memory>
+#include <string>
+
+namespace spdlog
+{
+
+class logger
+{
+public:
+ logger(const std::string& logger_name, sink_ptr single_sink);
+ logger(const std::string& name, sinks_init_list);
+ template<class It>
+ logger(const std::string& name, const It& begin, const It& end);
+
+ virtual ~logger();
+ logger(const logger&) = delete;
+ logger& operator=(const logger&) = delete;
+
+
+ template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
+ template <typename... Args> void log(level::level_enum lvl, const char* msg);
+ template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
+
+ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args);
+ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* msg);
+ template <typename Arg1, typename... Args> void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+ template <typename Arg1, typename... Args> void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+ template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg);
+ template <typename... Args> void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void trace(const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void debug(const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void info(const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
+
+ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg);
+ template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void trace_if(const bool flag, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void debug_if(const bool flag, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void info_if(const bool flag, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void warn_if(const bool flag, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void error_if(const bool flag, const wchar_t* fmt, const Args&... args);
+ template <typename... Args> void critical_if(const bool flag, const wchar_t* fmt, const Args&... args);
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+ template <typename T> void log(level::level_enum lvl, const T&);
+ template <typename T> void trace(const T&);
+ template <typename T> void debug(const T&);
+ template <typename T> void info(const T&);
+ template <typename T> void warn(const T&);
+ template <typename T> void error(const T&);
+ template <typename T> void critical(const T&);
+
+ template <typename T> void log_if(const bool flag, level::level_enum lvl, const T&);
+ template <typename T> void trace_if(const bool flag, const T&);
+ template <typename T> void debug_if(const bool flag, const T&);
+ template <typename T> void info_if(const bool flag, const T&);
+ template <typename T> void warn_if(const bool flag, const T&);
+ template <typename T> void error_if(const bool flag, const T&);
+ template <typename T> void critical_if(const bool flag, const T&);
+
+ bool should_log(level::level_enum) const;
+ void set_level(level::level_enum);
+ level::level_enum level() const;
+ const std::string& name() const;
+ void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
+ void set_formatter(formatter_ptr);
+
+ // automatically call flush() if message level >= log_level
+ void flush_on(level::level_enum log_level);
+
+ virtual void flush();
+
+ const std::vector<sink_ptr>& sinks() const;
+
+ // error handler
+ virtual void set_error_handler(log_err_handler);
+ virtual log_err_handler error_handler();
+
+protected:
+ virtual void _sink_it(details::log_msg&);
+ virtual void _set_pattern(const std::string&, pattern_time_type);
+ virtual void _set_formatter(formatter_ptr);
+
+ // default error handler: print the error to stderr with the max rate of 1 message/minute
+ virtual void _default_err_handler(const std::string &msg);
+
+ // return true if the given message level should trigger a flush
+ bool _should_flush_on(const details::log_msg&);
+
+ const std::string _name;
+ std::vector<sink_ptr> _sinks;
+ formatter_ptr _formatter;
+ spdlog::level_t _level;
+ spdlog::level_t _flush_level;
+ log_err_handler _err_handler;
+ std::atomic<time_t> _last_err_time;
+ std::atomic<size_t> _msg_counter;
+};
+}
+
+#include "spdlog/details/logger_impl.h"
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/android_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/android_sink.h
new file mode 100644
index 0000000..239f2d2
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/android_sink.h
@@ -0,0 +1,90 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#if defined(__ANDROID__)
+
+#include "spdlog/sinks/sink.h"
+
+#include <mutex>
+#include <string>
+#include <android/log.h>
+#include <thread>
+#include <chrono>
+
+#if !defined(SPDLOG_ANDROID_RETRIES)
+#define SPDLOG_ANDROID_RETRIES 2
+#endif
+
+namespace spdlog
+{
+namespace sinks
+{
+
+/*
+* Android sink (logging using __android_log_write)
+* __android_log_write is thread-safe. No lock is needed.
+*/
+class android_sink : public sink
+{
+public:
+ explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {}
+
+ void log(const details::log_msg& msg) override
+ {
+ const android_LogPriority priority = convert_to_android(msg.level);
+ const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
+
+ // See system/core/liblog/logger_write.c for explanation of return value
+ int ret = __android_log_write(priority, _tag.c_str(), msg_output);
+ int retry_count = 0;
+ while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ ret = __android_log_write(priority, _tag.c_str(), msg_output);
+ retry_count++;
+ }
+
+ if (ret < 0)
+ {
+ throw spdlog_ex("__android_log_write() failed", ret);
+ }
+ }
+
+ void flush() override
+ {
+ }
+
+private:
+ static android_LogPriority convert_to_android(spdlog::level::level_enum level)
+ {
+ switch(level)
+ {
+ case spdlog::level::trace:
+ return ANDROID_LOG_VERBOSE;
+ case spdlog::level::debug:
+ return ANDROID_LOG_DEBUG;
+ case spdlog::level::info:
+ return ANDROID_LOG_INFO;
+ case spdlog::level::warn:
+ return ANDROID_LOG_WARN;
+ case spdlog::level::err:
+ return ANDROID_LOG_ERROR;
+ case spdlog::level::critical:
+ return ANDROID_LOG_FATAL;
+ default:
+ return ANDROID_LOG_DEFAULT;
+ }
+ }
+
+ std::string _tag;
+ bool _use_raw_msg;
+};
+
+}
+}
+
+#endif
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/ansicolor_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/ansicolor_sink.h
new file mode 100644
index 0000000..56fd3fd
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/ansicolor_sink.h
@@ -0,0 +1,133 @@
+//
+// Copyright(c) 2017 spdlog authors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/common.h"
+#include "spdlog/details/os.h"
+
+#include <string>
+#include <map>
+
+namespace spdlog
+{
+namespace sinks
+{
+
+/**
+ * This sink prefixes the output with an ANSI escape sequence color code depending on the severity
+ * of the message.
+ * If no color terminal detected, omit the escape codes.
+ */
+template <class Mutex>
+class ansicolor_sink: public base_sink<Mutex>
+{
+public:
+ ansicolor_sink(FILE* file): target_file_(file)
+ {
+ should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal();
+ colors_[level::trace] = cyan;
+ colors_[level::debug] = cyan;
+ colors_[level::info] = reset;
+ colors_[level::warn] = yellow + bold;
+ colors_[level::err] = red + bold;
+ colors_[level::critical] = bold + on_red;
+ colors_[level::off] = reset;
+ }
+ virtual ~ansicolor_sink()
+ {
+ _flush();
+ }
+
+ void set_color(level::level_enum color_level, const std::string& color)
+ {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
+ colors_[color_level] = color;
+ }
+
+ /// Formatting codes
+ const std::string reset = "\033[00m";
+ const std::string bold = "\033[1m";
+ const std::string dark = "\033[2m";
+ const std::string underline = "\033[4m";
+ const std::string blink = "\033[5m";
+ const std::string reverse = "\033[7m";
+ const std::string concealed = "\033[8m";
+
+ // Foreground colors
+ const std::string grey = "\033[30m";
+ const std::string red = "\033[31m";
+ const std::string green = "\033[32m";
+ const std::string yellow = "\033[33m";
+ const std::string blue = "\033[34m";
+ const std::string magenta = "\033[35m";
+ const std::string cyan = "\033[36m";
+ const std::string white = "\033[37m";
+
+ /// Background colors
+ const std::string on_grey = "\033[40m";
+ const std::string on_red = "\033[41m";
+ const std::string on_green = "\033[42m";
+ const std::string on_yellow = "\033[43m";
+ const std::string on_blue = "\033[44m";
+ const std::string on_magenta = "\033[45m";
+ const std::string on_cyan = "\033[46m";
+ const std::string on_white = "\033[47m";
+
+protected:
+ virtual void _sink_it(const details::log_msg& msg) override
+ {
+ // Wrap the originally formatted message in color codes.
+ // If color is not supported in the terminal, log as is instead.
+ if (should_do_colors_)
+ {
+ const std::string& prefix = colors_[msg.level];
+ fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_);
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
+ fwrite(reset.data(), sizeof(char), reset.size(), target_file_);
+ }
+ else
+ {
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
+ }
+ _flush();
+ }
+
+ void _flush() override
+ {
+ fflush(target_file_);
+ }
+ FILE* target_file_;
+ bool should_do_colors_;
+ std::map<level::level_enum, std::string> colors_;
+};
+
+
+template<class Mutex>
+class ansicolor_stdout_sink: public ansicolor_sink<Mutex>
+{
+public:
+ ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
+ {}
+};
+
+template<class Mutex>
+class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
+{
+public:
+ ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
+ {}
+};
+
+typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
+typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
+
+typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
+typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
+
+} // namespace sinks
+} // namespace spdlog
+
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/base_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/base_sink.h
new file mode 100644
index 0000000..926f493
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/base_sink.h
@@ -0,0 +1,50 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// base sink templated over a mutex (either dummy or real)
+// concrete implementation should only override the _sink_it method.
+// all locking is taken care of here so no locking needed by the implementers..
+//
+
+#include "spdlog/sinks/sink.h"
+#include "spdlog/formatter.h"
+#include "spdlog/common.h"
+#include "spdlog/details/log_msg.h"
+
+#include <mutex>
+
+namespace spdlog
+{
+namespace sinks
+{
+template<class Mutex>
+class base_sink:public sink
+{
+public:
+ base_sink():_mutex() {}
+ virtual ~base_sink() = default;
+
+ base_sink(const base_sink&) = delete;
+ base_sink& operator=(const base_sink&) = delete;
+
+ void log(const details::log_msg& msg) SPDLOG_FINAL override
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _sink_it(msg);
+ }
+ void flush() SPDLOG_FINAL override
+ {
+ _flush();
+ }
+
+protected:
+ virtual void _sink_it(const details::log_msg& msg) = 0;
+ virtual void _flush() = 0;
+ Mutex _mutex;
+};
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/dist_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/dist_sink.h
new file mode 100644
index 0000000..4d4b6b6
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/dist_sink.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2015 David Schury, Gabi Melman
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/details/log_msg.h"
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/sinks/sink.h"
+
+#include <algorithm>
+#include <mutex>
+#include <memory>
+#include <vector>
+
+// Distribution sink (mux). Stores a vector of sinks which get called when log is called
+
+namespace spdlog
+{
+namespace sinks
+{
+template<class Mutex>
+class dist_sink: public base_sink<Mutex>
+{
+public:
+ explicit dist_sink() :_sinks() {}
+ dist_sink(const dist_sink&) = delete;
+ dist_sink& operator=(const dist_sink&) = delete;
+ virtual ~dist_sink() = default;
+
+protected:
+ std::vector<std::shared_ptr<sink>> _sinks;
+
+ void _sink_it(const details::log_msg& msg) override
+ {
+ for (auto &sink : _sinks)
+ {
+ if( sink->should_log( msg.level))
+ {
+ sink->log(msg);
+ }
+ }
+ }
+
+ void _flush() override
+ {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
+ for (auto &sink : _sinks)
+ sink->flush();
+ }
+
+public:
+
+
+ void add_sink(std::shared_ptr<sink> sink)
+ {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
+ _sinks.push_back(sink);
+ }
+
+ void remove_sink(std::shared_ptr<sink> sink)
+ {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
+ _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
+ }
+};
+
+typedef dist_sink<std::mutex> dist_sink_mt;
+typedef dist_sink<details::null_mutex> dist_sink_st;
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/file_sinks.h b/geom_matching/wasserstein/include/spdlog/sinks/file_sinks.h
new file mode 100644
index 0000000..421acc8
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/file_sinks.h
@@ -0,0 +1,242 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/details/file_helper.h"
+#include "spdlog/fmt/fmt.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <mutex>
+#include <string>
+#include <cerrno>
+
+namespace spdlog
+{
+namespace sinks
+{
+/*
+ * Trivial file sink with single file as target
+ */
+template<class Mutex>
+class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
+{
+public:
+ explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
+ {
+ _file_helper.open(filename, truncate);
+ }
+
+ void set_force_flush(bool force_flush)
+ {
+ _force_flush = force_flush;
+ }
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ _file_helper.write(msg);
+ if(_force_flush)
+ _file_helper.flush();
+ }
+ void _flush() override
+ {
+ _file_helper.flush();
+ }
+private:
+ details::file_helper _file_helper;
+ bool _force_flush;
+};
+
+typedef simple_file_sink<std::mutex> simple_file_sink_mt;
+typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
+
+/*
+ * Rotating file sink based on size
+ */
+template<class Mutex>
+class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
+{
+public:
+ rotating_file_sink(const filename_t &base_filename,
+ std::size_t max_size, std::size_t max_files) :
+ _base_filename(base_filename),
+ _max_size(max_size),
+ _max_files(max_files),
+ _current_size(0),
+ _file_helper()
+ {
+ _file_helper.open(calc_filename(_base_filename, 0));
+ _current_size = _file_helper.size(); //expensive. called only once
+ }
+
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ _current_size += msg.formatted.size();
+ if (_current_size > _max_size)
+ {
+ _rotate();
+ _current_size = msg.formatted.size();
+ }
+ _file_helper.write(msg);
+ }
+
+ void _flush() override
+ {
+ _file_helper.flush();
+ }
+
+private:
+ static filename_t calc_filename(const filename_t& filename, std::size_t index)
+ {
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
+ if (index)
+ w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
+ else
+ w.write(SPDLOG_FILENAME_T("{}"), filename);
+ return w.str();
+ }
+
+ // Rotate files:
+ // log.txt -> log.txt.1
+ // log.txt.1 -> log.txt.2
+ // log.txt.2 -> log.txt.3
+ // lo3.txt.3 -> delete
+
+ void _rotate()
+ {
+ using details::os::filename_to_str;
+ _file_helper.close();
+ for (auto i = _max_files; i > 0; --i)
+ {
+ filename_t src = calc_filename(_base_filename, i - 1);
+ filename_t target = calc_filename(_base_filename, i);
+
+ if (details::file_helper::file_exists(target))
+ {
+ if (details::os::remove(target) != 0)
+ {
+ throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
+ }
+ }
+ if (details::file_helper::file_exists(src) && details::os::rename(src, target))
+ {
+ throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
+ }
+ }
+ _file_helper.reopen(true);
+ }
+ filename_t _base_filename;
+ std::size_t _max_size;
+ std::size_t _max_files;
+ std::size_t _current_size;
+ details::file_helper _file_helper;
+};
+
+typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
+typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
+
+/*
+ * Default generator of daily log file names.
+ */
+struct default_daily_file_name_calculator
+{
+ // Create filename for the form basename.YYYY-MM-DD_hh-mm
+ static filename_t calc_filename(const filename_t& basename)
+ {
+ std::tm tm = spdlog::details::os::localtime();
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
+ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
+ return w.str();
+ }
+};
+
+/*
+ * Generator of daily log file names in format basename.YYYY-MM-DD
+ */
+struct dateonly_daily_file_name_calculator
+{
+ // Create filename for the form basename.YYYY-MM-DD
+ static filename_t calc_filename(const filename_t& basename)
+ {
+ std::tm tm = spdlog::details::os::localtime();
+ std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
+ w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ return w.str();
+ }
+};
+
+/*
+ * Rotating file sink based on date. rotates at midnight
+ */
+template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
+class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
+{
+public:
+ //create daily file sink which rotates on given time
+ daily_file_sink(
+ const filename_t& base_filename,
+ int rotation_hour,
+ int rotation_minute) : _base_filename(base_filename),
+ _rotation_h(rotation_hour),
+ _rotation_m(rotation_minute)
+ {
+ if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
+ throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
+ _rotation_tp = _next_rotation_tp();
+ _file_helper.open(FileNameCalc::calc_filename(_base_filename));
+ }
+
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ if (std::chrono::system_clock::now() >= _rotation_tp)
+ {
+ _file_helper.open(FileNameCalc::calc_filename(_base_filename));
+ _rotation_tp = _next_rotation_tp();
+ }
+ _file_helper.write(msg);
+ }
+
+ void _flush() override
+ {
+ _file_helper.flush();
+ }
+
+private:
+ std::chrono::system_clock::time_point _next_rotation_tp()
+ {
+ auto now = std::chrono::system_clock::now();
+ time_t tnow = std::chrono::system_clock::to_time_t(now);
+ tm date = spdlog::details::os::localtime(tnow);
+ date.tm_hour = _rotation_h;
+ date.tm_min = _rotation_m;
+ date.tm_sec = 0;
+ auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
+ if (rotation_time > now)
+ return rotation_time;
+ else
+ return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
+ }
+
+ filename_t _base_filename;
+ int _rotation_h;
+ int _rotation_m;
+ std::chrono::system_clock::time_point _rotation_tp;
+ details::file_helper _file_helper;
+};
+
+typedef daily_file_sink<std::mutex> daily_file_sink_mt;
+typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/msvc_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/msvc_sink.h
new file mode 100644
index 0000000..68b0255
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/msvc_sink.h
@@ -0,0 +1,51 @@
+//
+// Copyright(c) 2016 Alexander Dalshov.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#if defined(_MSC_VER)
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/details/null_mutex.h"
+
+#include <WinBase.h>
+
+#include <mutex>
+#include <string>
+
+namespace spdlog
+{
+namespace sinks
+{
+/*
+* MSVC sink (logging using OutputDebugStringA)
+*/
+template<class Mutex>
+class msvc_sink : public base_sink < Mutex >
+{
+public:
+ explicit msvc_sink()
+ {
+ }
+
+
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ OutputDebugStringA(msg.formatted.c_str());
+ }
+
+ void _flush() override
+ {}
+};
+
+typedef msvc_sink<std::mutex> msvc_sink_mt;
+typedef msvc_sink<details::null_mutex> msvc_sink_st;
+
+}
+}
+
+#endif
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/null_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/null_sink.h
new file mode 100644
index 0000000..ed4b5e4
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/null_sink.h
@@ -0,0 +1,34 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/details/null_mutex.h"
+
+#include <mutex>
+
+namespace spdlog
+{
+namespace sinks
+{
+
+template <class Mutex>
+class null_sink : public base_sink < Mutex >
+{
+protected:
+ void _sink_it(const details::log_msg&) override
+ {}
+
+ void _flush() override
+ {}
+
+};
+typedef null_sink<details::null_mutex> null_sink_st;
+typedef null_sink<details::null_mutex> null_sink_mt;
+
+}
+}
+
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/ostream_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/ostream_sink.h
new file mode 100644
index 0000000..f056107
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/ostream_sink.h
@@ -0,0 +1,47 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/sinks/base_sink.h"
+
+#include <ostream>
+#include <mutex>
+
+namespace spdlog
+{
+namespace sinks
+{
+template<class Mutex>
+class ostream_sink: public base_sink<Mutex>
+{
+public:
+ explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {}
+ ostream_sink(const ostream_sink&) = delete;
+ ostream_sink& operator=(const ostream_sink&) = delete;
+ virtual ~ostream_sink() = default;
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ _ostream.write(msg.formatted.data(), msg.formatted.size());
+ if (_force_flush)
+ _ostream.flush();
+ }
+
+ void _flush() override
+ {
+ _ostream.flush();
+ }
+
+ std::ostream& _ostream;
+ bool _force_flush;
+};
+
+typedef ostream_sink<std::mutex> ostream_sink_mt;
+typedef ostream_sink<details::null_mutex> ostream_sink_st;
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/sink.h b/geom_matching/wasserstein/include/spdlog/sinks/sink.h
new file mode 100644
index 0000000..0974f33
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/sink.h
@@ -0,0 +1,53 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+
+#pragma once
+
+#include "spdlog/details/log_msg.h"
+
+namespace spdlog
+{
+namespace sinks
+{
+class sink
+{
+public:
+ sink()
+ {
+ _level = level::trace;
+ }
+
+ virtual ~sink() {}
+ virtual void log(const details::log_msg& msg) = 0;
+ virtual void flush() = 0;
+
+ bool should_log(level::level_enum msg_level) const;
+ void set_level(level::level_enum log_level);
+ level::level_enum level() const;
+
+private:
+ level_t _level;
+
+};
+
+inline bool sink::should_log(level::level_enum msg_level) const
+{
+ return msg_level >= _level.load(std::memory_order_relaxed);
+}
+
+inline void sink::set_level(level::level_enum log_level)
+{
+ _level.store(log_level);
+}
+
+inline level::level_enum sink::level() const
+{
+ return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
+}
+
+}
+}
+
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/stdout_sinks.h b/geom_matching/wasserstein/include/spdlog/sinks/stdout_sinks.h
new file mode 100644
index 0000000..dcdcc7c
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/stdout_sinks.h
@@ -0,0 +1,77 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/sinks/base_sink.h"
+
+#include <cstdio>
+#include <memory>
+#include <mutex>
+
+namespace spdlog
+{
+namespace sinks
+{
+
+template <class Mutex>
+class stdout_sink SPDLOG_FINAL : public base_sink<Mutex>
+{
+ using MyType = stdout_sink<Mutex>;
+public:
+ stdout_sink()
+ {}
+ static std::shared_ptr<MyType> instance()
+ {
+ static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
+ return instance;
+ }
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
+ _flush();
+ }
+
+ void _flush() override
+ {
+ fflush(stdout);
+ }
+};
+
+typedef stdout_sink<details::null_mutex> stdout_sink_st;
+typedef stdout_sink<std::mutex> stdout_sink_mt;
+
+
+template <class Mutex>
+class stderr_sink SPDLOG_FINAL : public base_sink<Mutex>
+{
+ using MyType = stderr_sink<Mutex>;
+public:
+ stderr_sink()
+ {}
+ static std::shared_ptr<MyType> instance()
+ {
+ static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
+ return instance;
+ }
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
+ _flush();
+ }
+
+ void _flush() override
+ {
+ fflush(stderr);
+ }
+};
+
+typedef stderr_sink<std::mutex> stderr_sink_mt;
+typedef stderr_sink<details::null_mutex> stderr_sink_st;
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/syslog_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/syslog_sink.h
new file mode 100644
index 0000000..0b509c4
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/syslog_sink.h
@@ -0,0 +1,81 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/common.h"
+
+#ifdef SPDLOG_ENABLE_SYSLOG
+
+#include "spdlog/sinks/sink.h"
+#include "spdlog/details/log_msg.h"
+
+#include <array>
+#include <string>
+#include <syslog.h>
+
+
+namespace spdlog
+{
+namespace sinks
+{
+/**
+ * Sink that write to syslog using the `syscall()` library call.
+ *
+ * Locking is not needed, as `syslog()` itself is thread-safe.
+ */
+class syslog_sink : public sink
+{
+public:
+ //
+ syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER):
+ _ident(ident)
+ {
+ _priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
+ _priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
+ _priorities[static_cast<int>(level::info)] = LOG_INFO;
+ _priorities[static_cast<int>(level::warn)] = LOG_WARNING;
+ _priorities[static_cast<int>(level::err)] = LOG_ERR;
+ _priorities[static_cast<int>(level::critical)] = LOG_CRIT;
+ _priorities[static_cast<int>(level::off)] = LOG_INFO;
+
+ //set ident to be program name if empty
+ ::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility);
+ }
+ ~syslog_sink()
+ {
+ ::closelog();
+ }
+
+ syslog_sink(const syslog_sink&) = delete;
+ syslog_sink& operator=(const syslog_sink&) = delete;
+
+ void log(const details::log_msg &msg) override
+ {
+ ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
+ }
+
+ void flush() override
+ {
+ }
+
+
+private:
+ std::array<int, 7> _priorities;
+ //must store the ident because the man says openlog might use the pointer as is and not a string copy
+ const std::string _ident;
+
+ //
+ // Simply maps spdlog's log level to syslog priority level.
+ //
+ int syslog_prio_from_level(const details::log_msg &msg) const
+ {
+ return _priorities[static_cast<int>(msg.level)];
+ }
+};
+}
+}
+
+#endif
diff --git a/geom_matching/wasserstein/include/spdlog/sinks/wincolor_sink.h b/geom_matching/wasserstein/include/spdlog/sinks/wincolor_sink.h
new file mode 100644
index 0000000..6611124
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/sinks/wincolor_sink.h
@@ -0,0 +1,117 @@
+//
+// Copyright(c) 2016 spdlog
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/common.h"
+
+#include <mutex>
+#include <string>
+#include <map>
+#include <wincon.h>
+
+namespace spdlog
+{
+namespace sinks
+{
+/*
+ * Windows color console sink. Uses WriteConsoleA to write to the console with colors
+ */
+template<class Mutex>
+class wincolor_sink: public base_sink<Mutex>
+{
+public:
+ const WORD BOLD = FOREGROUND_INTENSITY;
+ const WORD RED = FOREGROUND_RED;
+ const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
+ const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
+
+ wincolor_sink(HANDLE std_handle): out_handle_(std_handle)
+ {
+ colors_[level::trace] = CYAN;
+ colors_[level::debug] = CYAN;
+ colors_[level::info] = WHITE | BOLD;
+ colors_[level::warn] = YELLOW | BOLD;
+ colors_[level::err] = RED | BOLD; // red bold
+ colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
+ colors_[level::off] = 0;
+ }
+
+ virtual ~wincolor_sink()
+ {
+ this->flush();
+ }
+
+ wincolor_sink(const wincolor_sink& other) = delete;
+ wincolor_sink& operator=(const wincolor_sink& other) = delete;
+
+protected:
+ virtual void _sink_it(const details::log_msg& msg) override
+ {
+ auto color = colors_[msg.level];
+ auto orig_attribs = set_console_attribs(color);
+ WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
+ SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
+ }
+
+ virtual void _flush() override
+ {
+ // windows console always flushed?
+ }
+
+ // change the color for the given level
+ void set_color(level::level_enum level, WORD color)
+ {
+ std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
+ colors_[level] = color;
+ }
+
+private:
+ HANDLE out_handle_;
+ std::map<level::level_enum, WORD> colors_;
+
+ // set color and return the orig console attributes (for resetting later)
+ WORD set_console_attribs(WORD attribs)
+ {
+ CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
+ GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
+ SetConsoleTextAttribute(out_handle_, attribs);
+ return orig_buffer_info.wAttributes; //return orig attribs
+ }
+};
+
+//
+// windows color console to stdout
+//
+template<class Mutex>
+class wincolor_stdout_sink: public wincolor_sink<Mutex>
+{
+public:
+ wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
+ {}
+};
+
+typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
+typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
+
+//
+// windows color console to stderr
+//
+template<class Mutex>
+class wincolor_stderr_sink: public wincolor_sink<Mutex>
+{
+public:
+ wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
+ {}
+};
+
+typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
+typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
+
+}
+}
diff --git a/geom_matching/wasserstein/include/spdlog/spdlog.h b/geom_matching/wasserstein/include/spdlog/spdlog.h
new file mode 100644
index 0000000..fb4ea59
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/spdlog.h
@@ -0,0 +1,187 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+// spdlog main header file.
+// see example.cpp for usage example
+
+#pragma once
+
+#define SPDLOG_VERSION "0.13.0"
+
+#include "spdlog/tweakme.h"
+#include "spdlog/common.h"
+#include "spdlog/logger.h"
+
+#include <memory>
+#include <functional>
+#include <chrono>
+#include <string>
+
+namespace spdlog
+{
+
+//
+// Return an existing logger or nullptr if a logger with such name doesn't exist.
+// example: spdlog::get("my_logger")->info("hello {}", "world");
+//
+std::shared_ptr<logger> get(const std::string& name);
+
+
+//
+// Set global formatting
+// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
+//
+void set_pattern(const std::string& format_string);
+void set_formatter(formatter_ptr f);
+
+//
+// Set global logging level for
+//
+void set_level(level::level_enum log_level);
+
+//
+// Set global error handler
+//
+void set_error_handler(log_err_handler);
+
+//
+// Turn on async mode (off by default) and set the queue size for each async_logger.
+// effective only for loggers created after this call.
+// queue_size: size of queue (must be power of 2):
+// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction.
+//
+// async_overflow_policy (optional, block_retry by default):
+// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
+// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
+//
+// worker_warmup_cb (optional):
+// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
+//
+// worker_teardown_cb (optional):
+// callback function that will be called in worker thread upon exit
+//
+void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
+
+// Turn off async mode
+void set_sync_mode();
+
+
+//
+// Create and register multi/single threaded basic file logger.
+// Basic logger simply writes to given file without any limitatons or rotations.
+//
+std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
+std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
+
+//
+// Create and register multi/single threaded rotating file logger
+//
+std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
+std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
+
+//
+// Create file logger which creates new file on the given time (default in midnight):
+//
+std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
+std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
+
+//
+// Create and register stdout/stderr loggers
+//
+std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
+std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
+std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
+std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
+//
+// Create and register colored stdout/stderr loggers
+//
+std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
+std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
+std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
+std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
+
+
+//
+// Create and register a syslog logger
+//
+#ifdef SPDLOG_ENABLE_SYSLOG
+std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
+#endif
+
+#if defined(__ANDROID__)
+std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
+#endif
+
+// Create and register a logger with a single sink
+std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
+
+// Create and register a logger with multiple sinks
+std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
+template<class It>
+std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
+
+
+// Create and register a logger with templated sink type
+// Example:
+// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
+template <typename Sink, typename... Args>
+std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
+
+// Create and register an async logger with a single sink
+std::shared_ptr<logger> create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
+
+// Create and register an async logger with multiple sinks
+std::shared_ptr<logger> create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
+template<class It>
+std::shared_ptr<logger> create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
+
+// Register the given logger with the given name
+void register_logger(std::shared_ptr<logger> logger);
+
+// Apply a user defined function on all registered loggers
+// Example:
+// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
+void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
+
+// Drop the reference to the given logger
+void drop(const std::string &name);
+
+// Drop all references from the registry
+void drop_all();
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
+// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
+// SPDLOG_TRACE(..) will also print current file and line.
+//
+// Example:
+// spdlog::set_level(spdlog::level::trace);
+// SPDLOG_TRACE(my_logger, "some trace message");
+// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
+// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
+// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4);
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SPDLOG_TRACE_ON
+#define SPDLOG_STR_H(x) #x
+#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
+#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
+#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
+#else
+#define SPDLOG_TRACE(logger, ...)
+#endif
+
+#ifdef SPDLOG_DEBUG_ON
+#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
+#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__)
+#else
+#define SPDLOG_DEBUG(logger, ...)
+#endif
+
+}
+
+
+#include "spdlog/details/spdlog_impl.h"
diff --git a/geom_matching/wasserstein/include/spdlog/tweakme.h b/geom_matching/wasserstein/include/spdlog/tweakme.h
new file mode 100644
index 0000000..53f5cf7
--- /dev/null
+++ b/geom_matching/wasserstein/include/spdlog/tweakme.h
@@ -0,0 +1,141 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Edit this file to squeeze more performance, and to customize supported features
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
+// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
+// Uncomment to use it instead of the regular clock.
+//
+// #define SPDLOG_CLOCK_COARSE
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment if date/time logging is not needed and never appear in the log pattern.
+// This will prevent spdlog from quering the clock on each log call.
+//
+// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
+// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
+//
+// #define SPDLOG_NO_DATETIME
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
+// This will prevent spdlog from quering the thread id on each log call.
+//
+// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
+//
+// #define SPDLOG_NO_THREAD_ID
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment if logger name logging is not needed.
+// This will prevent spdlog from copying the logger name on each log call.
+//
+// #define SPDLOG_NO_NAME
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
+//
+// #define SPDLOG_DEBUG_ON
+// #define SPDLOG_TRACE_ON
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
+// Use only if your code never modifes concurrently the registry.
+// Note that upon creating a logger the registry is modified by spdlog..
+//
+// #define SPDLOG_NO_REGISTRY_MUTEX
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to avoid spdlog's usage of atomic log levels
+// Use only if your code never modifies a logger's log levels concurrently by different threads.
+//
+// #define SPDLOG_NO_ATOMIC_LEVELS
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable usage of wchar_t for file names on Windows.
+//
+// #define SPDLOG_WCHAR_FILENAMES
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
+//
+// #define SPDLOG_EOL ";-)\n"
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
+// In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
+//
+// #define SPDLOG_FMT_EXTERNAL
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable syslog (disabled by default)
+//
+// #define SPDLOG_ENABLE_SYSLOG
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable wchar_t support (convert to utf8)
+//
+// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to prevent child processes from inheriting log file descriptors
+//
+// #define SPDLOG_PREVENT_CHILD_FD
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to mark some types as final, allowing more optimizations in release
+// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
+// for instance.
+//
+// #define SPDLOG_FINAL final
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable message counting feature. Adds %i logger pattern that
+// prints log message sequence id.
+//
+// #define SPDLOG_ENABLE_MESSAGE_COUNTER
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable user defined tag names
+//
+// #define SPDLOG_LEVEL_NAMES { " TRACE", " DEBUG", " INFO",
+// " WARNING", " ERROR", "CRITICAL", "OFF" };
+/////////////////////////////////////////////////////////////////////////////// \ No newline at end of file
diff --git a/geom_matching/wasserstein/include/wasserstein.h b/geom_matching/wasserstein/include/wasserstein.h
index c3e9280..d843a04 100644
--- a/geom_matching/wasserstein/include/wasserstein.h
+++ b/geom_matching/wasserstein/include/wasserstein.h
@@ -26,138 +26,319 @@ derivative works thereof, in binary and source code form.
*/
-#ifndef WASSERSTEIN_H
-#define WASSERSTEIN_H
+#ifndef HERA_WASSERSTEIN_H
+#define HERA_WASSERSTEIN_H
#include <vector>
#include <map>
#include <math.h>
+#include "def_debug_ws.h"
#include "basic_defs_ws.h"
+#include "diagram_reader.h"
+#include "auction_runner_gs.h"
+#include "auction_runner_gs_single_diag.h"
+#include "auction_runner_jac.h"
+#include "auction_runner_fr.h"
-// use Gauss-Seidel version; comment out to switch to Jacobi (not recommended)
-#define GAUSS_SEIDEL_AUCTION
-namespace geom_ws {
+namespace hera
+{
-using PairVector = std::vector<std::pair<double, double>>;
+template<class PairContainer_, class PointType_ = typename std::remove_reference< decltype(*std::declval<PairContainer_>().begin())>::type >
+struct DiagramTraits
+{
+ using Container = PairContainer_;
+ using PointType = PointType_;
+ using RealType = typename std::remove_reference< decltype(std::declval<PointType>()[0]) >::type;
-// get Wasserstein distance between two persistence diagrams
-double wassersteinDistVec(const std::vector<DiagramPoint>& A,
- const std::vector<DiagramPoint>& B,
- const double q,
- const double delta,
- const double _internal_p = std::numeric_limits<double>::infinity(),
- const double _initialEpsilon = 0.0,
- const double _epsFactor = 0.0);
+ static RealType get_x(const PointType& p) { return p[0]; }
+ static RealType get_y(const PointType& p) { return p[1]; }
+};
-// get Wasserstein cost (distance^q) between two persistence diagrams
-double wassersteinCostVec(const std::vector<DiagramPoint>& A,
- const std::vector<DiagramPoint>& B,
- const double q,
- const double delta,
- const double _internal_p = std::numeric_limits<double>::infinity(),
- const double _initialEpsilon = 0.0,
- const double _epsFactor = 0.0);
+template<class PairContainer_>
+struct DiagramTraits<PairContainer_, std::pair<double, double>>
+{
+ using PointType = std::pair<double, double>;
+ using RealType = double;
+ using Container = std::vector<PointType>;
+ static RealType get_x(const PointType& p) { return p.first; }
+ static RealType get_y(const PointType& p) { return p.second; }
+};
-// compare as multisets
-template<class PairContainer>
-bool areEqual(PairContainer& dgm1, PairContainer& dgm2)
+
+namespace ws
{
- if (dgm1.size() != dgm2.size()) {
- return false;
- }
- std::map<std::pair<double, double>, int> m1, m2;
+ // compare as multisets
+ template<class PairContainer>
+ bool are_equal(const PairContainer& dgm1, const PairContainer& dgm2)
+ {
+ if (dgm1.size() != dgm2.size()) {
+ return false;
+ }
+
+ using Traits = typename hera::DiagramTraits<PairContainer>;
+ using PointType = typename Traits::PointType;
+
+ std::map<PointType, int> m1, m2;
+
+ for(const auto& pair1 : dgm1) {
+ m1[pair1]++;
+ }
- for(const auto& pair1 : dgm1) {
- m1[pair1]++;
+ for(const auto& pair2 : dgm2) {
+ m2[pair2]++;
+ }
+
+ return m1 == m2;
}
- for(const auto& pair2 : dgm2) {
- m2[pair2]++;
+ // to handle points with one coordinate = infinity
+ template<class RealType>
+ RealType get_one_dimensional_cost(std::vector<RealType>& set_A,
+ std::vector<RealType>& set_B,
+ const RealType wasserstein_power)
+ {
+ if (set_A.size() != set_B.size()) {
+ return std::numeric_limits<RealType>::infinity();
+ }
+ std::sort(set_A.begin(), set_A.end());
+ std::sort(set_B.begin(), set_B.end());
+ RealType result = 0.0;
+ for(size_t i = 0; i < set_A.size(); ++i) {
+ result += std::pow(std::fabs(set_A[i] - set_B[i]), wasserstein_power);
+ }
+ return result;
}
- return m1 == m2;
-}
+
+ template<class RealType>
+ struct SplitProblemInput
+ {
+ std::vector<DiagramPoint<RealType>> A_1;
+ std::vector<DiagramPoint<RealType>> B_1;
+ std::vector<DiagramPoint<RealType>> A_2;
+ std::vector<DiagramPoint<RealType>> B_2;
+
+ std::unordered_map<size_t, size_t> A_1_indices;
+ std::unordered_map<size_t, size_t> A_2_indices;
+ std::unordered_map<size_t, size_t> B_1_indices;
+ std::unordered_map<size_t, size_t> B_2_indices;
+
+ RealType mid_coord { 0.0 };
+ RealType strip_width { 0.0 };
+
+ void init_vectors(size_t n)
+ {
+
+ A_1_indices.clear();
+ A_2_indices.clear();
+ B_1_indices.clear();
+ B_2_indices.clear();
+
+ A_1.clear();
+ A_2.clear();
+ B_1.clear();
+ B_2.clear();
+
+ A_1.reserve(n / 2);
+ B_1.reserve(n / 2);
+ A_2.reserve(n / 2);
+ B_2.reserve(n / 2);
+ }
+
+ void init(const std::vector<DiagramPoint<RealType>>& A,
+ const std::vector<DiagramPoint<RealType>>& B)
+ {
+ using DiagramPointR = DiagramPoint<RealType>;
+
+ init_vectors(A.size());
+
+ RealType min_sum = std::numeric_limits<RealType>::max();
+ RealType max_sum = -std::numeric_limits<RealType>::max();
+ for(const auto& p_A : A) {
+ RealType s = p_A[0] + p_A[1];
+ if (s > max_sum)
+ max_sum = s;
+ if (s < min_sum)
+ min_sum = s;
+ mid_coord += s;
+ }
+
+ mid_coord /= A.size();
+
+ strip_width = 0.25 * (max_sum - min_sum);
+
+ auto first_diag_iter = std::upper_bound(A.begin(), A.end(), 0, [](const int& a, const DiagramPointR& p) { return a < (int)(p.is_diagonal()); });
+ size_t num_normal_A_points = std::distance(A.begin(), first_diag_iter);
+
+ // process all normal points in A,
+ // projections follow normal points
+ for(size_t i = 0; i < A.size(); ++i) {
+
+ assert(i < num_normal_A_points and A.is_normal() or i >= num_normal_A_points and A.is_diagonal());
+ assert(i < num_normal_A_points and B.is_diagonal() or i >= num_normal_A_points and B.is_normal());
+
+ RealType s = i < num_normal_A_points ? A[i][0] + A[i][1] : B[i][0] + B[i][1];
+
+ if (s < mid_coord + strip_width) {
+ // add normal point and its projection to the
+ // left half
+ A_1.push_back(A[i]);
+ B_1.push_back(B[i]);
+ A_1_indices[i] = A_1.size() - 1;
+ B_1_indices[i] = B_1.size() - 1;
+ }
+
+ if (s > mid_coord - strip_width) {
+ // to the right half
+ A_2.push_back(A[i]);
+ B_2.push_back(B[i]);
+ A_2_indices[i] = A_2.size() - 1;
+ B_2_indices[i] = B_2.size() - 1;
+ }
+
+ }
+ } // end init
+
+ };
+
+
+ // CAUTION:
+ // this function assumes that all coordinates are finite
+ // points at infinity are processed in wasserstein_cost
+ template<class RealType>
+ RealType wasserstein_cost_vec(const std::vector<DiagramPoint<RealType>>& A,
+ const std::vector<DiagramPoint<RealType>>& B,
+ const AuctionParams<RealType>& params,
+ const std::string& _log_filename_prefix)
+ {
+ if (params.wasserstein_power < 1.0) {
+ throw std::runtime_error("Bad q in Wasserstein " + std::to_string(params.wasserstein_power));
+ }
+ if (params.delta < 0.0) {
+ throw std::runtime_error("Bad delta in Wasserstein " + std::to_string(params.delta));
+ }
+ if (params.initial_epsilon < 0.0) {
+ throw std::runtime_error("Bad initial epsilon in Wasserstein" + std::to_string(params.initial_epsilon));
+ }
+ if (params.epsilon_common_ratio < 0.0) {
+ throw std::runtime_error("Bad epsilon factor in Wasserstein " + std::to_string(params.epsilon_common_ratio));
+ }
+
+ RealType result;
+
+ // just use Gauss-Seidel
+ AuctionRunnerGS<RealType> auction(A, B, params, _log_filename_prefix);
+ auction.run_auction();
+ result = auction.get_wasserstein_cost();
+ return result;
+ }
+
+} // ws
+
+
template<class PairContainer>
-double wassersteinDist(PairContainer& A, PairContainer& B, const double q, const double delta, const double _internal_p = std::numeric_limits<double>::infinity(), const double _initialEpsilon = 0.0, const double _epsFactor = 0.0)
+typename DiagramTraits<PairContainer>::RealType
+wasserstein_cost(const PairContainer& A,
+ const PairContainer& B,
+ const AuctionParams< typename DiagramTraits<PairContainer>::RealType >& params,
+ const std::string& _log_filename_prefix = "")
{
- if (areEqual(A, B)) {
+ using Traits = DiagramTraits<PairContainer>;
+
+ //using PointType = typename Traits::PointType;
+ using RealType = typename Traits::RealType;
+
+ if (hera::ws::are_equal(A, B)) {
return 0.0;
}
- bool a_empty { true };
- bool b_empty { true };
+ bool a_empty = true;
+ bool b_empty = true;
+ RealType total_cost_A = 0.0;
+ RealType total_cost_B = 0.0;
+
+ using DgmPoint = hera::ws::DiagramPoint<RealType>;
- std::vector<DiagramPoint> dgmA, dgmB;
+ std::vector<DgmPoint> dgm_A, dgm_B;
+ // coordinates of points at infinity
+ std::vector<RealType> x_plus_A, x_minus_A, y_plus_A, y_minus_A;
+ std::vector<RealType> x_plus_B, x_minus_B, y_plus_B, y_minus_B;
// loop over A, add projections of A-points to corresponding positions
// in B-vector
- for(auto& pairA : A) {
+ for(auto& pair_A : A) {
a_empty = false;
- double x = pairA.first;
- double y = pairA.second;
- dgmA.push_back(DiagramPoint(x, y, DiagramPoint::NORMAL));
- dgmB.push_back(DiagramPoint(x, y, DiagramPoint::DIAG));
+ RealType x = Traits::get_x(pair_A);
+ RealType y = Traits::get_y(pair_A);
+ if ( x == std::numeric_limits<RealType>::infinity()) {
+ y_plus_A.push_back(y);
+ } else if (x == -std::numeric_limits<RealType>::infinity()) {
+ y_minus_A.push_back(y);
+ } else if (y == std::numeric_limits<RealType>::infinity()) {
+ x_plus_A.push_back(x);
+ } else if (y == -std::numeric_limits<RealType>::infinity()) {
+ x_minus_A.push_back(x);
+ } else {
+ dgm_A.emplace_back(x, y, DgmPoint::NORMAL);
+ dgm_B.emplace_back(x, y, DgmPoint::DIAG);
+ total_cost_A += std::pow(dgm_A.back().persistence_lp(params.internal_p), params.wasserstein_power);
+ }
}
// the same for B
- for(auto& pairB : B) {
+ for(auto& pair_B : B) {
b_empty = false;
- double x = pairB.first;
- double y = pairB.second;
- dgmA.push_back(DiagramPoint(x, y, DiagramPoint::DIAG));
- dgmB.push_back(DiagramPoint(x, y, DiagramPoint::NORMAL));
+ RealType x = Traits::get_x(pair_B);
+ RealType y = Traits::get_y(pair_B);
+ if (x == std::numeric_limits<RealType>::infinity()) {
+ y_plus_B.push_back(y);
+ } else if (x == -std::numeric_limits<RealType>::infinity()) {
+ y_minus_B.push_back(y);
+ } else if (y == std::numeric_limits<RealType>::infinity()) {
+ x_plus_B.push_back(x);
+ } else if (y == -std::numeric_limits<RealType>::infinity()) {
+ x_minus_B.push_back(x);
+ } else {
+ dgm_A.emplace_back(x, y, DgmPoint::DIAG);
+ dgm_B.emplace_back(x, y, DgmPoint::NORMAL);
+ total_cost_B += std::pow(dgm_B.back().persistence_lp(params.internal_p), params.wasserstein_power);
+ }
}
- if (a_empty && b_empty)
- return 0.0;
+ RealType infinity_cost = ws::get_one_dimensional_cost(x_plus_A, x_plus_B, params.wasserstein_power);
+ infinity_cost += ws::get_one_dimensional_cost(x_minus_A, x_minus_B, params.wasserstein_power);
+ infinity_cost += ws::get_one_dimensional_cost(y_plus_A, y_plus_B, params.wasserstein_power);
+ infinity_cost += ws::get_one_dimensional_cost(y_minus_A, y_minus_B, params.wasserstein_power);
if (a_empty)
- dgmA.clear();
+ return total_cost_B + infinity_cost;
if (b_empty)
- dgmB.clear();
+ return total_cost_A + infinity_cost;
- return wassersteinDistVec(dgmA, dgmB, q, delta, _internal_p, _initialEpsilon, _epsFactor);
-}
-template<class PairContainer>
-double wassersteinCost(PairContainer& A, PairContainer& B, const double q, const double delta, const double _internal_p = std::numeric_limits<double>::infinity(), const double _initialEpsilon = 0.0, const double _epsFactor = 0.0)
-{
- if (areEqual(A, B)) {
- return 0.0;
+ if (infinity_cost == std::numeric_limits<RealType>::infinity()) {
+ return infinity_cost;
+ } else {
+ return infinity_cost + wasserstein_cost_vec(dgm_A, dgm_B, params, _log_filename_prefix);
}
- std::vector<DiagramPoint> dgmA, dgmB;
- // loop over A, add projections of A-points to corresponding positions
- // in B-vector
- for(auto& pairA : A) {
- double x = pairA.first;
- double y = pairA.second;
- dgmA.push_back(DiagramPoint(x, y, DiagramPoint::NORMAL));
- dgmB.push_back(DiagramPoint(x, y, DiagramPoint::DIAG));
- }
- // the same for B
- for(auto& pairB : B) {
- double x = pairB.first;
- double y = pairB.second;
- dgmA.push_back(DiagramPoint(x, y, DiagramPoint::DIAG));
- dgmB.push_back(DiagramPoint(x, y, DiagramPoint::NORMAL));
- }
-
- return wassersteinCostVec(dgmA, dgmB, q, delta, _internal_p, _initialEpsilon, _epsFactor);
}
+template<class PairContainer>
+typename DiagramTraits<PairContainer>::RealType
+wasserstein_dist(PairContainer& A,
+ PairContainer& B,
+ const AuctionParams<typename DiagramTraits<PairContainer>::RealType> params,
+ const std::string& _log_filename_prefix = "")
+{
+ using Real = typename DiagramTraits<PairContainer>::RealType;
+ return std::pow(hera::wasserstein_cost(A, B, params, _log_filename_prefix), Real(1.)/params.wasserstein_power);
+}
-// fill in result with points from file fname
-// return false if file can't be opened
-// or error occurred while reading
-bool readDiagramPointSet(const char* fname, PairVector& result);
-bool readDiagramPointSet(const std::string& fname, PairVector& result);
-void removeDuplicates(PairVector& dgmA, PairVector& dgmB);
-
-} // end of namespace geom_ws
+} // end of namespace hera
#endif
diff --git a/geom_matching/wasserstein/include/wasserstein_pure_geom.hpp b/geom_matching/wasserstein/include/wasserstein_pure_geom.hpp
new file mode 100644
index 0000000..2a57599
--- /dev/null
+++ b/geom_matching/wasserstein/include/wasserstein_pure_geom.hpp
@@ -0,0 +1,87 @@
+#ifndef WASSERSTEIN_PURE_GEOM_HPP
+#define WASSERSTEIN_PURE_GEOM_HPP
+
+#define WASSERSTEIN_PURE_GEOM
+
+
+#include "diagram_reader.h"
+#include "auction_oracle_kdtree_pure_geom.h"
+#include "auction_runner_gs.h"
+#include "auction_runner_jac.h"
+
+namespace hera
+{
+namespace ws
+{
+
+ template <class Real>
+ using DynamicTraits = typename hera::ws::dnn::DynamicPointTraits<Real>;
+
+ template <class Real>
+ using DynamicPoint = typename hera::ws::dnn::DynamicPointTraits<Real>::PointType;
+
+ template <class Real>
+ using DynamicPointVector = typename hera::ws::dnn::DynamicPointVector<Real>;
+
+ template <class Real>
+ using AuctionRunnerGSR = typename hera::ws::AuctionRunnerGS<Real, hera::ws::AuctionOracleKDTreePureGeom<Real>, hera::ws::dnn::DynamicPointVector<Real>>;
+
+ template <class Real>
+ using AuctionRunnerJacR = typename hera::ws::AuctionRunnerJac<Real, hera::ws::AuctionOracleKDTreePureGeom<Real>, hera::ws::dnn::DynamicPointVector<Real>>;
+
+
+double wasserstein_cost(const DynamicPointVector<double>& set_A, const DynamicPointVector<double>& set_B, const AuctionParams<double>& params)
+{
+ if (params.wasserstein_power < 1.0) {
+ throw std::runtime_error("Bad q in Wasserstein " + std::to_string(params.wasserstein_power));
+ }
+
+ if (params.delta < 0.0) {
+ throw std::runtime_error("Bad delta in Wasserstein " + std::to_string(params.delta));
+ }
+
+ if (params.initial_epsilon < 0.0) {
+ throw std::runtime_error("Bad initial epsilon in Wasserstein" + std::to_string(params.initial_epsilon));
+ }
+
+ if (params.epsilon_common_ratio < 0.0) {
+ throw std::runtime_error("Bad epsilon factor in Wasserstein " + std::to_string(params.epsilon_common_ratio));
+ }
+
+ if (set_A.size() != set_B.size()) {
+ throw std::runtime_error("Different cardinalities of point clouds: " + std::to_string(set_A.size()) + " != " + std::to_string(set_B.size()));
+ }
+
+ DynamicTraits<double> traits(params.dim);
+
+ DynamicPointVector<double> set_A_copy(set_A);
+ DynamicPointVector<double> set_B_copy(set_B);
+
+ // set point id to the index in vector
+ for(size_t i = 0; i < set_A.size(); ++i) {
+ traits.id(set_A_copy[i]) = i;
+ traits.id(set_B_copy[i]) = i;
+ }
+
+ if (params.max_bids_per_round == 1) {
+ hera::ws::AuctionRunnerGSR<double> auction(set_A_copy, set_B_copy, params);
+ auction.run_auction();
+ return auction.get_wasserstein_cost();
+ } else {
+ hera::ws::AuctionRunnerJacR<double> auction(set_A_copy, set_B_copy, params);
+ auction.run_auction();
+ return auction.get_wasserstein_cost();
+ }
+
+}
+
+double wasserstein_dist(const DynamicPointVector<double>& set_A, const DynamicPointVector<double>& set_B, const AuctionParams<double>& params)
+{
+ return std::pow(wasserstein_cost(set_A, set_B, params), 1.0 / params.wasserstein_power);
+}
+
+} // ws
+} // hera
+
+
+#endif
diff --git a/geom_matching/wasserstein/src/auction_oracle.cpp b/geom_matching/wasserstein/src/auction_oracle.cpp
deleted file mode 100644
index b19c0ae..0000000
--- a/geom_matching/wasserstein/src/auction_oracle.cpp
+++ /dev/null
@@ -1,1289 +0,0 @@
-/*
-
-Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
-All rights reserved.
-
-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.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-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 HOLDER 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.
-
-You are under no obligation whatsoever to provide any bug fixes, patches, or
-upgrades to the features, functionality or performance of the source code
-(Enhancements) to anyone; however, if you choose to make your Enhancements
-available either publicly, or directly to copyright holder,
-without imposing a separate written license agreement for such Enhancements,
-then you hereby grant the following license: a non-exclusive, royalty-free
-perpetual license to install, use, modify, prepare derivative works, incorporate
-into other computer software, distribute, and sublicense such enhancements or
-derivative works thereof, in binary and source code form.
-
- */
-
-#include <assert.h>
-#include <algorithm>
-#include <functional>
-#include <iterator>
-
-#include "def_debug_ws.h"
-#include "auction_oracle.h"
-
-namespace geom_ws {
-
-AuctionOracleAbstract::AuctionOracleAbstract(const std::vector<DiagramPoint>& _bidders, const std::vector<DiagramPoint>& _items, const double _wassersteinPower, const double _internal_p) :
- bidders(_bidders),
- items(_items),
- prices(items.size(), 0.0),
- wassersteinPower(_wassersteinPower),
- internal_p(_internal_p)
-{
-}
-
-double AuctionOracleAbstract::getValueForBidder(size_t bidderIdx, size_t itemIdx)
-{
- return pow(distLp(bidders[bidderIdx], items[itemIdx], internal_p), wassersteinPower) + prices[itemIdx];
-}
-
-// *****************************
-// AuctionOracleLazyHeap
-// *****************************
-
-AuctionOracleLazyHeap::AuctionOracleLazyHeap(const std::vector<DiagramPoint>& b,
- const std::vector<DiagramPoint>& g,
- double _wassersteinPower,
- const double internal_p) :
- AuctionOracleAbstract(b, g, _wassersteinPower, internal_p),
- maxVal(std::numeric_limits<double>::min()),
- biddersUpdateMoments(b.size(), 0),
- updateCounter(0)
-{
- assert(b.size() == g.size() );
- assert(b.size() > 1);
-
- weightMatrix.reserve(b.size());
- //const double maxDistUpperBound = 3 * getFurthestDistance3Approx(b, g);
- //weightAdjConst = pow(maxDistUpperBound, wassersteinPower);
- // init weight matrix
- for(const auto& pointA : bidders) {
- std::vector<double> weightVec;
- weightVec.clear();
- weightVec.reserve(b.size());
- for(const auto& pointB : items) {
- double val = pow(distLp(pointA, pointB, internal_p), wassersteinPower);
- if (val > maxVal) {
- maxVal = val;
- }
- weightVec.push_back( val );
- }
- weightMatrix.push_back(weightVec);
- }
- fillInLossesHeap();
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- updateList.push_back(std::make_pair(static_cast<IdxType>(itemIdx), 0));
- }
- for(auto updateListIter = updateList.begin(); updateListIter != updateList.end(); ++updateListIter) {
- itemsIterators.push_back(updateListIter);
- }
-}
-
-void AuctionOracleLazyHeap::updateQueueForBidder(IdxType bidderIdx)
-{
- assert(0 <= bidderIdx and bidderIdx < static_cast<int>(bidders.size()));
- assert(bidderIdx < static_cast<int>(biddersUpdateMoments.size()));
-
- int bidderLastUpdateTime = biddersUpdateMoments[bidderIdx];
- auto iter = updateList.begin();
- while (iter != updateList.end() and iter->second >= bidderLastUpdateTime) {
- IdxType itemIdx = iter->first;
- IdxValPair newVal { itemIdx, weightMatrix[bidderIdx][itemIdx] + prices[itemIdx]};
- // to-do: change indexing of lossesHeapHandles
- lossesHeap[bidderIdx]->decrease(lossesHeapHandles[bidderIdx][itemIdx], newVal);
- iter++;
- }
- biddersUpdateMoments[bidderIdx] = updateCounter;
-}
-
-void AuctionOracleLazyHeap::fillInLossesHeap(void)
-{
- for(size_t bidderIdx = 0; bidderIdx < bidders.size(); ++bidderIdx) {
- lossesHeap.push_back( new LossesHeap() );
- std::vector<LossesHeap::handle_type> handlesVec;
- lossesHeapHandles.push_back(handlesVec);
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- IdxValPair vp { itemIdx, weightMatrix[bidderIdx][itemIdx] + prices[itemIdx] };
- lossesHeapHandles[bidderIdx].push_back( lossesHeap[bidderIdx]->push(vp) );
- }
- }
-}
-
-AuctionOracleLazyHeap::~AuctionOracleLazyHeap()
-{
- for(auto h : lossesHeap) {
- delete h;
- }
-}
-
-void AuctionOracleLazyHeap::setPrice(IdxType itemIdx, double newPrice)
-{
- assert( prices.at(itemIdx) < newPrice );
-#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- std::cout << "price incremented by " << prices.at(itemIdx) - newPrice << std::endl;
-#endif
-#endif
- prices[itemIdx] = newPrice;
- // lazy: record the moment we updated the price of the items,
- // do not update queues.
- // 1. move the items with updated price to the front of the updateList,
- updateList.splice(updateList.begin(), updateList, itemsIterators[itemIdx]);
- // 2. record the moment we updated the price and increase the counter
- updateList.front().second = updateCounter++;
-}
-
-// subtract min. price from all prices
-void AuctionOracleLazyHeap::adjustPrices(void)
-{
- double minPrice = *(std::min_element(prices.begin(), prices.end()));
- std::transform(prices.begin(), prices.end(), prices.begin(), [minPrice](double a) { return a - minPrice; });
-}
-
-DebugOptimalBid AuctionOracleLazyHeap::getOptimalBidDebug(IdxType bidderIdx)
-{
- assert(bidderIdx >=0 and bidderIdx < static_cast<IdxType>(bidders.size()) );
- assert(lossesHeap.at(bidderIdx) != nullptr);
- assert(lossesHeap[bidderIdx]->size() >= 2);
-
- updateQueueForBidder(bidderIdx);
- DebugOptimalBid result;
-
- auto pHeap = lossesHeap[bidderIdx];
- auto topIter = pHeap->ordered_begin();
- result.bestItemIdx = topIter->first;
- result.bestItemValue = topIter->second;
- ++topIter; // now points to the second-best items
- result.secondBestItemValue = topIter->second;
- result.secondBestItemIdx = topIter->first;
-
-#ifndef FOR_R_TDA
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
-#endif
-
- return result;
-}
-
-IdxValPair AuctionOracleLazyHeap::getOptimalBid(const IdxType bidderIdx)
-{
- assert(bidderIdx >=0 and bidderIdx < static_cast<IdxType>(bidders.size()) );
- assert(lossesHeap.at(bidderIdx) != nullptr);
- assert(lossesHeap[bidderIdx]->size() >= 2);
-
- updateQueueForBidder(bidderIdx);
-
- auto pHeap = lossesHeap[bidderIdx];
- auto topIter = pHeap->ordered_begin();
- IdxType bestItemIdx = topIter->first;
- double bestItemValue = topIter->second;
- ++topIter; // now points to the second-best items
- double secondBestItemValue = topIter->second;
-
-#ifndef FOR_R_TDA
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
-#endif
-
- // bid value: price + value difference + epsilon
- return std::make_pair(bestItemIdx,
- prices[bestItemIdx] +
- ( secondBestItemValue - bestItemValue ) +
- epsilon );
-}
-
-// *****************************
-// AuctionOracleLazyHeapRestricted
-// *****************************
-
-AuctionOracleLazyHeapRestricted::AuctionOracleLazyHeapRestricted(const std::vector<DiagramPoint>& b,
- const std::vector<DiagramPoint>& g,
- double _wassersteinPower,
- double internal_p) :
- AuctionOracleAbstract(b, g, _wassersteinPower),
- maxVal(std::numeric_limits<double>::min()),
- biddersUpdateMoments(b.size(), 0),
- updateCounter(0),
- heapHandlesIndices(items.size(), std::numeric_limits<size_t>::max()),
- bestDiagonalItemsComputed(false)
-{
- assert(b.size() == g.size() );
- assert(b.size() > 1);
-
- weightMatrix.reserve(b.size());
- //const double maxDistUpperBound = 3 * getFurthestDistance3Approx(b, g);
- //weightAdjConst = pow(maxDistUpperBound, wassersteinPower);
- // init weight matrix
- for(const auto& pointA : bidders) {
- std::vector<double> weightVec;
- weightVec.clear();
- weightVec.reserve(b.size());
- for(const auto& pointB : items) {
- double val = pow(distLp(pointA, pointB, internal_p), wassersteinPower);
- weightVec.push_back( val );
- if ( val > maxVal )
- maxVal = val;
- }
- weightMatrix.push_back(weightVec);
- }
- fillInLossesHeap();
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- updateList.push_back(std::make_pair(static_cast<IdxType>(itemIdx), 0));
- }
- for(auto updateListIter = updateList.begin(); updateListIter != updateList.end(); ++updateListIter) {
- itemsIterators.push_back(updateListIter);
- }
-
- size_t handleIdx {0};
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- if (items[itemIdx].isDiagonal() ) {
- heapHandlesIndices[itemIdx] = handleIdx++;
- diagHeapHandles.push_back(diagItemsHeap.push(std::make_pair(itemIdx, 0)));
- }
- }
- // todo: this must be done in readFiles procedure
-}
-
-void AuctionOracleLazyHeapRestricted::updateQueueForBidder(IdxType bidderIdx)
-{
- assert(0 <= bidderIdx and bidderIdx < static_cast<int>(bidders.size()));
- assert(bidderIdx < static_cast<int>(biddersUpdateMoments.size()));
- assert(lossesHeap[bidderIdx] != nullptr );
-
- int bidderLastUpdateTime = biddersUpdateMoments[bidderIdx];
- auto iter = updateList.begin();
- while (iter != updateList.end() and iter->second >= bidderLastUpdateTime) {
- IdxType itemIdx = iter->first;
- size_t handleIdx = itemsIndicesForHeapHandles[bidderIdx][itemIdx];
- if (handleIdx < items.size() ) {
- IdxValPair newVal { itemIdx, weightMatrix[bidderIdx][itemIdx] + prices[itemIdx]};
- // to-do: change indexing of lossesHeapHandles
- lossesHeap[bidderIdx]->decrease(lossesHeapHandles[bidderIdx][handleIdx], newVal);
- }
- iter++;
- }
- biddersUpdateMoments[bidderIdx] = updateCounter;
-}
-
-void AuctionOracleLazyHeapRestricted::fillInLossesHeap(void)
-{
- for(size_t bidderIdx = 0; bidderIdx < bidders.size(); ++bidderIdx) {
- DiagramPoint bidder { bidders[bidderIdx] };
- // no heap for diagonal bidders
- if ( bidder.isDiagonal() ) {
- lossesHeap.push_back( nullptr );
- lossesHeapHandles.push_back(std::vector<LossesHeap::handle_type>());
- itemsIndicesForHeapHandles.push_back( std::vector<size_t>() );
- continue;
- } else {
- lossesHeap.push_back( new LossesHeap() );
- assert( lossesHeap.at(bidderIdx) != nullptr );
- itemsIndicesForHeapHandles.push_back( std::vector<size_t>(items.size(), std::numeric_limits<size_t>::max() ) );
-
- std::vector<LossesHeap::handle_type> handlesVec;
- lossesHeapHandles.push_back(handlesVec);
- size_t handleIdx { 0 };
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- assert( itemsIndicesForHeapHandles.at(bidderIdx).at(itemIdx) > 0 );
- DiagramPoint item { items[itemIdx] };
- if ( item.isNormal() ) {
- // item can be assigned to bidder, store in heap
- IdxValPair vp { itemIdx, weightMatrix[bidderIdx][itemIdx] + prices[itemIdx] };
- lossesHeapHandles[bidderIdx].push_back( lossesHeap[bidderIdx]->push(vp) );
- // keep corresponding index in itemsIndicesForHeapHandles
- itemsIndicesForHeapHandles[bidderIdx][itemIdx] = handleIdx++;
- }
- }
- }
- }
-}
-
-AuctionOracleLazyHeapRestricted::~AuctionOracleLazyHeapRestricted()
-{
- for(auto h : lossesHeap) {
- delete h;
- }
-}
-
-void AuctionOracleLazyHeapRestricted::setPrice(IdxType itemIdx, double newPrice)
-{
- assert( prices.at(itemIdx) < newPrice );
-#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- std::cout << "price incremented by " << prices.at(itemIdx) - newPrice << std::endl;
-#endif
-#endif
- prices[itemIdx] = newPrice;
- if (items[itemIdx].isNormal() ) {
- // lazy: record the moment we updated the price of the items,
- // do not update queues.
- // 1. move the items with updated price to the front of the updateList,
- updateList.splice(updateList.begin(), updateList, itemsIterators[itemIdx]);
- // 2. record the moment we updated the price and increase the counter
- updateList.front().second = updateCounter++;
- } else {
- // diagonal items are stored in one heap
- diagItemsHeap.decrease(diagHeapHandles[heapHandlesIndices[itemIdx]], std::make_pair(itemIdx, newPrice));
- bestDiagonalItemsComputed = false;
- }
-}
-
-// subtract min. price from all prices
-void AuctionOracleLazyHeapRestricted::adjustPrices(void)
-{
-}
-
-DebugOptimalBid AuctionOracleLazyHeapRestricted::getOptimalBidDebug(IdxType bidderIdx)
-{
- DebugOptimalBid result;
- assert(bidderIdx >=0 and bidderIdx < static_cast<IdxType>(bidders.size()) );
-
- DiagramPoint bidder = bidders[bidderIdx];
- std::vector<IdxValPair> candItems;
- // corresponding point is always considered as a candidate
-
- size_t projItemIdx = bidderIdx;
- assert( 0 <= projItemIdx and projItemIdx < items.size() );
- DiagramPoint projItem = items[projItemIdx];
- assert(projItem.type != bidder.type);
- //assert(projItem.projId == bidder.id);
- //assert(projItem.id == bidder.projId);
- // todo: store precomputed distance?
- double projItemValue = pow(distLp(bidder, projItem, internal_p), wassersteinPower) + prices[projItemIdx];
- candItems.push_back( std::make_pair(projItemIdx, projItemValue) );
-
- if (bidder.isNormal()) {
- assert(lossesHeap.at(bidderIdx) != nullptr);
- assert(lossesHeap[bidderIdx]->size() >= 2);
- updateQueueForBidder(bidderIdx);
- auto pHeap = lossesHeap[bidderIdx];
- assert( pHeap != nullptr );
- auto topIter = pHeap->ordered_begin();
- candItems.push_back( *topIter );
- ++topIter; // now points to the second-best items
- candItems.push_back( *topIter );
- std::sort(candItems.begin(), candItems.end(), CompPairsBySecondStruct());
- assert(candItems[1].second >= candItems[0].second);
- } else {
- // for diagonal bidder the only normal point has already been added
- // the other 2 candidates are diagonal items only, get from the heap
- // with prices
- assert(diagItemsHeap.size() > 1);
- auto topDiagIter = diagItemsHeap.ordered_begin();
- auto topDiag1 = *topDiagIter++;
- auto topDiag2 = *topDiagIter;
- candItems.push_back(topDiag1);
- candItems.push_back(topDiag2);
- std::sort(candItems.begin(), candItems.end(), CompPairsBySecondStruct());
- assert(candItems.size() == 3);
- assert(candItems[2].second >= candItems[1].second);
- assert(candItems[1].second >= candItems[0].second);
- }
-
- result.bestItemIdx = candItems[0].first;
- result.secondBestItemIdx = candItems[1].first;
- result.bestItemValue = candItems[0].second;
- result.secondBestItemValue = candItems[1].second;
-
- // checking code
-
- //DebugOptimalBid debugMyResult(result);
- //DebugOptimalBid debugNaiveResult;
- //debugNaiveResult.bestItemValue = 1e20;
- //debugNaiveResult.secondBestItemValue = 1e20;
- //double currItemValue;
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if ( bidders[bidderIdx].type != items[itemIdx].type and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- //currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- //if (currItemValue < debugNaiveResult.bestItemValue) {
- //debugNaiveResult.bestItemValue = currItemValue;
- //debugNaiveResult.bestItemIdx = itemIdx;
- //}
- //}
-
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if (itemIdx == debugNaiveResult.bestItemIdx) {
- //continue;
- //}
- //if ( bidders[bidderIdx].type != items[itemIdx].type and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- //currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- //if (currItemValue < debugNaiveResult.secondBestItemValue) {
- //debugNaiveResult.secondBestItemValue = currItemValue;
- //debugNaiveResult.secondBestItemIdx = itemIdx;
- //}
- //}
-
- //if ( fabs( debugMyResult.bestItemValue - debugNaiveResult.bestItemValue ) > 1e-6 or
- //fabs( debugNaiveResult.secondBestItemValue - debugMyResult.secondBestItemValue) > 1e-6 ) {
- //std::cerr << "bidderIdx = " << bidderIdx << "; ";
- //std::cerr << bidders[bidderIdx] << std::endl;
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //std::cout << itemIdx << ": " << items[itemIdx] << "; price = " << prices[itemIdx] << std::endl;
- //}
- //std::cerr << "debugMyResult: " << debugMyResult << std::endl;
- //std::cerr << "debugNaiveResult: " << debugNaiveResult << std::endl;
- //auto pHeap = lossesHeap[bidderIdx];
- //assert( pHeap != nullptr );
- //for(auto topIter = pHeap->ordered_begin(); topIter != pHeap->ordered_end(); ++topIter) {
- //std::cerr << "in heap: " << topIter->first << ": " << topIter->second << "; real value = " << distLp(bidder, items[topIter->first]) + prices[topIter->first] << std::endl;
- //}
- //for(auto ci : candItems) {
- //std::cout << "ci.idx = " << ci.first << ", value = " << ci.second << std::endl;
- //}
-
- ////std::cerr << "twoBestItems: " << twoBestItems[0].d << " " << twoBestItems[1].d << std::endl;
- //assert(false);
- //}
-
-
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
-
- return result;
-}
-
-IdxValPair AuctionOracleLazyHeapRestricted::getOptimalBid(const IdxType bidderIdx)
-{
- IdxType bestItemIdx;
- //IdxType secondBestItemIdx;
- double bestItemValue;
- double secondBestItemValue;
-
- auto& bidder = bidders[bidderIdx];
- IdxType projItemIdx = bidderIdx;
- assert( 0 <= projItemIdx and projItemIdx < items.size() );
- DiagramPoint projItem = items[projItemIdx];
- assert(projItem.type != bidder.type);
- //assert(projItem.projId == bidder.id);
- //assert(projItem.id == bidder.projId);
- // todo: store precomputed distance?
- double projItemValue = pow(distLp(bidder, projItem, internal_p), wassersteinPower) + prices[projItemIdx];
-
- if (bidder.isDiagonal()) {
- // for diagonal bidder the only normal point has already been added
- // the other 2 candidates are diagonal items only, get from the heap
- // with prices
- assert(diagItemsHeap.size() > 1);
- if (!bestDiagonalItemsComputed) {
- auto topDiagIter = diagItemsHeap.ordered_begin();
- bestDiagonalItemIdx = topDiagIter->first;
- bestDiagonalItemValue = topDiagIter->second;
- topDiagIter++;
- secondBestDiagonalItemIdx = topDiagIter->first;
- secondBestDiagonalItemValue = topDiagIter->second;
- bestDiagonalItemsComputed = true;
- }
-
- if ( projItemValue < bestDiagonalItemValue) {
- bestItemIdx = projItemIdx;
- bestItemValue = projItemValue;
- secondBestItemValue = bestDiagonalItemValue;
- //secondBestItemIdx = bestDiagonalItemIdx;
- } else if (projItemValue < secondBestDiagonalItemValue) {
- bestItemIdx = bestDiagonalItemIdx;
- bestItemValue = bestDiagonalItemValue;
- secondBestItemValue = projItemValue;
- //secondBestItemIdx = projItemIdx;
- } else {
- bestItemIdx = bestDiagonalItemIdx;
- bestItemValue = bestDiagonalItemValue;
- secondBestItemValue = secondBestDiagonalItemValue;
- //secondBestItemIdx = secondBestDiagonalItemIdx;
- }
- } else {
- // for normal bidder get 2 best items among non-diagonal (=normal) points
- // from the corresponding heap
- assert(diagItemsHeap.size() > 1);
- updateQueueForBidder(bidderIdx);
- auto topNormIter = lossesHeap[bidderIdx]->ordered_begin();
- IdxType bestNormalItemIdx { topNormIter->first };
- double bestNormalItemValue { topNormIter->second };
- topNormIter++;
- double secondBestNormalItemValue { topNormIter->second };
- //IdxType secondBestNormalItemIdx { topNormIter->first };
-
- if ( projItemValue < bestNormalItemValue) {
- bestItemIdx = projItemIdx;
- bestItemValue = projItemValue;
- secondBestItemValue = bestNormalItemValue;
- //secondBestItemIdx = bestNormalItemIdx;
- } else if (projItemValue < secondBestNormalItemValue) {
- bestItemIdx = bestNormalItemIdx;
- bestItemValue = bestNormalItemValue;
- secondBestItemValue = projItemValue;
- //secondBestItemIdx = projItemIdx;
- } else {
- bestItemIdx = bestNormalItemIdx;
- bestItemValue = bestNormalItemValue;
- secondBestItemValue = secondBestNormalItemValue;
- //secondBestItemIdx = secondBestNormalItemIdx;
- }
- }
-
- IdxValPair result;
-
- assert( secondBestItemValue >= bestItemValue );
-
- result.first = bestItemIdx;
- result.second = ( secondBestItemValue - bestItemValue ) + prices[bestItemIdx] + epsilon;
-
-
- // checking code
-
- //DebugOptimalBid debugMyResult;
- //debugMyResult.bestItemIdx = bestItemIdx;
- //debugMyResult.bestItemValue = bestItemValue;
- //debugMyResult.secondBestItemIdx = secondBestItemIdx;
- //debugMyResult.secondBestItemValue = secondBestItemValue;
- //DebugOptimalBid debugNaiveResult;
- //debugNaiveResult.bestItemValue = 1e20;
- //debugNaiveResult.secondBestItemValue = 1e20;
- //double currItemValue;
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if ( bidders[bidderIdx].type != items[itemIdx].type and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- //currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- //if (currItemValue < debugNaiveResult.bestItemValue) {
- //debugNaiveResult.bestItemValue = currItemValue;
- //debugNaiveResult.bestItemIdx = itemIdx;
- //}
- //}
-
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if (itemIdx == debugNaiveResult.bestItemIdx) {
- //continue;
- //}
- //if ( bidders[bidderIdx].type != items[itemIdx].type and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- //currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- //if (currItemValue < debugNaiveResult.secondBestItemValue) {
- //debugNaiveResult.secondBestItemValue = currItemValue;
- //debugNaiveResult.secondBestItemIdx = itemIdx;
- //}
- //}
- ////std::cout << "got naive result" << std::endl;
-
- //if ( fabs( debugMyResult.bestItemValue - debugNaiveResult.bestItemValue ) > 1e-6 or
- //fabs( debugNaiveResult.secondBestItemValue - debugMyResult.secondBestItemValue) > 1e-6 ) {
- //std::cerr << "bidderIdx = " << bidderIdx << "; ";
- //std::cerr << bidders[bidderIdx] << std::endl;
- //for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //std::cout << itemIdx << ": " << items[itemIdx] << "; price = " << prices[itemIdx] << std::endl;
- //}
- //std::cerr << "debugMyResult: " << debugMyResult << std::endl;
- //std::cerr << "debugNaiveResult: " << debugNaiveResult << std::endl;
- //auto pHeap = lossesHeap[bidderIdx];
- //if ( pHeap != nullptr ) {
- //for(auto topIter = pHeap->ordered_begin(); topIter != pHeap->ordered_end(); ++topIter) {
- //std::cerr << "in heap: " << topIter->first << ": " << topIter->second << "; real value = " << distLp(bidder, items[topIter->first]) + prices[topIter->first] << std::endl;
- //}
- //}
- ////for(auto ci : candItems) {
- ////std::cout << "ci.idx = " << ci.first << ", value = " << ci.second << std::endl;
- ////}
-
- ////std::cerr << "twoBestItems: " << twoBestItems[0].d << " " << twoBestItems[1].d << std::endl;
- //assert(false);
- // }
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
-
- return result;
-}
-
-
-// *****************************
-// AuctionOracleKDTree
-// *****************************
-
-AuctionOracleKDTree::AuctionOracleKDTree(const std::vector<DiagramPoint>& _bidders,
- const std::vector<DiagramPoint>& _items,
- double _wassersteinPower,
- double internal_p) :
- AuctionOracleAbstract(_bidders, _items, _wassersteinPower, internal_p),
- heapHandlesIndices(items.size(), std::numeric_limits<size_t>::max()),
- kdtreeItems(items.size(), std::numeric_limits<size_t>::max())
-{
- //assert(wassersteinPower == 1.0); // temporarily, to-do: update dnn to search with any q
- size_t dnnItemIdx { 0 };
- size_t trueIdx { 0 };
- dnnPoints.clear();
- // store normal items in kd-tree
- for(const auto& g : items) {
- if (g.isNormal()) {
- kdtreeItems[trueIdx] = dnnItemIdx;
- // index of items is id of dnn-point
- DnnPoint p(trueIdx);
- p[0] = g.getRealX();
- p[1] = g.getRealY();
- dnnPoints.push_back(p);
- assert(dnnItemIdx == dnnPoints.size() - 1);
- dnnItemIdx++;
- }
- trueIdx++;
- }
-
- assert(dnnPoints.size() < items.size() );
- for(size_t i = 0; i < dnnPoints.size(); ++i) {
- dnnPointHandles.push_back(&dnnPoints[i]);
- }
- DnnTraits traits;
- traits.internal_p = internal_p;
- kdtree = new dnn::KDTree<DnnTraits>(traits, dnnPointHandles, wassersteinPower);
-
- size_t dnnItemIdxAll { 0 };
- dnnPointsAll.clear();
- // store all items in kd-tree
- for(const auto& g : items) {
- DnnPoint p(dnnItemIdxAll++);
- p[0] = g.getRealX();
- p[1] = g.getRealY();
- dnnPointsAll.push_back(p);
- assert(dnnItemIdxAll == dnnPointsAll.size());
- }
-
- for(size_t i = 0; i < dnnPointsAll.size(); ++i) {
- dnnPointHandlesAll.push_back(&dnnPointsAll[i]);
- }
- kdtreeAll = new dnn::KDTree<DnnTraits>(traits, dnnPointHandlesAll, wassersteinPower);
-
- size_t handleIdx {0};
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- if (items[itemIdx].isDiagonal() ) {
- heapHandlesIndices[itemIdx] = handleIdx++;
- diagHeapHandles.push_back(diagItemsHeap.push(std::make_pair(itemIdx, 0)));
- }
- }
- //to-do: remove maxVal from
- maxVal = 3*getFurthestDistance3Approx(_bidders, _items);
- maxVal = pow(maxVal, wassersteinPower);
- weightAdjConst = maxVal;
-}
-
-DebugOptimalBid AuctionOracleKDTree::getOptimalBidDebug(IdxType bidderIdx)
-{
- DebugOptimalBid result;
- DiagramPoint bidder = bidders[bidderIdx];
- DnnPoint bidderDnn;
- bidderDnn[0] = bidder.getRealX();
- bidderDnn[1] = bidder.getRealY();
-
- std::vector<IdxValPair> candItems;
-
-
- if ( bidder.isDiagonal() ) {
- //
- auto twoBestItems = kdtree->findK(bidderDnn, 2);
- candItems.push_back( std::make_pair(twoBestItems[0].p->id(), twoBestItems[0].d) );
- candItems.push_back( std::make_pair(twoBestItems[1].p->id(), twoBestItems[1].d) );
- assert(diagItemsHeap.size() > 1);
- auto topDiagIter = diagItemsHeap.ordered_begin();
- auto topDiag1 = *topDiagIter++;
- auto topDiag2 = *topDiagIter;
- candItems.push_back(topDiag1);
- candItems.push_back(topDiag2);
- assert(candItems.size() == 4);
- std::sort(candItems.begin(), candItems.end(), CompPairsBySecondStruct());
- assert(candItems[3].second >= candItems[2].second);
- assert(candItems[2].second >= candItems[1].second);
- assert(candItems[1].second >= candItems[0].second);
- } else {
- auto twoBestItems = kdtreeAll->findK(bidderDnn, 2);
- candItems.push_back( std::make_pair(twoBestItems[0].p->id(), twoBestItems[0].d) );
- candItems.push_back( std::make_pair(twoBestItems[1].p->id(), twoBestItems[1].d) );
- //size_t projItemIdx { biddersProjIndices.at(bidderIdx) };
- //assert(items[projItemIdx].projId == bidder.id);
- //double projItemValue { pow(distLp(bidder, items[projItemIdx]), wassersteinPower) + prices.at(projItemIdx) };
- //candItems.push_back( std::make_pair(projItemIdx, projItemValue) );
- assert(candItems.size() == 2);
- assert(candItems[1].second >= candItems[0].second);
- }
-
- result.bestItemIdx = candItems[0].first;
- result.secondBestItemIdx = candItems[1].first;
- result.bestItemValue = candItems[0].second;
- result.secondBestItemValue = candItems[1].second;
- //double bestItemsPrice = prices[bestItemIdx];
- //if (items[result.bestItemIdx].type == DiagramPoint::DIAG) {
- //double bestItemValue1 = pow( distLp(bidder, items[result.bestItemIdx]), q) + prices[result.bestItemIdx];
- //if ( fabs(result.bestItemValue - bestItemValue1) > 1e-6 ) {
- //std::cerr << "XXX: " << result.bestItemValue << " vs " << bestItemValue1 << std::endl;
- //result.bestItemValue = bestItemValue1;
- //}
-
- //}
-
-
- // checking code
- /*
-
- DebugOptimalBid debugMyResult(result);
- DebugOptimalBid debugNaiveResult;
- debugNaiveResult.bestItemValue = 1e20;
- debugNaiveResult.secondBestItemValue = 1e20;
- double currItemValue;
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if ( bidders[bidderIdx].type == DiagramPoint::NORMAL and
- //items[itemIdx].type == DiagramPoint::DIAG and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- if (currItemValue < debugNaiveResult.bestItemValue) {
- debugNaiveResult.bestItemValue = currItemValue;
- debugNaiveResult.bestItemIdx = itemIdx;
- }
- }
-
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- if (itemIdx == debugNaiveResult.bestItemIdx) {
- continue;
- }
- currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- if (currItemValue < debugNaiveResult.secondBestItemValue) {
- debugNaiveResult.secondBestItemValue = currItemValue;
- debugNaiveResult.secondBestItemIdx = itemIdx;
- }
- }
- //std::cout << "got naive result" << std::endl;
-
- if ( fabs( debugMyResult.bestItemValue - debugNaiveResult.bestItemValue ) > 1e-6 or
- fabs( debugNaiveResult.secondBestItemValue - debugMyResult.secondBestItemValue) > 1e-6 ) {
- kdtreeAll->printWeights();
- std::cerr << "bidderIdx = " << bidderIdx << "; ";
- std::cerr << bidders[bidderIdx] << std::endl;
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- std::cout << itemIdx << ": " << items[itemIdx] << "; price = " << prices[itemIdx] << std::endl;
- }
- std::cerr << "debugMyResult: " << debugMyResult << std::endl;
- std::cerr << "debugNaiveResult: " << debugNaiveResult << std::endl;
- //std::cerr << "twoBestItems: " << twoBestItems[0].d << " " << twoBestItems[1].d << std::endl;
- assert(false);
- }
- //std::cout << "returning" << std::endl;
-
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << secondBestItemIdx << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[secondBestItemIdx] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << secondBestItemIdx << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[secondBestItemIdx] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- */
-
- return result;
-}
-
-IdxValPair AuctionOracleKDTree::getOptimalBid(IdxType bidderIdx)
-{
- IdxValPair result;
- DebugOptimalBid debugMyResult = getOptimalBidDebug(bidderIdx);
- result.first = debugMyResult.bestItemIdx;
- result.second = ( debugMyResult.secondBestItemValue - debugMyResult.bestItemValue ) + prices[debugMyResult.bestItemIdx] + epsilon;
- return result;
-}
-/*
-a_{ij} = d_{ij}
-value_{ij} = a_{ij} + price_j
-*/
-void AuctionOracleKDTree::setPrice(IdxType itemIdx, double newPrice)
-{
- assert(prices.size() == items.size());
- assert( 0 < diagHeapHandles.size() and diagHeapHandles.size() <= items.size());
- assert(newPrice > prices.at(itemIdx));
- prices[itemIdx] = newPrice;
- if ( items[itemIdx].isNormal() ) {
- assert(0 <= itemIdx and itemIdx < kdtreeItems.size());
- assert(0 <= kdtreeItems[itemIdx] and kdtreeItems[itemIdx] < dnnPointHandles.size());
- kdtree->change_weight( dnnPointHandles[kdtreeItems[itemIdx]], newPrice);
- kdtreeAll->change_weight( dnnPointHandlesAll[itemIdx], newPrice);
- } else {
- kdtreeAll->change_weight( dnnPointHandlesAll[itemIdx], newPrice);
- assert(diagHeapHandles.size() > heapHandlesIndices.at(itemIdx));
- diagItemsHeap.decrease(diagHeapHandles[heapHandlesIndices[itemIdx]], std::make_pair(itemIdx, newPrice));
- }
-}
-
-void AuctionOracleKDTree::adjustPrices(void)
-{
-}
-
-AuctionOracleKDTree::~AuctionOracleKDTree()
-{
- delete kdtree;
- delete kdtreeAll;
-}
-
-void AuctionOracleKDTree::setEpsilon(double newVal)
-{
- assert(newVal >= 0.0);
- epsilon = newVal;
-}
-
-// *****************************
-// AuctionOracleRestricted
-// *****************************
-AuctionOracleRestricted::AuctionOracleRestricted(const std::vector<DiagramPoint>& b,
- const std::vector<DiagramPoint>& g,
- double _wassersteinPower,
- double internal_p) :
- AuctionOracleAbstract(b, g, _wassersteinPower, internal_p),
- maxVal(0.0)
-{
- assert(b.size() == g.size() );
- assert(b.size() > 1);
-
- weightMatrix.reserve(b.size());
- for(const auto& pointA : bidders) {
- std::vector<double> weightVec;
- weightVec.clear();
- weightVec.reserve(b.size());
- for(const auto& pointB : items) {
- double val = pow(distLp(pointA, pointB, internal_p), wassersteinPower);
- if (val > maxVal) {
- maxVal = val;
- }
- weightVec.push_back( val );
- }
- weightMatrix.push_back(weightVec);
- }
-}
-
-IdxValPair AuctionOracleRestricted::getOptimalBid(const IdxType bidderIdx)
-{
- assert(bidderIdx >=0 and bidderIdx < static_cast<IdxType>(bidders.size()) );
-
- const auto bidder = bidders[bidderIdx];
-
- IdxType bestItemIdx { -1 };
- double bestItemValue { std::numeric_limits<double>::max() };
- //IdxType secondBestItemIdx { -1 };
- double secondBestItemValue { std::numeric_limits<double>::max() };
-
- // find best items idx
- for(IdxType itemIdx = 0; itemIdx < static_cast<IdxType>(items.size()); ++itemIdx) {
- // non-diagonal point should be matched either to another
- // non-diagonal point or to its own projection
- if (isRestricted and bidder.isNormal() ) {
- auto item = items[itemIdx];
- if (item.isDiagonal() and itemIdx != bidderIdx)
- continue;
- }
- auto currItemValue = weightMatrix[bidderIdx][itemIdx] + prices[itemIdx];
- if ( currItemValue < bestItemValue ) {
- bestItemValue = currItemValue;
- bestItemIdx = itemIdx;
- }
- }
-
- // find second best items idx and value
-
- for(IdxType itemIdx = 0; itemIdx < static_cast<IdxType>(items.size()); ++itemIdx) {
- // non-diagonal point should be matched either to another
- // non-diagonal point or to its own projection
- if (isRestricted and bidder.isNormal() ) {
- auto itemsItem = items[itemIdx];
- if (itemsItem.isDiagonal() and itemIdx != bidderIdx)
- continue;
- }
-
- if (static_cast<IdxType>(itemIdx) == bestItemIdx)
- continue;
-
- auto currItemValue = weightMatrix[bidderIdx][itemIdx] + prices[itemIdx];
- if ( currItemValue < secondBestItemValue ) {
- secondBestItemValue = currItemValue;
- //secondBestItemIdx = itemIdx;
- }
- }
-
- assert(bestItemValue <= secondBestItemValue);
-
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << secondBestItemIdx << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[secondBestItemIdx] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << topIter->first << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[topIter->first] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
-
- // bid value: price + value difference + epsilon
-
- return std::make_pair(bestItemIdx,
- prices[bestItemIdx] +
- ( -bestItemValue + secondBestItemValue ) +
- epsilon );
-}
-
-void AuctionOracleRestricted::setPrice(const IdxType itemIdx, const double newPrice)
-{
- assert(prices.at(itemIdx) < newPrice );
- prices[itemIdx] = newPrice;
-}
-
-// *****************************
-// AuctionOracleKDTreeRestricted
-// *****************************
-
-AuctionOracleKDTreeRestricted::AuctionOracleKDTreeRestricted(const std::vector<DiagramPoint>& _bidders,
- const std::vector<DiagramPoint>& _items,
- const double _wassersteinPower,
- const double internal_p) :
- AuctionOracleAbstract(_bidders, _items, _wassersteinPower, internal_p),
- heapHandlesIndices(items.size(), std::numeric_limits<size_t>::max()),
- kdtreeItems(items.size(), std::numeric_limits<size_t>::max()),
- bestDiagonalItemsComputed(false)
-{
- size_t dnnItemIdx { 0 };
- size_t trueIdx { 0 };
- dnnPoints.clear();
- // store normal items in kd-tree
- for(const auto& g : items) {
- if (g.isNormal() ) {
- kdtreeItems[trueIdx] = dnnItemIdx;
- // index of items is id of dnn-point
- DnnPoint p(trueIdx);
- p[0] = g.getRealX();
- p[1] = g.getRealY();
- dnnPoints.push_back(p);
- assert(dnnItemIdx == dnnPoints.size() - 1);
- dnnItemIdx++;
- }
- trueIdx++;
- }
-
- assert(dnnPoints.size() < items.size() );
- for(size_t i = 0; i < dnnPoints.size(); ++i) {
- dnnPointHandles.push_back(&dnnPoints[i]);
- }
- DnnTraits traits;
- traits.internal_p = internal_p;
- kdtree = new dnn::KDTree<DnnTraits>(traits, dnnPointHandles, wassersteinPower);
-
- size_t handleIdx {0};
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- if (items[itemIdx].isDiagonal()) {
- heapHandlesIndices[itemIdx] = handleIdx++;
- diagHeapHandles.push_back(diagItemsHeap.push(std::make_pair(itemIdx, 0)));
- }
- }
- //to-do: remove maxVal from
- maxVal = 3*getFurthestDistance3Approx(_bidders, _items);
- maxVal = pow(maxVal, wassersteinPower);
- weightAdjConst = maxVal;
-}
-
-DebugOptimalBid AuctionOracleKDTreeRestricted::getOptimalBidDebug(IdxType bidderIdx)
-{
- DebugOptimalBid result;
- DiagramPoint bidder = bidders[bidderIdx];
-
- // corresponding point is always considered as a candidate
- // if bidder is a diagonal point, projItem is a normal point,
- // and vice versa.
-
- size_t projItemIdx = bidderIdx;
- assert( 0 <= projItemIdx and projItemIdx < items.size() );
- DiagramPoint projItem = items[projItemIdx];
- assert(projItem.type != bidder.type);
- //assert(projItem.projId == bidder.id);
- //assert(projItem.id == bidder.projId);
- // todo: store precomputed distance?
- double projItemValue = pow(distLp(bidder, projItem, internal_p), wassersteinPower) + prices[projItemIdx];
-
- if (bidder.isDiagonal()) {
- // for diagonal bidder the only normal point has already been added
- // the other 2 candidates are diagonal items only, get from the heap
- // with prices
- assert(diagItemsHeap.size() > 1);
- if (!bestDiagonalItemsComputed) {
- auto topDiagIter = diagItemsHeap.ordered_begin();
- bestDiagonalItemIdx = topDiagIter->first;
- bestDiagonalItemValue = topDiagIter->second;
- topDiagIter++;
- secondBestDiagonalItemIdx = topDiagIter->first;
- secondBestDiagonalItemValue = topDiagIter->second;
- bestDiagonalItemsComputed = true;
- }
-
- if ( projItemValue < bestDiagonalItemValue) {
- result.bestItemIdx = projItemIdx;
- result.bestItemValue = projItemValue;
- result.secondBestItemIdx = bestDiagonalItemIdx;
- result.secondBestItemValue = bestDiagonalItemValue;
- } else if (projItemValue < secondBestDiagonalItemValue) {
- result.bestItemIdx = bestDiagonalItemIdx;
- result.bestItemValue = bestDiagonalItemValue;
- result.secondBestItemIdx = projItemIdx;
- result.secondBestItemValue = projItemValue;
- } else {
- result.bestItemIdx = bestDiagonalItemIdx;
- result.bestItemValue = bestDiagonalItemValue;
- result.secondBestItemIdx = secondBestDiagonalItemIdx;
- result.secondBestItemValue = secondBestDiagonalItemValue;
- }
- } else {
- // for normal bidder get 2 best items among non-diagonal points from
- // kdtree
- DnnPoint bidderDnn;
- bidderDnn[0] = bidder.getRealX();
- bidderDnn[1] = bidder.getRealY();
- auto twoBestItems = kdtree->findK(bidderDnn, 2);
- size_t bestNormalItemIdx { twoBestItems[0].p->id() };
- double bestNormalItemValue { twoBestItems[0].d };
- size_t secondBestNormalItemIdx { twoBestItems[1].p->id() };
- double secondBestNormalItemValue { twoBestItems[1].d };
-
- if ( projItemValue < bestNormalItemValue) {
- result.bestItemIdx = projItemIdx;
- result.bestItemValue = projItemValue;
- result.secondBestItemIdx = bestNormalItemIdx;
- result.secondBestItemValue = bestNormalItemValue;
- } else if (projItemValue < secondBestNormalItemValue) {
- result.bestItemIdx = bestNormalItemIdx;
- result.bestItemValue = bestNormalItemValue;
- result.secondBestItemIdx = projItemIdx;
- result.secondBestItemValue = projItemValue;
- } else {
- result.bestItemIdx = bestNormalItemIdx;
- result.bestItemValue = bestNormalItemValue;
- result.secondBestItemIdx = secondBestNormalItemIdx;
- result.secondBestItemValue = secondBestNormalItemValue;
- }
- }
-
- return result;
-
- //std::cout << "got result: " << result << std::endl;
- //double bestItemsPrice = prices[bestItemIdx];
- //if (items[result.bestItemIdx].type == DiagramPoint::DIAG) {
- //double bestItemValue1 = pow( distLp(bidder, items[result.bestItemIdx]), wassersteinPower) + prices[result.bestItemIdx];
- //if ( fabs(result.bestItemValue - bestItemValue1) > 1e-6 ) {
- //std::cerr << "XXX: " << result.bestItemValue << " vs " << bestItemValue1 << std::endl;
- //result.bestItemValue = bestItemValue1;
- //}
-
- //}
-
-
- // checking code
-
- /*
- DebugOptimalBid debugMyResult(result);
- DebugOptimalBid debugNaiveResult;
- debugNaiveResult.bestItemValue = 1e20;
- debugNaiveResult.secondBestItemValue = 1e20;
- double currItemValue;
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- //if ( bidders[bidderIdx].type == DiagramPoint::NORMAL and
- //items[itemIdx].type == DiagramPoint::DIAG and
- //bidders[bidderIdx].projId != items[itemIdx].id)
- //continue;
-
- currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- if (currItemValue < debugNaiveResult.bestItemValue) {
- debugNaiveResult.bestItemValue = currItemValue;
- debugNaiveResult.bestItemIdx = itemIdx;
- }
- }
-
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- if (itemIdx == debugNaiveResult.bestItemIdx) {
- continue;
- }
- currItemValue = pow(distLp(bidders[bidderIdx], items[itemIdx]), wassersteinPower) + prices[itemIdx];
- if (currItemValue < debugNaiveResult.secondBestItemValue) {
- debugNaiveResult.secondBestItemValue = currItemValue;
- debugNaiveResult.secondBestItemIdx = itemIdx;
- }
- }
- //std::cout << "got naive result" << std::endl;
-
- if ( fabs( debugMyResult.bestItemValue - debugNaiveResult.bestItemValue ) > 1e-6 or
- fabs( debugNaiveResult.secondBestItemValue - debugMyResult.secondBestItemValue) > 1e-6 ) {
- kdtreeAll->printWeights();
- std::cerr << "bidderIdx = " << bidderIdx << "; ";
- std::cerr << bidders[bidderIdx] << std::endl;
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- std::cout << itemIdx << ": " << items[itemIdx] << "; price = " << prices[itemIdx] << std::endl;
- }
- std::cerr << "debugMyResult: " << debugMyResult << std::endl;
- std::cerr << "debugNaiveResult: " << debugNaiveResult << std::endl;
- //std::cerr << "twoBestItems: " << twoBestItems[0].d << " " << twoBestItems[1].d << std::endl;
- assert(false);
- }
- //std::cout << "returning" << std::endl;
-
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemValue = " << bestItemValue << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << secondBestItemIdx << "; secondBestValue = " << secondBestItemValue << "; secondBestPrice = " << prices[secondBestItemIdx] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- //std::cout << "getOptimalBid: bidderIdx = " << bidderIdx << "; bestItemIdx = " << bestItemIdx << "; bestItemsDist= " << (weightAdjConst - bestItemValue) << "; bestItemsPrice = " << prices[bestItemIdx] << "; secondBestItemIdx = " << secondBestItemIdx << "; secondBestDist= " << (weightAdjConst - secondBestItemValue) << "; secondBestPrice = " << prices[secondBestItemIdx] << "; bid = " << prices[bestItemIdx] + ( bestItemValue - secondBestItemValue ) + epsilon << "; epsilon = " << epsilon << std::endl;
- */
- return result;
-}
-
-IdxValPair AuctionOracleKDTreeRestricted::getOptimalBid(IdxType bidderIdx)
-{
-
-
- DiagramPoint bidder = bidders[bidderIdx];
-
- // corresponding point is always considered as a candidate
- // if bidder is a diagonal point, projItem is a normal point,
- // and vice versa.
-
- size_t bestItemIdx;
- double bestItemValue;
- double secondBestItemValue;
-
-
- size_t projItemIdx = bidderIdx;
- assert( 0 <= projItemIdx and projItemIdx < items.size() );
- DiagramPoint projItem = items[projItemIdx];
- assert(projItem.type != bidder.type);
- //assert(projItem.projId == bidder.id);
- //assert(projItem.id == bidder.projId);
- // todo: store precomputed distance?
- double projItemValue = pow(distLp(bidder, projItem, internal_p), wassersteinPower) + prices[projItemIdx];
-
- if (bidder.isDiagonal()) {
- // for diagonal bidder the only normal point has already been added
- // the other 2 candidates are diagonal items only, get from the heap
- // with prices
-
- if (not bestDiagonalItemsComputed) {
- auto topDiagIter = diagItemsHeap.ordered_begin();
- bestDiagonalItemIdx = topDiagIter->first;
- bestDiagonalItemValue = topDiagIter->second;
- if (diagItemsHeap.size() > 1) {
- topDiagIter++;
- secondBestDiagonalItemIdx = topDiagIter->first;
- secondBestDiagonalItemValue = topDiagIter->second;
- } else {
- // there is only one diagonal point at all,
- // ensure that second best diagonal value
- // will lose to projection item
- secondBestDiagonalItemValue = std::numeric_limits<double>::max();
- secondBestDiagonalItemIdx = -1;
- }
- bestDiagonalItemsComputed = true;
- }
-
- if ( projItemValue < bestDiagonalItemValue) {
- bestItemIdx = projItemIdx;
- bestItemValue = projItemValue;
- secondBestItemValue = bestDiagonalItemValue;
- } else if (projItemValue < secondBestDiagonalItemValue) {
- bestItemIdx = bestDiagonalItemIdx;
- bestItemValue = bestDiagonalItemValue;
- secondBestItemValue = projItemValue;
- } else {
- bestItemIdx = bestDiagonalItemIdx;
- bestItemValue = bestDiagonalItemValue;
- secondBestItemValue = secondBestDiagonalItemValue;
- }
- } else {
- // for normal bidder get 2 best items among non-diagonal points from
- // kdtree
- DnnPoint bidderDnn;
- bidderDnn[0] = bidder.getRealX();
- bidderDnn[1] = bidder.getRealY();
- auto twoBestItems = kdtree->findK(bidderDnn, 2);
- size_t bestNormalItemIdx { twoBestItems[0].p->id() };
- double bestNormalItemValue { twoBestItems[0].d };
- // if there is only one off-diagonal point in the second diagram,
- // kd-tree will not return the second candidate.
- // Set its value to inf, so it will always lose to the value of the projection
- double secondBestNormalItemValue { twoBestItems.size() == 1 ? std::numeric_limits<double>::max() : twoBestItems[1].d };
-
- if ( projItemValue < bestNormalItemValue) {
- bestItemIdx = projItemIdx;
- bestItemValue = projItemValue;
- secondBestItemValue = bestNormalItemValue;
- } else if (projItemValue < secondBestNormalItemValue) {
- bestItemIdx = bestNormalItemIdx;
- bestItemValue = bestNormalItemValue;
- secondBestItemValue = projItemValue;
- } else {
- bestItemIdx = bestNormalItemIdx;
- bestItemValue = bestNormalItemValue;
- secondBestItemValue = secondBestNormalItemValue;
- }
- }
-
- IdxValPair result;
-
- assert( secondBestItemValue >= bestItemValue );
-
- result.first = bestItemIdx;
- result.second = ( secondBestItemValue - bestItemValue ) + prices[bestItemIdx] + epsilon;
- return result;
-}
-/*
-a_{ij} = d_{ij}
-value_{ij} = a_{ij} + price_j
-*/
-void AuctionOracleKDTreeRestricted::setPrice(IdxType itemIdx, double newPrice)
-{
- assert(prices.size() == items.size());
- assert( 0 < diagHeapHandles.size() and diagHeapHandles.size() <= items.size());
- // adjustPrices decreases prices
- bool itemGoesDown = newPrice > prices[itemIdx];
- //assert(newPrice > prices.at(itemIdx));
- prices[itemIdx] = newPrice;
- if ( items[itemIdx].isNormal() ) {
- assert(0 <= itemIdx and itemIdx < kdtreeItems.size());
- assert(0 <= kdtreeItems[itemIdx] and kdtreeItems[itemIdx] < dnnPointHandles.size());
- kdtree->change_weight( dnnPointHandles[kdtreeItems[itemIdx]], newPrice);
- } else {
- assert(diagHeapHandles.size() > heapHandlesIndices.at(itemIdx));
- if (itemGoesDown) {
- diagItemsHeap.decrease(diagHeapHandles[heapHandlesIndices[itemIdx]], std::make_pair(itemIdx, newPrice));
- } else {
- diagItemsHeap.increase(diagHeapHandles[heapHandlesIndices[itemIdx]], std::make_pair(itemIdx, newPrice));
- }
- bestDiagonalItemsComputed = false;
- }
-}
-
-void AuctionOracleKDTreeRestricted::adjustPrices(void)
-{
- double minPrice = *(std::min_element(prices.begin(), prices.end()));
- std::transform(prices.begin(), prices.end(), prices.begin(), [minPrice](double a) { return a - minPrice; });
- for(size_t itemIdx = 0; itemIdx < items.size(); ++itemIdx) {
- setPrice(itemIdx, prices[itemIdx]);
- }
-}
-
-AuctionOracleKDTreeRestricted::~AuctionOracleKDTreeRestricted()
-{
- delete kdtree;
-}
-
-void AuctionOracleKDTreeRestricted::setEpsilon(double newVal)
-{
- assert(newVal >= 0.0);
- epsilon = newVal;
-}
-
-std::ostream& operator<< (std::ostream& output, const DebugOptimalBid& db)
-{
- output << "bestItemValue = " << db.bestItemValue;
- output << "; bestItemIdx = " << db.bestItemIdx;
- output << "; secondBestItemValue = " << db.secondBestItemValue;
- output << "; secondBestItemIdx = " << db.secondBestItemIdx;
- return output;
-}
-
-} // end of namespace geom_ws
diff --git a/geom_matching/wasserstein/src/auction_runner_gs.cpp b/geom_matching/wasserstein/src/auction_runner_gs.cpp
deleted file mode 100644
index 6489a65..0000000
--- a/geom_matching/wasserstein/src/auction_runner_gs.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
-
-Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
-All rights reserved.
-
-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.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-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 HOLDER 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.
-
-You are under no obligation whatsoever to provide any bug fixes, patches, or
-upgrades to the features, functionality or performance of the source code
-(Enhancements) to anyone; however, if you choose to make your Enhancements
-available either publicly, or directly to copyright holder,
-without imposing a separate written license agreement for such Enhancements,
-then you hereby grant the following license: a non-exclusive, royalty-free
-perpetual license to install, use, modify, prepare derivative works, incorporate
-into other computer software, distribute, and sublicense such enhancements or
-derivative works thereof, in binary and source code form.
-
- */
-
-
-#include <assert.h>
-#include <stdexcept>
-#include <algorithm>
-#include <functional>
-#include <iterator>
-#include <chrono>
-
-#include "def_debug_ws.h"
-#include "auction_runner_gs.h"
-#include "wasserstein.h"
-
-#ifdef FOR_R_TDA
-#include "Rcpp.h"
-#endif
-
-//#define PRINT_DETAILED_TIMING
-
-namespace geom_ws {
-
-// *****************************
-// AuctionRunnerGS
-// *****************************
-
-AuctionRunnerGS::AuctionRunnerGS(const std::vector<DiagramPoint>& A, const std::vector<DiagramPoint>& B, const double q, const double _delta, const double _internal_p, const double _initialEpsilon, const double _epsFactor) :
- bidders(A),
- items(B),
- numBidders(A.size()),
- numItems(A.size()),
- itemsToBidders(A.size(), -1),
- biddersToItems(A.size(), -1),
- wassersteinPower(q),
- delta(_delta),
- internal_p(_internal_p),
- initialEpsilon(_initialEpsilon),
- epsilonCommonRatio(_epsFactor == 0.0 ? 5.0 : _epsFactor)
-{
- assert(initialEpsilon >= 0.0 );
- assert(epsilonCommonRatio >= 0.0 );
- assert(A.size() == B.size());
- oracle = std::unique_ptr<AuctionOracle>(new AuctionOracle(bidders, items, wassersteinPower, internal_p));
-}
-
-void AuctionRunnerGS::assignItemToBidder(IdxType itemIdx, IdxType bidderIdx)
-{
- numRounds++;
- //sanityCheck();
- // only unassigned bidders should submit bids and get items
- assert(biddersToItems[bidderIdx] == -1);
- IdxType oldItemOwner = itemsToBidders[itemIdx];
-
- // set new owner
- biddersToItems[bidderIdx] = itemIdx;
- itemsToBidders[itemIdx] = bidderIdx;
- // remove bidder from the list of unassigned bidders
-#ifdef KEEP_UNASSIGNED_ORDERED
- unassignedBidders.erase(std::make_pair(bidderIdx, bidders[bidderIdx]));
-#else
- unassignedBidders.erase(bidderIdx);
-#endif
-
- // old owner becomes unassigned
- if (oldItemOwner != -1) {
- biddersToItems[oldItemOwner] = -1;
-#ifdef KEEP_UNASSIGNED_ORDERED
- unassignedBidders.insert(std::make_pair(oldItemOwner, bidders[oldItemOwner]));
-#else
- unassignedBidders.insert(oldItemOwner);
-#endif
- }
-}
-
-
-void AuctionRunnerGS::flushAssignment(void)
-{
- for(auto& b2i : biddersToItems) {
- b2i = -1;
- }
- for(auto& i2b : itemsToBidders) {
- i2b = -1;
- }
- // we must flush assignment only after we got perfect matching
- assert(unassignedBidders.empty());
- // all bidders become unassigned
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
-#ifdef KEEP_UNASSIGNED_ORDERED
- unassignedBidders.insert(std::make_pair(bidderIdx, bidders[bidderIdx]));
-#else
- unassignedBidders.insert(bidderIdx);
-#endif
- }
- assert(unassignedBidders.size() == bidders.size());
- oracle->adjustPrices();
-}
-
-void AuctionRunnerGS::runAuction(void)
-{
- relativeError = std::numeric_limits<double>::max();
-#ifdef PRINT_DETAILED_TIMING
- std::chrono::high_resolution_clock hrClock;
- std::chrono::time_point<std::chrono::high_resolution_clock> startMoment;
- startMoment = hrClock.now();
- std::vector<double> iterResults;
- std::vector<double> iterEstRelErrors;
- std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>> iterTimes;
-#endif
- // choose some initial epsilon
- if (initialEpsilon == 0.0)
- oracle->setEpsilon(oracle->maxVal / 4.0);
- else
- oracle->setEpsilon(initialEpsilon);
- assert( oracle->getEpsilon() > 0 );
- int iterNum { 0 };
- bool notDone { false };
- double currentResult;
- do {
- flushAssignment();
- runAuctionPhase();
- iterNum++;
- //std::cout << "Iteration " << iterNum << " completed. " << std::endl;
- // result is d^q
- currentResult = getDistanceToQthPowerInternal();
- double denominator = currentResult - numBidders * oracle->getEpsilon();
- currentResult = pow(currentResult, 1.0 / wassersteinPower);
-#ifdef PRINT_DETAILED_TIMING
-#ifndef FOR_R_TDA
- iterResults.push_back(currentResult);
- iterTimes.push_back(hrClock.now());
- std::cout << "Iteration " << iterNum << " finished. ";
- std::cout << "Current result is " << currentResult << ", epsilon = " << oracle->getEpsilon() << std::endl;
- std::cout << "Number of rounds (cumulative): " << numRounds << std::endl;
-#endif
-#endif
- if ( denominator <= 0 ) {
- //std::cout << "Epsilon is too big." << std::endl;
- notDone = true;
- } else {
- denominator = pow(denominator, 1.0 / wassersteinPower);
- double numerator = currentResult - denominator;
-#ifdef PRINT_DETAILED_TIMING
-#ifndef FOR_R_TDA
- std::cout << " numerator: " << numerator << " denominator: " << denominator;
- std::cout << "; error bound: " << numerator / denominator << std::endl;
-#endif
-#endif
- relativeError = numerator / denominator;
- // if relative error is greater than delta, continue
- notDone = ( numerator / denominator > delta );
- }
- // decrease epsilon for the next iteration
- oracle->setEpsilon( oracle->getEpsilon() / epsilonCommonRatio );
- if (iterNum > maxIterNum) {
-#ifndef FOR_R_TDA
- std::cerr << "Maximum iteration number exceeded, exiting. Current result is:";
- std::cerr << wassersteinDistance << std::endl;
-#endif
- throw std::runtime_error("Maximum iteration number exceeded");
- }
- } while ( notDone );
- //printMatching();
-#ifdef PRINT_DETAILED_TIMING
-#ifndef FOR_R_TDA
- for(size_t iterIdx = 0; iterIdx < iterResults.size(); ++iterIdx) {
- double trueRelError = ( iterResults.at(iterIdx) - currentResult ) / currentResult;
- auto iterCumulativeTime = iterTimes.at(iterIdx) - startMoment;
- std::chrono::duration<double, std::milli> iterTime = ( iterIdx > 0) ? iterTimes[iterIdx] - iterTimes[iterIdx - 1] : iterTimes[iterIdx] - startMoment;
- std::cout << "iteration " << iterIdx << ", true rel. error " <<
- trueRelError << ", elapsed time " <<
- std::chrono::duration<double, std::milli>(iterCumulativeTime).count() <<
- ", iteration time " << iterTime.count() << std::endl;
- }
-#endif
-#endif
-}
-
-void AuctionRunnerGS::runAuctionPhase(void)
-{
- //std::cout << "Entered runAuctionPhase" << std::endl;
- do {
-#ifdef KEEP_UNASSIGNED_ORDERED
- size_t bidderIdx = unassignedBidders.begin()->first;
-#else
- size_t bidderIdx = *unassignedBidders.begin();
-#endif
- auto optimalBid = oracle->getOptimalBid(bidderIdx);
- auto optimalItemIdx = optimalBid.first;
- auto bidValue = optimalBid.second;
- assignItemToBidder(optimalBid.first, bidderIdx);
- oracle->setPrice(optimalItemIdx, bidValue);
- //printDebug();
-#ifdef FOR_R_TDA
- if ( numRounds % 10000 == 0 ) {
- Rcpp::checkUserInterrupt();
- }
-#endif
- } while (not unassignedBidders.empty());
- //std::cout << "runAuctionPhase finished" << std::endl;
-
-#ifdef DEBUG_AUCTION
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
- if ( biddersToItems[bidderIdx] < 0) {
-#ifndef FOR_R_TDA
- std::cerr << "After auction terminated bidder " << bidderIdx;
- std::cerr << " has no items assigned" << std::endl;
-#endif
- throw std::runtime_error("Auction did not give a perfect matching");
- }
- }
-#endif
-
-}
-
-double AuctionRunnerGS::getDistanceToQthPowerInternal(void)
-{
- sanityCheck();
- double result = 0.0;
- //std::cout << "-------------------------------------------------------------------------\n";
- for(size_t bIdx = 0; bIdx < numBidders; ++bIdx) {
- auto pA = bidders[bIdx];
- assert( 0 <= biddersToItems[bIdx] and biddersToItems[bIdx] < static_cast<int>(items.size()) );
- auto pB = items[biddersToItems[bIdx]];
- //std::cout << "pA = " << pA << ", pB = " << pB << ", pow(distLp(pA, pB, internal_p), wassersteinPower) = " << pow(distLp(pA, pB, internal_p), wassersteinPower) << ", dist = " << distLp(pA, pB, internal_p) << std::endl;
- result += pow(distLp(pA, pB, internal_p), wassersteinPower);
- }
- //std::cout << "-------------------------------------------------------------------------\n";
- wassersteinCost = result;
- wassersteinDistance = pow(result, 1.0 / wassersteinPower);
- return result;
-}
-
-double AuctionRunnerGS::getWassersteinDistance(void)
-{
- runAuction();
- return wassersteinDistance;
-}
-
-double AuctionRunnerGS::getWassersteinCost(void)
-{
- runAuction();
- return wassersteinCost;
-}
-
-
-
-// Debug routines
-
-void AuctionRunnerGS::printDebug(void)
-{
-#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- sanityCheck();
- std::cout << "**********************" << std::endl;
- std::cout << "Current assignment:" << std::endl;
- for(size_t idx = 0; idx < biddersToItems.size(); ++idx) {
- std::cout << idx << " <--> " << biddersToItems[idx] << std::endl;
- }
- std::cout << "Weights: " << std::endl;
- //for(size_t i = 0; i < numBidders; ++i) {
- //for(size_t j = 0; j < numItems; ++j) {
- //std::cout << oracle->weightMatrix[i][j] << " ";
- //}
- //std::cout << std::endl;
- //}
- std::cout << "Prices: " << std::endl;
- for(const auto price : oracle->getPrices()) {
- std::cout << price << std::endl;
- }
- std::cout << "**********************" << std::endl;
-#endif
-#endif
-}
-
-
-void AuctionRunnerGS::sanityCheck(void)
-{
-#ifdef DEBUG_AUCTION
- if (biddersToItems.size() != numBidders) {
-#ifndef FOR_R_TDA
- std::cerr << "Wrong size of biddersToItems, must be " << numBidders << ", is " << biddersToItems.size() << std::endl;
-#endif
- throw std::runtime_error("Wrong size of biddersToItems");
- }
-
- if (itemsToBidders.size() != numBidders) {
-#ifndef FOR_R_TDA
- std::cerr << "Wrong size of itemsToBidders, must be " << numBidders << ", is " << itemsToBidders.size() << std::endl;
-#endif
- throw std::runtime_error("Wrong size of itemsToBidders");
- }
-
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
- if ( biddersToItems[bidderIdx] >= 0) {
-
- if ( std::count(biddersToItems.begin(),
- biddersToItems.end(),
- biddersToItems[bidderIdx]) > 1 ) {
-#ifndef FOR_R_TDA
- std::cerr << "Item " << biddersToItems[bidderIdx];
- std::cerr << " appears in biddersToItems more than once" << std::endl;
-#endif
- throw std::runtime_error("Duplicate in biddersToItems");
- }
-
- if (itemsToBidders.at(biddersToItems[bidderIdx]) != static_cast<int>(bidderIdx)) {
-#ifndef FOR_R_TDA
- std::cerr << "Inconsitency: bidderIdx = " << bidderIdx;
- std::cerr << ", itemIdx in biddersToItems = ";
- std::cerr << biddersToItems[bidderIdx];
- std::cerr << ", bidderIdx in itemsToBidders = ";
- std::cerr << itemsToBidders[biddersToItems[bidderIdx]] << std::endl;
-#endif
- throw std::runtime_error("inconsistent mapping");
- }
- }
- }
-
- for(IdxType itemIdx = 0; itemIdx < static_cast<IdxType>(numBidders); ++itemIdx) {
- if ( itemsToBidders[itemIdx] >= 0) {
-
- // check for uniqueness
- if ( std::count(itemsToBidders.begin(),
- itemsToBidders.end(),
- itemsToBidders[itemIdx]) > 1 ) {
-#ifndef FOR_R_TDA
- std::cerr << "Bidder " << itemsToBidders[itemIdx];
- std::cerr << " appears in itemsToBidders more than once" << std::endl;
-#endif
- throw std::runtime_error("Duplicate in itemsToBidders");
- }
- // check for consistency
- if (biddersToItems.at(itemsToBidders[itemIdx]) != static_cast<int>(itemIdx)) {
-#ifndef FOR_R_TDA
- std::cerr << "Inconsitency: itemIdx = " << itemIdx;
- std::cerr << ", bidderIdx in itemsToBidders = ";
- std::cerr << itemsToBidders[itemIdx];
- std::cerr << ", itemIdx in biddersToItems= ";
- std::cerr << biddersToItems[itemsToBidders[itemIdx]] << std::endl;
-#endif
- throw std::runtime_error("inconsistent mapping");
- }
- }
- }
-#endif
-}
-
-void AuctionRunnerGS::printMatching(void)
-{
-//#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- sanityCheck();
- for(size_t bIdx = 0; bIdx < biddersToItems.size(); ++bIdx) {
- if (biddersToItems[bIdx] >= 0) {
- auto pA = bidders[bIdx];
- auto pB = items[biddersToItems[bIdx]];
- std::cout << pA << " <-> " << pB << "+" << pow(distLp(pA, pB, internal_p), wassersteinPower) << std::endl;
- } else {
- assert(false);
- }
- }
-#endif
-//#endif
-}
-
-} // end of namespace geom_ws
diff --git a/geom_matching/wasserstein/src/auction_runner_jac.cpp b/geom_matching/wasserstein/src/auction_runner_jac.cpp
deleted file mode 100644
index 0b26a9f..0000000
--- a/geom_matching/wasserstein/src/auction_runner_jac.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
-
-Copyright (c) 2016, M. Kerber, D. Morozov, A. Nigmetov
-All rights reserved.
-
-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.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-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 HOLDER 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.
-
-You are under no obligation whatsoever to provide any bug fixes, patches, or
-upgrades to the features, functionality or performance of the source code
-(Enhancements) to anyone; however, if you choose to make your Enhancements
-available either publicly, or directly to copyright holder,
-without imposing a separate written license agreement for such Enhancements,
-then you hereby grant the following license: a non-exclusive, royalty-free
-perpetual license to install, use, modify, prepare derivative works, incorporate
-into other computer software, distribute, and sublicense such enhancements or
-derivative works thereof, in binary and source code form.
-
- */
-
-#include <assert.h>
-#include <algorithm>
-#include <functional>
-#include <iterator>
-
-#include "def_debug_ws.h"
-#include "auction_runner_jac.h"
-#include "wasserstein.h"
-
-namespace geom_ws {
-
-// *****************************
-// AuctionRunnerJac
-// *****************************
-
-AuctionRunnerJac::AuctionRunnerJac(const std::vector<DiagramPoint>& A, const std::vector<DiagramPoint>& B, const double q, const double _delta, const double _internal_p) :
- bidders(A),
- items(B),
- numBidders(A.size()),
- numItems(A.size()),
- itemsToBidders(A.size(), -1),
- biddersToItems(A.size(), -1),
- wassersteinPower(q),
- delta(_delta),
- internal_p(_internal_p),
- bidTable(A.size(), std::make_pair(-1, std::numeric_limits<double>::lowest()) ),
- itemReceivedBidVec(B.size(), 0 )
-{
- assert(A.size() == B.size());
- oracle = std::unique_ptr<AuctionOracle>(new AuctionOracle(bidders, items, wassersteinPower, internal_p));
-}
-
-void AuctionRunnerJac::assignGoodToBidder(IdxType itemIdx, IdxType bidderIdx)
-{
- //sanityCheck();
- IdxType myOldItem = biddersToItems[bidderIdx];
- IdxType currItemOwner = itemsToBidders[itemIdx];
-
- // set new owner
- biddersToItems[bidderIdx] = itemIdx;
- itemsToBidders[itemIdx] = bidderIdx;
-
-
- // remove bidder from the list of unassigned bidders
- unassignedBidders.erase( unassignedBiddersIterators[bidderIdx] );
- assert( 0 <= bidderIdx and bidderIdx < unassignedBiddersIterators.size() );
- unassignedBiddersIterators[bidderIdx] = unassignedBidders.end();
-
- if (-1 == currItemOwner) {
- // the item we want to assign does not belong to anybody,
- // just free myOldItem, if necessary
- // RE: this cannot be necessary. I submitted the best bid, hence I was
- // an unassigned bidder.
- if (myOldItem != -1) {
-#ifndef FOR_R_TDA
- std::cout << "This is not happening" << std::endl;
-#endif
- assert(false);
- itemsToBidders[myOldItem] = -1;
- }
- } else {
- // the current owner of itemIdx gets my old item (OK if it's -1)
- biddersToItems[currItemOwner] = myOldItem;
- // add the old owner of bids to the list of
- if ( -1 != myOldItem ) {
-#ifndef FOR_R_TDA
- std::cout << "This is not happening" << std::endl;
-#endif
- assert(false);
- // if I had something, update itemsToBidders, too
- // RE: nonsense: if I had something, I am not unassigned and did not
- // sumbit any bid
- itemsToBidders[myOldItem] = currItemOwner;
- }
- unassignedBidders.push_back(currItemOwner);
- assert( unassignedBiddersIterators[currItemOwner] == unassignedBidders.end() );
- unassignedBiddersIterators[currItemOwner] = std::prev( unassignedBidders.end() );
- }
- //sanityCheck();
-}
-
-
-void AuctionRunnerJac::assignToBestBidder(IdxType itemIdx)
-{
- assert( itemIdx >= 0 and itemIdx < static_cast<IdxType>(numItems) );
- assert( bidTable[itemIdx].first != -1);
- IdxValPair bestBid { bidTable[itemIdx] };
- assignGoodToBidder(itemIdx, bestBid.first);
- oracle->setPrice(itemIdx, bestBid.second);
- //dynamic_cast<AuctionOracleKDTree*>(oracle)->setNai
-}
-
-void AuctionRunnerJac::clearBidTable(void)
-{
- for(auto& itemWithBidIdx : itemsWithBids) {
- itemReceivedBidVec[itemWithBidIdx] = 0;
- bidTable[itemWithBidIdx].first = -1;
- bidTable[itemWithBidIdx].second = std::numeric_limits<double>::lowest();
- }
- itemsWithBids.clear();
-}
-
-void AuctionRunnerJac::submitBid(IdxType bidderIdx, const IdxValPair& itemsBidValuePair)
-{
- IdxType itemIdx = itemsBidValuePair.first;
- double bidValue = itemsBidValuePair.second;
- assert( itemIdx >= 0 );
- if ( bidTable[itemIdx].second < itemsBidValuePair.second ) {
- bidTable[itemIdx].first = bidderIdx;
- bidTable[itemIdx].second = bidValue;
- }
- if (0 == itemReceivedBidVec[itemIdx]) {
- itemReceivedBidVec[itemIdx] = 1;
- itemsWithBids.push_back(itemIdx);
- }
-}
-
-void AuctionRunnerJac::printDebug(void)
-{
-#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- sanityCheck();
- std::cout << "**********************" << std::endl;
- std::cout << "Current assignment:" << std::endl;
- for(size_t idx = 0; idx < biddersToItems.size(); ++idx) {
- std::cout << idx << " <--> " << biddersToItems[idx] << std::endl;
- }
- std::cout << "Weights: " << std::endl;
- //for(size_t i = 0; i < numBidders; ++i) {
- //for(size_t j = 0; j < numItems; ++j) {
- //std::cout << oracle->weightMatrix[i][j] << " ";
- //}
- //std::cout << std::endl;
- //}
- std::cout << "Prices: " << std::endl;
- for(const auto price : oracle->getPrices()) {
- std::cout << price << std::endl;
- }
- //std::cout << "Value matrix: " << std::endl;
- //for(size_t i = 0; i < numBidders; ++i) {
- //for(size_t j = 0; j < numItems; ++j) {
- //std::cout << oracle->weightMatrix[i][j] - oracle->prices[j] << " ";
- //}
- //std::cout << std::endl;
- //}
- std::cout << "**********************" << std::endl;
-#endif
-#endif
-}
-
-void AuctionRunnerJac::flushAssignment(void)
-{
- for(auto& b2g : biddersToItems) {
- b2g = -1;
- }
- for(auto& g2b : itemsToBidders) {
- g2b = -1;
- }
- oracle->adjustPrices();
-}
-
-void AuctionRunnerJac::runAuction(void)
-{
- relativeError = std::numeric_limits<double>::max();
- // choose some initial epsilon
- oracle->setEpsilon(oracle->maxVal / 4.0);
- assert( oracle->getEpsilon() > 0 );
- int iterNum { 0 };
- bool notDone { false };
- do {
- flushAssignment();
- runAuctionPhase();
- iterNum++;
- //std::cout << "Iteration " << iterNum << " completed. " << std::endl;
- // result is d^q
- double currentResult = getDistanceToQthPowerInternal();
- double denominator = currentResult - numBidders * oracle->getEpsilon();
- currentResult = pow(currentResult, 1.0 / wassersteinPower);
- //std::cout << "Current result is " << currentResult << std::endl;
- if ( denominator <= 0 ) {
- //std::cout << "Epsilon is too big." << std::endl;
- notDone = true;
- } else {
- denominator = pow(denominator, 1.0 / wassersteinPower);
- double numerator = currentResult - denominator;
- relativeError = numerator / denominator;
- //std::cout << " numerator: " << numerator << " denominator: " << denominator << std::endl;
- //std::cout << " error bound: " << numerator / denominator << std::endl;
- // if relative error is greater than delta, continue
- notDone = ( numerator / denominator > delta );
- }
- // decrease epsilon for the next iteration
- oracle->setEpsilon( oracle->getEpsilon() / epsilonCommonRatio );
- if (iterNum > maxIterNum) {
-#ifndef FOR_R_TDA
- std::cerr << "Maximum iteration number exceeded, exiting. Current result is:";
- std::cerr << wassersteinDistance << std::endl;
- std::exit(1);
-#else
- throw std::runtime_error("Max. iter. exceeded");
-#endif
- }
- } while ( notDone );
- //printMatching();
-}
-
-void AuctionRunnerJac::runAuctionPhase(void)
-{
- //std::cout << "Entered runAuctionPhase" << std::endl;
- //int numUnassignedBidders { 0 };
-
- // at the beginning of a phase all bidders are unassigned
- unassignedBidders.clear();
- unassignedBiddersIterators.clear();
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
- unassignedBidders.push_back(bidderIdx);
- unassignedBiddersIterators.push_back( std::prev( unassignedBidders.end() ));
- }
- do {
- // bidding phase
- clearBidTable();
- for(const auto bidderIdx : unassignedBidders) {
- submitBid(bidderIdx, oracle->getOptimalBid(bidderIdx));
- }
- //std::cout << "Number of unassignedBidders: " << unassignedBidders.size() << std::endl;
-
- // assignment phase
- // todo: maintain list of items that received a bid
- for(auto itemIdx : itemsWithBids ) {
- assignToBestBidder(itemIdx);
- }
- //std::cout << "Assignment phase done" << std::endl;
- //sanityCheck();
- //printDebug();
- } while (unassignedBidders.size() > 0);
- //std::cout << "runAuctionPhase finished" << std::endl;
-
-
-#ifdef DEBUG_AUCTION
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
- if ( biddersToItems[bidderIdx] < 0) {
- std::cerr << "After auction terminated bidder " << bidderIdx;
- std::cerr << " has no items assigned" << std::endl;
- throw "Auction did not give a perfect matching";
- }
- }
-#endif
-
-}
-
-// assertion: the matching must be perfect
-double AuctionRunnerJac::getDistanceToQthPowerInternal(void)
-{
- sanityCheck();
- double result = 0.0;
- for(size_t bIdx = 0; bIdx < numBidders; ++bIdx) {
- auto pA = bidders[bIdx];
- assert( 0 <= biddersToItems[bIdx] and biddersToItems[bIdx] < items.size() );
- auto pB = items[biddersToItems[bIdx]];
- result += pow(distLp(pA, pB, internal_p), wassersteinPower);
- }
- wassersteinCost = result;
- wassersteinDistance = pow(result, 1.0 / wassersteinPower);
- return result;
-}
-
-double AuctionRunnerJac::getWassersteinDistance(void)
-{
- runAuction();
- return wassersteinDistance;
-}
-
-double AuctionRunnerJac::getWassersteinCost(void)
-{
- runAuction();
- return wassersteinCost;
-}
-
-
-
-void AuctionRunnerJac::sanityCheck(void)
-{
-#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- if (biddersToItems.size() != numBidders) {
- std::cerr << "Wrong size of biddersToItems, must be " << numBidders << ", is " << biddersToItems.size() << std::endl;
- throw "Wrong size of biddersToItems";
- }
-
- if (itemsToBidders.size() != numBidders) {
- std::cerr << "Wrong size of itemsToBidders, must be " << numBidders << ", is " << itemsToBidders.size() << std::endl;
- throw "Wrong size of itemsToBidders";
- }
-
- for(size_t bidderIdx = 0; bidderIdx < numBidders; ++bidderIdx) {
- if ( biddersToItems[bidderIdx] >= 0) {
-
- if ( std::count(biddersToItems.begin(),
- biddersToItems.end(),
- biddersToItems[bidderIdx]) > 1 ) {
- std::cerr << "Good " << biddersToItems[bidderIdx];
- std::cerr << " appears in biddersToItems more than once" << std::endl;
- throw "Duplicate in biddersToItems";
- }
-
- if (itemsToBidders.at(biddersToItems[bidderIdx]) != static_cast<int>(bidderIdx)) {
- std::cerr << "Inconsitency: bidderIdx = " << bidderIdx;
- std::cerr << ", itemIdx in biddersToItems = ";
- std::cerr << biddersToItems[bidderIdx];
- std::cerr << ", bidderIdx in itemsToBidders = ";
- std::cerr << itemsToBidders[biddersToItems[bidderIdx]] << std::endl;
- throw "inconsistent mapping";
- }
- }
- }
-
- for(IdxType itemIdx = 0; itemIdx < static_cast<IdxType>(numBidders); ++itemIdx) {
- if ( itemsToBidders[itemIdx] >= 0) {
-
- // check for uniqueness
- if ( std::count(itemsToBidders.begin(),
- itemsToBidders.end(),
- itemsToBidders[itemIdx]) > 1 ) {
- std::cerr << "Bidder " << itemsToBidders[itemIdx];
- std::cerr << " appears in itemsToBidders more than once" << std::endl;
- throw "Duplicate in itemsToBidders";
- }
- // check for consistency
- if (biddersToItems.at(itemsToBidders[itemIdx]) != static_cast<int>(itemIdx)) {
- std::cerr << "Inconsitency: itemIdx = " << itemIdx;
- std::cerr << ", bidderIdx in itemsToBidders = ";
- std::cerr << itemsToBidders[itemIdx];
- std::cerr << ", itemIdx in biddersToItems= ";
- std::cerr << biddersToItems[itemsToBidders[itemIdx]] << std::endl;
- throw "inconsistent mapping";
- }
- }
- }
-#endif
-#endif
-}
-
-void AuctionRunnerJac::printMatching(void)
-{
-//#ifdef DEBUG_AUCTION
-#ifndef FOR_R_TDA
- sanityCheck();
- for(size_t bIdx = 0; bIdx < biddersToItems.size(); ++bIdx) {
- if (biddersToItems[bIdx] >= 0) {
- auto pA = bidders[bIdx];
- auto pB = items[biddersToItems[bIdx]];
- std::cout << pA << " <-> " << pB << "+" << pow(distLp(pA, pB, internal_p), wassersteinPower) << std::endl;
- } else {
- assert(false);
- }
- }
-#endif
-//#endif
-}
-
-} // end of namespace geom_ws
diff --git a/geom_matching/wasserstein/src/wasserstein.cpp b/geom_matching/wasserstein/src/wasserstein.cpp
deleted file mode 100644
index 7cca705..0000000
--- a/geom_matching/wasserstein/src/wasserstein.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
-
-Copyright (c) 2015, M. Kerber, D. Morozov, A. Nigmetov
-All rights reserved.
-
-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.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-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 HOLDER 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.
-
-You are under no obligation whatsoever to provide any bug fixes, patches, or
-upgrades to the features, functionality or performance of the source code
-(Enhancements) to anyone; however, if you choose to make your Enhancements
-available either publicly, or directly to copyright holder,
-without imposing a separate written license agreement for such Enhancements,
-then you hereby grant the following license: a non-exclusive, royalty-free
-perpetual license to install, use, modify, prepare derivative works, incorporate
-into other computer software, distribute, and sublicense such enhancements or
-derivative works thereof, in binary and source code form.
-
- */
-
-#include <assert.h>
-#include <algorithm>
-#include <functional>
-#include <iterator>
-
-#include "def_debug_ws.h"
-#include "wasserstein.h"
-
-#ifdef GAUSS_SEIDEL_AUCTION
-#include "auction_runner_gs.h"
-#else
-#include "auction_runner_jac.h"
-#endif
-
-namespace geom_ws {
-
-double wassersteinDistVec(const std::vector<DiagramPoint>& A,
- const std::vector<DiagramPoint>& B,
- const double q,
- const double delta,
- const double _internal_p,
- const double _initialEpsilon,
- const double _epsFactor)
-{
- if (q < 1) {
-#ifndef FOR_R_TDA
- std::cerr << "Wasserstein distance not defined for q = " << q << ", must be >= 1" << std::endl;
-#endif
- throw std::runtime_error("Bad q in Wasserstein " + std::to_string(q));
- }
- if (delta < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Relative error " << delta << ", must be > 0" << std::endl;
-#endif
- throw std::runtime_error("Bad delta in Wasserstein " + std::to_string(delta));
- }
- if (_initialEpsilon < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Initial epsilon = " << _initialEpsilon << ", must be non-negative" << std::endl;
-#endif
- throw std::runtime_error("Bad initial epsilon in Wasserstein" + std::to_string(_initialEpsilon));
- }
- if (_epsFactor < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Epsilon factor = " << _epsFactor << ", must be non-negative" << std::endl;
-#endif
- throw std::runtime_error("Bad epsilon factor in Wasserstein " + std::to_string(_epsFactor));
- }
-
- if (A.empty() && B.empty())
- return 0.0;
-
- if (A.empty()) {
- double result { 0.0 } ;
- for(const auto& pt : B) {
- result += pt.persistenceLp(_internal_p);
- }
- return result;
- }
-
- if (B.empty()) {
- double result { 0.0 } ;
- for(const auto& pt : A) {
- result += pt.persistenceLp(_internal_p);
- }
- return result;
- }
-
-
-#ifdef GAUSS_SEIDEL_AUCTION
- AuctionRunnerGS auction(A, B, q, delta, _internal_p, _initialEpsilon, _epsFactor);
-#else
- AuctionRunnerJac auction(A, B, q, delta, _internal_p);
-#endif
- double result = auction.getWassersteinDistance();
- return result;
-}
-
-double wassersteinCostVec(const std::vector<DiagramPoint>& A,
- const std::vector<DiagramPoint>& B,
- const double q,
- const double delta,
- const double _internal_p,
- const double _initialEpsilon,
- const double _epsFactor)
-{
- if (q < 1) {
-#ifndef FOR_R_TDA
- std::cerr << "Wasserstein distance not defined for q = " << q << ", must be >= 1" << std::endl;
-#endif
- throw std::runtime_error("Bad q in Wasserstein " + std::to_string(q));
- }
- if (delta < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Relative error " << delta << ", must be > 0" << std::endl;
-#endif
- throw std::runtime_error("Bad delta in Wasserstein " + std::to_string(delta));
- }
- if (_initialEpsilon < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Initial epsilon = " << _initialEpsilon << ", must be non-negative" << std::endl;
-#endif
- throw std::runtime_error("Bad initial epsilon in Wasserstein" + std::to_string(_initialEpsilon));
- }
- if (_epsFactor < 0.0) {
-#ifndef FOR_R_TDA
- std::cerr << "Epsilon factor = " << _epsFactor << ", must be non-negative" << std::endl;
-#endif
- throw std::runtime_error("Bad epsilon factor in Wasserstein " + std::to_string(_epsFactor));
- }
-#ifdef GAUSS_SEIDEL_AUCTION
- AuctionRunnerGS auction(A, B, q, delta, _internal_p, _initialEpsilon, _epsFactor);
-#else
- AuctionRunnerJac auction(A, B, q, delta, _internal_p);
-#endif
- double result = auction.getWassersteinCost();
- return result;
-}
-
-bool readDiagramPointSet(const std::string& fname, PairVector& result)
-{
- return readDiagramPointSet(fname.c_str(), result);
-}
-
-bool readDiagramPointSet(const char* fname, PairVector& result)
-{
- size_t lineNumber { 0 };
- result.clear();
- std::ifstream f(fname);
- if (!f.good()) {
-#ifndef FOR_R_TDA
- std::cerr << "Cannot open file " << fname << std::endl;
-#endif
- return false;
- }
- std::string line;
- while(std::getline(f, line)) {
- lineNumber++;
- // process comments: remove everything after hash
- auto hashPos = line.find_first_of("#", 0);
- if( std::string::npos != hashPos) {
- line = std::string(line.begin(), line.begin() + hashPos);
- }
- if (line.empty()) {
- continue;
- }
- // trim whitespaces
- auto whiteSpaceFront = std::find_if_not(line.begin(),line.end(),isspace);
- auto whiteSpaceBack = std::find_if_not(line.rbegin(),line.rend(),isspace).base();
- if (whiteSpaceBack <= whiteSpaceFront) {
- // line consists of spaces only - move to the next line
- continue;
- }
- line = std::string(whiteSpaceFront,whiteSpaceBack);
- double x, y;
- std::istringstream iss(line);
- if (not(iss >> x >> y)) {
-#ifndef FOR_R_TDA
- std::cerr << "Error in file " << fname << ", line number " << lineNumber << ": cannot parse \"" << line << "\"" << std::endl;
-#endif
- return false;
- }
- result.push_back(std::make_pair(x,y));
- }
- f.close();
- return true;
-}
-
-
-void removeDuplicates(PairVector& dgmA, PairVector& dgmB)
-{
- std::map<std::pair<double, double>, int> mapA, mapB;
- // copy points to maps
- for(const auto& ptA : dgmA) {
- mapA[ptA]++;
- }
- for(const auto& ptB : dgmB) {
- mapB[ptB]++;
- }
- // clear vectors
- dgmA.clear();
- dgmB.clear();
- // remove duplicates from maps
- for(auto& pointMultiplicityPair : mapA) {
- auto iterB = mapB.find(pointMultiplicityPair.first);
- if (iterB != mapB.end()) {
- int duplicateMultiplicity = std::min(pointMultiplicityPair.second, iterB->second);
- pointMultiplicityPair.second -= duplicateMultiplicity;
- iterB->second -= duplicateMultiplicity;
- }
- }
- // copy points back to vectors
- for(const auto& pointMultiplicityPairA : mapA) {
- assert( pointMultiplicityPairA.second >= 0);
- for(int i = 0; i < pointMultiplicityPairA.second; ++i) {
- dgmA.push_back(pointMultiplicityPairA.first);
- }
- }
-
- for(const auto& pointMultiplicityPairB : mapB) {
- assert( pointMultiplicityPairB.second >= 0);
- for(int i = 0; i < pointMultiplicityPairB.second; ++i) {
- dgmB.push_back(pointMultiplicityPairB.first);
- }
- }
-}
-
-} // end of namespace geom_ws
diff --git a/geom_matching/wasserstein/tests/data/test_100_A b/geom_matching/wasserstein/tests/data/test_100_A
new file mode 100644
index 0000000..8d126f0
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_100_A
@@ -0,0 +1,100 @@
+7.50638 7.78005
+0.991758 2.12178
+5.18481 6.61702
+6.14703 7.08581
+4.09936 4.83024
+3.79915 4.51283
+3.17645 3.75321
+0.61305 0.998622
+0.445643 1.13781
+6.38205 6.53669
+5.96392 6.44093
+7.21047 7.26005
+0.6703 1.26593
+0.529933 1.7027
+7.92495 8.83023
+2.1382 2.71695
+3.79209 4.5197
+5.23354 5.82214
+-0.395097 2.18831
+3.22028 3.88648
+5.56262 5.79949
+8.39623 9.37185
+2.7452 3.84539
+9.5022 10.2414
+1.01374 1.40504
+3.2029 3.89559
+7.61236 8.28485
+6.4371 6.909
+4.45616 5.35067
+1.57802 1.77895
+6.5991 7.76339
+6.66729 6.71714
+6.11898 6.57121
+2.60663 4.36396
+-0.259613 1.17683
+7.72857 9.48862
+4.68398 5.51521
+4.87447 5.4233
+6.86301 6.88244
+4.17814 4.25886
+8.70558 9.72902
+4.40873 4.57389
+6.1824 7.05049
+7.97557 8.79739
+8.52591 8.6985
+5.15336 5.27796
+9.70144 9.77031
+0.561778 1.39045
+9.32553 10.2456
+7.01495 7.74521
+6.83355 7.28255
+1.96721 3.01504
+5.78411 7.59464
+5.64012 6.10721
+3.7249 5.17086
+4.33297 5.91657
+7.11793 7.2545
+5.109 6.76878
+3.02787 3.04077
+0.999365 2.05566
+8.81392 8.9086
+6.20106 6.78943
+6.7987 7.05794
+0.438805 0.449602
+8.71793 9.79853
+-0.150282 0.51997
+5.72257 5.93156
+5.71098 6.09535
+9.0378 9.45942
+0.534987 0.872885
+7.72276 8.57754
+9.26069 9.40289
+4.148 4.80519
+1.04579 3.18109
+0.259767 0.93215
+0.250608 0.511569
+4.88108 5.62239
+4.6731 5.3348
+7.84979 7.93545
+0.912521 1.48142
+4.82553 5.38108
+6.02179 7.61665
+3.85848 6.39418
+5.10754 6.02118
+1.71956 1.86238
+6.47336 6.74034
+-0.0371018 0.212738
+3.97259 4.15465
+2.17413 3.20188
+4.49098 6.09812
+6.62445 7.84196
+6.57541 7.432
+6.81052 9.6653
+3.67502 4.69734
+3.92378 4.14743
+5.93127 6.46154
+0.63424 0.705454
+7.60129 9.23263
+4.23064 4.74575
+0.397705 1.24458
diff --git a/geom_matching/wasserstein/tests/data/test_100_B b/geom_matching/wasserstein/tests/data/test_100_B
new file mode 100644
index 0000000..852799d
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_100_B
@@ -0,0 +1,100 @@
+5.8232 6.36308
+2.16066 2.48668
+2.38754 4.91418
+4.77403 5.43982
+0.291412 1.11147
+3.77337 5.2686
+8.31344 9.05384
+0.734064 1.14844
+7.57606 7.8521
+3.16719 3.86953
+2.55072 2.64932
+9.51707 9.6071
+0.304643 2.41784
+2.79925 3.28234
+5.32901 7.7576
+5.19903 6.30449
+1.87819 2.99454
+8.92272 9.67105
+4.62414 5.05592
+4.0079 4.64148
+2.26369 3.44573
+2.69335 3.13426
+1.90706 2.42652
+2.68113 2.79133
+1.41065 1.56018
+6.55282 7.18725
+5.72986 6.37151
+7.26968 8.22623
+3.32643 3.73606
+3.77325 4.63115
+1.05457 1.83651
+8.6815 8.85251
+3.91285 4.17139
+0.380936 0.842109
+7.33227 7.69334
+8.45635 8.923
+4.1769 5.08373
+0.501374 2.23328
+-0.161782 1.28908
+3.44716 3.4662
+3.15394 4.30243
+8.71416 9.3781
+6.3034 8.62893
+6.53824 7.04611
+0.6386 1.35269
+0.862088 0.960371
+5.12963 5.20203
+1.58695 2.0452
+6.57698 6.63228
+3.87747 4.45669
+1.51273 3.25669
+-0.0992804 0.667302
+4.7489 4.80059
+0.0280559 1.90471
+6.7462 8.27612
+0.915652 1.30007
+2.79556 3.77404
+9.87989 10.0722
+9.39105 9.84229
+7.57235 9.37122
+5.09426 6.44266
+6.3994 6.72037
+5.73441 6.99341
+6.9079 7.88049
+4.2003 4.41933
+-1.72447 2.25877
+9.04907 9.64323
+4.40473 5.3593
+9.31201 9.96079
+7.28343 8.74163
+3.0172 6.23779
+8.08422 8.56069
+6.83038 6.99863
+2.32038 3.1289
+7.42302 8.26286
+6.66905 7.18496
+0.730748 1.61335
+4.00564 5.73993
+2.81231 3.67489
+2.33178 2.37845
+9.03302 9.68681
+0.567816 0.755485
+7.89783 8.7621
+0.177662 0.332833
+-0.181569 1.36821
+6.22158 6.55787
+4.67115 5.16995
+0.806432 0.820738
+7.69636 7.87402
+4.40933 4.54995
+9.1329 9.15037
+8.87416 9.04329
+1.14349 1.8993
+3.29756 4.7172
+6.67873 8.31364
+6.91238 7.1654
+0.483084 1.55006
+2.66058 3.86294
+5.93347 6.06085
+7.40514 9.05071
diff --git a/geom_matching/wasserstein/tests/data/test_200_A b/geom_matching/wasserstein/tests/data/test_200_A
new file mode 100644
index 0000000..164b71d
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_200_A
@@ -0,0 +1,200 @@
+0.471299 1.89241
+2.82136 3.97846
+8.81923 9.20678
+1.42474 1.65425
+8.36963 9.16097
+-0.236476 0.692489
+7.57182 8.06148
+2.89878 3.21958
+7.1285 7.51707
+5.75496 7.09461
+6.00081 6.10914
+9.60869 9.64676
+7.42889 8.97174
+7.26061 8.55944
+2.41226 2.5146
+8.5616 9.44847
+7.4946 8.86962
+5.42244 6.98028
+9.62386 9.96039
+7.70591 9.92849
+1.34119 3.2048
+3.92169 5.15228
+8.82955 9.60318
+7.94213 9.39997
+2.6716 4.02057
+0.375206 0.497663
+1.94572 3.65599
+1.03366 1.46356
+8.91855 10.1838
+7.11087 8.64425
+2.63266 2.78706
+8.93611 10.2943
+8.51999 9.28356
+1.31436 3.13725
+1.92871 4.00178
+8.30503 8.45555
+2.58739 2.82076
+3.20419 5.29453
+7.4015 8.13225
+9.07991 9.74729
+0.822366 0.938371
+2.90508 4.29367
+4.32385 5.4787
+3.63054 4.6918
+8.52962 9.87004
+1.16994 2.39465
+2.61903 3.33772
+4.15505 4.52942
+8.7068 9.66579
+8.10373 9.31351
+0.7954 1.23001
+7.82253 8.69505
+4.59616 5.91996
+1.02032 1.93931
+6.98421 8.46017
+8.42263 11.3447
+2.63444 3.7158
+7.49059 9.1137
+-0.122109 1.41074
+8.29578 8.81161
+6.24793 6.32368
+4.07212 4.39695
+5.32453 7.457
+8.3892 9.82048
+4.35981 4.78063
+5.49932 6.08321
+1.0107 1.53369
+2.48759 2.94139
+4.24977 5.52034
+1.93104 3.35207
+-0.733247 1.22412
+-0.354283 2.36812
+6.34728 6.44213
+5.98172 7.8753
+5.47963 6.82986
+6.01986 6.6588
+4.62793 5.22134
+7.73923 8.29761
+8.85565 9.51494
+5.55307 6.15804
+6.30963 7.17248
+9.4775 10.2636
+7.45333 7.74006
+1.79317 2.33273
+7.73056 8.44999
+3.94172 5.02778
+8.36177 9.85172
+5.91765 8.12935
+7.95436 8.97583
+5.06238 5.37907
+4.56153 4.97175
+9.47572 9.65038
+9.54745 9.922
+7.82271 8.66299
+4.19056 4.75156
+4.15657 5.72352
+0.213845 0.312444
+2.30944 2.6806
+2.42391 3.41888
+6.00512 6.88274
+6.64546 7.61145
+-0.204229 2.78228
+-0.417104 0.667252
+8.18696 8.67785
+9.27818 9.67924
+-0.0174685 0.21355
+5.91137 6.39606
+9.49268 10.1457
+2.11362 4.36526
+6.51084 7.82167
+6.07066 6.44843
+-0.653518 1.08588
+7.46736 7.87137
+5.26045 7.92188
+6.4171 6.8133
+6.73709 7.70383
+3.46451 4.23679
+0.122365 0.809853
+7.98627 8.0505
+1.71192 2.63047
+1.20624 2.12087
+4.6812 5.51566
+2.62672 2.67648
+4.203 5.1052
+5.26482 6.5186
+3.68166 3.74701
+2.72011 3.98338
+3.41652 3.71477
+2.26211 2.90374
+0.930209 1.43211
+1.98603 3.36662
+4.55838 5.9933
+5.66292 6.59838
+3.12432 3.87457
+6.54384 8.38959
+0.205059 0.331022
+8.70617 9.34121
+7.02182 7.38679
+2.36908 2.84197
+9.13221 9.76563
+7.50113 9.49245
+8.15671 8.45093
+1.9517 2.20923
+3.23368 3.43695
+2.97273 4.10133
+7.36338 8.96733
+4.77525 5.18347
+9.47774 10.3537
+1.75218 1.97051
+5.42544 6.18939
+9.75801 10.0151
+6.31285 6.38327
+8.43389 8.721
+7.6108 7.81113
+8.72029 10.3153
+5.18655 7.1101
+7.96243 8.43151
+0.798103 0.860125
+1.1289 2.77549
+5.91084 6.03085
+4.95884 5.46913
+5.88125 6.49667
+7.9394 8.9545
+5.07492 5.55063
+5.92251 6.08548
+3.88602 5.41487
+1.40122 2.15276
+8.74244 8.83223
+4.75577 6.60338
+0.921272 1.44873
+3.77361 3.90348
+8.8999 9.8518
+3.11077 4.85674
+8.56185 10.6345
+8.76335 9.00169
+6.8734 8.60197
+4.54408 4.93892
+5.57849 6.31727
+7.95161 8.30843
+1.55798 2.4957
+3.86082 3.97131
+6.45433 7.17065
+0.96021 2.32897
+6.84576 6.89531
+8.59095 8.70199
+3.57754 4.26457
+5.27979 5.74385
+2.06602 2.90525
+3.0856 4.18179
+2.76214 3.9982
+2.11943 5.4285
+3.1197 3.24389
+0.495798 1.23632
+3.18253 3.99433
+7.02072 8.37949
+2.77905 3.42643
+1.57093 2.30655
+7.10979 7.14006
+6.20994 6.72092
+8.15136 8.33899
diff --git a/geom_matching/wasserstein/tests/data/test_200_B b/geom_matching/wasserstein/tests/data/test_200_B
new file mode 100644
index 0000000..761943d
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_200_B
@@ -0,0 +1,200 @@
+1.17434 1.46837
+2.58198 4.16589
+0.234041 0.968658
+1.52703 1.59579
+6.7103 7.44033
+3.19227 4.41539
+5.42556 5.57369
+3.45417 4.86089
+3.82256 4.1092
+7.82551 7.90784
+3.9384 4.71796
+5.60335 5.9054
+7.96663 9.8987
+6.30305 6.64853
+7.33246 10.5316
+0.623312 1.09008
+2.63041 2.64616
+5.36028 6.28956
+4.64202 5.91858
+7.55219 7.96304
+7.73736 9.18221
+1.67114 1.84851
+5.07514 5.12159
+7.03732 7.05228
+7.5006 7.59212
+0.244947 1.55875
+0.0170454 1.10485
+1.95394 3.53669
+5.66015 6.01949
+5.88211 7.64639
+7.46698 9.27085
+6.37429 7.10154
+4.54535 4.81932
+8.21203 9.35896
+4.89933 6.20802
+3.68683 4.17831
+0.477467 0.828394
+6.17871 6.77834
+9.77523 9.92676
+0.854808 2.38709
+7.93326 8.3553
+2.10917 2.27771
+4.07045 4.72793
+8.2016 8.8011
+2.9205 3.95746
+2.89806 4.39725
+5.5654 5.78669
+9.5219 9.98543
+7.08591 7.19588
+8.35359 9.57893
+9.81348 10.0345
+8.5994 9.71835
+5.43903 7.25234
+1.82768 2.92724
+4.44952 6.79754
+5.66747 7.34386
+5.88153 6.39253
+3.34008 4.22032
+2.46068 2.76051
+0.370778 2.61681
+6.02508 6.26809
+4.32654 4.93262
+7.41536 7.99616
+8.84229 9.87911
+3.8551 5.84353
+1.56832 2.34694
+6.96099 7.42028
+8.15753 8.72014
+9.23141 10.3815
+7.4484 7.80228
+0.473671 0.874895
+3.15689 3.50687
+3.58122 4.09945
+3.55022 3.74767
+4.42708 5.80211
+4.40956 4.68699
+3.80576 4.61856
+7.29965 8.28614
+7.40582 8.15308
+1.69789 1.77669
+1.66419 3.44308
+0.473997 0.872506
+7.83959 8.52898
+6.22416 6.36949
+-0.187159 0.871822
+0.232336 0.585965
+9.29905 9.44357
+1.4459 2.40589
+2.83008 3.19758
+1.15291 2.12112
+2.58686 3.33896
+6.79362 7.88068
+0.228178 1.48318
+5.60001 6.20258
+4.97803 7.10992
+1.70429 1.962
+2.72659 3.13886
+9.22714 9.25889
+3.84694 3.88778
+-0.282077 1.48155
+9.28756 9.58517
+4.34069 5.59751
+8.63909 8.76839
+8.86236 10.7642
+6.77597 8.41888
+7.30621 8.64164
+0.685607 1.22755
+2.91514 3.22638
+2.72098 3.66837
+8.17528 8.32638
+5.19632 5.7506
+7.34177 8.70639
+5.74082 6.35524
+5.95975 6.69284
+9.40187 10.4488
+2.92761 3.36735
+0.399531 3.13082
+4.83399 4.92635
+7.74539 8.56852
+1.76322 3.5086
+6.54479 6.72963
+7.64362 8.12404
+1.35542 1.45313
+0.214385 0.718085
+1.7006 3.21962
+5.91009 6.47862
+2.21093 2.34636
+5.96919 6.79365
+6.59951 8.22203
+1.54571 1.59397
+3.27012 3.79128
+0.32455 0.622995
+1.73926 2.78017
+9.81035 9.84077
+7.38441 7.85171
+8.90372 9.34186
+7.26323 8.41174
+5.7363 5.97348
+8.25473 10.1281
+2.3981 2.52096
+8.53783 9.63442
+8.51755 9.2735
+6.48614 6.773
+3.40182 3.65137
+2.1353 3.04852
+2.95397 3.73285
+6.98063 7.4963
+4.50189 5.26384
+0.21416 1.49363
+0.632196 1.36307
+6.57833 6.60481
+8.0634 9.33903
+2.79759 2.94462
+4.43747 4.58861
+6.48733 6.86569
+2.28008 3.47037
+6.87452 7.77431
+-0.156821 2.71557
+0.72595 1.78862
+1.97586 2.38196
+8.61839 9.1468
+4.55496 5.68986
+0.26923 1.15728
+9.63757 9.7236
+1.39497 1.96698
+4.8643 5.04172
+6.64675 7.66435
+2.56256 2.6015
+-0.381989 0.611211
+0.676336 1.26896
+8.95304 9.03243
+5.62058 6.07997
+3.36522 4.04276
+8.64868 10.5024
+4.75813 5.19834
+1.96608 2.05864
+9.01449 9.10397
+3.72786 4.51921
+5.6938 6.96584
+1.73499 2.9314
+2.73099 3.41409
+8.77171 9.07665
+4.63865 4.67649
+8.6698 9.30782
+-0.168259 2.09581
+9.29672 9.56
+0.372544 2.60567
+0.450487 1.32919
+6.95341 7.6399
+3.4403 5.24993
+5.53469 6.97831
+-0.79664 1.21306
+5.68831 6.14413
+8.85601 8.95444
+3.83309 5.211
+5.51573 6.5114
+3.64009 3.99648
+4.40759 4.99283
+1.85198 2.6457
+2.72645 3.74803
diff --git a/geom_matching/wasserstein/tests/data/test_5000_A b/geom_matching/wasserstein/tests/data/test_5000_A
new file mode 100644
index 0000000..094c6e0
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_5000_A
@@ -0,0 +1,5000 @@
+0.471299 1.89241
+2.82136 3.97846
+8.81923 9.20678
+1.42474 1.65425
+8.36963 9.16097
+-0.236476 0.692489
+7.57182 8.06148
+2.89878 3.21958
+7.1285 7.51707
+5.75496 7.09461
+6.00081 6.10914
+9.60869 9.64676
+7.42889 8.97174
+7.26061 8.55944
+2.41226 2.5146
+8.5616 9.44847
+7.4946 8.86962
+5.42244 6.98028
+9.62386 9.96039
+7.70591 9.92849
+1.34119 3.2048
+3.92169 5.15228
+8.82955 9.60318
+7.94213 9.39997
+2.6716 4.02057
+0.375206 0.497663
+1.94572 3.65599
+1.03366 1.46356
+8.91855 10.1838
+7.11087 8.64425
+2.63266 2.78706
+8.93611 10.2943
+8.51999 9.28356
+1.31436 3.13725
+1.92871 4.00178
+8.30503 8.45555
+2.58739 2.82076
+3.20419 5.29453
+7.4015 8.13225
+9.07991 9.74729
+0.822366 0.938371
+2.90508 4.29367
+4.32385 5.4787
+3.63054 4.6918
+8.52962 9.87004
+1.16994 2.39465
+2.61903 3.33772
+4.15505 4.52942
+8.7068 9.66579
+8.10373 9.31351
+0.7954 1.23001
+7.82253 8.69505
+4.59616 5.91996
+1.02032 1.93931
+6.98421 8.46017
+8.42263 11.3447
+2.63444 3.7158
+7.49059 9.1137
+-0.122109 1.41074
+8.29578 8.81161
+6.24793 6.32368
+4.07212 4.39695
+5.32453 7.457
+8.3892 9.82048
+4.35981 4.78063
+5.49932 6.08321
+1.0107 1.53369
+2.48759 2.94139
+4.24977 5.52034
+1.93104 3.35207
+-0.733247 1.22412
+-0.354283 2.36812
+6.34728 6.44213
+5.98172 7.8753
+5.47963 6.82986
+6.01986 6.6588
+4.62793 5.22134
+7.73923 8.29761
+8.85565 9.51494
+5.55307 6.15804
+6.30963 7.17248
+9.4775 10.2636
+7.45333 7.74006
+1.79317 2.33273
+7.73056 8.44999
+3.94172 5.02778
+8.36177 9.85172
+5.91765 8.12935
+7.95436 8.97583
+5.06238 5.37907
+4.56153 4.97175
+9.47572 9.65038
+9.54745 9.922
+7.82271 8.66299
+4.19056 4.75156
+4.15657 5.72352
+0.213845 0.312444
+2.30944 2.6806
+2.42391 3.41888
+6.00512 6.88274
+6.64546 7.61145
+-0.204229 2.78228
+-0.417104 0.667252
+8.18696 8.67785
+9.27818 9.67924
+-0.0174685 0.21355
+5.91137 6.39606
+9.49268 10.1457
+2.11362 4.36526
+6.51084 7.82167
+6.07066 6.44843
+-0.653518 1.08588
+7.46736 7.87137
+5.26045 7.92188
+6.4171 6.8133
+6.73709 7.70383
+3.46451 4.23679
+0.122365 0.809853
+7.98627 8.0505
+1.71192 2.63047
+1.20624 2.12087
+4.6812 5.51566
+2.62672 2.67648
+4.203 5.1052
+5.26482 6.5186
+3.68166 3.74701
+2.72011 3.98338
+3.41652 3.71477
+2.26211 2.90374
+0.930209 1.43211
+1.98603 3.36662
+4.55838 5.9933
+5.66292 6.59838
+3.12432 3.87457
+6.54384 8.38959
+0.205059 0.331022
+8.70617 9.34121
+7.02182 7.38679
+2.36908 2.84197
+9.13221 9.76563
+7.50113 9.49245
+8.15671 8.45093
+1.9517 2.20923
+3.23368 3.43695
+2.97273 4.10133
+7.36338 8.96733
+4.77525 5.18347
+9.47774 10.3537
+1.75218 1.97051
+5.42544 6.18939
+9.75801 10.0151
+6.31285 6.38327
+8.43389 8.721
+7.6108 7.81113
+8.72029 10.3153
+5.18655 7.1101
+7.96243 8.43151
+0.798103 0.860125
+1.1289 2.77549
+5.91084 6.03085
+4.95884 5.46913
+5.88125 6.49667
+7.9394 8.9545
+5.07492 5.55063
+5.92251 6.08548
+3.88602 5.41487
+1.40122 2.15276
+8.74244 8.83223
+4.75577 6.60338
+0.921272 1.44873
+3.77361 3.90348
+8.8999 9.8518
+3.11077 4.85674
+8.56185 10.6345
+8.76335 9.00169
+6.8734 8.60197
+4.54408 4.93892
+5.57849 6.31727
+7.95161 8.30843
+1.55798 2.4957
+3.86082 3.97131
+6.45433 7.17065
+0.96021 2.32897
+6.84576 6.89531
+8.59095 8.70199
+3.57754 4.26457
+5.27979 5.74385
+2.06602 2.90525
+3.0856 4.18179
+2.76214 3.9982
+2.11943 5.4285
+3.1197 3.24389
+0.495798 1.23632
+3.18253 3.99433
+7.02072 8.37949
+2.77905 3.42643
+1.57093 2.30655
+7.10979 7.14006
+6.20994 6.72092
+8.15136 8.33899
+0.903753 1.57921
+0.380744 1.88053
+1.3164 1.79885
+0.199366 0.576639
+5.53683 6.49433
+8.8705 9.14396
+3.1401 3.98761
+4.82675 6.69396
+7.28593 8.08916
+8.11312 9.92914
+3.25737 3.42109
+2.58413 4.57223
+2.65202 2.77196
+-0.31616 1.51568
+4.1621 5.29563
+4.49155 5.21037
+7.28481 7.31722
+9.70568 10.0406
+2.39057 2.85874
+2.30626 3.16244
+1.15896 2.09616
+5.91343 7.33024
+-0.172181 0.177363
+5.08934 6.10928
+7.15508 7.55315
+4.43036 4.68093
+6.00772 6.49566
+7.9247 8.43809
+1.57028 2.52328
+3.45458 4.43692
+3.18257 4.83824
+1.48243 1.91225
+2.59025 2.99596
+6.93059 8.34359
+4.53752 7.51216
+6.42857 6.51513
+5.5663 6.13877
+7.54367 7.56485
+9.01229 9.19913
+2.84158 3.23347
+0.42959 0.620618
+8.77033 9.33787
+8.06103 8.30989
+6.48425 7.83668
+4.66949 6.00205
+4.46683 4.66247
+8.96738 9.07714
+0.463093 0.514808
+7.72986 9.44537
+6.31391 6.8289
+0.883295 1.70946
+3.73278 3.98015
+6.73453 6.92514
+3.58434 4.43262
+0.236286 1.4601
+2.00505 2.38684
+3.845 4.20708
+1.18852 3.04911
+0.766784 1.00641
+7.38781 8.11216
+8.9396 9.27589
+0.369822 1.53322
+8.76527 9.23987
+0.663408 0.803445
+9.69209 10.1455
+9.73379 10.234
+3.47602 4.89968
+4.12754 5.45249
+7.31984 8.57369
+0.989101 1.47678
+5.57662 5.83292
+4.99092 6.85044
+4.35272 4.3683
+0.0517815 1.92227
+7.2694 7.80634
+1.35565 1.42783
+8.5638 8.91707
+1.74557 2.74123
+2.18226 2.19664
+-0.901334 1.32691
+0.866613 1.03788
+5.30895 6.31774
+7.70178 8.5675
+4.06894 4.16071
+2.59697 3.53162
+4.30201 5.34212
+4.08762 4.19091
+-0.516192 0.848397
+9.85911 10.1066
+0.472991 1.40069
+6.26012 7.0054
+0.707801 2.15445
+1.73825 1.87193
+0.206301 0.831746
+7.77872 7.92715
+4.94477 5.60361
+3.86216 5.16839
+1.35166 1.54482
+2.91721 3.56193
+4.57242 4.75913
+7.09303 8.29678
+8.18431 8.92151
+6.80303 10.87
+1.29208 1.72027
+1.22224 1.31693
+5.26756 6.26459
+2.55221 3.94397
+6.58902 6.78354
+7.85917 8.89791
+6.59279 7.63115
+4.45787 7.26828
+3.48759 5.33991
+2.13838 3.86589
+1.53368 3.57313
+1.41722 3.19336
+3.48388 4.11958
+1.53756 2.98451
+7.29083 8.22109
+1.2713 2.16915
+6.94685 7.31029
+6.2494 7.04676
+3.00376 3.02028
+1.25232 1.43301
+1.50413 2.25
+2.89098 4.77198
+4.50766 5.89294
+5.5403 6.07893
+-0.101858 0.409209
+3.87963 6.1053
+-0.19564 1.46589
+-0.342077 0.694623
+6.63875 7.42223
+7.89982 9.05857
+4.91121 5.48031
+5.90743 8.35815
+7.13881 8.21597
+4.11493 5.08147
+3.59673 3.77176
+8.08236 8.3237
+1.35842 1.48934
+5.42963 5.85906
+4.60778 4.9734
+-0.607198 0.91225
+5.40263 5.7017
+5.08624 5.63236
+7.34529 9.02233
+7.30013 8.12907
+6.56106 6.75585
+6.82576 7.25434
+2.96037 3.56454
+3.60283 4.09137
+6.35408 6.71888
+-0.688103 0.84579
+8.45128 8.69771
+6.31749 7.3294
+5.82223 6.04761
+1.72983 2.38205
+6.94974 7.76985
+9.681 10.0141
+8.65394 9.2456
+2.52904 3.39548
+6.66609 7.35785
+4.18963 4.5473
+3.58643 3.80513
+2.88796 3.82413
+0.992066 1.1477
+4.57942 5.15419
+3.02826 4.32268
+5.91565 6.82087
+4.13694 5.13393
+6.28717 6.53257
+1.3149 2.36944
+0.488974 0.981461
+4.32763 4.98272
+-0.420836 0.63619
+7.13406 7.31362
+0.367782 1.91138
+6.14763 6.16713
+0.837933 1.92794
+9.3781 10.0609
+3.22887 5.44584
+5.91554 6.65974
+5.68608 6.02435
+7.99715 8.55953
+5.37106 5.38574
+0.254544 0.374268
+0.184701 0.917061
+9.38005 9.54042
+6.86741 7.35628
+0.87398 1.69093
+7.75845 8.53773
+0.370425 1.35335
+0.699837 0.810893
+3.73223 4.32089
+3.81804 3.85752
+4.85936 5.10097
+2.70381 3.10519
+3.82523 5.27699
+9.13422 10.3317
+4.62775 4.77988
+7.91139 7.95504
+2.72614 4.79958
+0.863357 1.88643
+4.14155 5.02327
+2.49683 4.11585
+1.06741 1.11516
+6.20979 8.38443
+0.936362 2.46703
+6.58231 7.85063
+1.89469 2.31422
+8.70668 9.14006
+5.76486 6.1122
+3.15778 3.76679
+0.248282 2.19881
+1.25317 1.92694
+7.81837 9.53067
+-0.582906 0.818904
+8.34194 8.37845
+4.4913 5.10164
+3.86376 5.5453
+6.94054 7.68699
+2.07462 2.5811
+0.518698 2.1529
+3.51773 4.60946
+8.39915 8.98446
+1.63685 2.19755
+6.60737 9.05917
+2.06667 3.23994
+3.18822 3.31219
+2.44332 3.22088
+4.755 4.87118
+4.34245 4.91565
+4.64562 4.8104
+2.73282 3.35646
+9.48057 9.86441
+0.640899 1.03356
+0.789475 0.925731
+1.4247 1.58347
+1.47747 2.25408
+8.8612 9.81597
+8.44147 10.6885
+9.07046 10.0877
+0.350125 1.15765
+4.45375 5.73964
+4.06526 4.26457
+7.50776 8.23491
+1.55452 1.63786
+2.8399 4.52559
+1.34319 1.47951
+5.21572 5.65702
+5.85695 6.75574
+8.21826 10.657
+-0.0479956 1.79162
+1.72905 1.78113
+3.35685 4.03797
+3.7123 5.82296
+8.56751 10.2992
+4.07987 4.25273
+5.8347 6.07253
+3.25283 3.99065
+9.31995 10.3647
+0.649025 1.14496
+1.06966 2.04182
+7.59831 7.85826
+3.35263 3.84374
+4.91371 5.10148
+8.0875 9.23421
+3.00484 5.14282
+8.60448 9.09442
+5.61429 6.24037
+3.4729 4.17907
+7.18702 7.26274
+0.729966 0.928899
+2.27264 2.81241
+2.67248 3.91661
+3.98443 4.64406
+5.50463 6.92435
+-0.257961 0.905736
+5.71363 6.67387
+4.12643 4.66467
+3.97623 4.64917
+9.71227 9.8243
+8.66674 8.97634
+7.94673 8.73904
+7.45228 8.14466
+3.78405 4.81189
+4.05505 5.00011
+2.84957 3.15451
+6.93785 7.58172
+1.85006 1.96814
+6.82256 7.00166
+2.42727 2.68161
+8.06129 8.76308
+1.53758 3.67482
+2.5057 4.18495
+7.09618 7.79664
+1.12843 1.46228
+1.61447 2.2668
+5.58639 5.70776
+8.15398 8.34959
+2.49733 2.57643
+5.18817 5.32462
+5.62963 5.86377
+1.95215 2.26641
+7.22129 7.23255
+5.99833 6.18639
+6.20875 7.13868
+6.814 7.6597
+7.41314 9.49798
+5.48819 6.31634
+4.81318 5.08156
+9.15434 9.42787
+4.83201 5.43295
+1.44477 2.6455
+8.8907 10.4727
+3.19487 3.21255
+1.47291 3.13113
+4.14796 5.10058
+5.85016 8.41212
+7.58183 8.53659
+9.5978 10.2877
+1.5954 2.58394
+2.65561 4.0975
+-0.157024 0.16627
+7.04379 7.75368
+2.39896 2.7728
+2.42376 3.58085
+0.158653 0.857045
+7.82101 9.01833
+6.80499 7.35261
+6.62833 6.66255
+4.90229 5.45681
+8.50205 8.69236
+2.01845 2.06924
+8.2503 9.04222
+4.58265 5.52758
+8.6176 9.74887
+1.28468 1.77019
+5.40424 6.20705
+0.943905 1.98023
+4.2702 4.41287
+3.89454 4.4814
+0.585132 1.56351
+9.45354 10.5374
+3.7501 5.07553
+7.53248 8.14004
+4.60026 4.78921
+1.61073 4.22368
+6.22591 7.40966
+0.57866 1.0522
+5.877 6.55882
+3.54573 3.7731
+7.64294 7.66103
+8.2399 9.14032
+0.307808 0.571367
+7.89791 9.25734
+5.2852 5.84332
+0.45713 2.42223
+7.73426 8.48158
+8.0058 9.46405
+8.03189 8.41868
+1.54624 1.98914
+7.24167 7.27699
+8.61226 8.83199
+5.38365 6.52599
+0.827018 1.51428
+7.19285 7.48372
+-0.0969552 2.16007
+4.08084 6.32563
+9.16026 10.2604
+0.206147 0.717435
+4.00643 4.8093
+2.16139 3.14759
+1.73359 3.13485
+9.1103 9.36052
+7.05621 7.43901
+4.83509 6.07581
+5.67164 7.89488
+7.93353 8.18454
+2.59566 3.1842
+3.62521 4.95121
+3.0297 3.18649
+1.08515 2.69289
+3.77063 5.7789
+0.106035 1.27409
+0.601099 2.13481
+6.98837 7.58833
+9.22955 9.79149
+1.6398 1.78726
+2.30423 3.34814
+2.3912 2.81842
+4.71724 6.16966
+5.13553 5.5016
+5.34117 5.45075
+9.32776 10.0676
+4.54244 5.29058
+4.01337 5.73188
+8.17427 8.39652
+1.96122 2.6921
+0.594267 1.00176
+-0.136973 0.977133
+6.41362 6.78056
+1.68388 2.39891
+9.50795 10.2315
+-0.262703 1.15557
+8.72229 9.683
+5.41821 5.67519
+4.93514 7.15867
+4.07046 4.26702
+-0.00866106 0.252353
+8.65877 10.1527
+2.10958 3.40353
+2.94947 3.43201
+6.32014 7.06279
+2.55034 2.89646
+3.87777 5.68044
+2.05325 3.00968
+5.11855 6.55784
+8.99618 9.22608
+3.36303 3.84902
+2.6234 3.70832
+7.35189 8.30856
+6.43777 7.28495
+5.33954 5.48055
+0.15999 0.391114
+3.4492 5.07088
+2.38503 2.42742
+3.88912 4.93246
+2.76303 4.54991
+6.56261 7.60381
+3.64198 3.72471
+1.70542 1.78806
+9.71115 9.88315
+0.471586 1.82377
+9.27611 10.5725
+0.486889 0.851587
+4.39234 5.10637
+9.54209 9.8955
+6.99602 7.42461
+2.98797 3.74623
+5.89424 6.4498
+9.63845 9.86333
+4.39828 5.18646
+2.30118 3.5758
+8.68128 9.56011
+9.00505 10.4304
+-0.22184 0.233296
+6.48492 7.60283
+1.54156 2.60693
+4.06808 6.39369
+5.52361 6.26241
+-0.698727 0.745524
+2.30742 2.41924
+0.655833 1.65752
+6.94733 7.81866
+7.83136 9.10651
+6.45284 7.1931
+6.09683 6.66159
+9.03299 9.4163
+1.86392 3.26189
+0.54863 2.07194
+0.364026 1.38376
+0.706954 1.0736
+-0.111015 0.584367
+1.54265 2.1413
+0.501172 0.57928
+2.09411 2.28902
+1.89894 4.09104
+8.72895 8.87045
+5.27709 5.72545
+0.164581 1.64188
+8.39203 8.73608
+8.21523 8.45002
+9.62006 10.1665
+4.79076 6.26442
+3.44189 3.68669
+8.93789 9.38343
+7.07036 7.64194
+8.34396 9.56445
+8.29992 8.64954
+5.82566 7.83287
+2.43008 3.4476
+1.81121 2.12203
+4.36108 4.3842
+6.28756 6.95939
+3.8698 5.79519
+0.644581 0.699162
+9.71982 10.2422
+4.86545 5.00544
+8.3808 9.29356
+1.34495 1.40972
+1.03641 2.01838
+8.55609 9.99958
+1.40813 3.46097
+3.20928 4.08838
+8.24345 9.11587
+1.27604 2.03462
+8.28298 8.46252
+2.20644 3.19585
+1.91396 2.30108
+4.2553 5.66006
+7.34925 8.38275
+6.28319 7.22199
+8.42171 9.62596
+1.12059 1.18262
+1.37555 1.84196
+4.39663 4.93639
+9.28044 9.58437
+5.47407 6.88593
+8.36529 8.49813
+9.40208 10.5922
+-0.451389 1.92404
+2.89431 3.19453
+6.51433 8.02158
+5.52163 6.06786
+7.63525 9.86457
+4.13898 5.05845
+0.136662 0.503748
+6.13492 6.74997
+8.37416 9.18971
+8.64164 8.82663
+0.941173 1.16158
+6.9073 8.26916
+8.37278 8.44326
+8.8951 9.20427
+8.40149 9.17577
+3.60361 4.57183
+8.67263 10.1127
+5.89716 6.45412
+1.97285 2.35593
+7.22973 8.13467
+9.55645 9.92529
+0.261578 0.836274
+9.04414 9.45485
+6.18043 6.54229
+8.88579 9.54087
+1.82613 2.93849
+5.7599 5.96366
+5.23105 5.5541
+0.425229 1.44436
+0.32065 2.60892
+7.83655 9.48786
+2.3168 3.45972
+2.35466 3.23342
+8.24494 8.27258
+7.32678 7.74264
+8.37275 10.0741
+4.17337 4.98348
+-0.162009 0.724431
+9.49757 9.76377
+8.69435 9.08089
+4.13841 4.7514
+7.26358 7.55514
+6.91912 6.93
+6.7883 7.18296
+-0.614315 1.1857
+6.05907 7.33557
+4.86704 4.87705
+8.50012 9.14836
+1.83683 3.09895
+6.16389 8.39248
+3.03556 4.41004
+4.72416 5.05782
+1.98183 3.19183
+2.77103 5.71501
+4.90002 4.9817
+0.269258 0.725628
+5.59551 7.05038
+2.95416 3.09633
+2.17662 3.18385
+6.67536 7.56762
+7.31678 7.39321
+3.84158 3.85359
+4.38203 5.10149
+0.816784 1.09918
+0.70131 0.739502
+0.276631 2.32633
+9.15839 9.49433
+0.721295 1.97673
+2.63875 2.75437
+5.00344 5.5645
+2.24555 2.81234
+7.74465 9.07646
+2.35244 4.90632
+0.812093 0.954419
+3.97904 5.2071
+4.19177 4.83646
+0.853012 1.331
+2.64112 3.25613
+0.0894716 1.61954
+3.39617 3.40859
+1.56906 2.28518
+2.37258 4.36515
+1.60997 1.7561
+5.15275 5.33281
+6.47824 7.47529
+2.51145 3.29949
+2.71094 2.77785
+8.84653 9.48398
+1.3246 1.66664
+7.43718 7.5546
+3.68985 4.24649
+4.50553 4.51669
+4.15617 4.54887
+8.55319 10.2136
+2.1448 3.55805
+4.64594 4.71505
+1.71763 1.99977
+4.98066 5.46709
+7.7779 8.36497
+-0.0604997 0.301294
+5.11873 7.32867
+4.94567 5.5986
+5.32645 6.26835
+2.5768 3.13511
+3.31604 3.87555
+1.60834 2.68762
+3.30552 3.85211
+4.26741 4.98636
+0.820475 1.18854
+7.91501 10.7203
+5.17391 5.30351
+4.00452 5.75141
+2.37722 3.4602
+2.2373 2.94837
+1.62462 3.48264
+7.98507 9.2566
+5.32504 5.56388
+0.760614 0.7802
+0.855659 1.01522
+3.11231 4.43223
+5.62821 6.21564
+4.96845 5.35921
+8.10559 9.11106
+1.02533 1.13103
+6.99976 7.0788
+1.87628 2.58801
+1.11023 2.24655
+5.23343 5.45086
+0.95875 2.25343
+7.45917 8.23614
+2.72245 3.04663
+8.66816 8.70698
+5.55734 7.12262
+1.01935 1.88271
+0.804417 1.76187
+7.82703 8.78426
+4.77321 6.15199
+2.91503 3.69352
+0.776735 1.08883
+5.19981 5.46442
+9.06483 10.4008
+7.72082 8.26398
+2.9854 3.26573
+0.584028 2.60443
+0.700994 1.10379
+5.47612 5.94502
+-0.318442 0.986786
+5.0704 5.08055
+3.83732 4.4826
+5.60843 6.59005
+8.81432 9.7851
+6.28792 6.65284
+1.16812 2.27701
+7.15234 9.66795
+9.02188 9.15339
+1.76112 2.04839
+7.49419 7.6204
+5.1765 5.497
+1.45855 1.94969
+1.35003 1.55822
+2.50453 2.86815
+7.10451 8.51352
+3.87727 4.1124
+1.84876 2.79351
+3.01661 4.05396
+-0.16814 0.366484
+-0.538895 0.608786
+7.59933 9.00744
+0.938648 2.38471
+5.76934 6.16572
+8.20416 8.62505
+8.64542 8.7264
+2.75784 2.7828
+0.794451 1.97817
+9.21331 10.5156
+8.46671 8.80572
+3.94814 5.16845
+6.73654 6.97397
+4.05301 4.93008
+6.37833 6.77985
+7.21161 8.15994
+3.74993 4.61029
+-0.127938 1.09853
+2.82957 4.28816
+6.50564 6.59445
+8.37926 8.48332
+0.757443 1.74585
+0.232438 0.972293
+6.99187 8.7098
+7.20368 8.44301
+4.4801 6.07501
+4.34015 6.1495
+2.19394 3.22334
+7.29817 8.41021
+1.16627 2.68901
+4.62423 5.08039
+0.955655 2.22882
+1.45862 3.07065
+1.69815 2.56291
+4.01179 4.36587
+7.135 7.75829
+6.6823 7.27765
+5.44745 5.67195
+4.94594 5.42067
+0.267772 0.301939
+0.676432 1.6296
+1.37335 1.91645
+1.89051 2.38631
+4.84386 6.30539
+8.95613 10.944
+2.18119 2.41821
+5.74901 6.16952
+7.5431 7.88071
+9.05278 9.8128
+0.201334 1.65323
+2.82787 3.82083
+3.07073 3.08475
+8.99671 10.1766
+5.43261 6.12137
+4.83693 4.86848
+6.31804 7.21544
+2.34491 3.2562
+6.05941 6.53725
+-0.483381 1.17347
+5.53777 5.72651
+3.28875 3.47472
+3.94609 4.9418
+7.45585 8.6871
+1.50066 2.57215
+8.10112 8.67954
+6.24737 6.42635
+0.64413 1.05759
+7.1741 8.21476
+0.502722 1.77462
+0.783663 1.74174
+9.52704 9.9109
+-0.473873 2.01013
+7.07946 7.11474
+0.857335 1.6501
+4.00225 4.84453
+5.40598 6.30573
+8.18802 8.30067
+9.00412 10.9588
+9.8362 10.1517
+7.62735 8.37572
+3.41477 4.63099
+2.51254 2.81559
+6.36702 6.56061
+5.11842 5.35515
+5.21873 5.73296
+5.73528 7.77276
+3.64993 4.66826
+7.12664 8.46553
+8.7257 9.16851
+1.27615 2.20376
+7.46188 8.20752
+2.64939 2.66373
+4.94147 6.0509
+6.3805 7.48841
+6.25197 7.12327
+0.0885331 1.2682
+4.97356 6.58488
+4.11917 4.27267
+9.08793 9.70385
+0.897277 1.31702
+6.87538 8.25544
+1.55565 1.59492
+2.62483 3.54274
+1.49631 2.92263
+9.45305 9.97728
+6.24087 6.4577
+3.94636 5.13669
+-0.000956745 1.08235
+1.46134 1.83767
+9.18517 10.6601
+-0.740186 1.81269
+5.81977 6.15818
+7.0529 7.99421
+6.17753 7.49749
+8.63091 9.04205
+9.65219 10.1558
+3.12839 4.99211
+2.97262 4.07384
+4.28605 4.33171
+4.15906 5.6805
+1.35165 1.50732
+0.524207 2.12307
+5.32468 7.01472
+3.16982 4.87893
+6.88786 8.34191
+3.81703 5.22335
+3.86772 4.37359
+8.19386 10.0705
+1.73155 3.16695
+4.31685 4.62261
+9.40618 9.89612
+4.41215 5.19659
+2.35595 2.63543
+7.88481 8.14203
+7.17062 10.3534
+9.41379 10.389
+7.99867 9.01239
+3.31382 4.04915
+5.39222 6.07657
+8.5187 8.65976
+9.30132 10.6131
+7.17351 7.93596
+4.80134 6.72724
+3.59756 4.65182
+1.08248 1.17479
+1.03124 1.06871
+6.43154 6.95098
+-0.102199 0.59801
+4.94795 5.18081
+6.39126 7.30258
+1.52738 2.04271
+1.80373 2.33093
+2.72251 3.01677
+0.296722 1.10844
+6.39104 6.51567
+8.70965 8.78714
+2.07858 4.41695
+1.91027 2.9718
+1.42944 2.01864
+1.96347 2.73397
+4.0701 4.22456
+4.02859 4.05107
+7.10977 7.98092
+8.21148 9.54287
+6.59231 7.65109
+0.144191 0.830665
+2.0192 2.23704
+8.03482 8.39591
+8.90473 9.98543
+2.18284 2.70166
+3.05569 3.54238
+8.35319 9.39539
+2.00803 2.42866
+2.35282 3.13763
+2.20155 2.79377
+1.76531 2.94892
+7.52749 7.72567
+3.02266 3.24778
+4.01741 4.22521
+9.75263 9.91571
+6.45502 6.67838
+2.24912 3.47262
+1.84372 2.26517
+4.09559 5.71188
+1.43811 2.33732
+3.89171 4.16905
+3.01506 3.9725
+0.0273168 1.51766
+5.04118 5.80507
+3.08517 4.17316
+8.81637 8.8332
+8.74055 9.50143
+2.36904 2.69008
+6.39733 6.44221
+8.53272 9.51114
+9.48423 9.55002
+8.65645 9.04872
+6.66352 7.48009
+5.64389 7.88656
+5.75301 6.6759
+3.28645 4.22757
+4.99482 5.82988
+3.00004 4.0273
+1.45249 1.56662
+4.96263 5.32195
+7.91351 8.1437
+1.02034 1.56249
+0.71376 1.73319
+0.709968 1.44651
+9.01414 9.54799
+4.02116 4.68005
+7.10903 7.27075
+7.21322 8.71031
+1.6644 2.27186
+3.71304 4.68764
+9.66257 10.1452
+5.34477 5.46136
+9.27722 10.583
+3.82328 4.83316
+4.77019 5.07928
+9.07112 9.23836
+6.99091 7.25634
+2.11871 2.3054
+7.88242 7.98572
+0.570809 0.714073
+8.51532 9.49151
+0.329247 2.48897
+4.78202 5.36476
+1.47969 2.78867
+6.75011 7.36769
+2.31162 2.39533
+3.50434 4.48026
+5.84603 6.28861
+7.5337 7.62538
+1.09955 2.85741
+6.09915 7.20381
+0.933825 2.37925
+0.576363 2.0182
+3.12126 3.22507
+2.85313 3.02656
+3.25724 3.92077
+5.48575 6.58011
+1.02824 1.65415
+8.68703 9.06219
+3.28389 3.86349
+5.80264 6.12775
+7.11846 7.47444
+1.29638 3.38601
+6.37908 7.05986
+-0.592438 0.854361
+0.39233 1.17768
+2.1497 3.94423
+2.42336 2.43441
+6.94806 8.30626
+0.79865 1.4173
+1.41455 1.43684
+0.240074 1.16712
+4.18593 5.9929
+6.161 6.564
+0.726091 1.90706
+0.375606 0.694866
+4.09349 4.26452
+2.01652 2.68038
+8.99301 9.90549
+8.93909 9.17673
+1.10427 2.01247
+9.12668 9.15282
+1.08098 1.55374
+0.377836 0.414809
+7.28611 8.86318
+8.3542 8.53251
+3.86353 4.12875
+6.7631 7.40068
+4.61679 4.78182
+2.12935 3.45492
+3.68233 4.0457
+6.68131 8.22111
+8.79197 8.80474
+1.8915 2.26287
+0.387586 3.073
+6.46798 7.57381
+7.79742 9.34526
+-1.21538 1.67303
+2.07845 2.66028
+3.14148 4.46647
+1.66789 2.73375
+8.49821 10.0574
+8.42537 8.89906
+0.618349 2.72887
+6.8229 7.54501
+6.99343 7.35322
+7.34223 8.68781
+3.18834 3.98274
+8.86908 10.2535
+3.39839 3.76171
+0.188168 0.392276
+1.68187 3.53401
+6.50423 6.71714
+7.61191 8.89715
+1.68463 2.09275
+5.94627 7.6003
+2.15865 3.24126
+7.93639 8.53826
+2.99626 3.43273
+7.71028 8.41172
+3.19472 4.60878
+8.36123 8.92549
+8.92975 9.02783
+2.87187 3.49672
+6.76949 7.71139
+8.09121 9.31151
+6.84199 7.44009
+0.730655 0.800873
+7.5208 7.54109
+6.75992 7.55017
+6.09818 6.17399
+0.639209 1.27283
+3.01917 3.22327
+1.69181 2.60614
+9.49245 9.5842
+-0.418034 1.08035
+5.77705 6.4705
+6.79362 8.51993
+9.73155 9.81609
+3.71503 3.92022
+6.18589 6.77634
+3.94124 4.23942
+7.59911 8.05275
+3.68865 5.10948
+7.63926 8.63019
+-0.049855 0.0844425
+-0.41188 0.807786
+1.35962 1.81039
+5.40579 6.15098
+1.38594 2.0059
+6.93444 8.71958
+7.01229 9.38008
+-0.0974976 0.962679
+4.74131 6.59152
+1.02343 1.59245
+1.55629 1.9128
+6.28353 8.38021
+5.66349 5.79644
+1.72629 2.28563
+1.64539 2.82654
+0.359466 1.05106
+4.37569 4.82703
+5.87657 6.37853
+1.46316 1.65333
+7.44494 7.5985
+1.16846 1.98579
+4.43371 4.93234
+2.31838 2.47777
+7.52687 8.44423
+6.08056 7.42253
+9.16144 9.94783
+4.74382 4.95616
+0.248344 1.05335
+6.12345 7.43941
+9.45647 9.99799
+3.77124 4.75199
+3.31315 3.44873
+-0.0909828 1.51271
+7.03234 8.02546
+8.11403 9.11461
+6.734 7.34799
+1.08903 1.18319
+2.72871 4.31607
+6.53688 7.52754
+1.07868 1.27792
+6.28409 6.56795
+4.89866 5.79458
+9.06994 9.1221
+2.54989 3.14338
+3.69971 5.71717
+3.7113 4.55076
+8.91864 9.64522
+8.75634 8.82359
+9.02242 9.03869
+3.59945 4.20169
+4.98557 5.07401
+4.81526 5.74009
+7.264 8.63749
+2.79579 4.43389
+2.87204 4.66432
+2.32487 3.72127
+3.99704 4.72567
+8.17229 9.0158
+-0.72926 0.823658
+2.52355 3.32725
+5.3446 6.63868
+4.48935 7.37833
+3.08173 3.0968
+2.41585 3.23026
+9.23187 10.5413
+5.50099 5.51888
+1.29634 1.88846
+-0.0113225 1.41526
+6.34508 7.53417
+0.383019 2.25144
+5.0649 5.40222
+3.08221 3.76296
+9.29111 10.6833
+2.19714 2.94735
+8.75711 9.48583
+8.61564 8.69349
+1.9362 3.96191
+3.7962 4.44382
+7.78501 9.03843
+5.79649 7.29675
+6.03048 7.4316
+1.31105 4.36446
+6.5882 7.03622
+4.88148 7.12474
+6.62305 6.90973
+2.27488 2.73731
+8.42703 9.28888
+7.72928 8.53989
+7.27021 8.63879
+0.992908 1.92001
+-0.282737 0.511292
+7.88963 8.03117
+6.93371 8.26148
+-0.0346633 0.403617
+8.7827 8.82558
+5.16876 5.59742
+8.61275 8.73843
+5.70578 6.02783
+9.08706 9.35903
+9.32728 9.83021
+9.20058 10.2497
+6.79824 7.3024
+7.17979 7.51783
+0.918555 2.67878
+7.9662 8.63241
+8.85903 10.3145
+9.34614 10.1964
+9.05027 9.79872
+8.28959 8.45837
+1.32518 2.04557
+8.72848 9.16742
+5.92126 6.77277
+8.31504 8.78631
+1.02487 1.70172
+4.45204 4.72842
+8.20636 10.4724
+5.86211 6.25457
+6.15754 7.71303
+8.37832 8.82577
+4.25233 4.59417
+9.41451 9.95324
+9.372 10.4461
+1.8346 2.7144
+8.00681 8.14717
+7.03997 7.06753
+7.16892 7.70102
+7.01781 7.46799
+1.78526 1.7969
+1.63304 2.2011
+8.59718 8.61083
+9.43498 10.3636
+4.98718 5.32487
+3.65451 4.89601
+8.82368 9.11333
+-0.104862 0.598773
+4.97756 5.62342
+1.99876 3.24686
+2.32242 4.01096
+4.04914 4.77892
+3.81545 4.32583
+3.30153 3.46783
+3.14287 4.04718
+8.65711 8.76768
+9.09385 10.6898
+4.82771 5.64405
+5.98783 6.36785
+7.93397 9.6543
+4.89726 5.65486
+5.08717 5.22314
+6.59377 7.23748
+3.58289 3.98597
+0.177884 0.397629
+5.30856 5.79257
+2.53943 3.98007
+6.51217 6.59644
+0.196888 1.99904
+3.8709 4.1901
+2.37646 2.50997
+7.03837 8.13287
+6.8674 6.89535
+0.576964 2.08851
+0.902928 2.86981
+8.59261 9.20279
+5.96621 7.49899
+0.617602 1.83267
+2.29189 2.6361
+4.34207 4.61026
+6.19963 6.76443
+1.15955 1.44068
+5.25852 6.33905
+6.37306 7.53103
+9.13334 10.039
+8.67743 9.93959
+5.62973 6.04375
+8.32286 8.77042
+3.28772 6.1722
+4.97902 5.47249
+3.37765 5.07238
+4.58025 4.84341
+2.76807 3.18817
+4.27581 4.43023
+6.92572 7.68461
+0.279247 1.0561
+1.10903 2.61106
+6.71708 6.73132
+2.66415 3.75702
+8.0513 9.80334
+4.82466 6.04232
+2.59971 2.85768
+9.04689 9.73945
+3.48338 5.16622
+7.90925 8.92774
+7.30264 8.06247
+7.59087 8.26476
+0.150315 0.423068
+8.62726 9.74636
+9.01983 9.71707
+6.84007 7.40541
+8.87829 9.48342
+2.69214 3.71481
+0.580036 0.77289
+9.474 9.83397
+5.80151 6.08527
+3.63837 5.27137
+8.5848 8.65779
+7.88338 8.13276
+3.27992 4.20963
+6.21244 7.28079
+-0.348924 1.38478
+5.26688 5.93787
+3.78941 5.01009
+6.41929 8.38529
+1.41724 1.67733
+0.212821 1.58246
+0.0697189 0.160497
+9.57839 10.2377
+9.03332 9.70645
+4.94572 5.15163
+3.16999 4.536
+5.77494 6.68359
+5.89258 6.11852
+7.72257 8.21668
+8.42501 8.44905
+8.7196 8.86465
+4.392 5.1076
+1.88249 2.54422
+1.38092 1.60326
+5.04142 6.37374
+4.31795 5.91152
+1.79399 2.09314
+6.65405 8.32262
+1.36356 1.58538
+5.45456 6.55824
+4.95807 6.22848
+8.72077 10.2825
+0.745968 2.01546
+6.85041 7.11076
+4.82028 5.34442
+5.77673 8.01294
+8.58582 9.33714
+8.49884 9.21533
+9.05574 9.4096
+3.19932 3.27024
+9.50227 9.96685
+0.771636 1.0617
+9.00621 9.36397
+-0.097167 1.39463
+7.79622 8.43537
+8.05228 10.1504
+2.21685 4.17072
+1.82476 2.45381
+4.34408 4.56578
+9.27538 9.54701
+6.80153 7.44115
+7.38884 7.84448
+7.17526 8.00165
+5.85812 7.67326
+5.25545 6.30791
+5.68518 5.94055
+3.77685 4.1545
+0.530928 0.737248
+3.9772 4.59563
+8.96322 9.45618
+7.98322 8.44165
+3.42929 4.22329
+3.66445 4.7583
+-0.183652 0.434842
+3.16492 3.95726
+6.87407 7.89944
+5.08921 5.7745
+2.85466 3.04505
+7.12685 7.41248
+0.715419 0.876676
+3.47441 5.21753
+9.13594 9.87799
+5.52459 6.35691
+5.12544 6.12935
+2.44296 2.91543
+2.75148 4.19886
+1.43478 4.3919
+6.6352 7.07768
+4.72283 5.11506
+6.36434 7.69268
+2.32643 2.75289
+5.46566 6.22854
+6.37995 6.76989
+1.52564 3.55871
+1.05807 2.19393
+1.95374 4.08727
+8.11273 8.12767
+2.85955 4.71355
+4.89009 6.88516
+2.06859 2.25512
+7.50556 8.77923
+4.43353 5.11915
+5.16308 5.9695
+1.12072 1.69277
+3.96494 4.38143
+7.06931 7.89524
+0.514522 1.51265
+6.56389 7.01146
+1.51362 1.8541
+4.90339 5.51209
+2.11422 3.15457
+2.50094 3.92557
+8.24319 8.62826
+-0.0843505 0.710377
+6.35956 7.89457
+-0.790954 1.2964
+4.02253 5.19193
+2.7425 3.46763
+9.41469 10.1367
+1.57636 2.25105
+3.18647 3.38738
+8.36644 9.66791
+0.180846 0.461221
+7.07675 7.71881
+0.265366 1.19275
+5.43007 6.4854
+9.13912 9.23294
+9.52905 10.4029
+1.18712 2.66227
+5.22714 5.62601
+1.87413 2.8716
+5.71882 6.16374
+5.38169 6.13948
+-0.196005 0.236738
+5.36803 6.4395
+8.27713 8.71262
+8.82031 8.8598
+-0.53845 0.564783
+1.80716 1.94173
+5.67905 7.47291
+2.37779 3.64893
+0.574727 0.766424
+4.60701 4.86591
+3.17989 4.37738
+1.36777 3.15112
+1.48092 3.06573
+3.37009 4.06028
+4.17179 6.2073
+6.47953 6.88994
+5.74431 6.5253
+4.88827 5.49049
+5.44148 5.59239
+1.66153 2.63132
+7.69387 7.82267
+5.20911 5.48488
+-0.103218 0.497402
+7.18003 7.33324
+6.54305 8.81732
+0.278108 1.27452
+2.38323 2.94772
+1.2461 2.03246
+5.47052 5.75781
+7.31505 7.35895
+2.22248 4.23589
+0.0305319 0.430773
+8.32043 10.1222
+6.11732 7.09764
+5.08797 5.74991
+6.62491 7.34164
+5.55795 5.66083
+3.27549 4.13145
+1.0113 3.24631
+0.0717186 0.519738
+7.82905 9.30839
+9.90251 9.98691
+0.841457 0.952276
+-0.133927 0.510174
+1.14614 2.82218
+2.59767 4.19742
+3.41095 3.95265
+3.38951 3.75838
+8.86766 9.19437
+9.27927 10.261
+2.33249 2.79402
+5.69681 5.93469
+4.02502 5.38732
+0.797157 1.22845
+7.23558 7.8651
+1.1253 1.99184
+8.58296 8.64267
+-0.400141 0.74698
+3.46677 3.50526
+6.03252 6.64842
+5.8493 6.68938
+3.53209 3.84014
+7.22078 8.98106
+6.21943 7.69944
+0.887198 2.07323
+3.36591 4.57442
+4.30143 6.41179
+4.07556 4.54276
+8.77552 10.0615
+6.7247 7.04044
+9.05494 10.1126
+7.46604 7.80632
+3.38601 3.8043
+5.60475 7.02788
+9.09811 9.4654
+9.33447 9.80012
+8.66499 10.1002
+3.87007 3.88281
+2.87624 3.11359
+5.92385 6.80801
+4.49711 5.01863
+8.84524 9.36225
+6.40944 6.82093
+2.90915 3.68288
+3.96801 4.82534
+5.51744 5.84233
+3.2959 3.44409
+0.18526 0.207322
+5.49717 5.72648
+6.9522 7.1952
+0.769572 1.79343
+5.95522 6.03918
+6.54327 7.7475
+2.13516 3.83957
+-0.388892 0.775076
+1.30128 2.29414
+9.76761 10.1604
+7.38731 8.66002
+7.09229 7.43495
+9.1379 9.52432
+6.75942 7.88717
+7.42824 8.82063
+6.93078 8.19884
+2.307 2.93836
+8.38254 8.84691
+3.90159 5.37938
+0.0413204 0.112203
+0.638217 1.53943
+0.88678 2.49435
+1.99104 2.60847
+6.61595 7.65064
+2.37641 4.71719
+7.94945 9.21404
+5.44594 7.13646
+5.45737 6.12335
+7.39207 8.02885
+5.51588 7.39341
+8.1581 10.0632
+0.197869 1.44516
+-0.0161139 0.73487
+4.24155 4.7304
+0.00374564 1.26481
+8.1878 8.20011
+-0.115544 1.74771
+0.912347 2.52922
+2.72234 4.83268
+6.45577 7.07776
+9.74201 10.1772
+7.11008 8.72914
+4.41921 4.8673
+7.97673 8.36658
+0.438659 0.647061
+4.35086 5.44482
+8.22919 9.34379
+3.53242 5.65576
+1.126 2.32177
+5.95118 6.51364
+3.10519 3.9447
+1.28889 2.26267
+7.04255 7.67791
+3.12614 3.17626
+8.09968 8.49162
+6.07924 7.219
+5.99554 6.52549
+9.14378 9.5773
+5.82872 6.53112
+9.3361 10.1704
+4.3751 6.51675
+3.288 3.98328
+4.56342 4.80185
+7.16574 7.43901
+7.7638 8.47758
+7.31514 7.78499
+7.98942 9.50252
+4.31211 4.70724
+2.9094 3.33304
+0.108736 0.668815
+8.08409 9.11098
+-0.0053985 0.589992
+1.31926 2.72153
+2.28664 2.38813
+7.95237 8.26388
+-0.0283047 0.452099
+7.50295 8.71708
+7.72356 8.63468
+3.48953 3.89579
+7.73814 8.73456
+4.54487 5.45817
+6.45109 7.00312
+3.99915 5.41716
+6.81436 8.5682
+6.86726 7.55999
+2.67863 3.7053
+2.6837 3.39567
+2.84347 2.91724
+5.79383 5.80493
+2.41217 3.0619
+8.78163 9.0998
+1.43456 2.50854
+6.73393 8.53005
+3.02563 3.56456
+3.34628 4.2934
+2.44985 2.92819
+1.38262 2.89341
+4.56337 5.75702
+0.275735 1.16604
+1.39763 2.8108
+9.69128 9.90361
+8.54868 9.66756
+3.01838 3.72831
+8.34017 10.5262
+1.82721 3.0476
+5.9732 6.83307
+2.77874 4.25603
+1.71987 3.1227
+6.33773 7.89103
+1.31982 2.82992
+4.21601 4.9627
+6.68166 6.72653
+0.87708 1.06828
+1.03483 1.26625
+2.996 4.03087
+1.54718 1.57452
+2.33087 2.38226
+2.29464 3.36496
+2.03064 3.41867
+5.8456 6.10406
+7.54614 8.2874
+1.42938 2.89154
+2.65935 3.53883
+3.98845 4.42049
+6.85632 7.15487
+8.12038 9.34993
+0.873558 1.32129
+6.61595 8.00766
+9.49147 9.81947
+-0.0225601 0.328769
+-0.481519 1.37204
+6.31457 7.45251
+4.59738 5.81395
+4.83304 5.63008
+1.95746 2.78657
+2.97889 3.74408
+9.08327 10.702
+3.58219 4.93634
+7.87068 9.77851
+0.330978 1.36031
+4.78145 5.36918
+4.94672 5.56644
+7.66806 8.08613
+4.78512 6.35461
+5.17367 5.74386
+8.65884 9.97088
+9.22053 9.60447
+6.15061 6.58817
+0.0875971 0.541643
+6.45597 7.92002
+0.636824 1.05267
+5.31677 6.12838
+6.51127 6.6731
+0.459551 1.69382
+5.75106 6.41377
+3.68438 4.67415
+9.36302 9.78281
+5.46195 7.99215
+2.55471 2.59231
+-0.0640245 1.33755
+9.36027 10.6198
+2.67955 2.77882
+3.51443 4.10619
+6.83904 7.23692
+2.59553 2.81418
+7.74628 8.95169
+3.86464 3.90067
+3.32377 3.992
+1.43737 2.52191
+3.83927 3.99402
+5.24426 5.35179
+0.833857 1.05277
+8.4062 8.51964
+9.62299 9.66254
+3.55427 5.19048
+1.7003 2.62107
+6.8059 7.49246
+-0.761104 0.773528
+1.37137 1.95048
+2.1222 2.59387
+0.633505 2.04796
+0.605156 1.04336
+7.22842 8.3365
+4.28716 5.28921
+8.14286 9.52015
+7.62467 9.18603
+1.11029 1.81278
+5.05654 5.13597
+0.862653 0.97827
+7.24766 7.92167
+8.80103 8.91467
+3.05841 4.82292
+1.28204 1.55969
+1.58886 2.57874
+0.0537678 1.28837
+8.09303 9.71678
+8.37599 8.85705
+3.18107 4.01971
+6.67019 7.75848
+6.29836 8.21603
+0.86271 1.30358
+3.86126 6.49694
+5.84446 6.24913
+0.475025 0.598151
+6.3477 7.08327
+3.60161 4.51824
+7.13317 8.13858
+4.89052 6.23701
+5.69441 5.99027
+7.18735 8.34853
+9.30182 9.34894
+6.06589 6.15141
+8.05917 8.89642
+5.6136 7.14058
+0.838337 0.959487
+0.56421 2.00841
+6.46876 6.63629
+2.4434 3.92184
+7.97216 9.45372
+8.17926 8.66756
+1.86522 2.01888
+0.983342 1.89079
+2.07322 2.09765
+3.52974 4.73717
+3.24973 4.88691
+5.47789 5.95384
+4.11218 4.57076
+6.61174 7.47165
+8.09302 8.36193
+1.67911 1.69022
+4.436 6.17515
+5.6544 5.74951
+1.50354 1.76043
+4.41433 4.83589
+1.82528 3.53979
+3.98749 4.65527
+6.42503 7.5787
+8.69554 10.1767
+8.39215 8.56999
+8.71646 9.66209
+1.59091 3.39622
+6.12574 6.94955
+7.92527 8.25049
+9.19858 9.49137
+2.73382 2.97747
+4.56558 5.94738
+8.33983 8.58548
+9.00798 9.01838
+4.93268 5.28383
+3.15356 3.58606
+8.28171 8.79351
+1.47358 1.9581
+0.891797 2.05345
+1.56032 2.38159
+2.5152 3.80485
+8.0963 9.88138
+1.1262 1.89279
+7.99336 8.2352
+5.25067 5.41251
+4.57532 5.20101
+1.87911 4.59415
+4.25142 4.70467
+8.18782 8.76989
+9.20478 9.3612
+7.70898 9.7752
+0.655865 0.958924
+4.8883 5.77477
+3.03279 3.47456
+1.01979 1.41661
+8.94219 10.6987
+2.49348 4.58195
+2.44003 3.29005
+3.4725 3.55572
+9.3917 9.95851
+7.08658 7.47686
+3.56413 4.44089
+-0.47868 1.11453
+6.82615 8.00596
+3.49451 3.74705
+8.13286 8.47976
+7.01732 8.82556
+1.19064 1.48887
+5.28851 6.88637
+8.83175 9.49879
+0.939903 1.17024
+7.49124 9.01906
+2.9285 4.23224
+5.68296 6.86273
+8.53897 8.77886
+2.9107 5.01232
+0.748655 1.10265
+4.77258 5.12626
+3.995 4.75289
+2.71467 4.26926
+6.21665 7.16233
+4.53883 5.75731
+4.37153 4.76125
+7.47635 8.75777
+4.82013 6.4699
+9.36015 9.58451
+4.43054 5.86752
+2.10934 2.34705
+3.00306 3.06539
+7.81957 8.44081
+1.97221 3.06916
+0.153393 0.503956
+5.05611 5.27536
+5.73277 5.89001
+3.64807 4.85582
+6.13416 6.1528
+3.85511 4.14045
+0.411725 1.36529
+2.26932 2.5447
+3.51164 4.93266
+2.50955 4.34201
+8.74663 9.39268
+6.2229 6.39303
+4.38059 4.91592
+7.29109 8.62318
+6.31801 6.99532
+9.75036 10.0514
+1.69019 2.41527
+4.20462 5.27729
+6.32944 6.94729
+3.5736 5.21496
+4.48316 4.61828
+1.76915 2.43263
+2.42026 4.09159
+2.08762 2.43074
+5.48161 6.02473
+5.51826 7.47042
+2.78026 3.1552
+7.89469 9.49867
+3.09084 4.54697
+7.20417 7.42408
+5.36528 5.74851
+1.86239 2.41988
+1.55994 2.85743
+4.40182 4.6638
+7.91619 9.01242
+3.08119 3.74714
+1.54108 2.08538
+3.61461 3.64631
+8.76851 9.66257
+-0.645571 1.3366
+-0.707083 1.21005
+7.19571 8.23405
+5.42865 5.89329
+2.12897 2.59222
+0.603102 0.930391
+3.75025 4.63181
+6.14867 6.4526
+7.30947 7.51461
+7.83183 8.79802
+6.06658 8.53246
+1.67895 2.50063
+1.14179 2.04478
+8.59583 8.90112
+-0.918885 1.30717
+0.182607 0.613593
+3.3335 3.77196
+0.783053 2.4368
+6.49331 7.60104
+2.85101 3.28637
+7.42273 7.66905
+3.96433 5.02573
+8.26262 8.40749
+9.24451 9.32783
+4.89036 7.07214
+6.45431 6.96815
+5.20701 6.58747
+7.40142 7.98615
+4.19768 4.9831
+3.32546 3.88811
+-0.471996 0.967855
+9.36172 9.63532
+1.96831 3.00632
+7.537 9.72671
+4.67746 4.81135
+3.10782 4.93642
+4.94809 6.54855
+4.16763 5.25628
+9.04992 10.316
+0.668935 0.680886
+4.02935 4.84503
+5.32499 5.76636
+5.32267 7.15457
+5.18886 5.23491
+4.37053 5.36801
+2.39955 2.74657
+8.19764 8.86645
+4.24706 5.69703
+1.87851 2.36988
+0.886553 1.05064
+7.27428 7.90224
+9.08417 9.44879
+3.11534 4.02019
+6.3284 7.39925
+4.96029 5.59076
+2.58248 3.11741
+7.76513 8.13505
+4.93613 6.51523
+0.565355 1.70512
+2.12366 3.0319
+5.32422 6.84146
+-0.135396 1.08591
+3.7901 4.86303
+4.49677 5.41847
+0.615748 0.834712
+0.64784 1.81859
+6.65505 7.13894
+-1.12744 1.61017
+0.550814 1.24316
+6.66639 6.89866
+6.19271 8.17468
+3.09382 3.78684
+4.41221 4.95889
+7.96091 8.48824
+8.32129 10.2917
+2.13168 2.19835
+1.4635 2.38989
+5.78102 6.42215
+1.70153 3.20616
+5.35068 5.71016
+4.27702 4.37203
+5.13888 6.04764
+4.79644 5.37927
+6.75314 7.66368
+6.04484 6.84064
+4.34947 4.45643
+9.08755 10.396
+0.88804 2.17441
+6.86788 7.88301
+7.94296 8.61664
+8.02387 8.79591
+1.79024 2.40559
+5.50404 6.01044
+7.76772 8.1932
+1.174 1.23693
+6.20571 6.9224
+4.20863 4.48702
+4.16743 4.39075
+-0.11965 0.120778
+7.01234 7.52619
+5.91415 6.46151
+5.28713 5.72354
+6.26981 6.31237
+0.643056 1.43864
+1.62562 1.99875
+0.544992 0.721386
+6.92522 7.09165
+-0.107048 1.12273
+0.644674 0.91244
+8.56904 9.01883
+2.23079 2.56598
+9.79034 10.1123
+8.13788 8.4131
+1.86753 1.95236
+2.97577 3.33272
+8.57454 10.0193
+1.20043 1.53408
+3.43515 3.86571
+5.79075 6.41293
+1.71558 2.52845
+2.39907 3.46652
+9.08289 9.91182
+0.859644 1.07475
+5.30144 5.42157
+-0.441497 0.890794
+3.55044 4.14918
+-0.340115 1.19239
+6.5534 6.76908
+0.836514 2.09196
+8.15747 8.96484
+4.80087 5.14227
+7.72071 8.88223
+5.11901 5.65559
+4.44996 4.63562
+-0.328404 1.3002
+0.90759 1.34556
+5.94839 6.35058
+1.76504 1.78344
+1.32585 2.08994
+7.18641 8.20945
+0.62657 1.06025
+6.28086 6.40975
+5.05525 5.16143
+5.82694 8.07685
+6.23677 7.55394
+-0.226265 0.661443
+1.42599 2.36325
+9.04445 9.92971
+-0.169286 0.185741
+8.34636 10.9042
+9.52494 10.0475
+8.39989 10.1377
+3.89956 4.70428
+3.98673 4.97185
+5.43008 5.67936
+7.76475 7.98591
+4.46895 4.73188
+0.584662 1.54474
+2.12622 2.79994
+1.40578 2.2461
+1.45171 2.02105
+2.05665 3.04611
+2.29818 2.68118
+1.77344 2.98698
+4.92092 5.55302
+7.18758 8.37525
+8.83407 10.0565
+2.91182 4.71932
+6.47293 6.74664
+2.49238 3.69749
+0.487234 0.541003
+4.703 4.92744
+9.8867 9.94918
+1.7713 1.9792
+5.68316 6.90242
+5.62485 6.70656
+7.24837 9.60197
+7.71147 7.76519
+5.22324 5.30661
+9.11258 9.28071
+8.2827 8.90115
+8.90865 9.42138
+7.47233 8.72308
+4.89712 5.65153
+4.12392 4.20239
+5.65325 6.55887
+8.65945 9.05845
+7.9165 8.36489
+1.4952 2.43991
+5.49665 5.80793
+1.67206 2.55302
+0.943544 1.58521
+0.47 0.860959
+6.47638 7.43101
+4.38434 5.97278
+8.12782 9.10574
+5.78143 6.6539
+9.23616 9.40513
+1.6845 2.00736
+6.56955 7.37847
+7.42993 9.04718
+-0.179632 0.450904
+8.65819 9.07724
+7.86567 8.80824
+1.85428 2.7755
+2.29222 2.65374
+4.03614 4.51729
+6.9326 7.07635
+5.50807 6.498
+6.17637 7.26432
+8.7688 9.16308
+7.65 8.88604
+3.74495 4.29991
+8.5671 10.5661
+7.41964 8.21083
+8.04302 9.78783
+0.269663 0.317601
+8.29804 9.40991
+3.90673 4.22298
+8.82384 10.9441
+7.27081 8.19548
+1.30184 1.98993
+8.13028 8.92385
+7.21224 7.35137
+5.02122 5.69666
+0.817219 0.974073
+-0.128067 0.589359
+2.12137 2.74019
+5.24657 5.44714
+1.55805 3.10332
+9.91031 10.0748
+7.13941 7.9143
+-0.183908 0.934079
+3.987 4.11053
+3.26678 3.67608
+9.75921 10.1121
+0.755391 1.06346
+7.28479 7.49982
+1.37522 3.80623
+0.944351 1.46531
+9.49278 10.2372
+8.28946 8.67597
+9.43537 9.58003
+7.73357 8.756
+2.64043 2.8687
+4.70448 6.99929
+3.82199 3.89343
+3.43825 4.10908
+4.42362 5.69086
+8.08536 8.60142
+8.43208 9.31272
+7.5889 8.39208
+7.90112 7.9734
+6.10381 6.80568
+8.76283 8.80151
+1.46728 2.53288
+1.68884 2.47102
+7.63343 8.29838
+9.18852 9.5558
+0.977174 1.4792
+4.7577 5.74873
+4.66433 6.37375
+-0.0952277 0.378184
+5.30929 6.07469
+4.98811 5.16146
+3.0744 5.59799
+0.944909 1.25222
+1.38561 2.51277
+3.77811 4.23459
+2.11604 3.32458
+0.920929 1.17187
+5.23496 6.34905
+6.9953 7.65745
+4.81803 4.89553
+4.69301 5.36777
+-0.208926 2.6111
+7.97334 9.83269
+-0.396673 0.779988
+5.91367 6.47682
+6.27249 6.83533
+5.58931 6.89679
+2.03394 2.67025
+-0.197752 0.383167
+4.69751 7.695
+4.5989 6.07875
+3.7285 4.27396
+7.9807 8.27693
+-0.00248985 1.30934
+2.68016 3.916
+0.881558 1.14567
+4.77066 5.05302
+0.552268 0.919467
+2.46862 2.65199
+1.4009 2.51701
+-0.171185 1.28252
+9.27842 9.90243
+3.51778 3.57728
+6.10071 6.92163
+9.05387 10.0894
+5.52995 5.89082
+8.40216 9.0272
+9.02911 10.1602
+4.85818 7.38829
+1.6762 2.32334
+6.45416 8.62337
+5.44592 6.0245
+3.45876 3.94274
+9.07144 9.16725
+6.51885 7.6363
+7.92593 9.01986
+0.270702 1.57395
+6.11245 6.70173
+1.37658 2.19068
+2.42668 3.43827
+7.98226 8.43306
+3.72269 4.74654
+8.16252 8.58873
+8.81456 9.5363
+-0.338386 0.864811
+1.90693 2.25796
+3.5305 4.17475
+6.07997 7.67393
+0.53347 1.68815
+7.31182 8.44458
+7.77462 8.33613
+3.79975 4.15552
+9.08822 9.9771
+-0.101698 1.05442
+8.44869 9.95724
+-0.0318759 0.532673
+8.09238 9.51356
+5.51514 6.60799
+8.66528 10.3297
+8.25556 9.97893
+-0.220428 0.247563
+0.00453913 0.540444
+1.53599 1.61735
+3.77213 5.06896
+2.65332 2.75864
+6.05811 6.43112
+0.302534 0.803651
+0.393766 1.11611
+9.31035 9.62387
+0.568281 1.7765
+-0.721118 1.27387
+1.83573 2.62578
+1.39953 1.91158
+6.51883 8.52813
+2.60154 2.95054
+6.58322 9.37818
+5.61178 5.67661
+9.56511 10.2767
+9.09174 10.0697
+2.55846 3.39943
+8.38146 9.06138
+7.97019 8.18074
+6.1426 6.42787
+1.15876 1.55463
+2.93483 2.96376
+7.23883 7.29158
+9.64606 10.0674
+4.89825 6.70366
+7.35635 7.96328
+2.2702 2.36797
+-0.0464389 0.395244
+1.14883 2.10411
+4.7309 5.10593
+6.07946 7.37741
+5.50202 6.28822
+3.66574 3.96271
+1.9944 2.3862
+7.58159 7.59275
+1.98712 3.30587
+2.10317 2.89161
+9.66889 9.97287
+1.57327 2.15421
+8.14125 8.30983
+3.77185 4.12658
+8.51624 9.51485
+9.25211 9.722
+2.06312 3.0453
+9.34954 9.91254
+7.31475 8.89322
+7.85044 9.20242
+0.226243 1.28465
+0.0260868 0.575177
+9.40743 10.1655
+4.04324 5.05341
+1.02626 1.48332
+3.33074 3.72025
+9.2005 9.61387
+6.68317 7.14109
+6.82083 8.6312
+4.34816 4.50329
+-0.48874 0.975608
+4.26651 5.68526
+4.89731 5.57569
+2.68984 3.5661
+0.499714 1.01055
+9.01902 9.36142
+3.22067 4.59054
+0.65132 0.860512
+1.6791 2.36642
+8.28222 8.7579
+2.84349 3.24134
+5.64949 6.09512
+3.44026 4.43245
+2.60889 2.6248
+-0.00849234 0.984046
+7.5745 8.65675
+2.07464 2.6488
+2.59747 2.65772
+6.36828 7.42208
+3.83342 4.50127
+5.41167 5.7011
+6.53606 7.53796
+8.21975 9.56176
+1.90113 3.08684
+7.74402 7.82057
+4.26515 5.11327
+5.42743 5.59215
+8.69709 10.0912
+2.87019 3.94798
+9.27013 9.54627
+9.30568 10.2136
+9.02985 9.62917
+3.7589 4.59642
+6.59359 6.86939
+3.26574 4.83488
+5.14545 7.70086
+7.43163 8.05103
+5.72972 5.74457
+0.490113 1.31151
+7.018 7.42627
+6.22976 6.27589
+9.07732 9.30419
+4.3917 4.42663
+8.50298 10.3728
+6.18671 7.53619
+3.51053 4.097
+2.17172 3.56595
+6.08046 6.23691
+9.07361 10.9129
+3.95757 4.11333
+-0.136516 0.78995
+1.34923 3.47002
+0.0562579 0.686502
+9.10943 10.2731
+5.80174 6.16971
+7.86011 7.87649
+5.34384 5.51645
+6.32167 6.76387
+7.70566 8.28038
+-0.319903 0.867682
+-0.372564 0.570014
+8.83696 10.8439
+7.78947 9.38211
+9.25815 10.1092
+5.59721 5.67148
+0.234719 2.42489
+1.06314 2.87416
+1.75391 2.69799
+5.4536 6.66068
+1.35696 1.87102
+2.17418 3.1459
+7.61817 7.98287
+2.92194 3.18393
+7.68243 7.83261
+9.33163 10.5531
+1.14227 2.50228
+5.47911 5.70857
+9.88123 10.0758
+8.81249 8.83145
+6.02747 6.43183
+4.81158 5.79178
+6.65666 7.5047
+4.89085 5.56569
+0.935564 1.71547
+-0.0452879 1.16222
+4.03524 4.6206
+5.88518 6.02658
+2.06227 2.9581
+0.207246 2.2404
+2.81757 3.70318
+0.704257 1.98403
+2.38486 3.30039
+3.57073 4.78358
+1.53721 3.00546
+8.37852 8.39527
+1.54619 1.59789
+1.00447 1.40778
+5.31977 6.43861
+3.82175 3.83209
+3.37738 4.44002
+5.46644 5.97254
+3.88191 5.00688
+4.88046 5.39031
+4.15092 4.93679
+1.75182 2.07399
+1.16434 1.76541
+3.37032 4.29676
+3.1685 3.98533
+1.28787 1.59775
+5.86338 6.17993
+3.8645 4.25757
+1.83947 2.48092
+0.184795 0.586724
+3.13752 3.76447
+2.14264 3.03248
+5.78182 5.9029
+2.47969 3.8088
+2.59206 3.08362
+-0.354871 1.03627
+7.94041 8.05947
+9.48014 9.93817
+0.992727 1.48877
+6.1525 7.51387
+5.00071 6.15665
+7.42074 8.27606
+9.6539 9.83042
+1.08984 1.46387
+5.46282 6.95907
+4.74506 5.13968
+0.357565 1.91464
+9.07652 9.65474
+0.201095 0.646211
+7.04306 7.69131
+1.17728 1.2237
+5.92183 6.29165
+-0.41356 0.491524
+8.38339 10.4509
+8.08648 8.11344
+3.32474 3.39522
+5.762 5.83732
+2.26778 2.55548
+4.86127 5.92868
+3.75949 5.16608
+4.83431 5.88632
+2.65122 3.31344
+9.30295 9.38948
+6.03391 6.32749
+5.12035 5.59414
+5.68785 5.979
+7.89553 9.15202
+2.54445 2.74715
+2.36337 2.49709
+4.71661 6.12198
+3.72447 4.68735
+1.0541 1.53187
+3.45178 4.80929
+7.90773 8.26212
+8.1816 9.18627
+0.322569 0.871041
+7.24823 7.32699
+1.7003 2.78087
+9.70927 10.2515
+3.75922 4.01081
+1.06949 1.40253
+1.21774 1.4734
+6.02123 6.32973
+2.76396 3.09484
+2.80358 3.00026
+5.25666 6.31369
+4.71714 5.30669
+4.37906 4.50557
+7.40824 7.44969
+7.89081 9.18263
+8.8136 10.3074
+9.1757 9.37261
+7.1938 8.51137
+0.580081 2.32678
+2.02497 2.95434
+6.01446 6.78851
+8.54365 9.21454
+3.03567 3.27089
+0.650657 1.5716
+5.05518 6.36521
+1.64301 2.76399
+4.15989 4.34183
+3.72791 3.98156
+3.91372 4.06182
+3.04507 3.88871
+3.82596 4.25694
+3.36492 5.00402
+6.50019 8.17974
+3.54621 3.81009
+0.546921 2.09811
+5.1789 5.9826
+4.98983 6.31221
+2.47557 2.63878
+4.64568 4.81101
+9.1462 9.21888
+7.37417 7.5276
+2.21604 2.50113
+8.29804 10.2722
+5.1282 6.06353
+8.58281 10.1589
+6.47084 7.04085
+6.27806 6.72674
+7.11554 7.85687
+6.01085 6.02773
+5.46694 7.52872
+9.51078 9.85387
+3.36627 4.06552
+2.96126 5.12635
+3.09314 3.40691
+5.05456 6.62548
+7.56469 8.97053
+1.33727 2.87482
+5.6572 6.28169
+0.120924 0.296475
+7.09139 7.41322
+2.10076 3.28507
+8.74998 10.1094
+7.45072 9.50796
+7.75294 8.59218
+0.740374 2.44897
+7.63001 8.91562
+4.30743 4.81716
+6.40813 7.29159
+6.30213 7.35987
+6.44392 7.32584
+8.41155 9.4466
+-0.377892 0.689618
+5.88857 6.70983
+6.67779 7.70668
+4.831 5.17464
+2.62342 3.82764
+8.57453 9.29283
+7.32648 7.96725
+9.32937 9.9726
+7.30774 7.65754
+3.97456 4.59876
+5.73092 7.20962
+1.958 2.10551
+3.2115 3.81482
+5.76497 7.42425
+2.77219 3.29692
+4.95297 5.14843
+5.96367 6.3619
+2.49872 4.61736
+8.21817 8.23425
+2.13615 2.61877
+7.84121 9.55011
+8.63416 10.2773
+5.61084 6.32053
+4.86002 5.79944
+9.07858 10.688
+5.95829 6.42504
+3.3289 4.17849
+3.27989 4.91625
+8.28046 9.23021
+2.18691 2.6072
+1.3962 1.75013
+2.13627 3.73117
+2.11128 2.57303
+7.22357 7.50136
+7.43862 8.03136
+2.80578 3.02886
+0.176906 0.761142
+5.99314 6.6075
+1.90398 3.3925
+5.48264 6.73579
+7.18447 7.49811
+3.24434 5.05219
+5.53263 6.04178
+1.71172 2.45682
+0.297266 0.481632
+2.62582 3.36142
+0.243865 1.49321
+8.86142 10.169
+3.93193 4.39404
+4.98182 5.15675
+7.99209 9.73159
+8.18033 9.45596
+4.25235 4.41546
+1.59603 2.15581
+6.47724 8.69649
+5.24138 7.93131
+3.4267 3.49003
+0.0439912 0.316407
+1.99983 3.87206
+-0.103124 0.568771
+4.62451 4.80117
+2.89065 4.16315
+5.82817 7.43232
+6.89255 7.82189
+5.71037 6.81241
+4.34551 5.24905
+7.33931 7.3741
+-0.855192 1.27129
+8.94842 9.10813
+9.19877 9.68412
+6.42658 7.77968
+-0.0669636 0.430107
+6.40366 7.40701
+8.91175 9.24059
+3.21388 4.21676
+3.38557 3.79509
+4.24411 4.97032
+7.259 7.34896
+2.55995 3.18361
+8.77438 10.5503
+8.18685 9.79378
+2.78397 4.4563
+8.6891 9.38337
+6.51749 7.59374
+2.52346 3.46101
+8.40526 9.35647
+5.26942 6.33572
+4.15094 5.00668
+0.471666 1.53842
+7.63441 7.96745
+2.39974 3.7814
+2.71989 3.02994
+8.12085 8.33493
+6.19474 6.84381
+6.6133 8.31417
+5.1758 6.94983
+8.66241 8.79095
+6.10878 8.22083
+8.81883 9.08031
+8.28969 8.45176
+7.19851 8.37836
+1.5722 1.6836
+4.57814 6.95063
+7.66128 8.10631
+0.742483 3.08515
+1.15026 1.98074
+3.90133 4.12003
+7.61145 7.73433
+8.32187 10.2933
+0.407205 1.08089
+1.26545 2.60796
+2.70646 3.92753
+-0.196551 1.83536
+-0.413063 1.23838
+0.264484 0.556192
+6.30158 6.96097
+-0.66246 0.809062
+9.34243 10.3521
+8.70093 8.79679
+0.745195 1.92911
+9.12892 9.68193
+7.98533 8.30538
+6.23671 7.04296
+6.5603 6.95483
+7.08133 7.10563
+2.15769 2.41541
+8.24752 8.73978
+5.27693 5.61904
+0.317419 1.48404
+3.48029 5.46979
+7.38901 8.56554
+1.06587 1.62384
+5.54926 6.04924
+4.46577 6.48749
+5.349 6.15584
+8.04249 8.17359
+3.37651 3.56179
+3.64706 5.57403
+4.49844 5.26404
+3.93361 4.92128
+5.82184 6.29881
+0.984273 1.70318
+1.12773 1.80726
+3.11675 3.17451
+5.07913 5.46291
+0.615591 1.81465
+1.08948 2.77847
+7.97412 8.07365
+5.05207 5.82497
+0.0407709 0.558817
+6.11142 6.48205
+3.4461 3.57315
+8.6547 8.80553
+8.18965 9.64194
+5.55564 7.9703
+4.01776 4.16989
+2.4668 2.82018
+0.911925 2.56662
+4.68814 5.03614
+7.4899 8.49036
+8.40912 9.14001
+2.32725 3.16377
+0.0723972 1.42049
+6.6441 6.6953
+3.65586 4.38222
+6.35844 6.57833
+4.44435 5.73077
+1.81779 2.57257
+4.17692 4.59722
+8.54682 9.38058
+1.61135 1.81629
+3.91926 5.00536
+9.78302 9.85143
+1.25632 3.10345
+5.53191 6.08174
+-0.231868 0.676811
+1.03054 2.58105
+0.0402172 0.690608
+0.974571 1.25234
+5.22227 5.92302
+8.05843 10.1439
+8.48938 8.50755
+0.0965782 1.24008
+6.53821 7.47765
+0.189991 0.475204
+8.48963 8.54995
+9.68363 10.0534
+1.49381 2.71302
+6.75552 7.41283
+7.91075 9.15302
+0.520922 1.40928
+1.17957 2.11221
+4.00122 4.40686
+7.32467 7.9273
+6.39126 7.90248
+6.50534 8.19308
+7.19888 7.22234
+-0.136797 0.731889
+0.870318 1.25614
+2.07126 2.28297
+6.10995 7.60408
+3.02174 3.90102
+-0.657541 1.57517
+0.993121 2.66255
+7.27232 7.50832
+2.16079 3.75428
+9.27397 9.75098
+8.36865 8.5545
+4.56641 4.97607
+3.60746 4.34616
+6.31304 7.70611
+2.91442 3.7602
+4.85814 6.70407
+4.45876 5.67661
+8.06749 8.82229
+2.02826 3.59873
+1.92922 2.50088
+2.63867 3.48117
+2.17665 2.54826
+8.62674 9.10675
+0.96648 1.47671
+8.86336 9.53978
+5.52747 5.80829
+0.77099 3.06106
+2.2307 2.53877
+6.29165 6.63018
+4.44717 5.04651
+3.80894 4.63054
+7.47519 7.87072
+0.933668 1.9949
+-0.712945 1.21558
+7.30206 8.38376
+5.80281 7.00022
+7.96578 8.6558
+0.24063 0.706466
+4.41893 4.58472
+4.0132 4.92768
+5.29626 6.09397
+3.43602 4.00728
+0.780945 1.66499
+8.83861 9.15168
+9.06411 9.75544
+3.45618 3.62529
+9.51413 10.0148
+0.842106 1.75695
+1.06485 1.44192
+6.89987 8.05314
+7.89154 8.73313
+8.71675 9.79526
+2.40574 2.58062
+8.55952 8.78132
+5.61716 6.06282
+3.71059 3.77929
+2.50187 2.99137
+6.92852 7.23184
+7.00442 7.93869
+8.82232 8.8756
+4.10389 4.53702
+7.6907 7.77577
+9.31554 10.2674
+8.51082 8.70339
+4.83332 6.70311
+4.6904 5.42892
+4.71407 5.67695
+4.12047 4.44089
+7.51845 8.26858
+1.31781 1.9397
+6.26854 8.02093
+6.92072 7.77774
+0.438156 2.18909
+0.898035 1.72074
+3.81121 3.8643
+1.01676 1.34308
+9.28445 9.42801
+2.43133 4.43516
+4.33664 5.9904
+2.19178 2.37244
+1.5457 1.79785
+2.23781 2.6481
+6.8365 6.87073
+0.82568 2.74775
+9.59404 9.89262
+8.48394 9.15107
+6.16085 6.42224
+3.5667 5.97689
+4.16931 7.41638
+6.95636 7.0982
+6.87987 7.09233
+3.12619 3.14821
+7.76636 7.98459
+4.60371 5.59136
+3.92189 4.88302
+6.90694 8.19858
+3.85132 5.19015
+8.05045 8.32483
+4.8338 6.05032
+1.93576 3.18873
+2.4182 3.55039
+5.56905 5.8172
+4.18436 6.01283
+3.04944 3.85936
+7.83585 8.95233
+1.76281 3.0558
+3.44143 3.6317
+8.69887 9.40371
+5.67867 5.73236
+0.859809 1.76578
+6.92284 7.13601
+8.27495 9.13414
+5.44385 6.42599
+-0.941603 1.8215
+7.88161 8.79447
+0.965018 1.17732
+5.56442 5.79546
+8.79032 10.9909
+5.28632 5.79175
+4.80998 6.07002
+8.4021 9.81107
+3.16415 5.08682
+-0.212163 1.2052
+5.683 5.83625
+0.738527 1.31842
+2.03835 2.52756
+7.4543 8.91243
+-0.568869 1.44659
+3.58855 4.42371
+6.33058 7.18779
+-0.645579 1.94313
+2.18149 2.24157
+3.80281 4.67323
+5.14628 5.97452
+2.55239 3.45685
+3.56586 6.23355
+6.74752 7.19693
+8.99006 10.5533
+0.259833 0.99882
+8.9005 9.75401
+7.28058 8.41075
+4.57803 5.70104
+9.82823 9.90251
+6.94375 7.42033
+7.8878 8.81951
+5.01137 5.52693
+2.25503 2.28817
+6.65772 7.11107
+0.647512 2.04416
+4.34796 4.5706
+4.5395 4.91529
+2.65423 3.49531
+7.97384 8.78377
+5.53873 5.76806
+9.78641 10.1351
+6.01664 6.44407
+3.47406 3.75187
+4.59421 4.81804
+1.20641 2.00488
+2.48904 2.80768
+5.77372 6.10692
+2.49363 3.24499
+1.8483 2.62965
+2.28556 3.22209
+3.17819 4.51426
+3.5548 4.23276
+3.25239 3.57895
+0.613317 2.44862
+3.82234 4.78976
+4.72809 6.46528
+9.54107 10.4426
+9.20052 10.1921
+8.24152 9.89817
+5.17594 6.19462
+7.83364 8.17082
+5.67992 6.36464
+8.9943 10.1426
+2.20598 3.0081
+3.38199 5.16464
+0.599928 1.35804
+0.841211 1.60761
+5.29338 5.89919
+1.11061 2.29219
+3.41512 3.95907
+7.9375 10.0203
+8.73852 9.66917
+9.95092 10.0162
+4.02544 4.30219
+4.1461 5.37827
+1.08089 1.59196
+7.87513 8.21121
+7.85673 8.0973
+0.96237 1.56519
+2.45532 2.61678
+6.1829 6.23637
+0.135135 0.613518
+3.18324 3.78587
+5.64796 7.20194
+0.861169 1.71661
+9.66005 10.1169
+0.880774 1.12452
+1.29389 1.57658
+5.60299 5.89759
+5.53642 7.07306
+0.540268 1.31836
+6.60244 7.98105
+4.21877 4.57159
+3.15367 4.75356
+7.81055 8.40378
+8.00942 8.10861
+5.78071 6.35646
+3.15993 4.99293
+2.36526 2.78444
+8.1361 8.76071
+1.40066 2.96474
+-0.17906 1.05877
+5.94933 6.6808
+3.43187 4.05553
+3.02912 4.68942
+2.1506 2.29413
+2.3516 2.38305
+2.87496 3.0851
+6.71294 8.99858
+3.76177 5.52545
+1.58417 2.57063
+2.74646 4.27445
+5.48407 6.60696
+-0.133321 0.716559
+3.53886 3.58196
+2.28729 2.40198
+-0.209646 1.21263
+6.62795 7.32318
+0.714781 0.880466
+2.96831 4.05932
+6.00935 6.38896
+5.39798 6.11983
+8.47679 9.70468
+1.4339 1.74845
+2.35223 4.75732
+4.31357 4.9642
+5.26493 7.70538
+7.91888 8.94851
+6.51439 7.69469
+7.69015 7.85411
+3.94274 5.15373
+6.25852 7.32402
+2.73567 3.43236
+7.09564 8.42242
+2.55932 4.29066
+1.79489 1.84559
+8.86427 9.35669
+7.23854 8.27062
+7.8488 7.98307
+0.710888 2.22424
+0.0413624 1.10578
+1.54302 1.97696
+1.64924 3.01081
+8.15535 10.2397
+0.305662 1.63835
+8.20375 9.03123
+1.66617 2.08748
+4.01691 4.88505
+3.32856 4.10709
+5.15342 5.44616
+5.80751 7.38872
+0.485341 1.07648
+3.98267 4.20211
+6.96867 9.37655
+9.48055 9.85181
+8.39277 9.50424
+2.47589 2.79873
+3.48046 5.37417
+4.98279 6.01686
+1.29845 2.31199
+-0.204348 2.50289
+0.23488 1.00378
+6.26899 7.14823
+4.62903 5.49672
+0.199052 0.822066
+6.97297 7.69083
+4.23783 4.65198
+0.530497 0.839143
+6.61355 8.22831
+-0.0103941 2.16307
+0.148601 1.20044
+0.314438 0.67632
+3.43415 4.06208
+1.95498 3.25728
+4.23094 4.84942
+9.5493 10.0057
+2.64964 3.52073
+8.21388 9.1622
+-0.112496 0.47622
+7.97551 8.19546
+2.16155 2.6289
+0.38442 1.46032
+3.85675 3.91557
+1.00605 1.42016
+2.4853 3.53086
+-0.0765625 0.504366
+8.11302 8.46334
+7.18135 7.80218
+6.46466 8.12336
+3.75968 5.33954
+9.08789 9.73677
+6.47459 7.49454
+0.90148 1.47681
+3.44733 3.54163
+7.32968 7.909
+1.30055 2.75407
+8.78735 8.92461
+8.21764 8.59414
+0.888162 1.08404
+5.65195 6.26421
+6.23881 7.33284
+8.69731 9.88088
+3.55695 5.36909
+6.49248 7.02755
+2.31407 3.01952
+5.7803 6.1286
+6.30415 8.01577
+7.73864 8.27496
+0.741078 1.69184
+4.94671 5.68254
+3.28226 5.02894
+1.97446 2.60413
+3.26555 5.54754
+2.56179 3.41897
+3.20058 3.32486
+6.96915 7.69055
+6.89302 7.39856
+1.9659 2.61404
+1.31107 1.36867
+6.23573 6.33677
+0.17616 0.517772
+8.55548 8.79155
+9.61632 10.2651
+2.20331 2.34017
+6.93195 7.42939
+1.25398 2.23119
+1.06577 1.41048
+5.99156 7.09818
+3.34821 4.1271
+4.61618 6.90956
+2.30853 3.52875
+0.0518063 0.113732
+5.38513 6.09734
+1.73468 2.64838
+7.24679 7.63999
+4.18175 5.06728
+7.09602 7.13414
+9.06191 9.11605
+6.2309 7.67675
+3.96576 4.63237
+-0.552645 1.37948
+5.76964 6.89903
+4.9272 7.5832
+4.59003 6.49719
+4.12281 6.9664
+5.12256 5.58387
+1.78859 2.76954
+4.36808 5.05712
+8.41165 9.54483
+8.11513 8.43405
+8.42016 9.52715
+8.50598 9.34564
+1.80028 1.83558
+8.08502 8.43726
+6.96667 8.12021
+5.4726 6.26772
+7.1141 9.29989
+7.83324 8.1744
+8.74368 9.16491
+2.5515 3.01706
+5.92375 6.44495
+7.45628 8.41582
+9.04829 9.71503
+4.58856 4.75356
+7.26064 7.32462
+1.81695 2.40529
+6.88067 7.95052
+1.91408 2.02663
+1.32005 1.45025
+7.47733 8.42004
+4.99403 5.85297
+0.95744 2.28875
+-0.089503 0.536888
+1.09978 2.48865
+1.9959 3.098
+0.376116 0.448329
+3.98908 6.49684
+9.10178 9.51012
+5.41394 7.31529
+6.60708 7.59071
+4.38466 5.76203
+1.84355 2.60304
+0.658705 0.933522
+6.98725 7.05509
+5.60064 6.98804
+1.0603 1.1521
+4.06192 4.46156
+0.351018 1.00807
+9.17113 10.2192
+6.71641 6.74659
+8.85392 10.5272
+4.50816 5.22908
+1.13934 2.43196
+3.3746 3.57576
+7.62137 9.3234
+6.93482 7.16058
+5.66506 6.44324
+5.13364 5.86068
+1.97257 2.03803
+2.69746 2.73067
+0.989591 1.38342
+4.18819 4.69676
+4.31608 5.12265
+2.79556 4.87763
+3.55387 5.34421
+-0.112466 0.585639
+2.43156 2.70221
+5.88314 7.08322
+-0.450332 1.29858
+1.22202 2.46588
+6.74433 7.32925
+5.14596 5.37435
+6.76754 7.41799
+1.48762 2.45958
+4.57241 5.3382
+4.17465 5.02296
+6.48856 7.5576
+0.383566 0.823394
+8.50875 9.08734
+1.79393 2.24548
+5.06812 6.49508
+3.55364 3.61502
+5.58973 6.58885
+0.631097 2.10041
+0.323995 0.941782
+8.8798 10.1036
+6.37114 6.88202
+6.9393 7.83288
+3.09503 3.41759
+3.65269 3.70305
+5.61676 5.8924
+1.90199 2.38446
+4.23683 5.35609
+9.18649 9.88878
+8.28736 8.37734
+3.04805 5.03593
+8.02926 8.38975
+3.58161 4.01331
+3.20923 3.48207
+6.85118 8.78281
+5.06518 7.49843
+1.57456 3.05552
+3.0154 4.23782
+2.83557 3.24854
+1.29358 1.89328
+1.42911 2.72661
+3.17067 3.96651
+0.353554 1.48472
+3.46227 4.25611
+6.17639 7.24657
+5.60878 7.13562
+4.11129 5.52476
+9.17596 9.4123
+3.08339 3.65766
+4.083 4.63013
+0.628465 1.92141
+7.81989 8.39386
+3.35589 3.87531
+5.35974 5.41743
+4.48321 4.64376
+0.888177 1.37343
+8.49061 8.50063
+5.09078 6.01801
+9.65314 9.99342
+1.60502 3.0115
+1.37928 2.77642
+6.60322 8.94046
+6.74573 7.45157
+-0.291156 0.325249
+6.1518 6.66827
+5.38199 6.46959
+3.50128 5.22692
+1.31774 1.70159
+0.550546 1.48845
+7.51187 7.85496
+4.2014 5.23193
+3.88255 3.97209
+2.60555 2.84958
+5.15706 6.00036
+2.99284 3.27867
+0.395077 0.768383
+3.30863 3.60594
+0.0325231 0.379208
+6.55183 7.33346
+1.44554 2.95846
+0.245421 1.29751
+3.98808 5.41226
+5.90242 6.266
+4.11097 5.02597
+0.134753 0.929866
+0.609287 1.16311
+0.231832 1.06825
+6.22093 7.05584
+9.18429 9.80637
+0.544139 1.30185
+1.69143 2.90306
+6.75109 8.67893
+5.49641 6.76104
+8.91712 10.0271
+7.09126 8.12404
+8.82958 10.2847
+0.569249 0.69421
+3.59288 5.8053
+9.25068 9.4226
+8.51607 9.26259
+5.00906 5.17068
+1.83467 2.99102
+7.32006 7.64842
+0.405532 0.603773
+6.41882 6.60495
+0.721592 1.44375
+2.78393 4.46732
+7.14806 7.16117
+5.12868 5.27918
+2.74138 3.1737
+2.95394 4.26547
+7.82827 8.21028
+9.06982 10.7279
+0.108376 0.290131
+6.34574 6.97141
+4.39022 5.50801
+7.98339 8.79674
+3.80026 4.71831
+5.43594 5.66916
+1.6743 2.61476
+4.29763 5.47402
+7.95714 8.69311
+3.4152 3.7387
+8.01715 8.8941
+6.53723 7.44437
+-0.0222108 1.04503
+4.96242 6.01648
+1.70885 1.75511
+7.62434 8.53526
+6.82863 7.39782
+8.19967 9.22283
+1.99719 2.11323
+-0.0124079 0.240201
+4.48577 4.87935
+2.586 4.41666
+6.37848 6.67092
+4.39606 5.67983
+9.39761 10.1095
+7.10866 7.15796
+3.91993 4.52129
+5.10552 5.36997
+7.08963 8.32694
+9.12669 9.58509
+9.26894 10.1974
+0.737413 1.26523
+2.77833 5.28516
+3.19039 4.36857
+4.32954 5.45614
+4.98069 5.49295
+7.54745 8.76142
+5.70425 7.05749
+-0.250513 0.36578
+4.76997 5.89607
+2.03075 2.32111
+6.54141 6.55896
+4.19799 4.45091
+8.7796 9.11915
+5.75014 6.05646
+5.36885 5.42775
+-0.00433056 0.865304
+3.65169 5.4751
+0.23625 0.872926
+0.0386337 0.792214
+4.40245 4.56789
+6.44567 7.42162
+5.51394 5.98005
+3.50146 3.83147
+8.47573 9.85647
+7.91378 8.47044
+9.44297 9.90241
+5.962 6.68782
+4.49034 4.86927
+2.17774 3.47061
+8.80614 9.26595
+7.92096 8.13838
+7.80209 7.93317
+7.78915 8.29735
+6.98111 7.162
+0.491059 1.14901
+8.97965 9.38792
+3.30207 3.53207
+3.17127 4.43581
+3.97564 4.43746
+6.79008 7.17877
+5.61669 7.51263
+4.0853 5.38748
+4.42181 6.32041
+8.08599 8.9095
+2.46938 2.55499
+1.20692 2.06351
+1.98995 2.35216
+8.17116 9.17158
+7.53414 8.37086
+3.7744 4.46197
+3.29464 4.38
+7.33246 7.81959
+4.89572 6.2831
+-0.152496 1.56427
+0.636242 0.938556
+5.62066 6.06096
+9.42994 10.473
+8.95446 9.46311
+0.277891 2.80311
+6.92968 7.29845
+2.75677 3.02975
+2.2859 2.51308
+2.96683 3.03857
+6.44584 6.49518
+8.33607 8.60407
+7.20303 7.7616
+7.07205 7.46849
+2.35677 2.7904
+5.86404 6.12906
+2.42371 4.62735
+3.27237 4.11016
+0.0668578 0.644021
+6.77371 7.45187
+3.66596 5.21874
+0.718034 0.755944
+6.06162 7.05
+6.91473 7.09753
+5.0749 6.08914
+0.759874 1.11978
+8.54591 9.64997
+3.97266 4.70679
+1.51133 3.17164
+7.21772 7.69877
+8.00719 8.37052
+0.284003 0.38001
+4.08842 4.16227
+5.04461 5.79973
+4.60604 5.31979
+2.2461 2.28157
+6.25631 7.69766
+9.12404 9.67586
+7.73134 8.98923
+1.0513 1.46408
+9.17225 9.42567
+3.66795 4.29168
+8.84403 9.28497
+9.93613 9.94729
+3.16615 5.09374
+-0.149675 1.43397
+2.30855 2.48392
+0.843172 0.951801
+8.32945 8.8506
+8.97012 9.94697
+2.8559 2.92359
+-0.321715 0.89173
+7.48565 7.71775
+1.44301 1.47795
+6.6649 6.76187
+9.10721 9.93599
+1.82207 3.03935
+3.61893 4.5306
+2.53746 3.81243
+0.424528 1.29026
+9.51874 9.68841
+7.94954 8.14396
+7.49282 7.52655
+3.04604 3.69656
+9.45461 9.47212
+7.69209 8.16412
+0.607135 1.16564
+0.150291 0.450632
+3.28914 4.01085
+5.45597 6.02921
+8.16157 8.36605
+9.76573 10.2325
+0.151994 1.07074
+1.3748 1.68548
+2.29854 2.63593
+7.92162 8.68895
+1.28795 1.61949
+7.85469 8.26683
+4.32268 6.33174
+6.13964 6.3596
+1.25714 1.88002
+2.05006 2.28294
+8.31864 8.34151
+9.0613 10.0715
+-0.041568 0.25139
+6.79984 6.81686
+5.10674 5.70583
+5.92937 7.01652
+7.57649 8.05834
+0.937157 2.25362
+7.48567 7.59746
+2.33403 2.88942
+7.3805 7.73421
+5.586 5.75327
+0.828854 2.12988
+8.22741 8.59954
+9.69214 10.1207
+6.55359 7.08114
+1.85342 2.634
+6.08756 7.28893
+6.34993 6.93474
+8.93548 10.1463
+5.49445 5.8338
+1.47951 2.14646
+2.62089 2.75888
+2.35352 3.10539
+4.98774 5.28931
+4.92239 5.40697
+7.52674 8.78948
+4.52451 4.83819
+5.45854 5.75838
+5.51553 6.3901
+4.82905 6.44407
+6.20655 6.4483
+5.68116 7.06109
+7.97301 9.15827
+8.8854 9.91623
+9.60198 10.2449
+1.93322 2.67684
+0.441852 1.87646
+5.19981 5.71385
+2.54799 2.5809
+4.34591 5.02655
+7.54678 8.75202
+6.1496 7.19033
+1.13658 1.30756
+1.38724 2.08252
+2.13624 2.30419
+8.49196 9.41369
+-0.21387 0.685093
+2.47875 4.19982
+3.69093 4.00543
+5.70643 6.50331
+7.07443 8.83588
+6.85756 7.68937
+1.69241 1.83242
+0.140565 0.502156
+2.15007 3.23784
+4.97403 5.69757
+5.08315 6.25554
+3.45026 4.01942
+3.37482 4.73009
+7.4154 8.34013
+6.95399 7.59768
+8.07995 9.86199
+8.43993 8.88645
+7.66844 8.12742
+-0.0200687 0.435846
+4.47984 5.9416
+7.66804 10.0801
+4.14578 5.98717
+0.256533 0.89254
+6.43216 7.4474
+3.60295 4.79419
+3.90129 4.29812
+5.30606 5.58674
+9.11552 9.79229
+5.60702 5.85299
+2.39514 2.64865
+4.51164 4.60227
+6.31154 6.77441
+3.12239 3.13505
+7.50962 7.89478
+3.90928 4.88831
+9.64625 10.0629
+5.9465 6.02877
+2.08554 2.10477
+0.087547 0.34748
+2.34295 2.53703
+6.846 7.3043
+1.37869 2.11446
+4.03371 4.26278
+5.38623 6.40917
+4.75848 6.25417
+6.03478 6.15991
+-0.585543 1.2091
+2.52824 3.57712
+3.95687 5.40914
+4.42955 4.71909
+2.9328 3.98848
+5.40471 6.97056
+7.64789 9.30279
+6.19595 7.07492
+8.27328 9.41596
+8.29855 9.13665
+2.54035 3.10488
+8.25038 8.87903
+5.66599 5.88701
+7.40067 8.71195
+6.63762 6.67694
+-0.0163105 1.72666
+4.77743 6.60356
+0.567991 1.14977
+5.96841 6.42987
+3.02266 4.58424
+6.72121 7.35492
+7.51477 8.65223
+8.55055 8.81014
+5.59759 5.82816
+0.218944 1.82542
+1.03197 2.36652
+7.84302 9.1594
+2.11542 4.7332
+6.37618 6.92981
+1.34963 1.59353
+0.516281 1.08859
+-0.0151152 0.0624718
+6.45603 7.23063
+4.76865 5.46588
+7.5893 8.42021
+1.04534 1.99034
+2.62638 3.04997
+5.00254 5.98512
+7.7588 9.44547
+6.82186 7.34321
+8.7629 9.82272
+-0.309554 0.54858
+4.19843 4.92981
+5.62327 6.80846
+6.40599 7.6073
+6.1547 6.53898
+3.93941 4.44985
+3.28342 3.72778
+7.17256 7.23035
+2.31269 3.05437
+7.99132 8.20926
+7.19338 8.08381
+7.29139 7.96441
+4.25818 4.4208
+0.802894 0.81938
+0.990115 1.04066
+0.443898 0.490936
+3.44459 3.95977
+9.07812 10.3145
+7.86709 8.26338
+-0.230114 0.390748
+7.50905 8.49199
+-0.715424 1.97535
+2.92248 3.12903
+5.44209 6.62431
+0.991914 1.99037
+5.92275 6.7724
+9.10818 9.2797
+4.46434 4.83462
+4.21122 5.48353
+1.8721 3.40764
+7.04713 7.60526
+3.76581 4.76317
+7.00667 7.36475
+6.87244 7.39431
+7.6349 8.46695
+-0.0728922 0.351194
+5.02925 5.6834
+1.45779 2.83702
+0.339175 1.46574
+6.40414 6.9165
+6.30401 6.91007
+5.6392 5.78172
+5.63288 6.44639
+7.45285 8.89031
+4.09741 4.57929
+8.76728 9.06448
+1.62324 1.84552
+5.72509 6.54553
+8.65273 10.3652
+7.3362 7.8953
+8.96224 10.3113
+0.217316 1.7827
+1.28523 2.90127
+8.78746 9.11602
+8.6075 9.9606
+-0.647788 0.779976
+4.9702 5.47602
+4.15553 4.57497
+1.08174 2.80993
+4.01011 4.8145
+6.79603 7.2317
+4.3711 4.43566
+9.65087 9.79922
+8.19407 9.21784
+3.04008 4.94094
+6.0883 7.23919
+6.88708 6.97172
+0.438073 2.00665
+8.53083 8.83004
+4.22258 4.65233
+6.72856 7.61085
+3.17591 3.18978
+5.07955 5.6938
+2.09043 2.55351
+3.28085 4.43556
+7.29225 8.03925
+7.78034 7.82608
+6.59677 7.67719
+5.56032 6.38139
+8.72663 8.82535
+2.96505 3.11983
+1.67192 2.11727
+7.85386 8.49025
+6.30965 6.72799
+8.53082 9.3801
+2.8976 3.39972
+8.90427 9.44661
+-0.0665785 0.410254
+4.58099 5.81135
+1.88015 2.21838
+3.38693 4.10149
+7.46378 7.68246
+8.80985 9.72212
+6.54877 7.25642
+7.18249 7.71509
+7.41978 8.64805
+1.00839 1.78626
+0.39421 0.526751
+7.13516 7.28317
+7.28011 7.80573
+1.90158 2.49032
+1.66181 4.17994
+8.81106 9.21133
+9.46151 10.5007
+0.52585 0.575082
+7.66495 8.64072
+7.72794 8.50991
+4.92409 6.23879
+2.28769 3.21774
+3.62671 4.40932
+5.77688 7.31314
+8.94253 10.0664
+0.416618 0.635111
+1.81377 2.41356
+7.91755 8.83363
+1.62715 1.82626
+8.22065 9.51656
+0.020228 0.724724
+8.81119 9.95764
+8.66509 9.61079
+5.28201 5.32152
+3.95724 4.22138
+7.09339 7.77685
+9.63428 10.1514
+3.69117 3.779
+3.82195 4.76757
+5.75824 6.62265
+3.69984 4.75163
+3.58259 4.8788
+8.32635 8.85873
+5.81959 7.32506
+8.11265 9.14304
+2.20118 3.32511
+7.79582 8.52691
+6.5891 7.13584
+0.385393 0.440656
+1.13414 1.9951
+1.76482 2.69977
+4.76194 5.82757
+6.33633 7.5348
+4.7663 5.65083
+1.98298 3.01285
+0.956973 2.38629
+5.60955 7.65827
+9.11064 9.14351
+2.99933 3.37975
+3.36304 4.57437
+3.89886 4.50733
+8.73838 9.3514
+9.11824 9.66042
+1.47961 2.93909
+0.615297 1.13217
+1.70646 2.32478
+4.35168 4.99362
+0.986601 1.18332
+4.01551 4.92342
+7.68944 9.86838
+4.47611 4.85316
+0.051839 0.86288
+3.59624 5.5628
+7.08729 8.90374
+9.53464 9.9196
+4.69871 5.74189
+8.43146 10.6889
+3.23774 3.77777
+9.35399 9.62741
+1.15259 2.69451
+6.6368 7.33773
+5.26395 6.09857
+6.87415 7.21717
+-0.925645 1.06118
+8.49641 8.67659
+2.31486 3.25354
+2.51057 2.58303
+0.921997 1.48762
+6.29777 6.74738
+4.03945 4.27305
+7.1668 7.3471
+8.44572 8.80343
+7.16937 7.84526
+8.89008 9.4332
+7.01815 7.66319
+-0.420804 0.982878
+1.59755 1.64672
+0.700814 0.72579
+6.3138 7.37379
+8.49276 9.93522
+-0.795656 0.819657
+0.772253 1.05926
+0.917593 1.3917
+3.30076 4.12274
+-0.0237176 0.260552
+6.82385 7.49119
+6.98638 7.52012
+8.24915 9.08397
+4.17486 4.25908
+3.08435 4.64404
+8.17114 9.91412
+8.95923 9.3751
+0.596707 1.89462
+0.982917 1.40236
+6.79552 7.36657
+8.2599 9.69551
+8.85126 10.4342
+7.98967 9.14514
+0.766355 1.63286
+7.76211 7.99247
+5.23163 5.89338
+4.4707 5.50457
+9.257 9.27862
+2.11049 2.59654
+1.65501 2.06238
+0.551768 2.10873
+0.0285582 0.194421
+3.66153 3.85615
+5.07097 5.55806
+5.29145 5.80735
+2.03499 3.0863
+0.73171 2.29494
+2.63023 3.6417
+6.17478 6.25997
+5.79646 6.46363
+9.10633 10.5516
+7.23415 7.84823
+6.49951 7.1506
+9.94647 10.0242
+7.91236 8.09097
+7.8127 7.97662
+3.34238 3.42907
+8.50796 9.8311
+8.75792 8.98028
+9.34125 10.4441
+2.84413 3.71602
+1.49884 2.35652
+7.52317 9.18955
+9.5587 10.3525
+4.85436 6.1181
+5.91369 6.12194
+6.88463 8.34471
+1.51952 2.99156
+4.29022 5.12674
+8.0229 8.41246
+2.44841 3.04779
+7.59975 7.91485
+2.74491 3.62575
+0.0169968 0.240919
+5.85351 6.29039
+4.99825 5.93439
+2.153 3.35304
+1.19032 1.41763
+6.13337 7.80615
+7.05013 7.89364
+8.38056 9.92
+6.35525 6.64521
+3.93342 5.15885
+2.87656 3.47116
+3.56176 4.41159
+6.72084 7.9171
+8.03801 9.12442
+1.03329 2.56255
+7.64542 8.61709
+2.85627 2.89437
+8.8303 9.79838
+1.08032 2.34573
+7.51366 8.39169
+9.09685 10.3677
+6.753 7.41904
+-0.217662 1.42059
+6.72492 7.97553
+7.99325 8.8976
+0.765372 1.10894
+4.37958 6.04671
+8.72027 9.58928
+6.00234 7.29277
+8.43912 9.25356
+2.98816 3.17389
+0.4292 0.552167
+1.69827 2.80318
+5.99904 7.09981
+4.68367 5.87791
+-0.203208 1.09587
+2.65367 3.28472
+3.87051 3.88111
+6.36178 7.18853
+-0.0815935 0.200848
+6.82255 7.18488
+4.99295 5.08405
+2.67155 3.08447
+5.82099 6.77145
+8.76434 9.01086
+5.94982 6.18665
+3.23369 3.71743
+0.934129 2.73456
+1.13233 1.24208
+8.21931 9.03906
+6.25425 6.69972
+0.971299 4.21205
+5.68426 6.45281
+2.85786 3.3955
+8.17715 8.88439
+2.72802 5.01235
+6.12811 6.53711
+5.22878 6.45864
+0.453655 0.922032
+0.466162 0.85262
+2.40916 2.64759
+5.63583 6.37476
+2.18434 3.14105
+0.912811 2.08432
+8.9464 10.1515
+6.77323 7.27133
+0.7167 1.60579
+5.89813 6.32997
+7.66593 8.4611
+6.54145 6.88337
+5.95421 6.21188
+1.10383 1.92226
+5.37168 5.65707
+1.51874 1.66339
+4.60748 5.00384
+8.97354 10.5394
+2.05022 3.20373
+8.17316 8.19283
+2.33792 2.72643
+8.81577 9.3704
+5.95738 6.59855
+5.69025 6.17335
+6.91889 7.88142
+2.17529 2.81528
+6.65046 7.19698
+7.10694 8.78046
+2.09613 2.47484
+1.92423 2.25333
+-0.0906039 0.861376
+6.61481 7.70609
+6.7448 6.84276
+3.43655 5.28405
+6.05404 6.48902
+3.24637 3.36149
+5.69528 5.77157
+5.12352 5.31024
+7.65858 8.80828
+3.69209 4.4851
+0.25765 1.29637
+3.1537 4.13191
+3.12328 3.83765
+2.99798 4.8748
+4.49566 4.67827
+3.50822 4.03987
+8.00334 8.34084
+8.61039 9.00704
+0.0332147 0.166124
+4.55193 5.36546
+4.20308 4.31589
+0.955466 1.20095
+-0.382972 1.47625
+0.803913 1.01916
+3.78333 4.0707
+6.58604 6.70173
+3.02066 3.82562
+1.16515 2.2816
+7.87242 8.24214
+4.057 4.60368
+2.84714 3.59726
+2.77804 3.69136
+-0.139996 1.36416
+4.50647 6.37579
+8.86647 10.1549
+9.24372 10.1442
+0.743107 1.84722
+6.18761 7.65841
+0.214887 0.443938
+8.8513 9.55063
+4.04856 5.79916
+6.69671 7.10025
+6.97477 7.79702
+4.82551 5.10538
+0.821772 2.85828
+9.3086 9.74805
+5.45623 6.66668
+7.64296 8.67451
+7.53052 8.31024
+7.45352 7.5063
+1.31412 1.4979
+7.81176 8.48083
+0.228964 1.73207
+5.52579 5.93103
+6.58544 8.48938
+2.75407 4.18746
+3.21396 3.96298
+1.13806 2.34106
+0.899331 2.87945
+5.69016 6.61624
+2.48172 2.58082
+0.676004 1.43405
+3.41766 3.9503
+0.104554 0.298373
+6.50673 8.21831
+6.00349 6.69385
+9.62453 10.3147
+4.09379 6.02911
+-1.12803 1.68823
+1.83324 3.43133
+8.30576 9.09288
+-0.363911 0.405218
+1.91686 3.34928
+6.98331 7.15885
+-0.31591 0.996789
+0.145813 0.35408
+5.32016 6.13679
+6.99664 7.48644
+-0.140875 0.821045
+9.0119 9.19742
+1.69616 1.72453
+4.38064 5.07542
+6.85898 7.55478
+3.9145 4.27935
+5.77592 6.16806
+1.45345 3.01301
+4.13024 5.59662
+8.14557 8.41669
+4.00075 4.829
+-0.283024 1.59436
+5.92468 7.17507
+6.84458 7.18356
+1.06987 1.42839
+8.618 8.66235
+0.432802 1.59512
+3.78058 4.36051
+1.6113 4.21135
+6.73475 7.01057
+6.74819 6.90498
+6.93146 8.54681
+9.02605 9.91613
+3.888 4.00526
+1.07958 2.27915
+4.78456 5.10764
+3.92952 4.89555
+9.09348 9.30304
+6.73107 7.21036
+7.42366 7.73225
+4.04079 4.72919
+1.21726 1.34683
+8.82903 9.73504
+0.577663 1.00786
+1.56822 2.08675
+4.84315 5.44994
+1.01008 1.16524
+5.99782 6.01535
+0.996739 1.75305
+4.76128 6.39012
+8.93563 9.51972
+-0.0698166 0.453543
+5.9084 6.95139
+0.535401 0.692258
+4.80729 5.48335
+3.42581 4.76147
+3.58715 4.43541
+7.50236 7.71227
+5.19742 5.87901
+1.79752 2.43097
+7.29762 7.57608
+5.65169 6.47762
+6.02249 7.93642
+0.840131 2.81176
+6.2646 6.96911
+7.46014 8.17136
+9.79049 9.94054
+4.61094 5.35819
+7.30902 7.41873
+1.21883 1.84621
+6.41743 6.57736
+1.53114 1.62859
+6.73237 7.47083
+6.05618 7.56831
+7.63374 8.16821
+5.20839 6.05382
+0.619043 1.44065
+2.38657 4.88786
+3.88289 5.18788
+4.42283 4.51106
+9.15575 9.22388
+3.18678 3.72243
+3.20638 3.65935
+4.2219 4.97961
+4.63725 6.90959
+2.57123 2.59101
+4.08581 4.37325
+6.53926 6.80412
+1.77475 3.89149
+9.22854 10.426
+2.67375 4.36551
+3.17194 4.0433
+1.10304 2.0348
+3.67233 4.04957
+3.23526 3.5012
+5.17684 6.47301
+4.4 4.67662
+4.15842 5.85903
+5.88496 6.61616
+7.26489 7.51279
+4.17402 6.6997
+-0.540216 0.54573
+4.36063 4.60442
+9.18146 9.96239
+0.977216 2.42032
+9.09143 9.61178
+5.06686 6.23076
+2.04772 2.92048
+1.4154 2.00707
+7.80442 9.54824
+4.55549 4.7148
+7.56964 8.80172
+1.35347 3.21648
+9.12751 10.0605
+1.81735 2.06061
+5.63798 8.0918
+6.79492 7.98002
+9.23053 10.1299
+1.74639 2.02724
+3.71825 4.09879
+2.4995 3.42493
+4.1255 5.66758
+4.75493 6.99687
+8.22481 8.72036
+6.13689 7.53026
+5.41315 5.66358
+4.21425 5.62743
+6.22936 6.44392
+2.02303 2.96414
+-0.0655462 0.130508
+6.1487 6.70994
+4.36674 6.24985
+7.51129 7.63176
+7.9729 8.98613
+3.92117 4.15596
+4.54398 5.43399
+6.34248 6.92305
+-0.550253 1.16167
+9.15142 9.53717
+8.99354 9.65952
+6.28988 7.90053
+3.36505 3.782
+8.80005 9.70553
+1.99947 2.18573
+1.15023 2.07069
+0.253723 1.32958
+2.0392 2.93977
+2.89024 3.00836
+8.16735 8.2049
+1.49508 2.95258
+6.19092 7.44552
+2.33328 3.06758
+6.44166 7.4649
+7.34749 7.79488
+7.5557 8.04141
+2.76812 5.13617
+7.97308 9.25668
+4.73539 5.22234
+7.53377 8.41651
+-0.355405 1.36499
+4.72111 4.73744
+-1.22753 1.84842
+5.5033 5.63808
+3.95421 4.19313
+6.24752 6.98229
+4.49694 5.07277
+3.70871 4.30093
+1.31984 1.50116
+7.16044 8.70868
+2.44587 3.34659
+6.08322 7.20376
+2.75012 2.82555
+0.0394442 0.863634
+3.90896 4.73696
+4.77494 5.04158
+8.41473 8.82932
+1.7049 2.71024
+9.01684 10.1044
+4.57959 5.01066
+3.2484 4.21875
+1.11596 2.88792
+3.53616 4.14001
+5.80672 5.82478
+5.85844 7.70293
+6.18239 6.30124
+2.22913 3.23412
+2.97521 3.08481
+2.2749 3.70611
+2.3457 3.86609
+2.71695 4.04473
+7.22863 7.40115
+1.80428 2.39815
+1.80781 2.33028
+2.78662 3.14936
+9.40717 9.94906
+6.54968 6.80773
+2.76862 3.24492
+1.96034 2.87404
+3.53295 3.54927
+5.36042 5.67073
+8.63339 8.93752
+0.932415 1.70402
+4.32923 4.55594
+9.3491 10.4969
+0.121416 1.15499
+-0.172998 1.36319
+2.02349 2.29887
+1.75352 2.35329
+0.502555 2.01665
+6.71754 6.80014
+3.35175 3.93337
+7.17133 7.9851
+8.51299 9.95771
+5.86103 6.36734
+1.44593 3.46008
+4.49699 4.60855
+2.29261 3.10053
+6.68713 8.19499
+5.43776 6.84387
+8.58591 9.02134
+5.24746 6.15595
+1.3847 2.5074
+6.62143 7.07775
+1.7101 2.077
+9.70898 10.2789
+6.67451 7.92058
+2.56858 3.60374
+1.61917 2.60482
+1.77902 2.16908
+4.76209 6.00461
+3.8894 3.98269
+7.0278 7.276
+5.4866 5.59417
+1.69306 2.92506
+2.45744 3.76592
+2.5689 2.80611
+5.98283 6.06154
+9.5267 9.9626
+9.55652 10.4128
+6.6111 6.64606
+3.38826 4.26111
+3.61887 3.73455
+4.14924 6.13796
+7.15863 7.28212
+5.78984 6.56662
+2.09304 2.2681
+4.39867 4.88557
+6.20907 6.81034
+4.63336 4.91476
+0.939805 2.43621
+1.56774 5.13599
+8.40908 9.82058
+2.06273 2.20306
+2.53524 5.01482
+2.9314 3.23661
+2.48535 3.47368
+0.366389 1.4699
+8.81131 9.01654
+2.05102 2.87972
+1.9699 2.22723
+6.18333 6.98224
+1.3425 2.4443
+0.767883 1.77423
+3.1538 3.43248
+0.891006 0.903458
+5.7136 5.8713
+0.437569 1.37539
+3.98533 5.01646
+8.65764 9.89518
+3.74889 3.98561
+7.74507 8.30657
+8.24671 8.43006
+6.88467 7.42036
+1.99426 2.03522
+1.90486 2.04181
+1.12911 1.71807
+9.25253 10.105
+1.92989 2.0346
+5.60044 6.25585
+9.16848 10.087
+6.67824 6.72257
+1.88522 2.80222
+0.242758 0.852689
+5.09249 7.71484
+5.54974 6.44414
+7.66767 8.72936
+3.48884 3.83156
+3.4697 5.14646
+8.48582 8.7692
+6.76259 7.06897
+7.83856 8.62005
+5.81853 6.76571
+2.04803 2.94439
+6.85396 7.22985
+6.52449 7.39764
+9.26838 9.34809
+4.64449 5.64975
+7.51845 7.80279
+-1.27268 1.65344
+0.686101 4.20373
+7.80552 8.37119
+7.20419 8.35284
+9.33962 9.87975
+4.31238 5.58683
+2.39198 3.84398
+1.29248 2.59269
+4.71612 6.195
+5.27332 6.52174
+7.71492 7.84702
+8.18064 8.72233
+1.55223 2.31294
+6.34374 7.31758
+6.48966 6.92611
+2.62268 3.09895
+4.09064 5.06197
+2.66367 4.12126
+1.53391 2.93092
+8.64516 8.70655
+9.20047 9.28957
+4.00474 4.11716
+8.5418 9.45901
+8.73386 9.73253
+9.22596 9.63179
+1.34204 2.7834
+8.4489 9.21027
+7.75158 8.51085
+5.98856 6.11212
+1.29906 2.08111
+9.24315 9.76291
+6.84345 6.91607
+5.88294 5.90545
+3.03022 3.39941
+3.22222 3.4089
+1.12791 1.65898
+6.70232 7.36336
+8.60042 8.82377
+1.26451 1.30362
+2.76682 2.92693
+1.54763 2.19582
+6.5865 7.24173
+4.15242 4.21742
+8.39615 8.78524
+1.67017 2.08717
+9.03012 9.97041
+2.31463 2.45877
+8.29872 8.51813
+1.66689 2.51919
+-0.807606 1.47794
+1.31452 1.94418
+0.0739222 0.512106
+4.23577 4.25889
+7.45273 7.97505
+3.34234 3.46608
+7.97304 9.14131
+1.9029 2.99946
+8.84087 9.79689
+4.92769 5.68685
+2.74004 3.04513
+0.889257 2.20448
+3.10574 3.9758
+3.91547 4.32303
+7.17475 8.07377
+5.92903 8.03738
+1.57974 1.65527
+3.76339 4.2285
+2.28263 2.7559
+9.53105 9.87477
+2.10154 3.2222
+4.23683 5.34347
+1.38769 2.48482
+5.39838 5.51872
+8.50975 10.1382
+8.04108 9.21474
+8.5252 9.67823
+8.893 10.7726
+7.1294 8.18211
+1.15642 1.73882
+0.436911 0.756698
+3.66142 3.71659
+6.37686 6.7
+8.88506 8.99896
+2.31891 3.79061
+2.61615 2.81909
+1.35216 1.39571
+3.04217 3.52564
+7.8409 9.34099
+0.470596 1.49157
+6.94014 7.18365
+8.05483 8.49979
+8.18511 8.25608
+7.03597 8.45251
+2.47411 2.65172
+0.112716 0.193057
+4.45039 5.10584
+4.53906 5.80013
+5.11702 5.27151
+1.24211 1.90757
+4.64114 4.72912
+1.77254 3.45037
+-0.1275 0.99675
+5.69637 7.67722
+2.21252 2.44032
+3.89623 4.40192
+2.79904 3.15569
+8.37911 9.42424
+5.27199 6.2026
+1.84791 3.75411
+0.522801 0.54806
+3.83644 4.64785
+6.65497 7.55638
+6.75104 6.98564
+2.34979 2.98049
+8.27176 8.60459
+5.07773 5.44064
+1.16934 1.98872
+4.2914 5.37557
+6.83462 7.95408
+3.91405 5.73229
+7.77473 8.53538
+9.23441 9.90185
+4.03659 5.2473
+5.99336 7.8004
+4.2432 5.89276
+6.39847 7.81276
+9.13909 10.4164
+1.12009 2.5649
+2.35157 3.18468
+4.46918 5.55572
+3.82512 5.53666
+7.18238 8.10193
+8.40911 9.23639
+8.99555 10.4192
+6.69547 8.87825
+4.79012 5.34858
+9.23615 10.0986
+-0.590587 1.14603
+2.74289 3.48244
+-0.248403 1.1591
+3.35663 3.7229
+7.37504 7.6481
+6.64796 7.64695
+0.778081 2.41212
+4.49242 4.7772
+8.01419 8.109
+6.6494 7.76619
+8.86331 10.1205
+0.817273 0.937855
+8.00019 8.21556
+6.19405 6.54794
+6.11798 7.30421
+3.40164 4.28921
+5.31927 6.21584
+2.79377 3.40938
+3.59494 3.90321
+7.79218 10.3132
+2.99546 3.86111
+1.71916 3.40283
+6.1768 6.81915
+7.59629 8.68568
+1.59724 2.348
+6.20151 6.5391
+6.30798 6.70478
+0.489786 1.51322
+1.28396 1.86775
+5.18031 5.43007
+6.61276 7.70565
+1.4909 2.11165
+4.56163 5.49277
+1.93598 4.35147
+3.65568 5.00224
+1.68028 2.00363
+7.82874 8.67399
+1.23053 2.87598
+-0.164275 0.194491
+2.58349 4.01302
+1.23279 2.02984
+0.599702 1.09257
+9.50965 9.80164
+2.33108 3.58037
+4.38659 4.62378
+2.65097 2.87349
+0.0537058 0.576502
+4.20415 5.15693
+5.41458 5.98382
+4.37747 5.10347
+2.62536 2.70073
+6.96845 8.15356
+6.91533 8.41638
+9.36431 9.58863
+6.13019 7.25915
+2.7818 3.4951
+9.44641 10.1301
+2.68817 4.19772
+5.65664 5.6814
+8.25308 10.498
+3.50374 3.64458
+1.47063 1.78567
+8.21901 8.98152
+3.99261 4.47339
+2.51395 3.38788
+4.66178 5.24959
+9.08415 10.3467
+5.92351 7.20447
+1.74145 2.35957
+7.62653 8.96797
+6.43542 7.68775
+5.49047 6.56355
+4.9225 6.22555
+0.257967 0.704837
+3.42488 3.52873
+8.67606 9.77824
+5.12981 6.37856
+5.97359 6.32151
+1.58517 2.28129
+0.530687 1.54545
+7.04056 8.15296
+1.8541 3.1902
+9.46286 10.0098
+3.17967 4.02237
+3.49966 3.80271
+6.24894 6.49618
+1.19944 1.59215
+5.30422 6.94823
+7.49137 7.78584
+7.02669 8.16282
+2.21673 3.36471
+7.66183 8.34763
+4.13876 4.87421
+3.0199 4.07124
+8.40439 8.82943
+-0.274649 0.429602
+6.11215 7.65609
+3.34781 5.03006
+1.28231 1.77258
+7.57256 8.59456
+5.36208 5.81735
+7.18511 8.96618
+8.86053 9.49567
+5.39097 5.93211
+6.59094 7.26553
+3.69159 4.01914
+8.73797 8.81611
+1.41144 1.82827
+7.83555 8.07567
+6.13821 8.44907
+5.83432 6.48744
+2.93596 4.66271
+7.03215 7.59658
+1.65252 2.45337
+2.91358 4.90129
+8.94415 9.14642
+5.21005 6.1166
+0.33225 1.17039
+7.40089 8.07586
+9.30427 9.40776
+2.34188 2.81124
+1.02811 3.3344
+6.28117 7.62089
+4.80105 7.10049
+8.90252 10.0057
+2.20932 2.33093
+8.20746 10.6268
+4.31887 5.23945
+9.258 9.8985
+3.80742 4.15057
+1.11313 1.23711
+9.53067 9.70165
+7.95467 8.67187
+9.66031 9.83223
+3.61385 4.09571
+0.623148 0.652817
+7.77809 8.62118
+0.86157 1.98606
+3.12528 3.75815
+1.9363 3.22621
+8.23521 8.99175
+8.95406 10.4487
+3.38526 3.90652
+0.34281 2.0226
+0.739384 0.793829
+3.55637 3.96384
+6.1562 7.28647
+5.30211 7.51385
+1.46275 1.78688
+-0.0144662 0.868488
+0.0740443 0.119828
+9.26206 9.83923
+0.743397 1.40754
+4.68823 6.62985
+8.8291 9.17373
+4.66711 5.09313
+4.023 4.44994
+5.68937 5.79194
+8.04835 8.06763
+6.94662 8.00075
+0.774686 1.13428
+0.381819 2.34757
+2.1235 2.63524
+5.42795 6.89998
+6.07662 6.29083
+6.47821 6.7872
+2.58305 2.6192
+-0.598336 1.23458
+4.29089 5.43332
+8.09914 8.28229
+5.05925 5.35258
+0.181705 0.444466
+3.27849 4.79932
+0.310314 1.43125
+1.13836 2.76825
+1.2529 4.09336
+0.198106 0.608625
+1.77442 2.23777
+6.29823 6.64574
+1.22125 1.37929
+1.77592 3.04011
+1.43999 2.90926
+4.20495 5.32396
+4.98874 5.33211
+8.36483 8.67875
+5.65373 7.14132
+2.8303 3.05037
+3.41509 3.54032
+7.21702 7.85857
+5.79848 5.8913
+4.52711 5.65511
+2.04663 2.66919
+5.76363 6.77903
+-0.153046 0.77878
+4.37474 4.49936
+6.86786 7.81999
+8.92084 9.16368
+7.5975 8.56954
+0.197493 1.07503
+3.99803 4.67019
+9.35123 9.60094
+1.46442 2.4256
+5.21501 5.79612
+8.80879 8.99085
+-0.109252 1.30631
+7.34965 7.87947
+7.08703 8.12723
+0.985946 2.14663
+0.48521 0.856118
+6.57719 6.70623
+8.71165 8.72286
+3.16883 4.8714
+-0.21189 0.412944
+3.6526 4.37121
+0.364649 1.3363
+9.27722 9.35135
+3.47517 3.8793
+5.67149 6.28631
+6.52789 6.61039
+9.36323 10.4189
+4.30743 6.18638
+7.93653 9.4877
+8.24024 8.41475
+2.75692 3.13935
+8.80223 9.1489
+3.42656 3.90557
+8.96598 9.3217
+1.99582 4.84803
+5.1178 5.8688
+4.47901 5.73433
+7.30397 7.85527
+0.18463 0.656832
+7.80806 8.41666
+8.50023 9.42628
+7.67563 8.64756
+9.46783 9.52169
+2.06797 3.3107
+5.08863 5.10167
+0.82146 1.39055
+7.5428 8.04736
+9.09407 10.8208
+9.15219 9.99706
+1.61637 3.46354
+5.3812 5.96247
+9.34115 9.73575
+6.49221 7.30031
+1.56337 3.39704
+8.463 8.60841
+-0.322779 0.658242
+8.50173 8.72125
+0.854851 1.83405
+7.81945 8.12841
+2.60724 3.19985
+5.03714 5.55331
+1.85041 3.36816
+7.26119 7.64364
+5.75223 5.99653
+4.00026 4.43863
+5.00939 6.03009
+6.22879 6.6638
+6.44231 7.18622
+1.66002 1.88587
+2.59781 2.84919
+4.96399 5.40545
+5.70327 6.95018
+6.97141 7.08346
+3.98535 4.6179
+5.96795 7.14997
+5.07581 5.8262
+4.65665 4.98851
+8.65646 8.70986
+3.04175 3.19155
+0.796073 1.0952
+8.24859 8.57828
+5.26952 6.34245
+7.94685 8.78542
+0.193963 0.968524
+6.61159 6.80186
+9.37533 9.79195
+8.94048 9.74959
+5.77272 6.48616
+6.0092 7.86718
+2.52621 2.58938
+8.91347 9.22421
+4.20466 5.45909
+4.82157 5.09167
+7.35464 7.36611
+5.12413 6.45063
+2.12404 2.81187
+8.88875 9.20752
+2.59731 3.20587
+1.54772 2.08968
+6.70449 7.08952
+-0.0967146 0.888521
+3.10207 4.34815
+1.20807 2.35497
+6.00753 6.08492
+3.21837 4.00446
+4.34095 6.0963
+4.24821 4.79576
+4.49523 6.6669
+1.49483 2.52552
+0.0411497 0.641526
+8.3854 9.20954
+8.12419 8.93702
+0.0201515 1.06914
+1.57617 3.44176
+1.4173 1.76847
+5.42261 6.51539
+6.6744 8.46109
+3.05234 3.59179
+6.49059 7.24618
+7.01989 7.69901
+2.34664 2.90601
+9.17324 9.86754
+5.50919 5.70258
+2.40486 4.6009
+3.74368 4.00093
+6.9211 7.33554
+7.41358 8.7413
+8.78284 9.62033
+2.96753 3.01602
+-0.200693 0.535532
+6.4383 6.45049
+8.28835 9.42148
+4.2379 5.35128
+4.68397 5.04593
+5.7185 7.03177
+5.37494 6.43977
+2.99518 4.08138
+7.28064 7.54638
+9.10622 10.4907
+1.23839 1.50915
+6.7823 9.24533
+8.45679 8.62427
+0.106718 0.69304
+2.84119 3.50375
+3.60466 4.51338
+8.04709 8.19023
+3.31632 3.48054
+5.9027 7.46331
+-0.621072 1.13003
+8.57167 8.69262
+0.301047 1.42246
+1.24229 2.44616
+0.962706 1.55754
+8.77327 9.55275
+1.77899 2.44335
+0.368575 1.84726
+9.05918 9.07832
+6.61723 7.68015
+8.59899 8.80383
+-0.133927 0.20271
+5.69266 5.74809
+0.68202 1.52342
+7.17359 7.91482
+5.74049 7.34297
+7.88371 7.9683
+1.58749 2.26543
+3.96728 4.45988
+5.4354 6.88336
+1.68706 1.6976
+6.56907 7.40696
+0.518151 0.724124
+8.99155 9.36995
+6.00506 7.91529
+5.26967 6.11536
+2.37416 2.50887
+8.47983 9.31509
+2.38155 2.54346
+8.08415 9.65214
+1.00693 3.19203
+1.6523 1.73553
+6.85283 6.9958
+4.11425 4.90193
+1.39597 1.58725
+5.05551 5.19458
+5.89969 6.11394
+5.9148 7.26824
+9.32271 9.93989
+1.62911 2.58215
+5.43399 6.14203
+3.11393 3.58792
+8.57619 9.3788
+2.23349 2.7884
+8.09022 8.54326
+9.7114 9.87076
+5.91086 6.10885
+7.35805 7.57276
+0.650002 0.879866
+3.24977 3.75531
+2.73948 3.41051
+3.08006 4.62772
+3.71868 4.31519
+4.13905 5.78577
+1.77297 3.02983
+5.85931 5.89333
+3.68972 3.93193
+1.43922 2.98425
+6.51534 8.33386
+6.6821 7.50874
+1.2541 2.11722
+5.40634 5.54411
+4.55502 4.60346
+4.32484 4.68497
+3.40711 4.31975
+9.20893 10.0853
+6.12594 6.9009
+0.12664 0.347193
+7.53378 8.82258
+7.79572 8.91645
+2.50098 3.39701
+4.17873 4.97732
+1.05822 2.47032
+3.1012 4.97736
+8.42126 8.74568
+7.83301 8.17843
+9.26618 10.584
+3.02667 3.7128
+8.30654 8.69242
+2.87173 3.43275
+6.59917 7.54843
+7.03447 8.20283
+5.75718 6.40637
+2.62899 4.1855
+-0.520148 0.902194
+5.1538 5.61763
+1.25625 2.33142
+4.16336 4.3384
+3.01296 3.41026
+3.16515 3.26305
+2.40944 3.21897
+6.80742 8.57141
+1.15125 1.54824
+8.72251 9.50878
+3.15703 4.04482
+0.470308 0.726093
+0.199979 0.878243
+7.8908 9.17915
+6.91793 7.16038
+4.64783 5.00217
+2.21237 3.03073
+1.5825 1.63362
+3.18806 5.20795
+5.92725 5.95595
+4.65027 5.09868
+6.42204 6.87201
+8.08637 9.40221
+4.95542 6.16185
+7.45987 7.57979
+8.826 9.13961
+4.42078 5.27977
+7.26216 8.97585
+2.26919 3.33512
+9.4077 9.81908
+0.722223 1.03378
+-0.0497795 1.19355
+8.22686 9.31082
+0.0733766 0.93037
+7.47883 7.9062
+5.28731 5.74181
+1.78496 3.61348
+0.690987 0.764344
+2.56442 3.54999
+4.27944 5.43124
+4.04412 5.35376
+0.663756 1.88268
+1.73567 1.78553
+8.07666 9.41842
+7.70138 8.42873
+9.27113 9.55873
+0.982413 2.5565
+0.994807 1.02007
+5.01226 5.31923
+8.87435 10.0597
+1.74836 3.22578
+0.151528 0.191719
+5.82608 6.06438
+1.17239 1.59955
+3.18515 4.11049
+1.92258 3.70604
+1.63692 1.8266
+1.83542 2.66228
+2.21954 3.11425
+3.97546 4.3928
+0.731374 2.05705
+5.44809 5.96359
+9.85934 10.0553
+8.42169 9.63654
+6.68148 8.43446
+2.00014 3.3485
+0.735924 1.73
+1.61833 3.05643
+7.87934 8.67316
+8.67441 10.0032
+0.794573 1.2493
+6.11926 6.41441
+5.2715 5.82597
+9.41854 9.46625
+3.46299 4.06742
+5.6487 5.79053
+1.46503 3.43496
+0.9957 1.53939
+2.4714 3.1301
+0.887049 1.6196
+3.04884 3.19662
+1.24337 2.05758
+7.61142 8.55766
+0.998279 1.64425
+4.74917 5.85236
+2.97632 3.94611
+4.11786 5.62709
+7.40498 7.62083
+1.47854 3.64736
+2.1176 2.64511
+4.55533 5.89436
+7.44618 9.79926
+-0.410242 0.737348
+5.719 6.30861
+8.33692 8.38222
+8.78122 9.98195
+3.75452 4.26866
+2.23771 2.44985
+6.72455 7.61362
+5.9797 6.58846
+6.72885 7.60315
+-0.710302 1.18332
+7.73287 9.383
+6.61213 7.67394
+4.12945 5.68527
+-0.300887 0.341615
+5.77978 6.41534
+8.34001 9.96874
+1.62323 1.8283
+1.71788 2.23905
+7.43484 8.0418
+3.47322 3.8047
+3.47311 3.51261
+7.82093 8.86377
+2.46973 2.99125
+7.91166 8.55065
+3.39807 3.43599
+8.94792 10.0437
+7.70859 8.32487
+5.86852 7.13103
+7.64996 9.19033
+6.66742 7.8092
+7.88241 8.58675
+9.42939 10.3479
diff --git a/geom_matching/wasserstein/tests/data/test_5000_B b/geom_matching/wasserstein/tests/data/test_5000_B
new file mode 100644
index 0000000..5f6e43c
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_5000_B
@@ -0,0 +1,5000 @@
+1.17434 1.46837
+2.58198 4.16589
+0.234041 0.968658
+1.52703 1.59579
+6.7103 7.44033
+3.19227 4.41539
+5.42556 5.57369
+3.45417 4.86089
+3.82256 4.1092
+7.82551 7.90784
+3.9384 4.71796
+5.60335 5.9054
+7.96663 9.8987
+6.30305 6.64853
+7.33246 10.5316
+0.623312 1.09008
+2.63041 2.64616
+5.36028 6.28956
+4.64202 5.91858
+7.55219 7.96304
+7.73736 9.18221
+1.67114 1.84851
+5.07514 5.12159
+7.03732 7.05228
+7.5006 7.59212
+0.244947 1.55875
+0.0170454 1.10485
+1.95394 3.53669
+5.66015 6.01949
+5.88211 7.64639
+7.46698 9.27085
+6.37429 7.10154
+4.54535 4.81932
+8.21203 9.35896
+4.89933 6.20802
+3.68683 4.17831
+0.477467 0.828394
+6.17871 6.77834
+9.77523 9.92676
+0.854808 2.38709
+7.93326 8.3553
+2.10917 2.27771
+4.07045 4.72793
+8.2016 8.8011
+2.9205 3.95746
+2.89806 4.39725
+5.5654 5.78669
+9.5219 9.98543
+7.08591 7.19588
+8.35359 9.57893
+9.81348 10.0345
+8.5994 9.71835
+5.43903 7.25234
+1.82768 2.92724
+4.44952 6.79754
+5.66747 7.34386
+5.88153 6.39253
+3.34008 4.22032
+2.46068 2.76051
+0.370778 2.61681
+6.02508 6.26809
+4.32654 4.93262
+7.41536 7.99616
+8.84229 9.87911
+3.8551 5.84353
+1.56832 2.34694
+6.96099 7.42028
+8.15753 8.72014
+9.23141 10.3815
+7.4484 7.80228
+0.473671 0.874895
+3.15689 3.50687
+3.58122 4.09945
+3.55022 3.74767
+4.42708 5.80211
+4.40956 4.68699
+3.80576 4.61856
+7.29965 8.28614
+7.40582 8.15308
+1.69789 1.77669
+1.66419 3.44308
+0.473997 0.872506
+7.83959 8.52898
+6.22416 6.36949
+-0.187159 0.871822
+0.232336 0.585965
+9.29905 9.44357
+1.4459 2.40589
+2.83008 3.19758
+1.15291 2.12112
+2.58686 3.33896
+6.79362 7.88068
+0.228178 1.48318
+5.60001 6.20258
+4.97803 7.10992
+1.70429 1.962
+2.72659 3.13886
+9.22714 9.25889
+3.84694 3.88778
+-0.282077 1.48155
+9.28756 9.58517
+4.34069 5.59751
+8.63909 8.76839
+8.86236 10.7642
+6.77597 8.41888
+7.30621 8.64164
+0.685607 1.22755
+2.91514 3.22638
+2.72098 3.66837
+8.17528 8.32638
+5.19632 5.7506
+7.34177 8.70639
+5.74082 6.35524
+5.95975 6.69284
+9.40187 10.4488
+2.92761 3.36735
+0.399531 3.13082
+4.83399 4.92635
+7.74539 8.56852
+1.76322 3.5086
+6.54479 6.72963
+7.64362 8.12404
+1.35542 1.45313
+0.214385 0.718085
+1.7006 3.21962
+5.91009 6.47862
+2.21093 2.34636
+5.96919 6.79365
+6.59951 8.22203
+1.54571 1.59397
+3.27012 3.79128
+0.32455 0.622995
+1.73926 2.78017
+9.81035 9.84077
+7.38441 7.85171
+8.90372 9.34186
+7.26323 8.41174
+5.7363 5.97348
+8.25473 10.1281
+2.3981 2.52096
+8.53783 9.63442
+8.51755 9.2735
+6.48614 6.773
+3.40182 3.65137
+2.1353 3.04852
+2.95397 3.73285
+6.98063 7.4963
+4.50189 5.26384
+0.21416 1.49363
+0.632196 1.36307
+6.57833 6.60481
+8.0634 9.33903
+2.79759 2.94462
+4.43747 4.58861
+6.48733 6.86569
+2.28008 3.47037
+6.87452 7.77431
+-0.156821 2.71557
+0.72595 1.78862
+1.97586 2.38196
+8.61839 9.1468
+4.55496 5.68986
+0.26923 1.15728
+9.63757 9.7236
+1.39497 1.96698
+4.8643 5.04172
+6.64675 7.66435
+2.56256 2.6015
+-0.381989 0.611211
+0.676336 1.26896
+8.95304 9.03243
+5.62058 6.07997
+3.36522 4.04276
+8.64868 10.5024
+4.75813 5.19834
+1.96608 2.05864
+9.01449 9.10397
+3.72786 4.51921
+5.6938 6.96584
+1.73499 2.9314
+2.73099 3.41409
+8.77171 9.07665
+4.63865 4.67649
+8.6698 9.30782
+-0.168259 2.09581
+9.29672 9.56
+0.372544 2.60567
+0.450487 1.32919
+6.95341 7.6399
+3.4403 5.24993
+5.53469 6.97831
+-0.79664 1.21306
+5.68831 6.14413
+8.85601 8.95444
+3.83309 5.211
+5.51573 6.5114
+3.64009 3.99648
+4.40759 4.99283
+1.85198 2.6457
+2.72645 3.74803
+2.04751 3.00998
+3.19365 3.9383
+8.09529 9.45596
+8.88173 9.5618
+0.609816 2.67806
+1.57288 2.60884
+1.68354 1.80124
+4.92058 5.9959
+1.48728 1.84885
+0.299669 0.413905
+9.02156 9.56731
+7.50854 8.49023
+0.667131 1.89987
+2.22472 2.58793
+5.84395 8.79426
+2.35839 2.66935
+3.43111 3.69982
+6.71023 7.36801
+6.75697 9.0991
+2.35352 2.85316
+6.73054 7.39006
+9.48673 10.1493
+6.71226 6.72805
+9.22083 9.71889
+8.36513 9.33921
+0.0652672 0.319993
+3.26467 4.60881
+7.62269 7.79878
+6.48608 6.69515
+6.21737 6.88645
+6.56094 6.9699
+1.61317 2.7167
+5.08621 6.29754
+2.24676 2.8076
+3.09943 3.93921
+9.74093 10.0968
+0.417699 1.6381
+9.2958 10.2973
+3.6663 4.1369
+0.0229943 0.448065
+9.2928 10.1833
+3.66334 4.22032
+0.812172 2.19952
+7.88025 8.71192
+8.69379 9.34922
+1.70691 2.95352
+8.28737 8.29985
+5.22491 5.354
+4.03526 4.14084
+6.49359 7.05924
+0.24853 1.44718
+7.86936 8.75135
+5.76 6.68919
+5.85437 6.14314
+5.86292 6.0516
+2.35692 2.91295
+0.0990674 1.29386
+2.9911 3.56188
+0.552671 0.638929
+5.80756 6.22761
+1.21067 1.77175
+4.51882 4.72936
+1.99969 2.89732
+3.65968 6.61987
+0.478582 1.33355
+0.259061 0.289813
+3.89264 5.02474
+0.349236 0.438003
+7.70442 8.1729
+7.90389 10.1876
+3.06898 3.15219
+4.77811 5.29095
+9.76561 9.89451
+4.14295 4.2489
+0.540224 2.28623
+9.60749 10.2284
+8.49555 9.00307
+7.89668 9.76886
+4.12631 4.70897
+7.87898 7.91814
+9.29858 9.52908
+9.24009 9.91544
+3.73498 4.70275
+5.44467 5.9274
+2.13453 4.85697
+2.29607 2.88771
+0.807842 1.82319
+0.319005 2.08473
+1.62 1.63649
+2.20431 3.23869
+2.00132 2.77136
+6.64752 6.71706
+8.12249 8.28379
+7.55541 7.7813
+3.51763 3.64484
+9.21075 10.2811
+2.24501 2.54426
+5.64566 6.46452
+6.72307 8.56698
+3.91728 4.60262
+4.02535 4.29548
+3.0499 3.53135
+1.30677 2.32194
+9.36908 9.90669
+3.92387 4.33848
+2.87312 3.08243
+4.49167 5.2749
+5.43143 6.74905
+6.94595 9.16539
+1.9784 2.00429
+9.42478 10.2226
+3.7185 3.96576
+9.37729 9.50574
+5.34283 6.89736
+4.95341 5.84626
+2.91654 4.28413
+8.26544 9.3076
+6.23114 7.20273
+1.02585 2.21446
+1.6718 2.29591
+2.28926 2.73452
+-0.648145 1.105
+1.44194 2.18562
+6.78584 8.37985
+2.00402 2.08248
+1.64076 2.04067
+0.0419044 0.455962
+5.51723 5.72098
+5.42117 6.81165
+5.70331 5.81707
+4.97889 5.60741
+3.38797 4.46846
+7.20995 7.66784
+3.45388 4.23386
+3.11057 4.35995
+3.29476 4.04676
+2.0519 2.94719
+5.37101 5.38271
+5.99383 6.64188
+7.48444 7.6377
+2.15043 3.05399
+1.54093 1.83799
+5.59223 5.87222
+5.8005 6.08042
+6.1208 7.22563
+1.2938 2.17454
+4.92405 5.0402
+6.91336 7.79688
+1.47978 1.99762
+9.00494 10.8315
+5.84805 6.15303
+4.95368 5.49096
+8.21704 8.31192
+0.715222 2.33517
+0.904183 2.54741
+3.42349 3.74906
+0.627978 2.37092
+8.41477 8.72651
+3.55373 5.58652
+4.12685 4.61533
+0.775274 1.41263
+4.78915 5.08639
+6.35791 8.15015
+2.88242 3.63509
+2.78526 3.07834
+0.39129 1.15933
+3.26615 4.74531
+8.78873 9.8888
+7.25612 7.45972
+4.97452 5.30012
+0.757255 1.35518
+1.29516 2.52446
+4.66838 4.96648
+-0.0720209 0.832726
+2.68276 3.21123
+8.44253 10.0311
+3.40809 4.70506
+1.09617 2.35452
+5.19302 6.73599
+1.25316 1.58101
+6.2056 7.10099
+7.7666 8.52777
+0.301239 1.37275
+0.522829 0.731575
+6.73869 6.96826
+2.41703 3.20567
+4.94617 6.22052
+5.41524 7.68272
+6.0408 6.29938
+0.0642067 0.600878
+6.32122 6.63505
+3.25427 4.70611
+5.88704 6.1678
+8.71533 9.08476
+6.25575 6.88392
+1.54462 1.56366
+3.95938 4.03326
+2.54671 3.66798
+8.48603 9.10156
+3.37369 4.65454
+5.25469 5.64636
+7.02624 7.36449
+8.17061 8.44321
+9.23147 10.1149
+1.45589 2.25886
+6.47702 8.25307
+7.69088 7.91753
+0.442832 0.616926
+8.35355 8.83222
+7.65142 9.58906
+4.04877 5.17345
+0.76012 1.8686
+6.54877 7.40058
+7.41774 7.65429
+8.69655 9.3325
+4.8302 6.25306
+1.77203 1.90284
+3.67485 4.94569
+6.60162 6.72031
+3.05924 3.35235
+0.0823162 1.3486
+8.42991 8.86331
+6.81596 6.83527
+-0.0174808 1.6745
+-0.758131 1.08928
+4.14423 5.46342
+9.26253 9.85714
+7.42749 8.58214
+4.88849 5.86936
+8.99339 9.38141
+8.85301 9.18285
+2.64303 3.45658
+7.1133 8.66473
+3.50745 3.8629
+7.81615 7.83244
+2.94991 3.8741
+7.91822 8.8362
+-0.0553088 1.19059
+7.35558 7.77948
+2.11257 2.20563
+8.57533 9.16338
+2.3515 3.13009
+4.13239 5.38696
+2.20763 2.93941
+7.2451 7.3211
+0.834388 1.57413
+4.47024 6.17855
+4.75947 6.80913
+2.99607 3.54732
+8.15998 8.80718
+8.94688 9.27971
+4.46335 4.9522
+5.30725 5.69786
+7.63989 7.86974
+6.69589 7.69565
+5.34018 6.08115
+8.59515 9.62264
+1.80557 3.22507
+8.62429 8.94242
+1.34532 1.95042
+8.60707 8.80741
+8.03275 9.40327
+4.30357 4.39095
+8.97954 10.7076
+2.18143 3.51825
+3.98695 5.62062
+2.48813 2.60622
+5.56957 6.43776
+5.18249 5.55459
+3.45373 3.64101
+-0.00345662 0.570597
+3.61127 4.69761
+9.37858 10.6205
+7.24635 8.19639
+4.70142 4.80735
+3.30055 5.14257
+0.860415 1.31635
+4.72924 5.46893
+7.47391 7.72644
+6.22287 6.57754
+0.181652 1.01366
+2.14494 2.80736
+2.16406 3.19383
+8.52991 9.22722
+8.74411 9.06935
+0.575831 1.02699
+2.47315 3.0331
+4.2872 4.59813
+5.82293 7.03604
+2.08982 2.25319
+7.3626 8.41994
+1.23867 3.10885
+7.397 7.78026
+6.78837 7.32977
+0.829368 1.56617
+9.23075 9.35172
+6.76562 7.23578
+0.42851 0.839401
+4.83626 5.5852
+0.150032 2.45322
+2.16811 2.47439
+5.60245 5.81188
+7.43707 7.46519
+0.167317 1.44642
+4.9732 7.58343
+8.67391 10.2957
+0.318592 0.639284
+3.365 4.25929
+7.83683 8.88895
+5.20557 5.30114
+0.940347 1.74022
+5.39149 7.13464
+1.10826 1.27913
+1.9147 2.31276
+0.935283 2.49181
+3.67898 4.33336
+5.10125 6.07734
+0.348807 0.839242
+0.310986 0.58381
+2.03594 2.43119
+5.49755 5.94215
+1.94885 3.34409
+9.66806 9.97883
+4.79099 5.45155
+4.81158 5.30001
+5.17667 5.90019
+9.64168 9.99177
+1.14899 1.22792
+0.466236 0.952985
+6.93679 7.72282
+2.97285 3.23931
+9.22517 9.24315
+7.25783 7.46414
+4.37304 4.51815
+4.01069 4.02979
+0.843132 1.80443
+4.78572 6.04237
+6.02471 6.38074
+4.99536 7.76852
+4.97442 5.17384
+0.731231 1.49497
+2.2797 2.90845
+9.13211 10.341
+4.19545 5.08597
+6.13127 7.09817
+2.65124 5.02208
+5.40235 7.12912
+6.86258 6.91991
+8.24317 8.55273
+7.6877 7.69875
+3.33922 3.53776
+0.089484 0.40755
+1.34909 1.68726
+8.43113 8.72899
+4.90038 5.55994
+0.308089 0.490555
+5.42628 7.1433
+2.11647 2.56987
+3.56934 4.1619
+8.35794 9.24416
+8.8645 9.69151
+5.10701 6.0036
+7.66385 7.8412
+2.89952 4.55221
+6.01533 6.38269
+-0.945182 1.07304
+1.79537 1.85576
+-0.0313338 0.501067
+8.85689 8.87522
+1.61143 1.7773
+9.53229 9.84943
+3.51669 3.81091
+-0.448016 1.10903
+3.16889 3.439
+4.30534 4.36372
+6.20158 8.09618
+2.20949 5.23616
+3.29459 4.17079
+3.90101 4.05611
+5.89688 6.85702
+0.734474 1.04471
+7.84341 8.02702
+8.69431 10.1086
+9.73194 9.92195
+7.63516 7.70631
+8.51416 9.33072
+-0.0712911 0.361001
+2.76979 3.22708
+5.34404 7.5165
+5.64429 5.79865
+0.0755012 0.363483
+5.05631 7.27153
+-0.60164 1.09659
+0.457228 0.876186
+4.27092 5.38033
+8.63891 9.04134
+6.29608 7.04242
+8.97795 9.93013
+5.00385 5.57737
+8.48068 9.44885
+7.35543 7.78492
+3.55698 4.01035
+-0.268117 0.649128
+0.633184 0.66254
+4.54216 4.81132
+6.15574 6.35276
+4.3663 6.36286
+1.45231 2.07039
+7.37031 7.9157
+5.30961 6.3506
+5.80757 5.81762
+4.91767 4.97519
+3.45667 3.60965
+1.7934 2.3807
+4.33289 5.41047
+1.16021 1.29979
+3.75172 4.53521
+3.10835 3.1275
+2.68167 3.00314
+4.10639 4.24094
+4.90776 5.74069
+7.30861 7.86666
+3.52822 3.70998
+5.22222 5.28033
+0.00866476 0.326377
+7.73247 8.61481
+4.25231 4.77499
+8.41148 8.54604
+2.95389 3.63497
+7.76126 9.69124
+6.41218 6.69682
+4.86285 6.44503
+1.84783 1.9065
+4.08788 4.11706
+9.17473 9.50133
+5.15091 5.78649
+1.27694 3.33579
+2.92648 3.37855
+9.37392 10.5123
+4.81878 6.34991
+8.38702 8.83716
+5.07771 6.33618
+1.47438 1.75588
+6.20935 7.11166
+1.08286 1.38
+1.40048 2.0157
+6.60355 7.26234
+5.28396 5.90003
+8.9525 9.59216
+-0.0305792 0.544664
+-0.571491 0.735471
+6.287 7.03679
+5.35225 5.4104
+0.095812 0.605976
+4.33523 4.4048
+6.13288 6.80949
+4.14654 5.5505
+-0.213214 0.260592
+7.56188 7.83088
+7.8132 8.65963
+8.02469 9.3254
+0.00641711 0.420562
+7.49269 8.15849
+9.26359 9.4366
+5.74008 7.86317
+8.62729 9.78825
+4.28946 6.20553
+3.19174 4.54991
+4.16992 4.25203
+2.46138 4.11583
+1.92971 2.16701
+3.43028 3.80842
+6.98297 7.28388
+7.51884 7.55947
+7.79102 9.44326
+0.416342 1.32311
+9.35398 9.85201
+8.72997 9.10384
+6.23398 6.88888
+7.46984 7.4875
+5.92603 6.66938
+1.77761 2.58697
+0.835966 2.06139
+9.63284 10.3565
+6.13677 6.42768
+0.151375 0.382404
+8.10315 8.13441
+5.52283 6.28113
+1.00873 2.44226
+2.5239 3.89871
+5.40599 6.72653
+3.43407 4.0068
+1.22444 1.26601
+7.04395 7.80599
+3.75987 3.77123
+6.43597 7.06249
+8.34435 8.87418
+8.25853 9.04009
+0.415321 2.32725
+0.639046 1.75525
+7.83226 8.12538
+9.55429 10.2734
+3.658 4.35867
+1.8588 3.93292
+7.38922 8.16823
+8.53626 9.23024
+0.848152 1.13412
+6.31667 7.2348
+3.81584 4.62621
+9.24748 10.7408
+2.75953 2.95288
+5.70188 6.62278
+4.22202 4.45368
+3.24616 3.88779
+1.05053 2.09846
+8.67718 9.52693
+2.14107 2.71959
+1.10518 2.69689
+5.01476 5.54139
+4.69244 5.20358
+8.88285 9.93012
+2.55228 2.77382
+6.72118 7.50057
+7.58999 8.71573
+1.83763 2.13481
+4.86135 5.27995
+4.39794 5.42117
+1.42512 2.98262
+4.80489 5.34701
+2.7993 5.13702
+7.52122 8.41729
+9.08613 9.37543
+1.1555 1.3935
+7.34071 8.0428
+8.33361 8.40349
+2.514 2.54741
+8.25732 8.41758
+1.65337 2.57964
+4.20527 5.06565
+0.723443 1.06987
+5.85836 6.64701
+7.908 8.07591
+5.01839 7.0105
+3.15908 5.10364
+4.3574 6.67192
+2.06662 2.46437
+7.5595 8.9108
+7.94919 9.20231
+4.11889 5.49714
+7.28151 7.51881
+1.30685 2.09479
+3.08479 4.47364
+4.50916 5.91511
+7.31605 7.63868
+3.14469 4.07035
+8.38431 8.87993
+7.42277 7.64967
+4.22472 4.26323
+8.49871 8.90746
+7.44431 8.09014
+8.35213 8.7504
+3.85634 4.37584
+9.12975 9.77042
+-0.326056 1.17374
+3.53802 4.06211
+6.81775 6.8467
+8.19106 8.64489
+4.20526 5.37727
+2.74081 3.72143
+0.49642 1.10775
+7.76606 8.00524
+4.79825 6.38926
+1.99478 3.20484
+4.69853 6.29508
+5.73058 7.18643
+8.11708 8.64257
+9.62375 9.84838
+5.95271 6.38989
+9.0193 9.93848
+3.91188 4.10834
+6.065 6.60546
+2.62354 3.60072
+8.53665 9.06824
+6.40691 7.44693
+1.62959 2.5825
+9.4128 9.87031
+6.24697 6.62684
+3.31136 3.45626
+4.80347 4.9935
+9.21711 9.53416
+-0.491547 1.72886
+4.75174 4.87217
+0.86329 1.53331
+2.4586 4.2578
+5.10301 6.2815
+5.08302 5.86788
+6.12434 6.78948
+9.68323 10.0973
+3.52576 4.40424
+4.9925 6.89084
+0.846638 1.80782
+1.09709 1.7685
+8.38592 9.03312
+9.56243 10.2682
+5.03592 6.6429
+7.8609 8.04599
+1.21404 1.31975
+7.47824 9.2063
+1.47849 2.68795
+1.2909 2.06381
+5.21288 5.54616
+6.27218 6.99345
+4.77622 5.08494
+3.30665 4.14735
+3.38947 3.55545
+7.71197 8.53162
+1.10565 1.50806
+0.7889 0.984689
+2.88598 3.33244
+5.70958 5.81232
+5.16567 6.24511
+6.91255 7.25784
+-0.401084 0.753156
+4.81138 6.20671
+1.67301 3.58501
+7.19478 8.25624
+1.24428 2.2523
+4.31631 5.33357
+0.589196 1.02017
+5.69207 6.97803
+2.3854 3.25501
+4.1362 4.91135
+8.9932 10.3184
+7.95871 8.45158
+4.03213 5.26347
+3.09506 4.06425
+-0.0310321 0.738171
+7.35733 7.61512
+2.48488 2.96253
+4.72098 5.69867
+5.5395 5.96914
+3.17854 4.283
+6.50012 7.33744
+1.93266 3.00941
+1.49319 2.71826
+9.0187 9.13099
+7.15259 7.40691
+8.71939 8.93257
+1.57203 3.11249
+0.933429 2.16903
+2.99663 3.71667
+2.06144 2.81311
+7.9072 8.2103
+0.490069 0.614737
+7.84111 9.70812
+1.15363 1.6421
+1.15475 1.43307
+8.2228 9.92116
+7.76825 8.40724
+6.15122 7.31322
+7.1427 7.49294
+2.30516 2.7019
+2.03336 4.03387
+8.3918 9.85707
+8.57214 9.59345
+3.8057 4.1119
+8.9223 9.38556
+9.32051 9.60504
+7.13349 7.69154
+1.71855 2.00425
+5.10333 6.0605
+-0.149137 0.80973
+0.466224 3.1698
+4.19165 4.29604
+1.64018 2.3161
+9.40397 9.89211
+5.21738 7.06323
+0.525526 1.0331
+8.06992 8.08704
+9.81539 10.0915
+9.33514 9.50521
+5.95494 6.54519
+9.21466 9.35909
+4.96603 5.18164
+3.89238 5.53056
+5.04546 6.32916
+8.63758 8.65805
+7.03226 7.57074
+0.32029 1.10893
+0.194176 0.992252
+8.79061 9.50848
+0.239137 0.300717
+7.7234 8.43747
+3.43679 4.28954
+8.31382 9.18928
+1.39612 1.67253
+6.95733 7.39846
+1.29129 2.12185
+7.93611 10.2305
+3.67764 4.17562
+1.38149 2.42407
+2.2847 2.48412
+9.28655 10.0661
+2.94115 3.21326
+7.04238 7.91536
+5.06012 5.61526
+8.95611 9.91736
+2.18524 3.44007
+1.0692 1.10987
+4.37019 5.18443
+5.82014 6.441
+1.68642 1.70458
+4.93783 4.9535
+9.42003 10.1196
+-0.356795 1.30987
+1.37778 2.80527
+9.54126 10.1758
+8.67413 10.5984
+0.914291 2.0697
+6.08696 7.09832
+2.82202 3.31103
+4.07818 5.50195
+2.85223 3.67449
+6.03006 7.10863
+8.84655 10.528
+5.08031 6.2001
+1.00272 2.74191
+0.169102 0.223253
+0.025089 1.26534
+-0.203339 0.64667
+0.899666 1.48885
+0.74235 1.49976
+6.28212 6.80412
+5.65159 5.93106
+2.1686 2.7445
+5.02936 5.43091
+8.07124 8.80499
+8.65696 8.95802
+7.68394 8.365
+8.65062 8.69146
+0.058248 1.19535
+5.74545 5.84057
+6.05244 6.52152
+9.3878 10.3328
+7.71379 8.88044
+3.32961 4.54105
+8.51987 8.62854
+4.10821 4.16902
+9.47639 9.78723
+4.72417 4.84793
+2.32867 2.94068
+9.52275 9.57815
+5.32867 6.96127
+4.32462 5.1508
+4.46919 6.45961
+8.62661 10.9533
+0.0244049 0.56156
+1.49207 1.73164
+7.03156 7.81168
+5.88587 5.89732
+8.43632 9.65085
+7.52612 8.06726
+4.82464 5.6193
+3.8062 3.90356
+2.42708 2.51646
+2.06458 2.45877
+0.427218 2.24283
+7.42616 8.11513
+0.617708 1.01833
+8.74218 9.03592
+-0.213346 0.599112
+9.05291 9.59124
+5.20365 5.54277
+2.85289 3.99509
+3.08468 4.58456
+0.136826 0.685254
+3.87191 3.99021
+0.970836 1.49673
+2.44355 2.53584
+8.88615 9.94545
+3.90591 4.52798
+1.52518 2.50658
+7.21268 9.07745
+0.767314 1.23833
+8.62975 9.42367
+6.71524 7.67686
+7.61205 8.48292
+5.312 5.65087
+4.75627 5.96261
+7.90674 8.0753
+7.37887 7.71478
+3.74483 5.52984
+2.7187 2.85544
+4.7902 5.62159
+2.89671 5.19815
+7.37268 8.72506
+7.11916 8.28301
+9.19814 9.75279
+1.2085 2.60113
+-0.632009 0.889734
+7.99858 8.39743
+6.48346 7.10822
+6.30616 7.06648
+5.56514 6.51183
+2.41505 3.56224
+6.83749 8.84447
+9.05111 9.46906
+7.77476 8.4091
+6.11378 6.31232
+4.42882 5.69917
+5.88107 8.052
+3.75437 4.08893
+2.70487 2.77664
+7.26028 9.77257
+1.44621 2.73267
+2.0384 2.60397
+2.95402 3.68949
+6.04766 6.4054
+0.716046 1.77698
+1.5084 1.72988
+2.58426 2.80376
+8.05255 8.59793
+6.06336 6.20709
+3.29813 4.31473
+2.53477 4.92554
+8.78711 9.88634
+9.26627 9.82082
+6.70683 6.81257
+8.25978 8.99788
+7.28054 7.64237
+5.14556 5.20711
+4.20431 5.87866
+8.96592 9.97597
+4.06216 4.41519
+8.49174 11.0491
+5.24547 6.03094
+4.65885 5.95835
+5.09606 6.34846
+6.30463 6.65933
+6.48022 9.56168
+1.38088 2.98143
+0.486911 1.16767
+8.16327 9.34821
+0.768885 0.782616
+4.51699 6.08086
+9.55086 9.70242
+2.86802 3.72489
+1.88496 2.18749
+4.31876 4.33726
+8.38871 9.45631
+8.80634 9.36198
+5.232 6.42399
+9.86919 10.0492
+3.61902 4.53086
+3.13118 4.42364
+2.69395 3.1816
+-0.303144 1.32309
+4.28871 4.36739
+3.8631 4.08745
+4.9291 4.98968
+7.14273 7.25505
+2.56623 2.59122
+6.01753 7.72265
+6.76051 7.53954
+6.76933 8.14741
+0.933137 1.83537
+7.9357 8.57904
+5.55847 5.59984
+4.25563 4.3345
+4.28087 5.21348
+8.88534 9.06554
+1.88575 2.94002
+5.26613 6.04562
+0.514249 1.45538
+0.130644 0.501645
+4.30564 4.86337
+1.57246 2.38451
+0.303814 1.77474
+4.22675 5.02783
+6.75381 6.78634
+5.64096 5.9725
+6.76159 6.98142
+2.35569 2.39119
+3.29794 4.95962
+6.55055 7.03366
+5.31474 5.69404
+9.01335 9.16988
+8.58306 9.37831
+7.06522 7.07778
+1.14695 1.74744
+3.15507 3.22865
+5.5925 7.14168
+2.14679 3.68767
+7.54789 9.71878
+1.5667 2.08742
+-0.604726 1.66718
+0.306012 0.54799
+2.61731 5.08257
+0.80957 1.74655
+8.22035 8.81637
+4.401 5.449
+8.89389 9.01988
+4.80574 4.9322
+2.19895 2.88565
+9.06688 10.8095
+1.23727 1.58699
+7.14836 7.92057
+4.65155 6.15149
+0.563061 2.16535
+7.66764 8.0341
+8.32324 8.42774
+4.54541 5.11069
+8.0237 8.09992
+7.26113 8.09404
+2.55196 3.20317
+8.1236 8.60951
+1.90437 3.23006
+0.888133 2.50365
+1.13618 1.69444
+2.60463 3.01696
+7.2311 7.73229
+5.06501 6.01487
+3.9893 4.10116
+1.45278 1.56937
+2.74601 3.80778
+9.40705 10.3843
+3.02533 3.52213
+8.7097 9.2268
+8.73145 10.5552
+2.56059 3.5742
+6.27328 7.57089
+3.23291 3.30811
+6.33804 7.20304
+7.63648 8.34564
+9.82053 9.93597
+3.14924 4.58629
+5.28253 6.33733
+1.99411 2.04104
+0.305593 0.420302
+5.39668 6.64188
+7.99992 9.37823
+5.25169 5.65987
+0.109291 0.875734
+3.08589 5.50296
+6.51358 6.62588
+5.34687 5.58204
+0.043587 0.932787
+-0.0264143 0.28677
+8.09722 8.37087
+5.46612 6.30285
+2.89146 3.49892
+6.73278 7.11762
+5.03068 5.90176
+1.7459 2.87727
+0.207707 0.448933
+7.00453 7.63105
+6.02789 7.87991
+8.1917 8.95854
+7.49014 8.73215
+8.48402 9.22063
+1.0467 2.4687
+0.241901 1.38802
+2.65867 2.81258
+5.46725 6.59448
+3.40578 3.75607
+4.2495 4.26392
+5.6608 6.213
+2.99705 3.88852
+6.17231 6.72018
+-0.17415 0.492739
+4.89596 6.49895
+4.68876 6.60218
+-0.0510568 1.46013
+8.87099 9.28179
+3.98894 4.5838
+-0.20015 1.59214
+8.80756 9.51828
+7.74921 8.53538
+-0.380041 0.440574
+2.69409 3.91466
+2.42747 4.56745
+6.25881 7.75647
+7.2941 7.77193
+2.60806 2.90251
+0.325028 1.22219
+8.29921 9.01416
+3.85524 5.45451
+5.2612 5.71877
+2.92849 3.73937
+8.6242 8.70808
+7.28153 7.35797
+1.65378 2.87917
+3.46034 4.24143
+3.99989 4.3199
+1.15266 3.60328
+4.8464 6.4887
+4.67021 4.68421
+6.96861 7.31554
+0.00723008 0.817047
+7.59589 8.88352
+5.56725 6.08465
+9.02409 9.19527
+5.81681 6.74193
+1.98221 2.98678
+8.76392 10.7646
+4.3903 4.50531
+1.0186 2.01838
+2.66674 2.74505
+2.41105 3.44579
+8.98295 9.18995
+8.14385 8.98779
+2.69613 3.60144
+0.445704 1.51319
+-0.634889 0.860409
+6.32737 7.10112
+7.88824 8.06838
+3.76271 4.43529
+7.66487 9.30221
+8.73974 9.59191
+3.08965 3.43006
+8.9518 9.96154
+0.727194 1.55228
+6.35214 7.12234
+2.71987 3.22996
+7.12786 7.4379
+-0.346107 0.837469
+8.47103 9.05987
+9.15744 10.628
+8.3237 10.093
+1.15052 1.16223
+6.95959 7.35423
+7.69488 8.44145
+4.21694 5.65913
+3.10562 3.37534
+9.73437 9.78252
+5.0458 5.53479
+7.80485 8.5163
+2.0442 3.75748
+-0.459177 0.498702
+7.38032 8.32195
+0.909659 2.11175
+7.08657 7.56877
+1.84997 3.10206
+4.2517 4.53819
+6.71655 7.04117
+1.06604 1.29153
+2.69273 3.31633
+2.19732 2.84826
+-0.723032 0.774865
+-0.289734 1.44645
+5.87194 7.71917
+1.01576 2.83685
+0.981927 0.998558
+3.07172 3.73201
+-0.112577 1.12642
+3.54063 3.67007
+7.21571 8.53815
+0.109957 0.839052
+6.86046 7.17211
+9.40878 9.69478
+0.989391 1.69046
+8.22287 8.8147
+1.48203 2.83308
+5.86418 8.0993
+0.749797 1.09852
+1.82996 2.50847
+0.28178 0.496017
+2.60694 3.00872
+8.6254 10.1698
+6.11641 6.41537
+7.19529 7.21342
+1.8214 2.17517
+0.991201 1.3788
+2.84924 3.12362
+5.75821 7.13843
+6.7106 7.35932
+6.6055 6.6829
+7.97105 8.58613
+1.56087 1.95845
+6.36782 7.10926
+2.43382 2.74699
+7.40509 8.8029
+8.42459 8.63142
+6.26128 7.55043
+4.6465 5.4431
+2.93214 3.29517
+0.157602 0.545214
+6.05931 6.72188
+1.7908 3.11731
+2.39217 3.58736
+7.07421 7.43852
+8.13419 9.74991
+9.53094 9.65319
+2.35726 3.68679
+-0.108561 0.874853
+6.87136 8.84157
+3.40803 4.58835
+7.50448 7.89932
+4.29762 5.53867
+0.863755 2.30032
+7.24847 7.95342
+2.89618 2.98849
+-0.928261 1.61922
+0.974105 2.91262
+6.99271 7.25856
+3.60995 5.01832
+1.26865 1.79704
+9.62217 9.80719
+1.02656 1.35514
+7.16502 7.32934
+3.12092 3.28857
+2.81374 3.84317
+8.73626 10.7585
+3.36982 3.61446
+5.08437 5.78949
+2.90218 3.0862
+9.2309 10.2523
+5.0967 5.48926
+9.73088 9.92708
+1.54013 2.78301
+6.73707 10.0789
+6.41735 6.838
+2.57935 3.04287
+5.719 6.70352
+5.42474 5.62732
+4.23904 4.69787
+4.16528 4.28736
+7.73477 8.84143
+4.90346 5.87324
+5.1969 6.14087
+3.48793 4.14182
+0.648493 1.07987
+6.88101 6.97932
+7.67342 8.74418
+5.16759 6.3871
+9.16292 9.49569
+8.77422 10.4817
+-0.358149 1.04178
+9.54616 10.3424
+3.12027 4.26504
+3.81223 4.73004
+1.2386 3.32247
+8.21963 8.42629
+9.10312 9.51016
+6.96107 7.43045
+2.98587 3.12365
+0.198165 0.211424
+5.19716 6.10856
+6.10553 6.3327
+3.98176 5.11099
+1.02512 1.04242
+4.27949 5.71947
+-0.457956 0.538807
+7.99877 9.48581
+5.88782 7.73303
+1.21496 2.85825
+8.46962 8.7862
+9.50182 10.1527
+5.85223 7.76234
+2.48368 2.52439
+2.35426 2.77405
+0.397056 0.482679
+0.160423 1.15786
+4.47109 4.8633
+4.74523 5.84196
+5.39177 5.75218
+9.00754 10.3249
+3.74813 4.03273
+3.08633 4.08426
+4.62672 5.29012
+1.92055 3.01442
+3.79279 4.5603
+9.52431 9.68334
+2.8126 3.32592
+-0.313688 0.490785
+9.06695 9.36835
+2.60321 4.14185
+4.40231 4.99535
+9.32205 10.1041
+5.3879 5.44349
+0.308728 1.0956
+9.05758 9.08283
+1.90313 2.50914
+7.1391 7.59459
+7.47357 7.95993
+1.25267 2.34827
+3.33222 4.36522
+8.78126 9.38611
+0.635979 1.62283
+8.3492 8.57545
+-0.572557 1.60517
+8.28855 9.03937
+3.47934 3.90271
+4.64498 5.43501
+0.872177 2.62011
+7.3893 7.58876
+2.14063 2.88063
+1.69361 1.75261
+4.19727 4.86483
+0.814676 1.27639
+8.19899 8.58203
+8.3394 8.99896
+9.31768 10.1694
+4.99218 5.19949
+4.2459 4.75266
+9.6314 9.64249
+4.038 4.75708
+7.92398 8.8579
+4.30747 4.54161
+7.51776 7.78258
+3.77611 5.1385
+3.53275 3.90278
+1.78619 2.12474
+3.18736 3.55159
+8.36759 8.52587
+5.52526 5.7843
+7.64174 8.09773
+4.37925 5.85699
+3.94295 5.31298
+-0.464623 0.795523
+7.02408 7.7048
+0.0690458 1.0008
+1.05445 1.32797
+0.126093 0.712612
+-0.809973 1.33869
+3.45568 3.96532
+0.433875 1.78679
+8.94667 9.65034
+1.98575 2.76936
+6.47974 7.67912
+2.74186 3.34153
+7.82655 9.32112
+4.71441 6.14932
+0.741177 1.76987
+8.05021 8.23239
+0.818503 0.882959
+7.13341 7.43399
+8.45939 8.93395
+4.44558 5.70161
+3.05657 3.86962
+8.75469 8.85537
+1.0922 2.23475
+6.00278 7.51598
+0.0530968 0.582206
+4.01788 5.48223
+1.30808 2.81869
+8.18848 8.59771
+2.43691 2.80685
+8.54045 10.6178
+7.58523 7.91634
+6.70183 7.49962
+5.81501 7.29783
+0.661853 1.58021
+6.50997 7.20411
+0.334292 1.40252
+-0.329585 0.742961
+2.38756 2.48935
+2.78114 3.58788
+7.97227 8.55379
+3.36753 4.57472
+5.14486 5.31457
+3.99691 5.47708
+6.36404 6.8741
+3.22519 4.63321
+1.80583 2.21824
+8.20747 9.42947
+5.90819 6.94368
+6.58559 6.85933
+2.99108 4.03832
+7.20963 9.6252
+3.10244 3.87265
+7.23018 7.91389
+7.18084 9.61063
+6.74145 6.86507
+8.40721 9.5751
+7.12352 9.45614
+2.06653 2.51786
+4.9155 5.04183
+1.34304 2.92777
+0.571315 1.17285
+8.05396 8.28214
+5.29142 6.24974
+8.93797 9.23051
+4.6108 5.45568
+6.46124 7.53124
+3.16695 4.37505
+2.76803 2.82764
+4.1531 4.84927
+8.19433 9.00383
+5.12114 6.22952
+2.2542 4.74849
+0.621192 0.871537
+1.37403 2.28895
+0.305704 1.3287
+8.70471 9.53085
+2.29164 3.56607
+2.42106 3.37382
+1.14309 2.03085
+9.17624 10.3281
+1.88864 2.70161
+0.932647 1.2473
+5.72385 6.55694
+5.25496 5.38675
+5.85771 6.69626
+7.37247 7.93094
+7.73499 7.98782
+4.91417 5.27506
+3.51127 3.84403
+6.96198 7.27686
+6.51176 7.74735
+4.19954 5.08222
+1.58115 1.83842
+2.11889 2.3408
+1.94427 2.00422
+5.59977 6.86555
+9.46942 10.3491
+9.18904 10.1988
+1.71938 3.01547
+4.3515 5.4951
+2.53792 5.85188
+0.3884 1.36228
+5.02469 5.62368
+6.82402 7.72203
+-0.320478 0.821539
+0.0885656 0.248783
+3.01725 3.44385
+6.63776 7.74386
+6.57899 7.9332
+4.82656 5.18327
+3.91909 4.43955
+0.0708319 0.101395
+2.59796 4.22775
+-0.433109 1.26978
+-0.434274 0.624336
+3.22018 4.25282
+4.63351 4.776
+8.889 9.56332
+5.35952 5.46531
+8.73832 9.22756
+2.03432 2.34194
+0.477994 1.43353
+1.2029 2.86844
+5.09945 5.76855
+8.89076 9.01771
+5.30284 6.31732
+1.56375 3.17543
+5.4773 6.36897
+5.30934 5.35073
+7.20096 7.7075
+6.51046 7.12888
+9.37127 10.0531
+-0.0484323 0.348687
+1.83241 2.2143
+8.28633 8.60462
+2.51014 3.65817
+3.27798 3.58685
+1.41567 2.23302
+7.50794 8.10378
+-0.0579575 2.10373
+-0.856005 0.96621
+2.96238 3.48434
+1.36304 1.90594
+2.9997 3.41214
+7.2763 8.087
+1.69207 2.74419
+5.41625 5.83917
+9.22139 9.53794
+1.17477 1.51576
+6.74597 7.90871
+0.678913 1.73759
+5.70863 5.7214
+8.88411 8.93377
+3.44409 4.54586
+0.958963 1.53878
+8.29904 9.3867
+2.52433 3.60238
+8.16257 8.21986
+2.11395 4.23455
+6.85451 7.40364
+0.20286 0.639736
+2.05848 2.49034
+0.547013 0.77336
+7.83609 8.11903
+2.16539 2.29286
+3.94232 4.31368
+8.53475 8.54519
+4.77462 6.4158
+4.08395 4.68214
+1.56902 2.23811
+1.61337 3.11768
+1.51181 1.65521
+0.0821202 2.09269
+4.77622 5.16243
+4.62148 5.75837
+0.613158 0.952696
+1.89128 3.04792
+7.57509 8.92834
+2.70465 4.2941
+4.95906 5.12442
+8.0927 8.17277
+0.796512 1.3215
+5.74134 6.45291
+9.68479 9.78978
+5.89261 6.2292
+6.11377 7.9339
+2.20571 2.67129
+3.46228 4.27058
+5.26723 6.2653
+4.04391 4.26562
+0.380382 0.818995
+7.58108 7.7618
+5.02375 6.13042
+4.20178 4.66297
+2.83039 3.0221
+2.36072 2.45545
+6.68854 9.20726
+2.91388 3.42464
+9.33373 9.73449
+7.75844 7.94625
+2.72414 2.89669
+6.65438 7.96623
+1.00647 1.05385
+2.63018 4.9432
+0.152187 1.60259
+8.86332 10.3631
+2.31523 2.6815
+8.00509 8.61794
+1.69711 1.75938
+8.65442 8.73697
+4.23917 4.32001
+9.66851 10.1925
+6.93943 7.49814
+8.51986 9.03561
+2.10317 2.5175
+0.539549 1.75683
+6.29423 7.93236
+8.64345 9.54556
+1.44069 1.6814
+9.10017 9.77088
+6.16731 7.40496
+0.570043 1.93192
+2.65761 3.88544
+1.46895 1.52688
+-0.369067 0.540647
+2.04868 2.72065
+2.1286 2.27858
+5.90157 6.16431
+1.55581 2.51851
+3.29705 3.81635
+3.4137 4.10991
+8.48699 10.1364
+8.43437 9.4642
+-0.037842 1.47589
+0.541735 1.7805
+0.499576 2.36065
+4.1415 4.72435
+4.77598 6.16872
+3.30388 3.61854
+6.43796 7.85966
+5.27157 5.70871
+0.919258 2.18653
+4.00961 4.07902
+3.78201 4.23596
+4.50464 4.791
+3.70253 5.30916
+8.59228 8.70036
+4.31103 5.69049
+5.31775 5.34378
+2.24198 2.64194
+6.66937 7.41675
+7.21753 8.42057
+4.35399 4.52398
+6.90698 7.30084
+3.44296 5.44378
+3.36719 4.89951
+3.38624 5.33856
+3.44877 3.53693
+5.81398 6.3016
+7.24655 7.33127
+3.86142 4.53134
+3.85894 6.88112
+6.45704 6.95699
+6.90997 8.24088
+5.68813 5.7611
+0.699911 0.824323
+6.73758 8.42517
+9.34644 10.07
+0.289929 1.30554
+8.30644 8.49113
+6.47667 7.16073
+4.28093 4.90236
+8.32678 8.57064
+7.09205 7.92316
+5.55599 5.63919
+1.0104 2.22875
+1.72237 3.53668
+2.54699 3.14823
+-0.668582 1.25485
+1.25582 1.37003
+1.35044 1.57807
+6.17083 6.30811
+0.618816 1.20065
+-0.540992 1.21634
+2.06673 4.28857
+9.03398 10.8112
+8.73735 9.22814
+1.30178 2.7599
+6.0221 7.34889
+8.30124 8.52159
+1.92023 3.34545
+2.99331 4.93358
+3.8412 3.93379
+1.455 2.77005
+0.538664 2.1118
+4.16926 4.26053
+0.699746 0.937609
+6.36024 6.6511
+2.34698 4.84463
+8.11109 8.76496
+4.05425 4.67773
+8.2284 9.78568
+1.64423 1.80353
+5.70109 6.93764
+7.2091 9.67224
+4.47015 5.23311
+4.29326 5.36144
+7.93151 8.96494
+4.61889 4.96487
+5.38473 5.43594
+9.35456 9.66489
+1.23737 3.04121
+1.16077 1.19519
+5.91037 7.55679
+6.88344 7.60181
+6.39705 8.65039
+1.61791 2.93041
+9.68285 10.1396
+9.03129 9.05839
+7.0683 8.29875
+7.19624 7.41696
+7.5266 8.38738
+-0.254571 1.01098
+1.30218 2.03222
+7.6432 7.73031
+4.99495 6.04757
+2.79394 3.29394
+0.31505 3.55963
+7.55382 8.18787
+6.00032 6.27664
+1.49355 1.79767
+5.21191 7.19537
+3.47816 3.75801
+2.10272 2.39494
+1.31887 1.93829
+7.14313 7.63055
+7.3141 7.77239
+6.03409 6.17265
+4.24946 4.53165
+0.0569272 2.56109
+8.44766 11.4872
+4.22879 4.28007
+6.92817 6.98775
+8.08859 9.0206
+-0.0748418 1.48605
+6.56698 7.0488
+4.46624 5.4952
+3.66085 4.05778
+6.44711 8.93943
+2.45735 2.49339
+0.0606673 0.416191
+-0.183838 0.750464
+7.08561 7.67275
+6.65423 6.90808
+0.0598735 0.364072
+5.2228 5.41543
+3.14042 3.19043
+2.40069 3.02829
+1.80441 2.1849
+3.47491 3.95775
+5.49162 6.63934
+2.03332 2.32288
+3.17234 5.25109
+2.58274 2.59951
+9.37994 9.97097
+4.71509 6.77966
+5.18915 6.84982
+6.12508 7.04354
+0.811202 1.98808
+1.46825 3.66984
+2.44386 4.15623
+7.60169 7.68242
+-0.43208 1.22222
+5.40569 5.66153
+0.391742 1.79408
+6.77742 8.43115
+9.12828 9.3851
+6.39541 6.80626
+5.17919 6.29326
+6.57263 8.12999
+3.439 4.62236
+8.40525 9.80086
+7.60986 7.70099
+1.32123 1.44213
+0.526574 1.92338
+9.57195 9.79591
+9.52103 9.63159
+3.79775 4.20608
+4.74446 4.89606
+9.00085 9.60025
+1.26279 2.08363
+4.13639 4.49232
+6.59409 7.18315
+1.0637 1.78425
+5.4213 6.87661
+4.24407 4.30987
+9.79732 10.1204
+4.24597 4.83541
+8.47705 8.5145
+4.34095 4.93842
+5.68168 6.96086
+7.48654 7.91072
+8.32005 8.45111
+5.24065 5.3032
+3.25982 3.94442
+5.58106 6.18764
+4.4243 5.58619
+6.36467 7.07082
+2.70051 3.56014
+5.94534 6.87688
+7.33638 7.7645
+4.57067 5.86652
+7.83993 8.36854
+9.03617 9.11629
+7.44672 7.95614
+6.05876 6.3594
+4.02496 4.97119
+3.01438 4.36109
+7.4932 7.92357
+7.51661 7.53354
+7.60692 8.49678
+1.76088 2.59103
+7.08578 7.39566
+4.83605 5.42933
+-0.186592 0.847663
+1.15879 2.33996
+8.11815 9.35472
+6.02584 7.62549
+0.881084 1.00015
+5.39067 6.27026
+0.910755 0.97444
+5.32174 5.65652
+7.69388 8.24928
+3.40521 3.7615
+5.60321 6.92388
+8.25096 8.34324
+0.00108913 2.16897
+0.508035 0.66573
+-0.0517816 0.0876197
+3.26472 4.30955
+1.99494 2.14903
+2.089 4.32368
+5.17378 5.75964
+2.8779 3.54361
+6.56459 6.90645
+1.43196 2.88337
+1.48917 1.94009
+3.40807 4.25784
+8.0334 8.13458
+8.22254 9.6175
+2.96308 3.14349
+5.04768 5.71252
+3.9977 4.72614
+1.44384 1.60782
+7.78147 8.19817
+7.03854 7.50085
+5.90747 7.06091
+2.89169 4.31831
+2.46167 3.16529
+9.13228 9.1504
+2.57975 3.05313
+5.75976 6.52958
+8.10499 8.98982
+8.43162 8.76377
+0.422917 0.668254
+1.69046 2.09962
+5.50386 6.66954
+0.0897183 0.564514
+3.50103 4.1041
+3.91918 4.52559
+4.39297 6.18876
+3.90396 4.90868
+9.69362 9.85528
+6.27994 6.95062
+8.01563 9.09433
+8.25641 9.72944
+8.22072 8.35423
+1.27059 1.61352
+8.53312 8.96235
+3.49185 3.9042
+5.79579 6.046
+0.489013 1.2147
+4.00074 4.177
+2.66625 2.93036
+7.20267 9.34958
+7.44659 8.54707
+8.99915 10.1143
+7.59922 7.82031
+8.38502 9.03222
+0.160075 0.294398
+4.14549 4.3927
+9.35554 10.248
+-0.159435 0.467922
+2.17272 3.72336
+8.63811 8.79976
+4.23215 4.66461
+6.99516 8.2035
+6.45212 7.88791
+6.20141 6.28633
+7.69396 8.82204
+4.50807 5.96315
+8.51725 8.89286
+0.939492 2.12064
+7.4615 7.62666
+8.46681 9.11769
+3.92828 5.40884
+9.05622 9.40015
+0.229625 0.860838
+2.05694 2.52113
+-0.217927 0.574255
+9.55146 10.3256
+8.31497 8.3417
+3.52833 4.47013
+7.7131 8.52058
+2.15462 2.66777
+5.7674 6.17553
+3.3358 4.10589
+2.06642 3.00412
+2.43387 2.85242
+5.09473 5.84276
+7.3287 7.54846
+4.64254 5.11465
+4.05785 4.93614
+4.25413 5.43513
+2.96476 3.15378
+3.53732 4.48239
+2.34137 4.01272
+2.24879 3.40626
+3.43323 4.49055
+0.871415 1.33068
+0.508929 1.65611
+4.92057 4.94743
+7.03376 7.35193
+5.73043 6.47264
+7.82417 9.93642
+5.24102 5.9178
+2.38528 4.1676
+6.27437 7.19631
+9.29267 9.53262
+4.10098 5.24964
+0.383714 0.833317
+5.97628 7.92987
+8.15799 8.96819
+8.1687 8.29862
+8.16289 10.5149
+7.38205 7.89765
+3.81883 4.15607
+9.46915 9.61755
+1.50756 1.51863
+2.34236 2.84689
+0.398473 1.15027
+1.05302 1.49155
+8.71687 9.35232
+1.0502 1.40395
+7.83685 8.76302
+7.7731 8.46899
+5.56618 5.97458
+2.90035 4.12043
+6.40644 6.68706
+3.95201 4.37075
+7.1831 7.67748
+9.42789 10.0914
+5.45198 7.01256
+8.64609 9.45756
+6.01864 6.31515
+7.64621 8.65533
+5.45654 5.76553
+2.04332 2.75476
+5.49454 5.85693
+6.13688 6.21927
+2.66255 2.7385
+4.8326 6.43772
+9.12704 9.45287
+-0.294488 0.318697
+2.26979 2.35516
+4.12949 5.58506
+3.01416 3.62246
+6.64697 7.10221
+6.44307 6.82985
+4.10484 4.73148
+7.42589 7.6029
+5.33055 5.50395
+2.46474 3.05847
+5.43677 5.59206
+2.13807 2.3988
+-0.38424 1.8121
+0.00916704 0.219304
+8.62621 10.2262
+3.33927 4.51169
+5.45656 7.13165
+5.29253 6.57917
+3.51704 3.9421
+8.63729 9.13361
+3.92626 4.3469
+6.83104 7.52695
+1.22258 2.28191
+0.447646 0.789809
+7.96543 8.54328
+2.24269 2.42571
+4.4161 5.40481
+0.813992 2.21792
+0.694664 0.974182
+6.50017 6.88902
+4.00723 4.65882
+9.05784 9.85062
+6.80276 6.98196
+0.105446 0.379881
+0.416982 0.53336
+9.01269 9.55204
+3.27197 4.23315
+6.7017 6.89704
+4.21512 4.42378
+4.86735 6.90949
+4.52235 5.36878
+2.62205 3.55746
+-0.551048 0.555476
+1.63274 2.71251
+4.26739 4.88085
+8.23129 9.77405
+2.49409 3.96851
+6.66387 6.88886
+7.72383 8.90611
+3.18511 3.27281
+0.73722 1.64435
+7.90329 9.02868
+6.94845 7.06773
+0.509461 1.54928
+4.63885 5.51652
+9.24395 9.5121
+1.00007 1.57161
+5.31349 5.45437
+0.929284 1.71034
+7.05985 7.63288
+7.38418 7.45373
+4.14937 6.5167
+7.49989 8.6081
+9.28313 9.57721
+4.41285 5.1497
+2.96148 3.09221
+1.58256 1.66419
+4.45116 5.43488
+5.70927 7.11699
+3.61105 3.93153
+5.92285 6.63773
+3.50065 4.48453
+7.97338 8.96464
+0.541394 0.965435
+4.46469 5.03925
+7.33695 7.6484
+9.64234 9.6938
+4.16378 4.17886
+7.10741 7.31549
+1.51829 2.62477
+2.1892 2.9684
+9.23767 10.0462
+7.87649 7.97087
+7.05385 7.85952
+6.91232 7.50277
+6.60185 7.81572
+2.27112 3.90211
+5.74452 8.2018
+6.25117 6.26911
+0.525893 1.53447
+0.392956 0.959233
+3.51591 4.71055
+3.10449 5.41156
+7.35964 7.65642
+2.89831 3.54334
+3.42338 3.68259
+4.75496 5.51419
+3.8792 4.48395
+2.18007 4.13236
+3.15798 3.16864
+8.91248 9.21598
+8.54636 9.34037
+7.93697 9.79709
+6.40205 8.9653
+9.67332 10.2252
+4.31771 5.25423
+7.27485 7.3725
+6.22971 7.11411
+1.16538 2.16762
+1.40443 1.6876
+3.14915 4.58847
+7.13761 8.72165
+4.38448 5.88699
+7.98309 9.19449
+8.491 9.05597
+5.55043 6.19359
+2.44006 3.36071
+9.56522 9.79903
+1.86129 2.00225
+1.65239 2.11478
+7.59392 8.4655
+2.44709 2.68538
+6.80932 7.04694
+8.8212 9.4182
+0.276845 0.956468
+8.17008 8.5538
+0.77864 2.06897
+6.78458 7.68692
+0.504907 2.67518
+5.56963 6.9058
+3.42691 3.5414
+2.52065 2.71583
+5.09924 6.26696
+0.223724 0.610039
+3.61258 5.29324
+0.388393 3.11131
+2.87763 4.45415
+8.26438 10.1169
+5.89742 8.17506
+1.89686 2.66651
+5.33662 5.68228
+3.37496 5.67291
+2.25713 2.36127
+0.402185 0.716342
+6.4231 6.79227
+3.22707 4.69314
+7.85092 8.77413
+3.31322 3.72455
+5.43021 7.7167
+8.5883 9.00864
+1.21371 1.71836
+6.35584 7.9706
+5.44438 5.50414
+5.31325 6.11217
+6.08888 6.78794
+7.70604 8.13257
+1.29858 2.48753
+5.54083 6.38817
+8.23948 9.82646
+7.5575 9.0224
+8.00415 9.39763
+0.488455 1.55358
+4.48727 5.20741
+6.46648 7.02349
+0.355939 0.611119
+9.4892 10.2189
+2.61893 2.82832
+0.268193 1.24687
+3.01964 5.19554
+6.29954 7.42795
+8.70796 9.48366
+5.39307 6.64597
+2.47001 2.60746
+5.30949 7.46529
+6.07973 6.16835
+0.764507 1.6144
+7.94123 8.52143
+1.88244 1.96592
+6.42103 7.02205
+7.673 7.73208
+6.66891 7.80675
+6.9305 7.5431
+6.62335 6.93873
+4.34846 5.77621
+0.49878 2.82928
+2.65221 2.80109
+7.45348 7.74372
+4.10129 5.26894
+3.87782 4.19855
+4.62113 5.22658
+2.84558 2.88185
+2.53209 2.86065
+8.94932 9.21967
+6.48046 6.89614
+5.81885 6.66829
+6.49129 6.62015
+8.5663 9.58685
+2.43346 3.89849
+9.36561 9.45886
+1.40232 2.65
+6.66 6.74041
+2.87007 3.49668
+4.71138 6.0184
+5.03638 6.08494
+7.77735 8.14253
+1.0301 1.27179
+2.58337 4.04699
+6.95419 7.89775
+7.81164 8.62551
+3.37669 3.96217
+4.68541 5.58692
+4.79697 6.34841
+3.95334 4.81962
+9.44006 10.0941
+4.91098 5.12386
+8.44273 10.1565
+3.6685 3.73156
+5.07599 5.33357
+-0.138235 0.260923
+2.71154 3.45603
+5.75096 6.16127
+4.4775 4.64308
+6.80557 8.97442
+5.49408 5.76652
+8.31937 9.12886
+3.26479 5.51673
+2.57425 3.03145
+2.42465 3.92714
+7.86505 8.50835
+3.29229 3.43549
+4.08537 4.69709
+7.83885 8.60616
+1.61622 2.72716
+9.10228 9.41907
+8.49325 9.46612
+-0.174644 0.351716
+1.95648 2.14825
+8.28844 8.4428
+4.35888 5.59521
+5.1396 5.22537
+0.330835 0.879566
+6.14972 6.82967
+3.14811 4.89049
+-0.231668 1.08277
+3.13526 4.94545
+8.32674 8.91046
+5.22364 5.48006
+9.84318 9.88416
+-1.07813 1.6965
+1.5095 2.0328
+8.03716 8.2792
+-1.01307 1.46703
+8.22096 9.0055
+8.9917 9.0415
+5.60623 6.74537
+3.15991 3.19314
+3.13863 3.26451
+0.739618 0.896958
+0.67599 1.18486
+3.37355 4.06303
+0.38605 0.410067
+3.03297 3.46387
+3.45867 3.79806
+8.67249 9.53278
+-0.213855 0.787363
+1.52316 2.61117
+8.31846 8.59736
+4.84685 4.94042
+7.41901 7.82623
+4.99029 5.09747
+5.3637 5.37612
+3.22123 4.77749
+3.21668 5.74743
+3.48748 4.59962
+8.00016 8.56269
+1.63631 2.42239
+1.05159 3.72319
+7.22643 7.95706
+5.65694 5.99195
+9.4469 9.66915
+0.857984 1.59867
+4.05869 5.69955
+2.65788 3.07259
+1.8038 3.38748
+3.91934 4.89922
+6.18534 6.70342
+2.85076 3.20176
+8.1897 9.9614
+0.685531 2.05322
+3.31097 4.00809
+8.26537 8.63475
+2.90508 4.07797
+1.25213 2.31285
+3.05752 4.03741
+-0.481913 0.605629
+1.87958 2.19386
+7.28689 7.76316
+4.82075 5.20688
+-0.08115 0.31839
+3.50085 5.89363
+1.31184 3.30269
+4.03137 4.81493
+6.11688 6.13923
+7.93584 8.43321
+4.9838 5.3733
+3.8174 4.11979
+8.56196 9.02824
+2.87809 3.89589
+7.95309 8.05432
+3.47469 3.63716
+6.61009 7.12576
+8.87374 9.79588
+7.31513 7.99089
+5.78084 6.68535
+4.96985 5.59877
+2.20236 2.52473
+0.661014 1.11035
+6.36183 6.58403
+6.67947 6.85093
+-0.242005 0.513902
+0.442966 1.72024
+6.4587 6.74016
+6.67958 8.0837
+8.98524 9.56405
+6.91409 7.59877
+3.73596 3.97705
+5.51193 5.96437
+3.72029 4.50126
+3.09896 3.23616
+8.83312 8.91774
+0.32173 0.486199
+4.21092 4.59955
+2.61616 4.07081
+1.89049 2.65559
+4.96401 5.00629
+9.33156 9.70914
+3.72455 4.61054
+8.91265 9.6563
+7.12401 8.04324
+6.65393 7.25572
+1.04032 1.24927
+3.0174 5.56006
+1.47645 2.26867
+3.49833 4.37416
+6.16877 6.34452
+6.47417 6.90595
+4.27056 4.92535
+7.60439 9.35352
+0.426219 0.754405
+8.5291 8.68579
+5.84717 6.64632
+1.99513 2.5284
+5.10669 7.83064
+6.99006 7.37272
+0.871836 1.5934
+3.6572 4.38875
+0.154617 0.290737
+5.91634 6.50415
+5.33982 5.45293
+6.92602 6.94691
+0.349512 1.61427
+8.02048 8.99943
+5.75598 7.34946
+5.86948 6.51327
+5.11707 5.6181
+6.27656 7.10043
+8.48922 9.73833
+8.35863 8.69746
+6.67829 7.07852
+8.51257 9.50383
+3.76636 4.33757
+4.82804 5.57166
+5.17892 5.61254
+1.35188 1.94069
+6.04969 6.06833
+5.34181 5.82455
+5.71714 6.71234
+0.966462 2.56278
+7.95169 8.15951
+6.53583 6.89673
+6.73246 8.34493
+8.71111 9.76467
+7.53215 9.56537
+0.619041 0.736713
+9.24399 9.50151
+8.57184 9.88538
+2.82682 3.77294
+4.2649 4.84602
+8.6621 8.96426
+8.04469 8.3189
+7.81877 8.42944
+2.78031 5.16589
+1.23076 1.43577
+8.35385 8.49632
+3.27901 3.66774
+0.595941 1.41354
+3.37123 5.16685
+9.11003 9.95515
+-0.25959 0.32069
+1.44153 1.70148
+1.61595 1.70799
+7.8201 8.53423
+8.5402 9.36947
+8.39125 9.8221
+6.2909 6.35773
+0.000933745 0.920054
+-0.21358 0.491025
+4.00874 4.03866
+3.43552 3.55874
+7.85942 7.98987
+7.6681 8.06748
+6.19541 7.14765
+8.53891 8.96393
+2.38789 3.26866
+-0.0162847 1.18379
+1.04764 1.52755
+0.400798 0.55475
+6.32001 6.94897
+5.9692 6.2012
+1.20784 2.67873
+3.58247 4.46372
+-0.229647 0.559407
+5.65979 5.73489
+2.32795 2.78462
+0.706144 1.55147
+4.56527 4.6636
+5.98901 7.20908
+9.27329 9.95152
+1.31296 1.70529
+5.82933 6.41329
+4.17452 5.29881
+4.15885 5.25117
+0.35549 1.3511
+1.0591 2.4463
+8.87597 9.55625
+6.66826 8.57255
+0.355945 1.40613
+5.39729 6.10218
+3.78791 4.37501
+9.53885 9.87184
+2.67568 2.82552
+4.44836 4.52939
+5.96764 7.40168
+6.35578 6.98546
+0.636857 1.19517
+3.3986 5.55448
+1.46493 2.66884
+-0.0676901 1.80921
+7.83396 8.53452
+8.3508 9.04561
+4.03407 6.0066
+0.997538 3.30635
+7.83064 8.03466
+4.57652 4.97461
+3.85952 4.15471
+0.960261 2.1069
+0.522458 1.52989
+8.81627 9.00768
+0.172256 1.29455
+5.69294 6.15752
+6.86391 7.75307
+2.32429 2.95415
+1.83191 1.90364
+0.400855 0.705649
+5.23264 5.39972
+5.19047 5.79842
+2.40804 2.89401
+0.462887 1.43874
+6.70213 7.90229
+4.37985 4.95797
+3.73768 4.30645
+0.756797 2.11146
+0.458724 0.472099
+3.93098 6.47147
+5.5294 6.00365
+2.97106 3.42174
+5.75875 5.80235
+7.96799 9.02105
+5.36254 7.22823
+6.56124 7.39973
+9.49234 9.57879
+2.87576 3.59509
+3.49182 4.28124
+8.68021 9.94486
+9.60199 10.1276
+9.70115 9.81776
+3.09609 3.98732
+5.00113 8.15031
+5.21248 5.74835
+9.23337 9.5696
+2.92777 3.56297
+2.52044 3.04905
+4.79908 5.05762
+1.98626 3.69659
+4.78726 6.20637
+7.39711 7.42831
+4.44319 4.78716
+9.28452 10.0832
+8.00774 8.85707
+9.22947 10.0523
+4.38653 4.86012
+5.15285 5.23106
+8.95413 9.75947
+2.90286 3.32407
+0.348694 1.39738
+-0.0660432 0.293424
+4.09079 4.2922
+6.0407 7.23031
+2.83771 3.72237
+8.54697 9.38898
+2.16742 3.45018
+8.83255 9.30343
+3.59112 3.6531
+7.53452 7.81898
+7.11781 7.19773
+8.2892 10.0259
+4.91072 5.06031
+5.8232 7.25924
+7.0641 8.31604
+9.477 9.63892
+5.8003 6.58306
+9.63988 10.2754
+0.654364 1.02398
+5.9488 6.85043
+7.86867 8.99891
+9.202 9.46902
+4.17932 4.20271
+6.50328 6.71085
+7.19848 8.15579
+5.62459 5.75449
+-0.159668 0.791409
+7.55683 8.7315
+1.03278 1.423
+0.751457 0.808785
+6.2203 6.50634
+1.64418 2.98238
+6.87468 7.00652
+6.36966 6.7117
+7.69893 8.89535
+0.419794 1.05032
+2.73928 4.31548
+0.409245 1.55228
+6.03905 6.39323
+0.409695 0.789834
+4.39148 4.40935
+1.14673 3.23607
+5.85562 6.44503
+6.87394 6.88742
+3.761 4.35136
+7.2208 8.01436
+6.49839 6.54002
+4.70791 5.91886
+4.24906 5.96619
+2.53482 3.91496
+-0.369254 0.69193
+4.81351 6.31088
+9.76066 10.0736
+1.4413 1.55942
+7.21917 8.88908
+6.03521 7.56826
+1.25568 1.42024
+5.47575 6.04431
+1.43099 1.99979
+5.1004 6.04088
+6.54798 6.56118
+1.0142 3.1564
+1.75267 2.1453
+1.28133 1.36044
+9.60996 10.0563
+1.14397 1.50247
+6.86688 7.08724
+8.38287 9.18041
+0.132076 1.27657
+6.52553 6.9301
+0.223917 1.41325
+1.18902 1.79753
+1.40084 1.75678
+1.94524 2.51929
+1.98276 2.62875
+0.763281 1.05483
+7.97952 8.51249
+1.059 3.05463
+5.33401 5.85094
+8.37442 8.64374
+6.62865 6.67315
+1.01707 1.64386
+0.113335 2.23523
+6.71906 8.00216
+-0.886574 1.29366
+4.20078 4.85294
+8.43336 9.06988
+3.55413 4.40768
+6.69076 9.01132
+6.18982 7.14109
+7.14381 7.7046
+8.63195 9.47031
+8.88052 9.84886
+3.34443 3.62637
+4.5862 4.72832
+4.95757 5.23571
+2.85993 4.04966
+1.43846 2.27425
+5.43112 6.30496
+-0.452609 1.55525
+7.17706 7.71287
+5.78537 5.79579
+8.51102 8.9466
+-0.472904 0.810489
+3.83688 4.24045
+8.04432 9.04582
+1.57259 3.37136
+1.22231 1.33582
+5.73006 6.26477
+2.90855 4.22125
+9.39717 9.4287
+3.63873 4.16094
+0.442035 2.08889
+0.294491 1.31124
+3.14755 3.86305
+6.57634 9.44898
+2.19051 2.41878
+4.99277 5.12569
+4.10425 4.9423
+4.83547 5.62769
+5.73879 5.78179
+8.90329 9.81905
+5.60168 6.17538
+5.83843 6.66128
+1.84271 2.57406
+3.01047 3.33662
+2.85037 3.79158
+3.49007 4.46877
+8.58942 9.51529
+0.613753 1.12824
+8.23757 9.54707
+9.27134 9.4196
+6.27126 6.56417
+2.17206 2.33018
+3.53588 5.17162
+8.6312 10.0868
+5.76342 6.66164
+9.01501 9.90984
+3.22004 4.18954
+4.37918 4.68595
+2.00928 3.49815
+6.64229 7.06693
+2.85962 4.58794
+8.45587 9.8897
+2.8481 3.65956
+8.65849 8.67336
+6.48486 6.58637
+5.82336 7.45254
+4.02267 4.59696
+2.24997 2.83815
+8.16201 8.55518
+7.40645 9.07085
+1.43137 1.50333
+8.7212 10.9419
+1.50523 2.44124
+1.92455 2.85819
+4.50866 4.53052
+5.75781 5.81275
+1.71013 2.0282
+9.31592 9.9015
+6.14972 8.18798
+1.85705 2.20068
+4.80971 5.10509
+3.31201 3.58168
+8.61824 9.31861
+7.19497 7.86933
+3.25381 4.53472
+5.62799 6.29537
+9.00659 10.021
+9.15156 9.74827
+2.0324 2.80741
+0.714403 1.00476
+1.26522 1.80417
+-0.362995 0.41758
+7.08785 7.71544
+5.55685 5.74045
+0.428866 3.03773
+7.61574 8.94894
+9.07256 9.69772
+4.49537 5.06822
+5.66388 6.48899
+1.69126 2.69607
+9.90539 10.0783
+6.56455 8.27128
+6.61988 7.86616
+7.42012 7.64493
+2.99922 3.35282
+4.69379 5.18947
+0.87894 1.30518
+9.47632 9.61234
+0.665754 0.855958
+-0.422778 2.11197
+3.38786 4.15441
+7.57236 8.75855
+1.89832 3.50224
+-0.185929 1.0777
+3.32911 5.53465
+6.91111 8.69247
+6.97456 7.86905
+4.75518 5.0948
+4.23733 5.83368
+8.52456 8.56086
+0.0978267 0.136564
+7.4668 7.99757
+5.52851 5.80858
+8.5833 8.88589
+5.10603 6.41348
+4.70682 6.59981
+8.60265 9.19477
+5.04514 6.24014
+4.49041 4.88669
+7.40925 9.15624
+0.80364 1.35315
+7.11604 8.01153
+0.267749 0.714089
+0.121521 0.822226
+9.30556 9.81307
+2.11398 2.92567
+5.97722 6.08022
+7.13483 7.22211
+8.00161 9.27274
+4.87681 5.51654
+3.11843 3.84311
+5.17747 5.98482
+3.6077 4.46306
+1.37454 2.2443
+4.93385 5.96357
+2.92184 4.0964
+4.88767 5.56353
+2.87394 3.76336
+0.326521 0.622872
+2.42056 2.45007
+-0.527891 0.827211
+6.73151 7.20155
+1.82048 2.54905
+1.35563 1.88701
+3.64202 4.10669
+5.22846 5.23953
+6.06271 6.46993
+2.71418 3.2081
+6.41179 7.57176
+7.45823 7.79609
+1.01227 1.39137
+9.0872 9.20132
+5.083 5.64038
+5.63236 5.97945
+-0.317645 0.822313
+0.865396 2.65587
+5.71839 5.98983
+1.50978 2.08611
+2.95824 3.70996
+0.264301 1.02233
+8.22351 8.98589
+4.87805 5.24127
+2.31329 3.46752
+6.19539 6.69676
+5.18643 5.40947
+7.15453 7.66643
+1.20694 2.25111
+7.87378 7.89093
+5.16514 5.604
+8.68354 9.01395
+7.33753 9.37499
+2.94627 4.06036
+8.64337 8.85727
+0.0353092 1.0128
+6.98437 7.35718
+6.77073 6.79959
+3.13559 3.70735
+7.40139 8.27017
+4.01174 4.39788
+3.22281 5.11647
+7.79256 8.58737
+0.642233 1.39694
+7.18603 8.69453
+3.37803 3.59355
+0.762287 0.84262
+4.21823 4.52565
+0.894924 1.35434
+0.844327 1.19938
+5.57958 7.05431
+-0.631066 1.55134
+3.78962 4.17935
+9.15329 10.4824
+5.47536 5.52975
+8.81243 9.16088
+4.45989 4.94123
+8.68235 9.45112
+8.04719 10.3938
+8.87349 9.40483
+0.502543 0.570283
+2.08512 2.47652
+-0.654512 0.804355
+3.68569 5.21711
+5.33017 5.80226
+3.44991 5.31415
+-0.00417763 2.95077
+7.17784 7.9318
+1.2721 1.31642
+5.24415 5.64173
+2.79434 3.28746
+4.54991 6.49276
+4.74707 5.12555
+7.36365 8.15777
+0.913049 1.79828
+1.47498 1.83425
+2.64271 3.38567
+4.78159 5.44718
+7.6087 7.66442
+-0.484393 0.909893
+3.16824 3.23757
+4.43021 5.90284
+4.25954 4.74995
+6.58742 7.24125
+1.86151 2.27069
+3.16459 4.5945
+1.06471 2.08965
+3.31028 3.83029
+1.52593 2.47521
+0.703789 1.42451
+1.47969 1.68923
+2.92352 4.24089
+9.84088 9.93485
+3.18205 4.27817
+7.39345 7.78945
+3.78808 5.41572
+4.38624 6.75693
+6.93065 7.16977
+1.01289 2.19807
+3.37006 3.54196
+5.50238 5.58285
+-0.225113 1.99347
+1.67796 1.86943
+6.1999 6.35102
+3.80494 4.74814
+1.13294 2.02639
+1.1965 2.6548
+-0.284453 0.856092
+4.26565 5.02196
+9.66948 9.88874
+6.20287 6.25528
+4.38154 6.65806
+3.77411 4.3075
+5.77177 6.78486
+9.16311 9.816
+3.22861 3.42009
+3.36573 4.34724
+6.76951 7.58025
+4.83813 5.23042
+3.87192 4.37372
+5.56859 5.90066
+-0.368163 1.4593
+5.44031 7.81671
+2.66342 2.99253
+6.11846 7.69499
+4.98811 5.17531
+4.45078 4.67513
+4.88206 4.94128
+2.51813 3.0268
+0.461292 1.15264
+2.34766 4.48652
+0.198819 2.23425
+6.85634 6.98248
+0.912438 1.81872
+4.84933 6.34912
+5.15613 5.82891
+0.172772 0.994083
+3.37381 3.64549
+1.98836 3.2697
+1.64344 2.06049
+0.766585 3.09305
+7.8979 8.28046
+4.34856 5.01341
+6.69809 7.24239
+5.19034 7.04959
+5.3747 6.92123
+7.8893 10.1251
+6.11207 6.99125
+5.37115 6.84111
+9.04786 9.11002
+6.45326 6.64415
+-0.76294 0.92832
+0.980411 1.8166
+3.05536 3.50031
+6.10827 7.55786
+6.57848 9.06264
+7.66855 8.861
+4.80276 4.82283
+1.99932 3.2877
+9.45229 9.80653
+0.457474 0.778684
+5.44444 5.95239
+5.60974 8.05169
+7.64656 7.79632
+6.78372 7.65134
+6.93878 7.82023
+9.08292 9.55308
+9.16329 9.25802
+1.4909 2.38688
+4.31881 5.3564
+5.12535 5.60119
+2.44591 2.54637
+7.36929 7.80452
+2.13707 2.97655
+9.73557 9.94588
+1.96637 3.36097
+0.857068 2.29643
+5.58224 5.91142
+8.90714 9.0255
+9.77988 10.0711
+0.41121 0.970955
+-0.518356 0.91447
+7.58725 7.71255
+8.1435 8.78712
+6.5487 7.17991
+4.94961 5.50283
+8.14086 9.03281
+3.51281 5.24806
+3.5786 3.84066
+6.27435 8.37735
+8.65263 9.5555
+2.87484 3.56117
+5.57707 6.55879
+-0.111276 0.264178
+3.60894 4.15477
+2.82322 3.64397
+8.40669 9.1127
+8.43089 10.5298
+1.38472 2.2453
+1.74464 2.00472
+4.01588 4.67572
+0.408869 0.938602
+2.06155 3.46067
+4.4871 5.08963
+2.4435 3.87355
+1.32864 2.55801
+4.78579 5.26122
+7.43027 7.57749
+6.90971 7.32716
+8.01095 9.46872
+9.41627 10.524
+8.83143 10.4474
+0.631816 1.75513
+1.72965 2.17123
+-0.513239 1.1886
+6.75616 6.7984
+2.49278 4.26493
+0.230011 0.966298
+3.78506 4.95013
+2.03886 3.14808
+7.87309 10.5637
+3.54564 3.58055
+1.91974 2.9194
+4.8503 4.98668
+4.66494 4.83558
+7.57916 8.24358
+4.78743 5.38547
+2.81034 3.34534
+1.00297 2.0138
+6.87593 7.17736
+3.93157 5.0655
+3.10361 4.05519
+8.22876 8.38326
+7.48377 8.31026
+2.23106 3.4905
+0.130934 0.606709
+1.22344 2.55898
+7.55829 7.92597
+4.10977 4.86263
+0.122756 0.264176
+3.8357 4.15854
+1.28414 2.13031
+0.280029 0.805504
+2.88245 4.1704
+5.57936 6.94781
+6.22719 6.33201
+-0.761013 1.16475
+6.16636 7.12566
+2.98935 3.192
+2.23263 2.34362
+3.9082 4.11692
+2.66865 3.5275
+1.87676 2.43966
+4.60035 5.39332
+2.74287 3.73097
+2.81953 3.96666
+5.73644 6.04042
+4.02179 4.52419
+3.26438 4.07346
+1.22509 2.13719
+7.91885 9.71846
+1.28152 1.48079
+7.97918 9.26344
+8.75682 9.8677
+7.25678 7.45362
+8.16161 8.87066
+1.21873 2.37224
+7.76739 9.06346
+3.89932 5.03406
+3.53901 4.37006
+2.93795 4.04333
+6.36543 6.72985
+4.72622 4.98804
+1.86205 1.99008
+0.784581 2.05264
+0.978519 2.91723
+3.9067 6.47195
+0.735425 2.38151
+8.37394 8.41609
+6.91804 7.23374
+9.39259 10.1026
+7.28732 7.43466
+4.75982 5.44264
+6.71504 7.53933
+2.17125 2.54358
+0.703562 1.39087
+-0.221711 0.541965
+9.22839 9.39159
+9.53465 9.97585
+1.44304 2.53666
+7.886 8.85051
+9.33174 10.1632
+2.60898 3.42889
+1.73658 2.02589
+6.31651 6.77762
+7.26755 8.34499
+9.01312 10.9837
+7.47361 8.96251
+7.98365 8.02586
+1.40065 3.44891
+9.44617 9.55391
+8.24265 9.34849
+9.366 10.4216
+0.375354 1.17522
+3.37114 3.38278
+-0.742469 1.07634
+4.29988 4.81879
+4.18605 7.21928
+7.41047 8.29198
+6.68173 7.60225
+3.24292 3.30695
+1.8951 3.42167
+3.97359 4.2857
+4.72621 5.80575
+4.75014 5.02239
+6.43454 6.94568
+7.16601 8.10355
+2.16685 2.23666
+6.70852 8.63284
+8.16988 8.95848
+0.277002 1.30648
+1.5803 2.28632
+2.22634 2.34679
+0.200623 0.814959
+5.27457 6.51651
+2.49533 2.90705
+6.48922 6.69528
+8.18879 8.63551
+2.75159 3.633
+0.884853 1.56434
+1.87488 3.98951
+4.24783 5.17713
+6.22499 6.56095
+5.68238 5.99272
+3.75553 3.85012
+4.87543 5.39966
+6.62671 8.72493
+3.50262 4.05131
+5.38952 5.4974
+7.84971 7.94051
+5.39123 6.66564
+5.43986 6.17256
+7.48968 7.7677
+4.2685 4.57296
+1.75169 4.11058
+5.03634 5.16102
+4.68003 4.84715
+8.36249 9.98761
+8.94297 10.6969
+6.21572 6.24305
+7.90303 8.0672
+7.90426 8.41244
+5.98512 7.93187
+5.13297 5.27136
+6.58367 7.4971
+4.72663 5.22956
+7.99667 8.59246
+4.3303 5.92188
+1.3132 1.80875
+7.70104 9.20947
+1.78862 2.00473
+9.63678 9.65571
+-0.264416 0.318054
+2.70004 3.40777
+2.74642 3.73608
+3.6494 3.6743
+8.03113 9.22851
+1.07526 1.3116
+4.05436 5.33291
+4.60907 6.03605
+7.83426 9.10358
+2.27997 2.77309
+5.63139 6.92616
+4.40717 5.25214
+1.98986 3.5062
+2.42393 2.9584
+1.94336 3.08456
+1.20185 3.01827
+0.641733 2.10095
+7.25046 8.29391
+0.640392 1.26451
+3.9588 4.85492
+6.83036 7.41101
+6.48581 7.08757
+6.48734 8.47856
+9.55084 10.3774
+-0.173137 1.26574
+2.32702 2.58775
+2.7463 3.13914
+7.86951 8.62807
+5.96916 7.92759
+0.217046 1.70373
+1.04207 1.05755
+4.79058 5.07902
+4.19024 5.35395
+9.00175 9.83715
+8.37554 9.275
+4.5204 4.82999
+3.49936 4.75519
+7.07916 7.74619
+6.66726 7.35282
+7.39513 8.55523
+7.50381 9.60708
+1.39304 2.26927
+2.84101 4.76376
+1.21779 1.59353
+4.7893 5.70143
+9.12195 9.57131
+5.40461 6.15443
+2.86445 4.26837
+8.01324 10.9082
+2.96659 3.8042
+2.4897 4.59323
+6.37887 7.15878
+7.00143 7.6419
+3.22491 3.41843
+1.22852 3.31783
+2.09881 2.56121
+4.43205 4.67051
+4.39328 4.66715
+2.38355 3.39058
+0.638802 1.59399
+5.32717 5.81376
+8.00353 9.72007
+8.77281 9.81649
+8.95552 9.86609
+2.90915 3.96556
+3.88176 4.93124
+6.45945 7.69942
+-0.108577 1.57424
+0.304992 0.320561
+6.47789 6.5515
+1.98211 2.05312
+6.2741 7.35626
+9.45057 9.67918
+5.2916 5.48037
+0.158802 0.712187
+6.42002 6.84323
+9.11085 9.13063
+9.42446 9.95882
+6.03624 6.96469
+9.2473 9.6286
+7.10342 8.71163
+3.34072 3.35187
+7.4602 8.58506
+8.7963 8.81873
+0.323615 2.22687
+0.326746 1.76681
+2.20062 2.76862
+7.55181 7.85006
+0.941035 1.28117
+8.76643 9.60019
+9.19731 9.33092
+6.0479 6.57293
+6.12725 6.24356
+7.53078 8.92488
+3.1841 3.48283
+1.85544 2.01888
+7.3849 9.29183
+5.03915 5.27721
+4.77142 5.46282
+-0.291696 1.14641
+1.61367 2.20867
+5.31853 6.69431
+2.52731 3.13139
+4.86677 5.18106
+4.50183 4.60969
+5.95881 6.09964
+2.41939 4.42026
+6.92003 7.17891
+4.29584 5.48557
+8.02803 9.06315
+2.82326 3.49599
+5.8647 5.87561
+2.38814 3.62688
+6.32418 7.17662
+5.99907 6.5059
+3.84698 4.14446
+9.80728 9.82132
+5.2366 6.30871
+9.81878 9.91004
+1.53024 1.83525
+2.98035 3.68531
+4.5418 6.98786
+5.19561 5.9846
+0.946068 1.46672
+3.26948 4.71928
+6.13476 7.68682
+8.36934 9.65072
+6.40074 7.14762
+1.01349 1.80493
+8.4514 10.25
+-0.180382 0.682704
+1.03041 1.63565
+5.64642 6.96069
+2.73598 3.67485
+5.8705 6.66157
+4.68343 4.7176
+5.83787 7.6818
+1.42529 2.234
+3.73726 4.19241
+5.38341 5.81884
+1.13088 1.69073
+0.968034 1.98828
+1.06007 1.11039
+3.50129 4.51807
+5.83133 6.77904
+-0.334945 1.29129
+0.326706 1.16015
+9.15028 10.4391
+5.13047 6.79712
+1.06952 1.38997
+5.79345 6.49898
+7.88149 8.5143
+8.00404 8.09794
+8.22725 8.28904
+6.38406 6.61356
+6.32166 6.73458
+7.17066 7.32049
+1.64338 3.79327
+7.92135 8.55568
+7.15578 7.42231
+6.70701 7.65427
+5.76608 7.63064
+3.54163 4.08662
+3.81402 6.76906
+4.5998 4.61865
+9.01471 9.18718
+5.21234 5.73335
+0.946018 1.39917
+6.99714 8.29739
+5.64458 6.93965
+3.39809 5.70907
+6.32284 6.83604
+8.50347 8.53097
+3.90118 4.85721
+9.41198 9.62076
+4.41514 5.55152
+7.69979 7.81249
+5.34434 5.37105
+7.25274 7.76114
+7.67383 7.92718
+5.82851 6.29417
+0.935651 1.2842
+0.639586 0.786771
+6.39823 8.31226
+3.67546 4.89035
+9.20323 9.57287
+2.33896 2.99576
+8.51933 8.80243
+0.983098 1.11084
+8.48215 9.42998
+7.29586 7.3914
+2.00695 2.89046
+2.60909 2.62433
+2.04118 2.92033
+7.60386 9.01419
+7.32896 7.73342
+9.51693 9.54891
+5.1323 5.24962
+6.35326 7.18302
+2.50952 3.51872
+3.4149 3.57431
+9.18412 10.4976
+5.68123 6.42697
+5.87758 6.0124
+6.3202 6.64588
+8.2547 9.98176
+5.50949 5.7854
+0.0824268 0.128178
+2.17292 3.76233
+7.22786 7.68214
+5.09326 6.4151
+4.30171 4.86688
+2.39206 3.47399
+7.95152 8.35556
+2.40133 2.936
+2.77819 3.87516
+3.252 4.33376
+9.54269 9.7513
+6.6747 6.73043
+0.611474 1.97158
+7.65864 8.53689
+1.58345 1.8017
+2.56917 2.8152
+8.5424 8.8301
+4.98159 5.44499
+5.82482 6.03578
+3.55509 4.90785
+5.77028 5.94747
+0.163965 1.1379
+2.85039 2.94261
+4.24343 4.67273
+3.22636 4.06084
+6.03662 6.21018
+4.55495 5.07697
+1.91173 2.51739
+6.54894 7.24036
+5.55502 6.92676
+0.245707 0.586188
+2.28044 3.29563
+4.71378 5.25198
+8.28953 8.44891
+9.42282 10.179
+5.0548 5.73614
+6.3203 6.43659
+6.54178 8.82077
+3.67181 4.17365
+8.51445 8.96445
+1.69648 2.84524
+0.194222 0.920147
+2.48533 2.59803
+1.66002 1.6739
+4.79833 4.95404
+1.09801 1.11009
+6.62529 7.4066
+8.0099 8.37262
+3.55429 4.11824
+6.2666 7.12137
+7.83222 8.01342
+9.37156 9.72961
+1.1647 4.04434
+4.50719 5.09068
+3.25175 3.75482
+8.28659 9.30848
+7.30009 8.60755
+-1.12073 1.21016
+0.310087 0.725712
+8.3746 9.13511
+9.11925 10.2234
+9.68703 9.7137
+0.603098 0.844291
+5.88373 6.99327
+0.852802 1.43297
+3.7463 5.82283
+7.69639 8.25446
+-0.337322 0.977113
+8.76606 9.16618
+3.43527 3.6616
+3.93587 5.39471
+5.66569 7.20462
+5.34999 5.7193
+3.25589 3.706
+1.52012 2.55985
+4.74228 5.43725
+5.15323 5.51652
+7.77237 8.33717
+-0.0514419 0.914127
+9.27182 9.59476
+8.14192 9.74859
+6.42702 7.20331
+7.51351 7.68358
+7.0947 7.46059
+7.97635 8.59449
+2.39931 2.66008
+5.79481 5.94673
+4.41054 4.81613
+2.72202 3.61239
+2.86039 3.44719
+1.81115 3.28151
+-0.566995 0.734214
+9.61998 9.72024
+7.4163 7.61821
+0.42522 0.552659
+3.17617 4.36942
+0.740294 1.36953
+7.13755 8.39966
+0.219186 0.892588
+3.78307 5.2905
+0.783026 1.76155
+3.01893 3.33055
+3.12143 3.5713
+-0.792679 0.92539
+0.668572 1.12961
+3.88619 4.30545
+7.59276 8.1776
+4.71509 5.05622
+6.53406 7.04242
+1.581 2.21984
+4.7144 5.51372
+2.76338 3.50211
+0.16364 0.293599
+5.92365 6.10636
+3.11174 4.29165
+5.49295 6.48717
+-0.503239 0.795707
+7.30558 8.12408
+2.10674 4.13456
+7.11985 8.01932
+1.34672 1.48739
+4.05778 5.69084
+2.83686 2.9115
+6.4471 6.53909
+0.467374 1.84371
+2.76467 2.94177
+4.87867 5.33932
+0.892878 1.96273
+1.13934 2.27205
+7.76918 8.39409
+6.72181 7.43901
+7.0836 8.68472
+7.12924 7.56332
+0.689172 1.62922
+9.41146 9.91043
+0.398265 1.27579
+6.29966 7.4874
+5.09438 5.65334
+6.04623 6.4687
+5.75305 6.89731
+0.308118 0.459501
+3.63692 4.30501
+3.1755 3.71009
+7.32787 7.52785
+2.23235 4.09978
+2.71191 2.91513
+0.209457 0.840713
+7.31489 7.38296
+6.21245 6.91111
+5.75983 5.96049
+5.92056 8.24518
+-0.354452 0.489638
+4.05894 5.34828
+1.56299 1.57414
+2.85083 3.27455
+8.78323 10.0012
+0.208502 1.26471
+6.10824 6.79285
+5.55114 8.03968
+6.13077 6.32342
+7.34865 7.82592
+3.94835 4.75016
+7.8268 8.72249
+4.85131 5.07903
+5.40398 6.0134
+2.85971 3.85293
+0.126379 1.45545
+3.15768 4.28388
+9.64566 9.70016
+4.47514 6.31064
+6.44995 7.2894
+4.70992 6.13777
+3.91128 4.22263
+7.35627 8.07739
+2.49215 2.68749
+8.09946 9.16929
+0.925679 1.92784
+8.48437 8.63892
+7.2255 9.18109
+-0.00840282 0.0168954
+5.91933 6.6274
+4.16086 4.64302
+8.27009 8.47868
+1.73372 2.93094
+8.17421 9.17705
+9.19136 9.43222
+0.0653594 0.648416
+8.69 8.96184
+8.99355 9.40832
+6.25088 6.26945
+6.72391 7.38424
+9.59708 10.3496
+6.00971 6.18818
+4.64468 5.75797
+3.64832 5.30079
+5.99378 7.8792
+6.5388 6.99379
+7.31694 8.43802
+3.66227 4.92638
+9.55964 10.3796
+-0.14433 0.29095
+3.56716 3.71252
+3.82062 4.27788
+8.10405 9.35275
+7.17891 7.46539
+1.17698 1.29602
+4.48829 5.03134
+7.18025 8.13852
+9.42388 10.3634
+5.2158 6.17469
+5.22926 6.52397
+5.16731 6.40625
+6.53348 7.269
+2.07834 2.39172
+9.65044 10.1519
+8.64551 10.1294
+9.29006 10.5993
+1.50639 2.83173
+5.71675 5.99216
+3.0783 3.35571
+0.115401 1.31431
+8.03988 8.11942
+3.11917 4.53956
+1.14552 1.1646
+5.34782 6.43017
+0.0701702 1.0368
+0.183335 0.440704
+6.33438 7.71607
+2.11533 2.64323
+7.12104 7.13366
+7.21548 8.26968
+3.13445 4.13568
+4.57452 4.74602
+2.60889 3.69846
+7.21184 7.47573
+6.87651 7.03405
+2.65431 3.94352
+1.50006 1.70087
+6.16344 7.54429
+7.02057 7.73067
+8.27833 9.01489
+0.500833 0.597845
+8.34751 9.88925
+2.80242 2.91837
+5.29424 6.079
+8.36276 9.10074
+0.216887 0.268335
+1.30798 3.16393
+3.93851 4.22658
+2.58028 3.14744
+9.31131 10.3451
+7.37262 8.84852
+4.45306 5.6723
+3.74535 4.0721
+4.90174 5.63875
+3.41847 4.65199
+3.10265 3.82548
+6.56126 6.97557
+2.57449 2.95098
+7.97418 9.06301
+9.2203 9.66829
+4.64922 4.96305
+8.83034 10.115
+6.6685 6.89847
+7.36585 7.48669
+8.66761 9.39287
+0.562216 0.961875
+7.56292 7.81006
+8.50715 8.95572
+0.572825 2.24755
+5.9367 6.06766
+-0.769578 0.776163
+4.48224 4.9553
+8.21069 9.29643
+8.57206 9.47175
+1.10032 1.21371
+3.76306 3.87152
+6.46197 8.18457
+6.96501 9.70727
+3.42357 3.93484
+0.818601 2.42743
+-0.119739 0.397938
+-0.378761 0.523892
+8.03394 9.35409
+3.60283 3.98864
+2.21789 2.70607
+-0.255329 0.319063
+6.20806 6.22609
+7.1283 8.04391
+3.73565 4.03638
+8.5374 9.10987
+5.22675 5.35072
+7.51777 7.58439
+9.12636 10.4159
+9.15003 9.40672
+0.217863 1.19883
+9.42416 9.9178
+2.55337 3.8088
+7.03655 7.98242
+9.17171 9.8047
+0.110162 0.722738
+3.16467 3.58897
+6.47244 7.12961
+1.0456 1.65614
+1.58456 1.62844
+1.79039 1.84035
+6.24286 7.07838
+9.08631 9.11684
+3.05068 3.40838
+2.81212 4.03665
+2.52011 3.09841
+8.06001 8.16356
+7.07649 7.52685
+2.47666 2.86548
+8.66241 8.95103
+-0.298116 0.662697
+0.822443 2.46125
+2.07619 2.33674
+4.84135 6.21034
+7.49625 8.13026
+7.85854 8.29618
+3.01722 3.37062
+6.54393 7.30306
+2.21113 2.30166
+8.32138 8.57349
+6.2995 6.33514
+3.0018 3.56886
+4.63378 5.52214
+6.71908 8.96715
+2.29809 2.95581
+8.34947 8.71096
+4.96902 5.32075
+9.51051 9.72733
+3.311 4.77789
+3.50233 3.66309
+4.95177 5.76002
+6.12876 7.22391
+6.27743 6.79393
+3.60638 3.6833
+5.20305 5.50848
+7.16065 8.88358
+9.45049 10.2539
+6.16892 7.0314
+8.08479 9.74516
+0.0717867 0.669987
+2.17569 3.34098
+4.81716 5.7509
+7.63999 8.01157
+5.87228 6.60363
+3.39621 4.35575
+4.96079 5.07901
+7.57639 8.82003
+8.27285 8.52609
+0.665858 1.64782
+1.74256 2.07126
+6.19512 6.4258
+0.664592 1.30896
+0.180682 1.43746
+6.46752 6.81303
+2.55562 3.08708
+9.33695 9.63312
+6.33821 6.77092
+7.88252 8.55026
+8.17937 8.45385
+-0.415294 0.793096
+5.22077 5.54553
+8.67808 9.10041
+1.72096 1.92024
+0.492778 1.48076
+2.29514 2.88531
+0.0934389 1.37614
+8.40297 9.11121
+3.66605 3.91717
+-0.274468 0.867811
+9.41428 9.56659
+1.22657 2.12792
+8.71336 9.85384
+5.09347 5.42905
+2.02877 2.36652
+5.99112 7.40703
+0.911618 3.48138
+4.84352 5.11131
+1.16309 2.08613
+5.58561 5.84805
+0.663543 1.08921
+1.83189 3.93269
+4.07731 4.38879
+4.04789 4.9989
+2.09457 3.61279
+2.00982 2.94396
+7.37537 7.66574
+5.08648 5.80122
+9.19959 10.2263
+4.26136 4.86194
+6.67875 7.61134
+1.27462 2.48399
+7.2237 7.82324
+-0.0141009 0.0441377
+8.32069 8.36192
+2.82562 3.0717
+-0.528918 1.25301
+1.90486 3.04714
+1.34489 1.75301
+3.14332 4.03322
+8.55351 10.8681
+9.09824 10.3828
+0.496868 0.622499
+7.11112 7.62765
+0.335233 0.528962
+6.71335 7.13937
+6.78574 7.71172
+9.13568 9.22251
+7.10299 7.64765
+3.64747 4.06685
+8.64041 9.40111
+2.43515 2.93081
+2.95476 3.68588
+0.0863751 0.777976
+6.5891 8.18165
+8.80986 10.0735
+0.568868 1.63785
+4.41677 5.42049
+8.72857 9.88426
+3.67467 4.25442
+8.98168 10.1087
+5.08611 5.50098
+1.28982 2.34422
+-0.914176 0.969847
+4.35042 4.42735
+4.02282 5.08164
+9.00566 9.4791
+2.71821 3.24065
+3.20252 3.24988
+8.61559 9.6733
+3.15157 4.7685
+3.59348 4.67872
+0.286864 0.36279
+8.40374 10.528
+7.53647 8.13523
+6.81591 6.90226
+7.68111 9.10704
+8.37958 8.73818
+3.25808 4.40893
+1.50348 3.00641
+0.20803 1.40949
+3.93771 4.84177
+2.79232 3.35329
+4.2666 4.63588
+0.341784 1.46241
+1.53063 2.06724
+8.58212 8.86194
+8.95411 9.70651
+8.71591 9.26292
+6.66088 6.7009
+3.95881 5.93917
+8.5823 8.76007
+-0.308958 0.34174
+1.03188 1.37677
+7.52675 9.84031
+4.27952 4.32984
+1.83465 2.27607
+8.12096 8.64922
+2.22605 4.40067
+0.00929139 0.871342
+0.376531 1.29354
+3.5257 4.30165
+1.95846 4.09589
+-0.200922 0.799272
+0.24432 1.76673
+4.73477 5.06514
+8.97059 9.2276
+2.89618 2.93969
+0.168256 1.03325
+3.44522 3.83622
+2.74889 2.76042
+6.07155 6.7979
+9.06484 9.09804
+7.56658 9.29215
+4.3084 5.45621
+-0.0786996 1.12809
+8.48249 9.74107
+6.4196 6.61469
+1.57119 1.87612
+3.15463 3.86251
+2.0089 2.57741
+6.53618 7.12035
+7.07901 7.29396
+0.114756 0.887413
+8.90806 9.65731
+4.0921 5.23407
+2.63441 2.92344
+4.31769 4.98832
+8.90336 9.26837
+8.25313 8.91204
+2.87758 4.00212
+-0.708243 0.789072
+3.28656 4.55872
+5.73177 5.80293
+5.40596 5.5844
+4.09318 4.57774
+6.16178 7.00613
+8.5514 10.5495
+6.83987 7.3056
+1.06341 1.97923
+7.82153 9.00722
+0.923069 1.42337
+1.83408 2.82331
+5.75305 7.23576
+0.241909 0.96721
+1.16645 2.10491
+5.41819 6.09839
+7.49242 7.69495
+0.347233 0.383681
+2.33245 2.53467
+4.91249 5.27759
+8.11182 9.2758
+0.0955456 0.548659
+-0.359909 0.48055
+1.64759 3.47097
+6.37855 7.40015
+0.423283 2.08857
+4.10068 4.46856
+2.58389 2.69626
+0.848483 2.72258
+8.67321 8.88903
+2.07607 3.42398
+3.11033 3.80741
+2.24735 3.48626
+8.11883 8.7687
+0.29959 1.41603
+4.84924 5.13641
+1.44102 1.74592
+1.13437 2.50296
+1.75876 3.09859
+4.75363 5.90838
+7.36423 8.20074
+5.51578 6.42652
+0.915633 1.86015
+9.44679 10.3479
+2.61691 3.60215
+6.26524 7.2538
+2.74326 3.12649
+5.83934 6.98592
+9.1111 10.4891
+2.27066 2.82709
+8.19359 8.67233
+0.834236 1.7066
+6.19288 7.21392
+-0.249843 0.891821
+2.5652 3.25132
+5.25356 5.88905
+3.99545 4.18918
+1.93186 2.51527
+4.44621 4.82421
+5.89181 7.36963
+2.77321 3.60627
+2.76444 3.88016
+2.50062 2.66105
+1.14846 1.66146
+2.951 4.81656
+3.81976 4.01061
+4.75674 5.59916
+6.74092 7.08289
+2.09989 2.32514
+0.220723 0.865303
+5.48107 5.71875
+1.48372 2.88941
+0.484577 1.12051
+8.93618 9.7185
+1.89769 3.65655
+2.0129 3.55214
+9.23173 10.3694
+0.286189 1.22753
+0.453785 1.55541
+2.24068 2.6541
+8.80917 10.4969
+7.12801 7.15222
+9.25745 9.9103
+5.73429 6.76901
+7.54454 8.47157
+1.96008 3.74997
+8.00837 9.25665
+1.66384 2.07613
+5.53014 5.97461
+-1.32495 1.33319
+4.58479 5.2263
+7.92048 8.75668
+6.87595 7.02522
+0.157713 0.389585
+7.48277 7.51261
+8.10714 8.38399
+0.0802723 0.587088
+1.13024 1.71135
+5.71769 6.84757
+3.08261 4.31054
+2.95952 5.09762
+6.62815 7.44256
+5.0805 5.8499
+3.38489 3.73265
+1.10644 2.18331
+4.70493 7.31686
+2.2253 2.6338
+0.778359 1.6265
+6.94211 7.2149
+4.20976 4.99342
+7.47792 7.67881
+7.06219 8.76137
+4.97677 6.12206
+6.30829 6.69361
+6.19977 6.76781
+9.67636 10.2172
+3.41958 3.69137
+0.374486 0.943711
+4.00837 4.80107
+8.85401 10.1325
+6.0938 7.8919
+1.51055 3.08998
+8.94594 10.3139
+1.06714 2.65511
+0.703202 0.850595
+4.87735 6.00659
+8.3067 8.39924
+7.78071 8.3155
+1.98737 2.81116
+6.86323 8.07974
+1.03479 1.34584
+0.490512 0.670211
+5.82122 6.1901
+5.50836 6.63385
+7.9192 8.9606
+0.913719 2.18983
+4.97151 5.70909
+4.19907 4.37712
+2.01188 2.63318
+7.84813 9.3771
+5.32667 7.48502
+8.50951 10.1006
+-0.551637 0.930509
+1.06511 2.23353
+1.91111 3.42663
+9.37528 9.55327
+7.27975 7.38741
+3.46676 4.83165
+7.36137 8.70333
+-0.295762 0.623228
+8.48558 8.81076
+2.71689 3.41873
+6.49265 8.11042
+5.25516 5.57336
+6.10462 7.05991
+8.76997 9.15164
+7.86196 8.30651
+7.82765 9.24604
+5.98573 6.34182
+6.45367 7.47857
+3.27018 5.10835
+1.3079 2.37423
+-0.261387 0.338113
+7.60683 8.81791
+4.7346 7.41988
+5.25597 5.28517
+3.76704 4.52226
+3.91774 6.66976
+5.69618 8.92667
+1.86148 3.07531
+5.57404 6.51438
+9.39049 9.77921
+2.38692 4.21416
+6.84909 7.23633
+7.2379 7.58761
+4.73485 6.11606
+7.80013 8.08105
+1.72701 3.40244
+2.44475 4.4072
+9.72565 9.77726
+3.35288 4.06493
+8.91215 9.92325
+8.67991 9.65865
+0.253195 0.764339
+4.23564 5.59497
+1.84752 2.14986
+-0.502957 0.528737
+1.32656 2.11347
+9.81563 10.0213
+7.60114 8.05035
+8.51797 8.83342
+-0.394604 1.41267
+7.71614 8.63514
+0.530249 0.968898
+2.90463 3.01675
+7.58098 8.71658
+-0.253747 1.28827
+1.832 2.39006
+7.2268 7.27225
+3.84076 4.66703
+2.2564 2.73185
+7.31508 8.46008
+4.99387 5.64948
+7.21239 7.79983
+7.60231 7.8473
+0.555482 1.41892
+1.25539 1.73665
+8.41561 8.96404
+5.06771 5.17664
+0.0655744 2.10718
+8.00222 8.65909
+7.48841 7.83609
+6.30909 6.89448
+9.1697 9.55942
+1.36259 2.22582
+-0.923854 0.994771
+3.43724 4.64053
+8.09411 8.19574
+4.84869 5.52805
+2.69802 3.4119
+9.0613 9.29405
+0.565216 0.687165
+5.93495 6.57426
+1.86925 2.75493
+1.41068 2.00824
+1.60612 1.62471
+3.11499 5.89909
+2.69806 3.90538
+3.76301 4.22135
+3.30065 4.70725
+5.56461 7.02137
+8.35335 8.99287
+4.79259 4.88508
+6.7911 7.52912
+8.14843 8.28583
+3.67629 5.06406
+5.71462 7.04747
+9.06721 9.99733
+4.87932 6.8586
+8.00814 9.57664
+7.03069 7.51623
+9.0989 9.57644
+2.70873 3.91296
+1.64425 1.7007
+6.53716 6.99033
+8.29874 8.74509
+1.88941 2.57626
+4.65337 5.23247
+9.04723 9.06665
+3.52258 3.61496
+1.1207 1.80143
+0.680601 1.98384
+2.94593 5.38692
+2.65685 2.67932
+9.00469 10.2054
+6.44318 7.2657
+4.66656 5.33893
+3.41719 4.72453
+3.12403 4.21748
+6.67973 6.81775
+3.54347 4.14978
+3.02554 3.50812
+3.91883 3.93657
+7.31721 7.89895
+1.86275 2.85473
+6.23266 6.4449
+7.59855 9.38013
+9.07084 9.49309
+0.557656 2.6851
+6.899 7.37693
+4.23162 5.84314
+8.54108 9.37672
+3.9513 4.79746
+7.299 8.29678
+5.58879 5.88551
+9.14992 9.22385
+9.1914 9.31788
+4.95663 5.56316
+2.16516 2.39538
+0.722262 1.89664
+7.76447 7.80026
+2.77772 3.39136
+9.59513 9.78635
+1.02728 1.2181
+7.32631 7.56611
+7.37389 7.46163
+6.87949 8.18689
+8.95602 9.81169
+5.85364 6.63434
+0.129136 2.13484
+7.09608 7.71167
+7.1487 8.34258
+7.48738 8.3172
+8.77247 9.95985
+7.6706 9.56828
+8.14268 8.61726
+6.5455 7.90871
+4.57866 4.78453
+3.63937 4.42349
+5.18905 5.36516
+4.68884 5.62995
+5.88338 7.83824
+-0.002979 0.145818
+4.42981 4.77141
+0.558593 1.07999
+6.91713 7.36159
+5.13948 6.20805
+3.34368 4.07112
+7.27365 7.8383
+9.6898 9.9514
+6.14332 6.75102
+2.16534 2.78843
+2.8007 4.37952
+7.67047 7.72955
+8.77377 9.85092
+1.26087 1.55193
+6.29788 6.58511
+7.68178 9.05777
+0.383336 1.21247
+2.55185 4.30874
+8.91971 9.21836
+3.67638 4.68596
+8.13843 9.13684
+-0.424353 1.97502
+2.71499 2.93977
+0.104447 0.387901
+4.09592 4.67009
+0.808593 0.920798
+5.01258 6.30662
+4.97668 6.57412
+8.44135 9.03816
+2.78932 3.72809
+6.71337 7.15128
+4.27234 4.50127
+6.78266 9.70351
+6.48026 7.51491
+7.36296 8.42283
+4.80339 7.3704
+6.24735 7.3132
+0.134626 0.364187
+2.73854 4.09422
+0.809997 1.19657
+6.41154 6.50244
+7.16847 8.56485
+1.53582 3.33985
+1.92874 2.86427
+6.95941 7.01378
+5.8592 6.54917
+2.51802 2.75622
+3.3894 3.6957
+2.2547 2.64693
+8.62054 9.1951
+8.86378 10.0003
+1.87892 2.28732
+3.83146 4.21484
+1.53262 1.99692
+7.86048 7.93125
+1.79437 2.68269
+3.92781 4.24119
+5.43044 5.47935
+0.322904 0.618854
+3.42383 5.18531
+4.00989 5.83612
+7.06304 8.76582
+5.88039 6.77654
+8.638 8.9479
+1.73391 2.09002
+1.04861 2.84779
+-0.313019 0.557659
+3.35245 4.1308
+9.05373 9.57205
+5.85684 6.49193
+6.00719 6.86421
+4.25244 4.65772
+5.15865 5.61529
+8.78498 9.77544
+8.33803 9.26452
+3.42915 4.92279
+8.29884 8.94035
+5.10232 5.35924
+4.33571 4.86695
+7.86184 7.90029
+7.34345 7.36214
+0.201624 0.800371
+9.05882 10.5503
+4.32188 5.03154
+-0.0456982 1.22298
+2.90921 3.60013
+1.69306 2.36564
+7.79029 9.15059
+4.65205 5.06538
+6.34552 8.32176
+7.92695 9.32585
+7.17903 8.09903
+9.53561 9.60287
+0.365723 1.23929
+0.542889 0.849053
+8.15061 8.93512
+6.40042 6.41053
+5.82666 6.5488
+1.00893 1.83717
+1.76021 2.11893
+7.61076 8.6242
+2.32513 3.4856
+4.39023 6.01094
+-0.11754 0.421848
+6.76371 6.94513
+4.67607 5.58732
+-0.201992 0.765553
+7.42926 7.44429
+2.57485 2.85339
+2.74138 4.13421
+8.21645 9.18107
+6.28184 6.4996
+7.129 7.74678
+7.76842 8.74924
+3.5359 4.35892
+3.04103 3.24376
+2.29389 2.92164
+6.96306 7.64415
+8.82053 9.04584
+1.19911 2.06167
+5.91357 7.6773
+0.150241 1.23291
+4.48681 5.41762
+6.89156 8.13443
+5.37828 5.79944
+2.13694 2.91102
+1.35932 2.08272
+6.14309 6.23278
+4.03202 5.88521
+7.23417 7.43704
+1.2501 2.60793
+2.12334 3.65727
+7.05722 8.77078
+0.437873 0.585933
+6.33338 9.16643
+2.44724 4.40234
+2.92012 3.64047
+1.2039 3.25195
+8.6611 10.0398
+8.0088 9.13622
+5.96562 7.08515
+5.45181 6.65246
+0.742546 2.38958
+4.12919 4.93571
+0.428823 0.84483
+1.45226 2.5869
+9.50727 10.0211
+7.69194 8.66273
+1.25706 2.44554
+5.23247 5.47762
+0.499178 1.70722
+7.26569 8.72467
+3.59815 5.50022
+1.03601 2.43445
+0.092414 0.927635
+3.7917 3.995
+6.21977 8.40398
+3.1513 3.81188
+9.80482 10.0951
+8.65564 9.33687
+2.06164 3.49152
+2.09754 2.34929
+0.66126 2.31888
+5.34832 6.82603
+7.91038 8.59105
+0.695523 0.800961
+2.33406 2.71629
+8.37774 10.9253
+9.7682 10.106
+9.44382 9.5743
+8.78009 9.47645
+1.87309 3.04127
+1.86724 2.13913
+3.30049 3.52416
+7.18508 7.6648
+1.3736 2.12153
+2.41879 5.06716
+1.87815 2.29743
+1.76646 2.06335
+9.73034 9.95475
+0.497489 0.686102
+6.96908 7.17338
+9.23111 10.0148
+3.30981 4.21787
+9.53311 10.3734
+-0.067709 0.147251
+8.44557 8.82047
+7.10407 7.47639
+0.834352 1.36908
+9.1598 10.6508
+4.22018 4.38279
+3.8295 3.95462
+3.97302 4.97247
+1.55856 2.31013
+0.0217962 0.486978
+1.69845 2.65786
+-0.489562 0.582187
+5.10621 5.6055
+9.8478 9.85919
+6.15389 6.92324
+7.11469 8.0418
+3.20625 3.78209
+5.93389 7.66892
+1.15134 1.18023
+6.84797 7.4239
+2.9483 3.22279
+-0.43205 0.764755
+7.17846 7.33313
+5.4729 8.26894
+6.19512 7.32132
+0.591934 0.767568
+0.49266 1.80691
+9.08272 9.72242
+5.85683 6.36905
+5.27625 7.13913
+3.60853 3.98155
+0.642811 1.10253
+1.27117 1.5329
+8.76096 9.61776
+4.9498 5.12847
+7.53282 7.80438
+1.40487 1.65657
+3.45329 3.84307
+3.25698 3.49614
+2.38651 2.89802
+8.23114 8.99016
+9.58516 10.4145
+5.5185 6.27733
+0.583068 0.892249
+1.93142 2.35084
+7.94845 9.22667
+2.47963 3.9005
+3.46106 3.86301
+7.77744 8.43735
+1.41072 1.73969
+9.12904 10.4307
+3.52227 3.66861
+5.4743 5.86487
+-0.51727 1.63657
+5.09861 7.03162
+5.14763 5.93411
+3.23526 3.47376
+0.9702 1.74063
+9.28366 10.3322
+1.80257 1.92265
+8.63597 9.08467
+4.16006 4.97753
+6.37418 6.39374
+5.82376 6.62429
+7.74751 8.68962
+1.63821 2.53399
+4.9864 6.40969
+0.680772 0.995608
+3.74344 3.83319
+0.898055 1.8593
+2.36839 2.39307
+7.21046 7.51006
+0.60893 2.91523
+3.15017 4.30412
+5.02219 6.28886
+4.60541 4.73379
+8.41196 8.58978
+8.84706 10.8198
+6.95807 7.54952
+5.54729 6.9315
+4.62574 4.73423
+3.35252 3.59865
+1.02207 1.71791
+3.16408 3.73993
+0.639222 1.69519
+7.57978 8.05813
+2.41371 2.77174
+8.65544 10.093
+6.31988 6.70758
+6.5301 8.39328
+0.578825 1.20331
+1.1243 2.07824
+5.86555 6.72397
+6.37103 6.81736
+1.12086 3.46541
+2.59503 3.44073
+5.27128 5.3616
+2.87331 3.4352
+5.48408 6.67916
+3.36016 4.09125
+0.813671 0.9811
+0.812162 3.22209
+6.85104 6.86306
+0.671311 0.979685
+2.75962 2.82659
+2.42186 2.49827
+7.82974 9.59669
+3.48851 3.60085
+8.47775 10.9574
+8.6863 10.6155
+0.196484 1.07469
+1.19293 1.45012
+5.79166 6.18466
+6.51597 6.6793
+9.78341 10.1992
+8.17879 9.59257
+6.96178 8.73165
+4.09161 4.31908
+3.19514 3.4072
+5.5101 6.32978
+0.188435 1.84481
+9.41738 10.0379
+1.46994 1.6294
+4.21915 5.39529
+5.46567 5.86638
+8.19414 9.201
+4.38616 4.55193
+2.31105 2.73345
+6.05897 6.12297
+8.84135 9.74232
+9.44943 10.2225
+3.27501 3.66059
+0.524904 1.74374
+7.7228 8.43799
+5.13274 5.82863
+8.2787 8.94491
+1.61463 2.03499
+7.04346 8.69977
+0.938308 2.38676
+0.00208037 0.0186158
+1.98744 2.05864
+7.05787 8.16211
+1.68383 2.04684
+2.24812 2.28257
+0.685061 1.2947
+2.25379 2.92827
+6.0052 6.24797
+7.06213 7.86344
+0.751849 2.96376
+0.0977921 0.513746
+8.43792 9.10545
+2.80769 3.09276
+9.19308 9.59741
+4.74365 4.99708
+8.41385 8.58422
+4.28093 5.9281
+4.26205 4.75417
+1.48035 1.61363
+1.4264 1.80922
+3.48365 4.52935
+1.78396 2.96351
+8.4134 8.96536
+8.65646 9.34781
+1.88027 2.97804
+8.92531 9.02743
+6.91815 7.88895
+5.68023 7.37521
+3.93105 4.6123
+0.385874 1.30785
+1.80151 2.24385
+8.43781 9.33927
+6.69643 8.001
+6.12062 6.673
+4.8316 6.63067
+9.00443 9.08204
+1.5651 2.65023
+3.57517 4.40231
+5.85127 8.02219
+8.02586 10.018
+2.00514 2.75138
+6.32104 7.84285
+8.90914 9.78319
+4.91325 6.27817
+3.87406 4.80327
+8.57887 9.30685
+1.40351 1.71079
+-0.217977 0.224497
+8.37711 9.56393
+5.54644 5.77714
+8.18206 8.36015
+0.774967 1.97899
+9.10434 10.1721
+9.39621 10.1135
+4.80108 4.90605
+9.45978 9.96227
+2.85412 4.05215
+5.71985 7.16735
+2.35081 2.82214
+9.34881 10.5899
+7.13014 7.82872
+4.54931 4.89725
+9.41397 9.9131
+8.90226 9.55276
+8.27995 9.83651
+9.2767 9.6053
+0.388751 1.40317
+5.01478 5.88869
+6.41521 7.21754
+1.22552 1.39067
+3.75771 3.81571
+4.39362 6.04592
+8.82639 8.84544
+4.59289 4.89208
+1.52235 1.64285
+1.85893 4.52056
+1.91781 2.78253
+3.34677 4.54987
+9.26905 10.6019
+7.10958 8.02083
+0.765257 1.66957
+2.17169 2.70214
+4.2293 6.38089
+1.41111 2.45992
+5.76864 6.85715
+2.19944 2.93182
+9.03727 9.6454
+3.82652 4.35225
+2.52642 3.09725
+7.77868 9.35844
+0.337665 1.00787
+1.92021 2.04418
+4.41862 5.95486
+9.47606 9.62386
+1.12113 1.55658
+5.26701 7.09712
+8.81478 9.34863
+6.29447 7.43847
+2.82379 3.57268
+7.9735 8.79936
+3.30176 5.50666
+6.36009 6.8864
+4.13583 5.00627
+0.510514 0.882245
+6.93864 7.78543
+2.04039 2.4298
+3.18337 3.19424
+4.20059 4.67088
+0.316687 0.707377
+0.169106 1.68194
+8.26665 10.2362
+1.1124 1.78306
+2.84603 3.33142
+9.48756 9.96651
+9.4301 9.7316
+0.234674 1.23981
+0.493232 0.522565
+1.342 1.41739
+2.57196 4.08221
+1.54201 2.52284
+5.32049 6.00052
+8.54218 8.59452
+-0.030524 1.64476
+2.31645 2.72528
+4.71898 4.92827
+0.0872997 1.74969
+0.994278 1.76964
+1.34411 2.77952
+0.156817 2.10245
+5.78094 6.34389
+3.09336 3.10563
+8.14245 9.0928
+9.34202 9.75484
+9.78415 10.0211
+4.68383 5.19384
+6.09441 7.90512
+6.74941 6.76118
+1.78693 1.91697
+2.34369 2.62141
+-0.20756 0.829082
+3.57681 5.11448
+2.44234 4.45153
+4.84521 5.31902
+1.80487 3.64498
+2.46125 3.02549
+-0.334514 0.64288
+8.06406 8.49886
+1.74559 2.54474
+6.05883 6.46529
+9.00014 10.2271
+8.00064 8.5061
+0.909467 0.980765
+8.65862 8.69736
+8.11457 8.77049
+3.93237 5.02485
+4.64507 5.34852
+3.92405 4.19449
+5.02381 5.21722
+3.76203 3.78396
+6.74925 7.34781
+8.2556 8.63585
+3.92521 3.99739
+1.44641 2.31509
+0.23516 0.917239
+4.4614 6.35489
+3.33221 3.95247
+1.73289 2.07919
+6.92385 7.55052
+8.74351 9.41125
+9.41451 10.1785
+7.4001 8.81081
+2.97038 3.12524
+7.26043 7.77569
+5.56625 5.59344
+5.70977 6.33362
+7.61443 8.43218
+1.05308 1.82947
+0.377602 1.57633
+6.42483 7.51505
+4.73011 5.43693
+7.72 8.09098
+9.20063 9.82287
+6.88273 7.35327
+6.88423 7.68356
+3.55293 3.6227
+0.947096 1.76021
+3.66411 5.67117
+9.31422 10.1035
+6.84847 6.98956
+2.16221 4.45806
+0.853284 3.15932
+2.48673 2.89793
+3.37633 3.90938
+1.6141 1.66363
+1.6658 3.01723
+0.419408 1.01736
+0.712239 0.791538
+2.25249 3.62425
+8.09772 10.2367
+1.46651 1.48148
+1.73233 2.0489
+7.42679 9.23232
+7.97446 9.53281
+2.74677 4.03955
+-0.169412 0.872795
+4.90663 5.40277
+7.62122 9.49334
+6.55971 7.34259
+3.71957 5.03775
+3.33226 4.06459
+0.634912 1.52603
+3.65506 4.09318
+4.31463 4.60534
+8.58287 9.85385
+0.334759 0.7232
+2.14027 2.866
+6.06611 6.3625
+2.53013 3.18764
+4.30913 4.89247
+6.53234 6.92328
+7.66419 8.61762
+1.37598 2.26754
+8.06279 8.80692
+5.28688 5.70608
+0.509396 1.34606
+8.80589 9.29765
+9.3801 10.3988
+0.402662 0.597735
+5.87213 6.63371
+2.79136 3.763
+6.20615 7.25189
+7.54065 9.62156
+4.12107 5.04454
+5.60294 6.69246
+1.20845 1.49272
+1.75679 2.56397
+2.83939 3.85147
+6.21362 6.71341
+9.01824 9.2388
+4.66617 5.04944
+1.48766 2.55674
+8.89442 9.42175
+5.29411 5.39531
+6.66271 8.68733
+0.730461 1.42466
+6.22927 6.55086
+0.566776 1.17299
+3.06104 4.17775
+5.28026 5.73966
+5.76071 7.61585
+7.83786 9.58106
+6.32199 6.46961
+8.89555 9.17429
+3.85772 4.97805
+6.63352 8.79211
+7.87234 7.94131
+6.53479 7.87979
+3.90293 4.50376
+6.21371 7.31788
+1.00497 1.75003
+6.90351 8.02435
+2.29142 3.9809
+6.26632 6.45283
+-0.0354471 1.31727
+8.80046 9.20169
+7.86905 8.36819
+0.848737 1.82393
+7.98136 9.40712
+3.97765 4.95399
+8.70252 9.26275
+3.13412 4.29389
+2.06446 3.55839
+7.67918 8.44509
+5.96082 6.40336
+1.20461 2.36924
+4.76741 5.74978
+9.41217 9.94305
+7.36736 8.07119
+3.44102 3.56957
+3.59893 4.52751
+4.2613 6.41628
+1.65321 3.02235
+3.70067 3.86657
+0.0176327 1.1215
+4.13592 4.88595
+8.73668 9.0834
+1.80944 2.95584
+6.05721 6.93009
+5.08206 5.38626
+1.86749 1.89392
+3.58284 4.10506
+0.0885586 1.47709
+5.46502 5.93066
+8.45338 8.47878
+3.46789 4.02317
+0.975506 1.15814
+7.12758 7.60073
+7.99582 8.56746
+3.11435 3.83805
+4.51166 4.52472
+8.39096 8.75934
+8.42208 9.66019
+2.48498 4.73703
+3.50301 4.81264
+6.40196 7.0733
+8.52138 8.82265
+1.49822 1.74634
+7.08547 7.93508
+3.47832 4.42948
+6.08646 6.20865
+4.46334 4.65278
+3.80008 4.08214
+3.4853 5.44308
+5.48109 6.18681
+6.59161 8.04307
+7.36554 7.38201
+5.2144 5.31725
+4.62607 7.25804
+1.24345 1.81263
+5.49248 8.16867
+6.4056 8.26316
+7.67828 8.56923
+2.46187 2.56974
+2.22942 3.27248
+7.14933 8.06682
+2.3236 2.95462
+4.18028 4.80535
+4.981 5.54215
+7.23261 7.34863
+4.96309 6.06396
+4.24402 4.44364
+0.917707 1.32687
+1.19806 1.65106
+3.7725 5.31412
+9.70021 9.84203
+5.99824 7.77474
+3.32636 3.84266
+8.59841 9.07653
+7.9916 9.14415
+0.786519 1.84655
+0.166394 1.47834
+2.87633 3.52808
+7.53804 9.56807
+6.14406 6.16189
+1.63428 3.96845
+1.10307 1.58897
+1.30032 2.85313
+7.89803 8.32723
+8.77593 8.90136
+1.68804 2.69447
+4.5956 4.6743
+7.39643 8.29839
+6.93629 7.82243
+6.10289 7.677
+1.83835 2.34295
+1.04506 1.91684
+8.95277 9.43817
+0.36153 0.643665
+1.02408 3.18963
+4.02972 4.40301
+2.80428 2.82552
+6.51718 6.64956
+0.185341 1.56607
+6.1658 7.59364
+4.09288 4.11687
+6.90205 7.16338
+5.0755 5.39478
+4.5614 4.83286
+5.8489 8.76295
+9.70079 10.0431
+8.44334 8.54714
+3.00139 3.66327
+5.34458 6.38895
+7.11481 7.96865
+4.66776 5.15678
+0.390138 0.53352
+2.01116 3.32839
+6.78761 6.97068
+4.57477 5.0724
+1.14098 1.36134
+7.4559 8.30327
+0.700932 2.40437
+1.90469 1.99046
+4.30064 5.11485
+7.84433 8.48543
+5.96212 7.49882
+3.93055 3.96317
+0.546327 1.04814
+8.96011 9.0404
+7.89255 8.06149
+0.669924 2.6944
+-0.00972314 0.837474
+6.07199 6.37467
+2.73659 3.19833
+5.9576 7.10966
+9.1316 9.14737
+5.26101 6.59184
+5.77697 8.08876
+0.68623 0.782805
+0.842619 1.39893
+5.82075 6.15957
+4.36394 4.88479
+7.1828 7.90107
+3.3331 4.39923
+6.54243 7.24966
+2.809 2.96491
+4.18514 4.61948
+4.95453 5.33929
+8.33288 9.52867
+9.05785 10.4276
+2.58917 4.74403
+1.34279 1.89014
+4.93626 5.75424
+7.75031 7.92788
+9.00904 9.02402
+3.91286 5.15124
+6.98486 7.34688
+-0.173169 0.415019
+0.652242 1.51556
+3.50184 5.30486
+3.72563 4.09022
+4.60463 6.58985
+3.34219 4.31833
+2.55445 2.58651
+6.14573 6.3669
+4.52336 6.37342
+9.37606 9.76836
+2.4198 3.36929
+2.89109 3.66974
+5.62463 6.05989
+0.674219 1.84165
+0.590017 1.71153
+1.54622 2.10277
+6.48003 6.55663
+3.56355 3.98573
+0.743671 1.87049
+3.27833 6.0722
+4.76267 6.9967
+2.80188 4.03037
+0.372694 0.482111
+5.73987 5.9529
+8.84732 9.49033
+0.833944 1.8157
+4.06126 4.36615
+0.884619 2.34622
+6.86723 6.99141
+8.07737 9.05193
+8.35903 9.00761
+8.84777 9.74001
+8.58058 9.05479
+8.28076 8.44566
+4.55155 5.65475
+2.44314 3.38966
+3.22172 4.50395
+7.1808 7.90376
+0.639636 1.72606
+5.46807 5.94322
+9.02146 9.87864
+-0.628472 0.655557
+4.29775 5.9553
+6.77525 7.38152
+6.51827 6.93542
+8.95252 9.52939
+0.755406 1.19973
+7.69455 7.86912
+0.170143 1.12949
+7.69254 8.08221
+2.08238 3.27051
+1.28662 1.78628
+1.21208 2.79391
+-0.155739 1.7071
+8.28833 9.10011
+5.77789 6.6954
+1.98395 2.35216
+5.77469 6.05177
+2.77565 5.81092
+-1.53416 2.30576
+6.93529 8.04506
+3.91553 4.55593
+5.57201 6.29071
+1.54149 1.86613
+4.09163 4.31234
+8.47834 11.1229
+5.70376 7.02981
+2.24683 3.79925
+5.4793 6.41036
+5.27644 6.81729
+0.13273 0.371437
+1.22959 1.41618
+0.551957 1.03276
+2.01269 2.4018
+8.77113 9.09395
+8.11753 8.46262
+0.39406 0.783705
+5.35964 5.48227
+7.24727 8.23233
+7.74525 8.85748
+0.0115736 0.882607
+1.78575 2.19684
+8.32514 9.0503
+3.11074 3.34975
+0.847464 2.13237
+0.310898 0.393528
+8.68841 10.518
+1.10032 1.83755
+2.42286 3.02554
+1.16364 2.80986
+1.46419 2.11421
+7.46021 7.90273
+6.86556 7.62542
+1.91466 3.79843
+3.1056 3.83969
+3.41591 4.26565
+0.285122 1.21372
+3.75441 5.52385
+1.07643 2.63403
+6.17341 7.67244
+9.13122 9.5132
+4.39696 6.15885
+3.7207 4.07729
+6.80782 7.1958
+6.65933 6.67105
+8.51535 8.7263
+5.98305 7.17759
+2.19609 2.46108
+8.21186 8.96908
+2.94729 3.60562
+3.31907 3.9297
+0.463492 0.626603
+2.62143 3.55089
+5.76898 6.79002
+1.45502 1.56525
+4.80797 5.62389
+5.87337 6.70588
+2.92765 3.50929
+0.412987 0.951057
+3.64594 4.40269
+-0.043401 0.502176
+4.54802 5.44613
+1.20218 1.8732
+2.75547 2.96745
+7.10348 7.7945
+9.74845 9.98835
+4.83718 6.4836
+2.30563 2.5903
+4.74016 6.30789
+6.42192 6.93329
+5.37294 6.73938
+2.4365 3.05787
+1.08283 3.79193
+4.90426 5.93563
+3.8874 4.83301
+0.13909 0.983027
+4.07407 5.61421
+8.62875 10.6851
+8.10233 8.29952
+2.23047 2.4691
+7.34617 7.39475
+1.66309 2.3032
+7.11207 10.0686
+4.52412 6.38666
+2.48323 2.96878
+7.47877 8.86547
+2.67607 4.33436
+6.83659 8.21075
+2.87313 3.39011
+2.81911 4.07729
+0.227877 0.657898
+6.57843 7.14311
+-0.591405 1.56925
+7.08018 7.59
+9.09558 9.3923
+9.48544 9.79474
+0.740233 2.33129
+8.51111 11.134
+7.79219 8.0281
+1.73821 1.89316
+9.21077 9.29488
+5.38354 6.46025
+4.30403 4.5472
+0.0893758 0.919238
+7.94464 8.51053
+5.27119 6.3741
+5.22476 6.00588
diff --git a/geom_matching/wasserstein/tests/data/test_5_A b/geom_matching/wasserstein/tests/data/test_5_A
new file mode 100644
index 0000000..51cff1c
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_5_A
@@ -0,0 +1,5 @@
+7.50638 7.78005
+0.991758 2.12178
+5.18481 6.61702
+6.14703 7.08581
+4.09936 4.83024
diff --git a/geom_matching/wasserstein/tests/data/test_5_B b/geom_matching/wasserstein/tests/data/test_5_B
new file mode 100644
index 0000000..be62ed3
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_5_B
@@ -0,0 +1,5 @@
+5.8232 6.36308
+2.16066 2.48668
+2.38754 4.91418
+4.77403 5.43982
+0.291412 1.11147
diff --git a/geom_matching/wasserstein/tests/data/test_list.txt b/geom_matching/wasserstein/tests/data/test_list.txt
new file mode 100644
index 0000000..b5ac730
--- /dev/null
+++ b/geom_matching/wasserstein/tests/data/test_list.txt
@@ -0,0 +1,18 @@
+test_5_A test_5_B 1.0 -1.0 4.320655
+test_5_A test_5_B 2.0 -1.0 1.7323016335246
+test_5_A test_5_B 3.0 -1.0 1.396199948
+test_5_A test_5_B 1.0 1.0 6.603006
+test_5_A test_5_B 2.0 1.0 2.73426775794691
+test_5_A test_5_B 3.0 1.0 2.13072755216204
+test_5_A test_5_B 1.0 2.0 5.1888795679967
+test_5_A test_5_B 2.0 2.0 2.26649677575857
+test_5_A test_5_B 3.0 2.0 1.86434961396614
+test_100_A test_100_B 1.0 -1.0 27.5573259
+test_100_A test_100_B 2.0 -1.0 3.38004972483238
+test_100_A test_100_B 3.0 -1.0 1.9045814702967
+test_100_A test_100_B 1.0 1.0 40.4375855
+test_100_A test_100_B 2.0 1.0 4.75252460986668
+test_100_A test_100_B 3.0 1.0 2.556594741819
+test_100_A test_100_B 1.0 2.0 31.7187555735287
+test_100_A test_100_B 2.0 2.0 3.81132239777023
+test_100_A test_100_B 3.0 2.0 2.09695346034248
diff --git a/geom_matching/wasserstein/tests/test_hera_wasserstein.cpp b/geom_matching/wasserstein/tests/test_hera_wasserstein.cpp
new file mode 100644
index 0000000..2ee49ad
--- /dev/null
+++ b/geom_matching/wasserstein/tests/test_hera_wasserstein.cpp
@@ -0,0 +1,203 @@
+#define LOG_AUCTION
+#include "catch/catch.hpp"
+
+#include <sstream>
+#include <iostream>
+
+
+#undef LOG_AUCTION
+
+#include "wasserstein.h"
+
+
+using PairVector = std::vector<std::pair<double, double>>;
+
+std::vector<std::string> split_on_delim(const std::string& s, char delim)
+{
+ std::stringstream ss(s);
+ std::string token;
+ std::vector<std::string> tokens;
+ while(std::getline(ss, token, delim)) {
+ tokens.push_back(token);
+ }
+ return tokens;
+}
+
+
+// single row in a file with test cases
+struct TestFromFileCase {
+
+ std::string file_1;
+ std::string file_2;
+ double q;
+ double internal_p;
+ double answer;
+
+ TestFromFileCase(std::string s)
+ {
+ auto tokens = split_on_delim(s, ' ');
+ assert(tokens.size() == 5);
+
+ file_1 = tokens.at(0);
+ file_2 = tokens.at(1);
+ q = std::stod(tokens.at(2));
+ internal_p = std::stod(tokens.at(3));
+ answer = std::stod(tokens.at(4));
+
+ if ( q < 1.0 or std::isinf(q) or
+ (internal_p != hera::get_infinity<double>() and internal_p < 1.0)) {
+ throw std::runtime_error("Bad line in test_list.txt");
+ }
+ }
+};
+
+std::ostream& operator<<(std::ostream& out, const TestFromFileCase& s)
+{
+ out << "[" << s.file_1 << ", " << s.file_2 << ", q = " << s.q << ", norm = ";
+ if (s.internal_p != hera::get_infinity()) {
+ out << s.internal_p;
+ } else {
+ out << "infinity";
+ }
+ out << ", answer = " << s.answer << "]";
+ return out;
+}
+
+
+TEST_CASE("simple cases", "wasserstein_dist")
+{
+ PairVector diagram_A, diagram_B;
+ hera::AuctionParams<double> params;
+ params.wasserstein_power = 1.0;
+ params.delta = 0.01;
+ params.internal_p = hera::get_infinity<double>();
+ params.initial_epsilon = 0.0;
+ params.epsilon_common_ratio = 0.0;
+ params.max_num_phases = 30;
+ params.gamma_threshold = 0.0;
+ params.max_bids_per_round = 0; // use Jacobi
+
+ SECTION("trivial: two empty diagrams") {
+ REQUIRE( 0.0 == hera::wasserstein_dist<>(diagram_A, diagram_B, params));
+ }
+
+ SECTION("trivial: one empty diagram, one single-point diagram") {
+
+ diagram_A.emplace_back(1.0, 2.0);
+
+ double d1 = hera::wasserstein_dist<>(diagram_A, diagram_B, params);
+ REQUIRE( fabs(d1 - 0.5) <= 0.00000000001 );
+
+ double d2 = hera::wasserstein_dist<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d2 - 0.5) <= 0.00000000001 );
+
+ params.internal_p = 2.0;
+ double corr_answer = 1.0 / std::sqrt(2.0);
+ double d3 = hera::wasserstein_dist<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d3 - corr_answer) <= 0.00000000001 );
+
+ }
+
+ SECTION("trivial: two single-point diagrams-1") {
+
+ diagram_A.emplace_back(10.0, 20.0); // (5, 5)
+ diagram_B.emplace_back(13.0, 19.0); // (3, 3)
+
+ double d1 = hera::wasserstein_dist<>(diagram_A, diagram_B, params);
+ double d2 = hera::wasserstein_dist<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d1 - d2) <= 0.00000000001 );
+ REQUIRE( fabs(d1 - 3.0) <= 0.00000000001 );
+
+ params.wasserstein_power = 2.0;
+ double d3 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d4 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d3 - d4) <= 0.00000000001 );
+ REQUIRE( fabs(d4 - 9.0) <= 0.00000000001 );
+
+ params.wasserstein_power = 1.0;
+ params.internal_p = 1.0;
+ double d5 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d6 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d5 - d6) <= 0.00000000001 );
+ REQUIRE( fabs(d5 - 4.0) <= 0.00000000001 );
+
+ params.internal_p = 2.0;
+ double d7 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d8 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d7 - d8) <= 0.00000000001 );
+ REQUIRE( fabs(d7 - std::sqrt(10.0)) <= 0.00000000001 );
+
+ }
+
+ SECTION("trivial: two single-point diagrams-2") {
+
+ diagram_A.emplace_back(10.0, 20.0); // (5, 5)
+ diagram_B.emplace_back(130.0, 138.0); // (4, 4)
+
+ double d1 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d2 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d1 - d2) <= 0.00000000001 );
+ REQUIRE( fabs(d1 - 9.0) <= 0.00000000001 );
+
+ params.wasserstein_power = 2.0;
+ double d3 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d4 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d3 - d4) <= 0.00000000001 );
+ REQUIRE( fabs(d4 - 41.0) <= 0.00000000001 ); // 5^2 + 4^2
+
+ params.wasserstein_power = 1.0;
+ params.internal_p = 1.0;
+ double d5 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d6 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d5 - d6) <= 0.00000000001 );
+ REQUIRE( fabs(d5 - 18.0) <= 0.00000000001 ); // 5 + 5 + 4 + 4
+
+ params.internal_p = 2.0;
+ double d7 = hera::wasserstein_cost<>(diagram_A, diagram_B, params);
+ double d8 = hera::wasserstein_cost<>(diagram_B, diagram_A, params);
+ REQUIRE( fabs(d7 - d8) <= 0.00000000001 );
+ REQUIRE( fabs(d7 - 9 * std::sqrt(2.0)) <= 0.00000000001 ); // sqrt(5^2 + 5^2) + sqrt(4^2 + 4^2) = 9 sqrt(2)
+
+ }
+
+}
+
+
+
+TEST_CASE("file cases", "wasserstein_dist")
+{
+ PairVector diagram_A, diagram_B;
+ hera::AuctionParams<double> params;
+ params.wasserstein_power = 1.0;
+ params.delta = 0.01;
+ params.internal_p = hera::get_infinity<double>();
+ params.initial_epsilon = 0.0;
+ params.epsilon_common_ratio = 0.0;
+ params.max_num_phases = 30;
+ params.gamma_threshold = 0.0;
+ params.max_bids_per_round = 1; // use Jacobi
+
+
+ SECTION("from file:") {
+ const char* file_name = "../tests/data/test_list.txt";
+ std::ifstream f;
+ f.open(file_name);
+ std::vector<TestFromFileCase> test_params;
+ std::string s;
+ while (std::getline(f, s)) {
+ test_params.emplace_back(s);
+ }
+
+ for(const auto& ts : test_params) {
+ params.wasserstein_power = ts.q;
+ params.internal_p = ts.internal_p;
+ bool read_file_A = hera::read_diagram_point_set<double, PairVector>(ts.file_1, diagram_A);
+ bool read_file_B = hera::read_diagram_point_set<double, PairVector>(ts.file_2, diagram_B);
+ REQUIRE( read_file_A );
+ REQUIRE( read_file_B );
+ double hera_answer = hera::wasserstein_dist(diagram_A, diagram_B, params);
+ REQUIRE( fabs(hera_answer - ts.answer) <= 0.01 * hera_answer );
+ std::cout << ts << " PASSED " << std::endl;
+ }
+ }
+}
diff --git a/geom_matching/wasserstein/tests/tests_main.cpp b/geom_matching/wasserstein/tests/tests_main.cpp
new file mode 100644
index 0000000..d24407e
--- /dev/null
+++ b/geom_matching/wasserstein/tests/tests_main.cpp
@@ -0,0 +1,3 @@
+#define LOG_AUCTION
+#define CATCH_CONFIG_MAIN
+#include "catch/catch.hpp"