summaryrefslogtreecommitdiff
path: root/geom_matching
diff options
context:
space:
mode:
Diffstat (limited to 'geom_matching')
-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
93 files changed, 43288 insertions, 3010 deletions
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"