mirror of
https://github.com/boostorg/histogram.git
synced 2026-01-30 07:52:11 +00:00
Merge pull request #126 from HDembinski/feature/axis_generalization
Mega-patch
This commit is contained in:
@@ -4,24 +4,62 @@
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
image: Visual Studio 2015
|
||||
image: Visual Studio 2017
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
install:
|
||||
- cd ..
|
||||
- git clone -b %APPVEYOR_REPO_BRANCH% https://github.com/boostorg/boost
|
||||
- cd boost
|
||||
- mkdir libs\histogram
|
||||
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\histogram
|
||||
- git submodule init libs/array
|
||||
- git submodule init libs/assert
|
||||
- git submodule init libs/bind
|
||||
- git submodule init libs/callable_traits
|
||||
- git submodule init libs/concept_check
|
||||
- git submodule init libs/config
|
||||
- git submodule init libs/container
|
||||
- git submodule init libs/container_hash
|
||||
- git submodule init libs/core
|
||||
- git submodule init libs/detail
|
||||
- git submodule init libs/function
|
||||
- git submodule init libs/integer
|
||||
- git submodule init libs/io
|
||||
- git submodule init libs/iterator
|
||||
- git submodule init libs/lexical_cast
|
||||
- git submodule init libs/math
|
||||
- git submodule init libs/move
|
||||
- git submodule init libs/mp11
|
||||
- git submodule init libs/mpl
|
||||
- git submodule init libs/multiprecision
|
||||
- git submodule init libs/numeric
|
||||
- git submodule init libs/optional
|
||||
- git submodule init libs/predef
|
||||
- git submodule init libs/preprocessor
|
||||
- git submodule init libs/range
|
||||
- git submodule init libs/rational
|
||||
- git submodule init libs/serialization
|
||||
- git submodule init libs/smart_ptr
|
||||
- git submodule init libs/spirit
|
||||
- git submodule init libs/static_assert
|
||||
- git submodule init libs/throw_exception
|
||||
- git submodule init libs/type_index
|
||||
- git submodule init libs/type_traits
|
||||
- git submodule init libs/typeof
|
||||
- git submodule init libs/units
|
||||
- git submodule init libs/utility
|
||||
- git submodule init libs/variant
|
||||
- git submodule init tools/build
|
||||
- git submodule update
|
||||
- bootstrap
|
||||
- b2 --with-serialization
|
||||
|
||||
build: off
|
||||
|
||||
environment:
|
||||
# MSVC_DEFAULT_OPTIONS: ON
|
||||
BOOST_ROOT: C:\Libraries\boost_1_67_0
|
||||
|
||||
test_script:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
-DBOOST_ROOT="%BOOST_ROOT%" -DBoost_USE_STATIC_LIBS="ON"
|
||||
- cmake --build .
|
||||
- ctest -V
|
||||
- b2 libs/histogram
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*build*
|
||||
histogram.sublime-workspace
|
||||
CMakeSettings.json
|
||||
doc/html/.buildinfo
|
||||
doc/html/.doctrees
|
||||
|
||||
28
.travis.yml
28
.travis.yml
@@ -15,26 +15,40 @@ branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
# g++-5 also needed for clang to see C++14 stdlib
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- libstdc++-5-dev libgcc-5-dev libstdc++6 libasan2 libtsan0 libubsan0
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
compiler: clang
|
||||
env: SERIAL=OFF
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: SERIAL=ON
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: SERIAL=ON
|
||||
- os: osx
|
||||
env: SERIAL=OFF
|
||||
- os: linux # coverage build
|
||||
compiler: gcc
|
||||
env: GCOV=gcov
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-5
|
||||
env:
|
||||
- MATRIX_EVAL="GCOV=gcov-5 && CC=gcc-5 && CXX=g++-5"
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
|
||||
# Install packages (pre-installed: pytest)
|
||||
install:
|
||||
- source tools/travis_install_boost.sh
|
||||
@@ -67,7 +81,7 @@ after_success:
|
||||
coveralls -r .. -b . --verbose --exclude ${TRAVIS_BUILD_DIR}/deps --gcov=`which ${GCOV}` --gcov-options '\-lpbc';
|
||||
fi
|
||||
|
||||
after_failure:
|
||||
# after_failure:
|
||||
# - otool -L histogram.so
|
||||
# - printf "r\nbt" > gdb.cmd
|
||||
# - for x in *_test; do
|
||||
|
||||
@@ -8,30 +8,18 @@ cmake_minimum_required (VERSION 3.5)
|
||||
|
||||
project(histogram CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(MIN_BOOST_VERSION 1.66)
|
||||
|
||||
if ("$ENV{BOOST_ROOT}" STREQUAL "")
|
||||
set(BOOST_INCLUDEDIR "${PROJECT_SOURCE_DIR}/../..")
|
||||
endif()
|
||||
|
||||
# setup build
|
||||
option(BUILD_BENCHMARKS "Build benchmarks" OFF)
|
||||
option(TEST_SERIALIZATION "Test serialization code" OFF)
|
||||
mark_as_advanced(BUILD_BENCHMARKS)
|
||||
mark_as_advanced(TEST_SERIALIZATION)
|
||||
|
||||
# set build type if none is specified
|
||||
set(default_build_type "Release")
|
||||
if (EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
set(default_build_type "Debug")
|
||||
endif()
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
|
||||
STRING "Choose the type of build." FORCE)
|
||||
# Set the possible values of build type for cmake-gui
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
# serialization is optional
|
||||
if (TEST_SERIALIZATION)
|
||||
@@ -57,7 +45,7 @@ function(compiled_test SRC)
|
||||
target_include_directories(${BASENAME} PUBLIC include ${Boost_INCLUDE_DIR})
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES coverage)
|
||||
target_compile_options(${BASENAME} PRIVATE -O0 -g --coverage)
|
||||
target_compile_options(${BASENAME} PRIVATE --coverage)
|
||||
target_link_libraries(${BASENAME} PRIVATE --coverage)
|
||||
endif()
|
||||
|
||||
@@ -65,24 +53,24 @@ function(compiled_test SRC)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_compile_options(${BASENAME} PRIVATE -D_SCL_SECURE_NO_WARNINGS)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${BASENAME} PRIVATE -Wall -Wextra)
|
||||
target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
|
||||
# -fpermissive needed for cpp_int in gcc-6
|
||||
target_compile_options(${BASENAME} PRIVATE -fpermissive)
|
||||
# -Wno-noexcept-type needed for callable_traits
|
||||
target_compile_options(${BASENAME} PRIVATE -fpermissive -Wno-noexcept-type)
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
target_compile_options(${BASENAME} PRIVATE -Wall -Wextra
|
||||
target_compile_options(${BASENAME} PRIVATE -Wall -Wextra -g -O0
|
||||
-Wno-unused-local-typedef -D__STRICT_ANSI__)
|
||||
endif()
|
||||
|
||||
## deactivate sanitizers for now, LeakSanitizer is crashing
|
||||
# # activate sanitizers for clang builds
|
||||
# if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# target_compile_options(${BASENAME} PRIVATE
|
||||
# -fsanitize=address,undefined
|
||||
# -fno-omit-frame-pointer)
|
||||
# target_link_libraries(${BASENAME} PRIVATE -fsanitize=address,undefined)
|
||||
# endif()
|
||||
# activate sanitizers for clang builds
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
target_compile_options(${BASENAME} PRIVATE
|
||||
-fsanitize=address,undefined
|
||||
-fno-omit-frame-pointer)
|
||||
target_link_libraries(${BASENAME} PRIVATE -fsanitize=address,undefined)
|
||||
endif()
|
||||
|
||||
if (${BASENAME} MATCHES "_fail")
|
||||
add_test(${BASENAME} python ${PROJECT_SOURCE_DIR}/tools/pass_on_fail.py ${BASENAME})
|
||||
@@ -93,9 +81,13 @@ endfunction()
|
||||
|
||||
compiled_test(test/adaptive_storage_test.cpp)
|
||||
compiled_test(test/array_storage_test.cpp)
|
||||
compiled_test(test/axis_test.cpp)
|
||||
compiled_test(test/axis_regular_test.cpp)
|
||||
compiled_test(test/axis_circular_test.cpp)
|
||||
compiled_test(test/axis_variable_test.cpp)
|
||||
compiled_test(test/axis_integer_test.cpp)
|
||||
compiled_test(test/axis_category_test.cpp)
|
||||
compiled_test(test/axis_variant_test.cpp)
|
||||
compiled_test(test/detail_test.cpp)
|
||||
compiled_test(test/histogram_dynamic_add_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_at_tuple_wrong_dimension_fail.cpp)
|
||||
@@ -103,7 +95,6 @@ compiled_test(test/histogram_dynamic_at_vector_wrong_dimension_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_at_wrong_dimension_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_reduce_wrong_order_fail.cpp)
|
||||
# test fail to compile (test/histogram_static_fill_one_dimensional_tuple_fail.cpp)
|
||||
compiled_test(test/histogram_static_add_fail.cpp)
|
||||
compiled_test(test/histogram_static_at_vector_wrong_dimension_fail.cpp)
|
||||
compiled_test(test/histogram_dynamic_test.cpp)
|
||||
compiled_test(test/histogram_mixed_test.cpp)
|
||||
@@ -118,7 +109,8 @@ compiled_test(examples/getting_started_listing_02.cpp)
|
||||
compiled_test(examples/guide_access_bin_counts.cpp)
|
||||
compiled_test(examples/guide_axis_with_labels.cpp)
|
||||
compiled_test(examples/guide_axis_with_uoflow_off.cpp)
|
||||
compiled_test(examples/guide_custom_axis.cpp)
|
||||
compiled_test(examples/guide_custom_modified_axis.cpp)
|
||||
compiled_test(examples/guide_custom_minimal_axis.cpp)
|
||||
compiled_test(examples/guide_custom_storage.cpp)
|
||||
compiled_test(examples/guide_fill_histogram.cpp)
|
||||
compiled_test(examples/guide_histogram_operators.cpp)
|
||||
@@ -139,9 +131,6 @@ if (TEST_SERIALIZATION)
|
||||
endif()
|
||||
|
||||
if (BUILD_BENCHMARKS)
|
||||
if (NOT ${CMAKE_BUILD_TYPE} EQUAL Release)
|
||||
message(WARNING "Benchmarks should be build in Release mode")
|
||||
endif()
|
||||
add_executable(speed_cpp test/speed_cpp.cpp)
|
||||
target_include_directories(speed_cpp PRIVATE include ${Boost_INCLUDE_DIR})
|
||||
target_compile_definitions(speed_cpp PRIVATE -DBOOST_DISABLE_ASSERTS)
|
||||
|
||||
6
Jamfile
6
Jamfile
@@ -8,9 +8,9 @@
|
||||
project histogram
|
||||
:
|
||||
requirements
|
||||
<toolset>gcc:<cxxflags>"-std=c++11 -pedantic -Wall -Wextra"
|
||||
<toolset>clang:<cxxflags>"-std=c++11 -pedantic -Wall -Wextra"
|
||||
<toolset>darwin:<cxxflags>"-std=c++11 -pedantic -Wall -Wextra"
|
||||
<toolset>gcc:<cxxflags>"-std=c++14 -Wall -Wextra -Wno-noexcept-type"
|
||||
<toolset>clang:<cxxflags>"-std=c++14 -Wall -Wextra"
|
||||
<toolset>darwin:<cxxflags>"-std=c++14 -Wall -Wextra"
|
||||
;
|
||||
|
||||
build-project test ;
|
||||
|
||||
@@ -174,8 +174,15 @@ In C++, users can implement their own axis class without touching any library co
|
||||
|
||||
The simplest way to make a custom axis is to derive from a builtin class. Here is a contrived example of a custom axis that inherits from the [classref boost::histogram::axis::integer integer axis] and accepts c-strings representing numbers.
|
||||
|
||||
[import ../examples/guide_custom_axis.cpp]
|
||||
[guide_custom_axis]
|
||||
[import ../examples/guide_custom_modified_axis.cpp]
|
||||
[guide_custom_modified_axis]
|
||||
|
||||
Alternatively, you can also make an axis completely from scratch. An minimal axis is a functor that maps an input to a bin index. The index has a range `[0, AxisType::size())`.
|
||||
|
||||
[import ../examples/guide_custom_minimal_axis.cpp]
|
||||
[guide_custom_minimal_axis]
|
||||
|
||||
Such a minimal axis works, even though it lacks convenience features provided by the builtin axis types. For example, one cannot iterate over this axis. Not even a bin description can be queried, because `operator[]` is not implemented. It is up to the user to implement these optional aspects.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
import testing ;
|
||||
|
||||
project histogram-example
|
||||
: requirements
|
||||
@@ -11,17 +12,18 @@ project histogram-example
|
||||
<include>.
|
||||
;
|
||||
|
||||
exe getting_started_listing_01 : getting_started_listing_01.cpp ;
|
||||
exe getting_started_listing_02 : getting_started_listing_02.cpp ;
|
||||
exe guide_access_bin_counts : guide_access_bin_counts.cpp ;
|
||||
exe guide_axis_with_labels : guide_axis_with_labels.cpp ;
|
||||
exe guide_axis_with_uoflow_off : guide_axis_with_uoflow_off.cpp ;
|
||||
exe guide_custom_axis : guide_custom_axis.cpp ;
|
||||
exe guide_custom_storage : guide_custom_storage.cpp ;
|
||||
exe guide_fill_histogram : guide_fill_histogram.cpp ;
|
||||
exe guide_histogram_operators : guide_histogram_operators.cpp ;
|
||||
exe guide_histogram_reduction : guide_histogram_reduction.cpp ;
|
||||
exe guide_histogram_serialization : guide_histogram_serialization.cpp /boost/serialization//boost_serialization/<link>static ;
|
||||
exe guide_histogram_streaming : guide_histogram_streaming.cpp ;
|
||||
exe guide_make_dynamic_histogram : guide_make_dynamic_histogram.cpp ;
|
||||
exe guide_make_static_histogram : guide_make_static_histogram.cpp ;
|
||||
run getting_started_listing_01.cpp ;
|
||||
run getting_started_listing_02.cpp ;
|
||||
run guide_access_bin_counts.cpp ;
|
||||
run guide_axis_with_labels.cpp ;
|
||||
run guide_axis_with_uoflow_off.cpp ;
|
||||
run guide_custom_minimal_axis.cpp ;
|
||||
run guide_custom_modified_axis.cpp ;
|
||||
run guide_custom_storage.cpp ;
|
||||
run guide_fill_histogram.cpp ;
|
||||
run guide_histogram_operators.cpp ;
|
||||
run guide_histogram_reduction.cpp ;
|
||||
run guide_histogram_serialization.cpp /boost/serialization//boost_serialization/<link>static ;
|
||||
run guide_histogram_streaming.cpp ;
|
||||
run guide_make_dynamic_histogram.cpp ;
|
||||
run guide_make_static_histogram.cpp ;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//[ getting_started_listing_01
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
int main() {
|
||||
namespace bh = boost::histogram;
|
||||
@@ -11,12 +14,13 @@ int main() {
|
||||
create a static 1d-histogram with an axis that has 6 equidistant
|
||||
bins on the real line from -1.0 to 2.0, and label it as "x"
|
||||
*/
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(6, -1.0, 2.0, "x"));
|
||||
auto h = bh::make_histogram(bh::axis::regular<>(6, -1.0, 2.0, "x"));
|
||||
|
||||
// fill histogram with data, typically this happens in a loop
|
||||
// STL algorithms are supported
|
||||
// Fill histogram with data, typically this happens in a loop.
|
||||
// STL algorithms are supported, but make sure to use std::ref in
|
||||
// the call to std::for_each to avoid copying the argument.
|
||||
auto data = {-0.5, 1.1, 0.3, 1.7};
|
||||
h = std::for_each(data.begin(), data.end(), h);
|
||||
std::for_each(data.begin(), data.end(), std::ref(h));
|
||||
|
||||
/*
|
||||
a regular axis is a sequence of semi-open bins; extra under- and
|
||||
@@ -39,40 +43,37 @@ int main() {
|
||||
iterate over bins with a fancy histogram iterator
|
||||
- order in which bins are iterated over is an implementation detail
|
||||
- iterator dereferences to histogram::element_type, which is defined by
|
||||
its storage class; by default something with value() and
|
||||
variance() methods; the first returns the
|
||||
actual count, the second returns a variance estimate of the count
|
||||
(see Rationale section for what this means)
|
||||
its storage class; by default this is a double
|
||||
- idx(N) method returns the index of the N-th axis
|
||||
- bin(N_c) method returns current bin of N-th axis; the suffx _c turns
|
||||
the argument into a compile-time number, which is needed to return
|
||||
different `bin_type`s for different axes
|
||||
a different `bin_type`s for each axis
|
||||
- `bin_type` usually is a semi-open interval representing the bin, whose
|
||||
edges can be accessed with methods `lower()` and `upper()`, but the
|
||||
implementation depends on the axis, please look it up in the reference
|
||||
*/
|
||||
std::cout.setf(std::ios_base::fixed);
|
||||
std::ostringstream os;
|
||||
os.setf(std::ios_base::fixed);
|
||||
for (auto it = h.begin(); it != h.end(); ++it) {
|
||||
const auto bin = it.bin(0_c);
|
||||
std::cout << "bin " << it.idx(0) << " x in [" << std::setprecision(1)
|
||||
<< std::setw(4) << bin.lower() << ", " << std::setw(4)
|
||||
<< bin.upper() << "): " << std::setprecision(1) << it->value()
|
||||
<< " +/- " << std::setprecision(3) << std::sqrt(it->variance())
|
||||
<< std::endl;
|
||||
os << "bin " << std::setw(2) << it.idx(0) << " [" << std::setprecision(1)
|
||||
<< std::setw(4) << bin.lower() << ", " << std::setw(4)
|
||||
<< bin.upper() << "): " << *it << "\n";
|
||||
}
|
||||
|
||||
/* program output: (note that under- and overflow bins appear at the end)
|
||||
std::cout << os.str() << std::endl;
|
||||
|
||||
bin 0 x in [-1.0, -0.5): 1 +/- 1
|
||||
bin 1 x in [-0.5, 0.0): 0 +/- 0
|
||||
bin 2 x in [ 0.0, 0.5): 1 +/- 1
|
||||
bin 3 x in [ 0.5, 1.0): 0 +/- 0
|
||||
bin 4 x in [ 1.0, 1.5): 0 +/- 0
|
||||
bin 5 x in [ 1.5, 2.0): 0 +/- 0
|
||||
bin 6 x in [ 2.0, inf): 2 +/- 1.41421
|
||||
bin -1 x in [-inf, -1): 1 +/- 1
|
||||
|
||||
*/
|
||||
assert(os.str() ==
|
||||
"bin 0 [-1.0, -0.5): 1.0\n"
|
||||
"bin 1 [-0.5, -0.0): 1.0\n"
|
||||
"bin 2 [-0.0, 0.5): 2.0\n"
|
||||
"bin 3 [ 0.5, 1.0): 0.0\n"
|
||||
"bin 4 [ 1.0, 1.5): 1.0\n"
|
||||
"bin 5 [ 1.5, 2.0): 1.0\n"
|
||||
"bin 6 [ 2.0, inf): 2.0\n"
|
||||
"bin -1 [-inf, -1.0): 1.0\n"
|
||||
);
|
||||
// note how under- and overflow bins appear at the end
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -8,50 +8,47 @@ namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
/*
|
||||
create a dynamic histogram with the factory `make_dynamic_histogram`
|
||||
Create a dynamic histogram with the factory `make_dynamic_histogram`.
|
||||
- axis can be passed directly just like for `make_static_histogram`
|
||||
- in addition, the factory also accepts iterators over a sequence of
|
||||
axis::any, the polymorphic type that can hold concrete axis types
|
||||
axis::variant, the polymorphic type that can hold concrete axis types
|
||||
*/
|
||||
std::vector<
|
||||
bh::axis::any<
|
||||
bh::axis::regular<>,
|
||||
bh::axis::category<std::string>
|
||||
>
|
||||
> axes;
|
||||
std::vector<bh::axis::variant<bh::axis::regular<>, bh::axis::category<std::string> > >
|
||||
axes;
|
||||
axes.emplace_back(bh::axis::category<std::string>({"red", "blue"}));
|
||||
axes.emplace_back(bh::axis::regular<>(5, 0, 1, "x"));
|
||||
axes.emplace_back(bh::axis::regular<>(5, 0, 1, "y"));
|
||||
auto h = bh::make_dynamic_histogram(axes.begin(), axes.end());
|
||||
axes.emplace_back(bh::axis::regular<>(3, 0, 1, "x"));
|
||||
axes.emplace_back(bh::axis::regular<>(3, 0, 1, "y"));
|
||||
auto h = bh::make_histogram(axes.begin(), axes.end());
|
||||
|
||||
// fill histogram with data, usually this happens in a loop
|
||||
h("red", 0.1, 0.2);
|
||||
h("blue", 0.3, 0.4);
|
||||
h("red", 0.5, 0.6);
|
||||
h("red", 0.7, 0.8);
|
||||
h("blue", 0.7, 0.3);
|
||||
h("red", 0.3, 0.7);
|
||||
h("red", 0.7, 0.7);
|
||||
|
||||
/*
|
||||
print dynamic histogram by iterating over bins
|
||||
- for most axis types, the for loop looks just like for a static
|
||||
histogram, except that we can pass runtime numbers, too
|
||||
- if the [bin type] of the axis is not convertible to a
|
||||
double interval, one needs to cast axis::any before looping;
|
||||
this is here the case for the category axis
|
||||
Print dynamic histogram by iterating over bins.
|
||||
If the [bin type] of the axis is not convertible to a
|
||||
double interval, you need to cast axis::variant before looping;
|
||||
this is here the case for the category axis.
|
||||
*/
|
||||
using cas = bh::axis::category<std::string>;
|
||||
for (auto cbin : static_cast<const cas&>(h.axis(0))) {
|
||||
for (auto cbin : bh::axis::get<cas>(h.axis(0))) {
|
||||
for (auto ybin : h.axis(2)) { // rows
|
||||
for (auto xbin : h.axis(1)) { // columns
|
||||
const auto v = h.at(cbin, xbin, ybin).value();
|
||||
const auto v = h.at(cbin, xbin, ybin);
|
||||
if (v)
|
||||
std::printf("%4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n",
|
||||
cbin.value().c_str(),
|
||||
xbin.lower(), xbin.upper(),
|
||||
ybin.lower(), ybin.upper(),
|
||||
v);
|
||||
std::printf("(%i, %i, %i) %4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n",
|
||||
cbin.idx(), xbin.idx(), ybin.idx(), cbin.value().c_str(),
|
||||
xbin.lower(), xbin.upper(), ybin.lower(), ybin.upper(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(h.at(0, 0, 0) == 1);
|
||||
assert(h.at(0, 0, 2) == 1);
|
||||
assert(h.at(0, 2, 2) == 1);
|
||||
assert(h.at(1, 2, 0) == 1);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//[ guide_access_bin_counts
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// make histogram with 2 x 2 = 4 bins (not counting under-/overflow bins)
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1),
|
||||
bh::axis::regular<>(2, 2, 4));
|
||||
auto h = bh::make_histogram(bh::axis::regular<>(2, -1, 1),
|
||||
bh::axis::regular<>(2, 2, 4));
|
||||
|
||||
h(bh::weight(1), -0.5, 2.5); // bin index 0, 0
|
||||
h(bh::weight(2), -0.5, 3.5); // bin index 0, 1
|
||||
@@ -17,46 +17,45 @@ int main() {
|
||||
h(bh::weight(4), 0.5, 3.5); // bin index 1, 1
|
||||
|
||||
// access count value, number of indices must match number of axes
|
||||
std::cout << h.at(0, 0).value() << " " << h.at(0, 1).value() << " "
|
||||
<< h.at(1, 0).value() << " " << h.at(1, 1).value() << std::endl;
|
||||
|
||||
// prints: 1 2 3 4
|
||||
|
||||
// access count variance, number of indices must match number of axes
|
||||
std::cout << h.at(0, 0).variance() << " " << h.at(0, 1).variance() << " "
|
||||
<< h.at(1, 0).variance() << " " << h.at(1, 1).variance()
|
||||
<< std::endl;
|
||||
// prints: 1 4 9 16
|
||||
|
||||
// this is more efficient when you want to query value and variance
|
||||
auto c11 = h.at(1, 1);
|
||||
std::cout << c11.value() << " " << c11.variance() << std::endl;
|
||||
// prints: 4 16
|
||||
assert(h.at(0, 0) == 1);
|
||||
assert(h.at(0, 1) == 2);
|
||||
assert(h.at(1, 0) == 3);
|
||||
assert(h.at(1, 1) == 4);
|
||||
|
||||
// histogram also supports access via container; using a container of
|
||||
// wrong size trips an assertion in debug mode
|
||||
auto idx = {0, 1};
|
||||
std::cout << h.at(idx).value() << std::endl;
|
||||
// prints: 2
|
||||
assert(h.at(idx) == 2);
|
||||
|
||||
// histogram also provides bin iterators
|
||||
auto sum = std::accumulate(h.begin(), h.end(),
|
||||
typename decltype(h)::element_type(0));
|
||||
std::cout << sum.value() << " " << sum.variance() << std::endl;
|
||||
// prints: 10 30
|
||||
auto sum = std::accumulate(h.begin(), h.end(), 0.0);
|
||||
assert(sum == 10);
|
||||
|
||||
// bin iterators are fancy iterators with extra methods
|
||||
// (note: iteration order is an implementation detail, don't rely on it)
|
||||
std::ostringstream os;
|
||||
for (auto it = h.begin(), end = h.end(); it != end; ++it) {
|
||||
const auto x = *it;
|
||||
std::cout << it.idx(0) << " " << it.idx(1) << ": "
|
||||
<< x.value() << " " << x.variance() << std::endl;
|
||||
os << std::setw(2) << it.idx(0) << " " << std::setw(2) << it.idx(1) << ": " << *it;
|
||||
}
|
||||
// prints: (iteration order is an implementation detail, don't rely on it)
|
||||
// 0 0: 1 1
|
||||
// 1 0: 3 9
|
||||
// ...
|
||||
// 2 -1: 0 0
|
||||
// -1 -1: 0 0
|
||||
|
||||
assert(os.str() ==
|
||||
" 0 0: 1"
|
||||
" 1 0: 3"
|
||||
" 2 0: 0"
|
||||
"-1 0: 0"
|
||||
" 0 1: 2"
|
||||
" 1 1: 4"
|
||||
" 2 1: 0"
|
||||
"-1 1: 0"
|
||||
" 0 2: 0"
|
||||
" 1 2: 0"
|
||||
" 2 2: 0"
|
||||
"-1 2: 0"
|
||||
" 0 -1: 0"
|
||||
" 1 -1: 0"
|
||||
" 2 -1: 0"
|
||||
"-1 -1: 0"
|
||||
);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -6,9 +6,10 @@ namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// create a 2d-histogram with an "age" and an "income" axis
|
||||
auto h = bh::make_static_histogram(
|
||||
auto h = bh::make_histogram(
|
||||
bh::axis::regular<>(20, 0, 100, "age in years"),
|
||||
bh::axis::regular<>(20, 0, 100, "yearly income in $1000"));
|
||||
bh::axis::regular<>(20, 0, 100, "yearly income in $1000")
|
||||
);
|
||||
|
||||
// do something with h
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// create a 1d-histogram for dice throws with integer values from 1 to 6
|
||||
auto h = bh::make_static_histogram(
|
||||
bh::axis::integer<>(1, 7, "eyes", bh::axis::uoflow_type::off));
|
||||
auto h = bh::make_histogram(
|
||||
bh::axis::integer<>(1, 7, "eyes", bh::axis::option_type::none)
|
||||
);
|
||||
|
||||
// do something with h
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
//[ guide_custom_axis
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
// custom axis, which adapts builtin integer axis
|
||||
struct custom_axis : public bh::axis::integer<> {
|
||||
using value_type = const char*; // type that is fed to the axis
|
||||
|
||||
using integer::integer; // inherit ctors of base
|
||||
|
||||
// the customization point
|
||||
// - accept const char* and convert to int
|
||||
// - then call index method of base class
|
||||
int index(value_type s) const { return integer::index(std::atoi(s)); }
|
||||
};
|
||||
|
||||
int main() {
|
||||
auto h = bh::make_static_histogram(custom_axis(0, 3));
|
||||
h("-10");
|
||||
h("0");
|
||||
h("1");
|
||||
h("9");
|
||||
|
||||
for (auto xi : h.axis()) {
|
||||
std::cout << "bin " << xi.idx() << " [" << xi.lower() << ", "
|
||||
<< xi.upper() << ") " << h.at(xi).value() << std::endl;
|
||||
}
|
||||
|
||||
/* prints:
|
||||
bin 0 [0, 1) 1
|
||||
bin 1 [1, 2] 1
|
||||
bin 2 [2, 3] 0
|
||||
*/
|
||||
}
|
||||
|
||||
//]
|
||||
23
examples/guide_custom_minimal_axis.cpp
Normal file
23
examples/guide_custom_minimal_axis.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//[ guide_custom_minimal_axis
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
// stateless axis which returns 1 if the input is even and 0 otherwise
|
||||
struct minimal_axis {
|
||||
int operator()(int x) const { return x % 2; }
|
||||
unsigned size() const { return 2; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
auto h = bh::make_histogram(minimal_axis());
|
||||
|
||||
h(0); h(1); h(2);
|
||||
|
||||
assert(h.at(0) == 2); // two even numbers
|
||||
assert(h.at(1) == 1); // one uneven number
|
||||
}
|
||||
|
||||
//]
|
||||
43
examples/guide_custom_modified_axis.cpp
Normal file
43
examples/guide_custom_modified_axis.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
//[ guide_custom_modified_axis
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
// custom axis, which adapts builtin integer axis
|
||||
struct custom_axis : public bh::axis::integer<> {
|
||||
using value_type = const char*; // type that is fed to the axis
|
||||
|
||||
using integer::integer; // inherit ctors of base
|
||||
|
||||
// the customization point
|
||||
// - accept const char* and convert to int
|
||||
// - then call index method of base class
|
||||
int operator()(value_type s) const { return integer::operator()(std::atoi(s)); }
|
||||
};
|
||||
|
||||
int main() {
|
||||
auto h = bh::make_histogram(custom_axis(3, 6));
|
||||
h("-10");
|
||||
h("3");
|
||||
h("4");
|
||||
h("9");
|
||||
|
||||
std::ostringstream os;
|
||||
for (auto xi : h.axis()) {
|
||||
os << "bin " << xi.idx()
|
||||
<< " [" << xi.lower() << ", " << xi.upper() << ") "
|
||||
<< h.at(xi) << "\n";
|
||||
}
|
||||
|
||||
std::cout << os.str() << std::endl;
|
||||
|
||||
assert(os.str() ==
|
||||
"bin 0 [3, 4) 1\n"
|
||||
"bin 1 [4, 5) 1\n"
|
||||
"bin 2 [5, 6) 0\n");
|
||||
}
|
||||
|
||||
//]
|
||||
@@ -7,8 +7,8 @@ namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// create static histogram with array_storage, using int as counter type
|
||||
auto h = bh::make_static_histogram_with(bh::array_storage<int>(),
|
||||
bh::axis::regular<>(10, 0, 1));
|
||||
auto h = bh::make_histogram_with(bh::array_storage<int>(),
|
||||
bh::axis::regular<>(10, 0, 1));
|
||||
|
||||
// do something with h
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
#include <boost/histogram.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(8, 0, 4),
|
||||
bh::axis::regular<>(10, 0, 5));
|
||||
auto h = bh::make_histogram(bh::axis::regular<>(8, 0, 4),
|
||||
bh::axis::regular<>(10, 0, 5));
|
||||
|
||||
// fill histogram, number of arguments must be equal to number of axes
|
||||
h(0, 1.1); // increases bin counter by one
|
||||
@@ -24,14 +27,17 @@ int main() {
|
||||
// functional-style processing is also supported
|
||||
std::vector<std::pair<int, double>> input_data{
|
||||
{0, 1.2}, {2, 3.4}, {4, 5.6}};
|
||||
// std::for_each takes the functor by value, thus it potentially makes
|
||||
// expensive copies of the histogram, but modern compilers are usually smart
|
||||
// enough to avoid the superfluous copies
|
||||
auto h2 =
|
||||
std::for_each(input_data.begin(), input_data.end(),
|
||||
bh::make_static_histogram(bh::axis::regular<>(8, 0, 4),
|
||||
bh::axis::regular<>(10, 0, 5)));
|
||||
|
||||
// std::for_each takes the functor by value, we use a reference wrapper
|
||||
// to avoid costly copies
|
||||
auto h2 = bh::make_histogram(bh::axis::regular<>(8, 0, 4),
|
||||
bh::axis::regular<>(10, 0, 5));
|
||||
std::for_each(input_data.begin(), input_data.end(),
|
||||
std::ref(h2));
|
||||
|
||||
// h2 is filled
|
||||
const double sum = std::accumulate(h2.begin(), h2.end(), 0.0);
|
||||
assert(sum == 3);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//[ guide_histogram_operators
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// make two histograms
|
||||
auto h1 = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1));
|
||||
auto h2 = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1));
|
||||
auto h1 = bh::make_histogram(bh::axis::regular<>(2, -1, 1));
|
||||
auto h2 = bh::make_histogram(bh::axis::regular<>(2, -1, 1));
|
||||
|
||||
h1(-0.5); // counts are: 1 0
|
||||
h2(0.5); // counts are: 0 1
|
||||
@@ -22,8 +22,7 @@ int main() {
|
||||
// accept and return rvalue references where possible
|
||||
auto h4 = h1 + h2 + h3; // counts are: 2 2
|
||||
|
||||
std::cout << h4.at(0).value() << " " << h4.at(1).value() << std::endl;
|
||||
// prints: 2 2
|
||||
assert(h4.at(0) == 2 && h4.at(1) == 2);
|
||||
|
||||
// multiply by number
|
||||
h4 *= 2; // counts are: 4 4
|
||||
@@ -31,27 +30,23 @@ int main() {
|
||||
// divide by number
|
||||
auto h5 = h4 / 4; // counts are: 1 1
|
||||
|
||||
std::cout << h5.at(0).value() << " " << h5.at(1).value() << std::endl;
|
||||
// prints: 1 1
|
||||
assert(h5.at(0) == 1 && h5.at(1) == 1);
|
||||
assert(h4 != h5 && h4 == 4 * h5);
|
||||
|
||||
// compare histograms
|
||||
std::cout << (h4 == 4 * h5) << " " << (h4 != h5) << std::endl;
|
||||
// prints: 1 1
|
||||
|
||||
// note: special effect of multiplication on counter variance
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(2, -1, 1));
|
||||
// note special effect of multiplication on weight_counter variance
|
||||
auto h = bh::make_histogram_with(bh::array_storage<bh::weight_counter<double>>(),
|
||||
bh::axis::regular<>(2, -1, 1));
|
||||
h(-0.5); // counts are: 1 0
|
||||
std::cout << "value " << (2 * h).at(0).value() << " "
|
||||
<< (h + h).at(0).value() << "\n"
|
||||
<< "variance " << (2 * h).at(0).variance() << " "
|
||||
<< (h + h).at(0).variance() << std::endl;
|
||||
// equality operator also checks variances, so the statement is false
|
||||
std::cout << (h + h == 2 * h) << std::endl;
|
||||
/* prints:
|
||||
value 2 2
|
||||
variance 4 2
|
||||
0
|
||||
*/
|
||||
|
||||
auto h_sum = h + h;
|
||||
auto h_mul = 2 * h;
|
||||
|
||||
// equality operator checks variances, so following statement is false
|
||||
assert(h_sum != h_mul);
|
||||
|
||||
// variance is different when histograms are scaled
|
||||
assert(h_sum.at(0).value() == 2 && h_mul.at(0).value() == 2);
|
||||
assert(h_sum.at(0).variance() == 2 && h_mul.at(0).variance() == 4);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//[ guide_histogram_reduction
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
@@ -18,8 +19,8 @@ int main() {
|
||||
using namespace bh::literals; // enables _c suffix
|
||||
|
||||
// make a 2d histogram
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(3, -1, 1),
|
||||
bh::axis::integer<>(0, 4));
|
||||
auto h = bh::make_histogram(bh::axis::regular<>(3, -1, 1),
|
||||
bh::axis::integer<>(0, 4));
|
||||
|
||||
h(-0.9, 0);
|
||||
h(0.9, 3);
|
||||
@@ -32,26 +33,26 @@ int main() {
|
||||
reduce does not remove counts; returned histograms are summed over
|
||||
the removed axes, so h, hr0, and hr1 have same number of total counts
|
||||
*/
|
||||
std::cout << sum(h).value() << " " << sum(hr0).value() << " "
|
||||
<< sum(hr1).value() << std::endl;
|
||||
// prints: 3 3 3
|
||||
assert(sum(h) == 3 && sum(hr0) == 3 && sum(hr1) == 3);
|
||||
|
||||
std::ostringstream os1;
|
||||
for (auto yi : h.axis(1_c)) {
|
||||
for (auto xi : h.axis(0_c)) { std::cout << h.at(xi, yi).value() << " "; }
|
||||
std::cout << std::endl;
|
||||
for (auto xi : h.axis(0_c)) { os1 << h.at(xi, yi) << " "; }
|
||||
os1 << "\n";
|
||||
}
|
||||
// prints: 1 0 0
|
||||
// 0 0 0
|
||||
// 0 1 0
|
||||
// 0 0 1
|
||||
assert(os1.str() ==
|
||||
"1 0 0 \n"
|
||||
"0 0 0 \n"
|
||||
"0 1 0 \n"
|
||||
"0 0 1 \n");
|
||||
|
||||
for (auto xi : hr0.axis()) std::cout << hr0.at(xi).value() << " ";
|
||||
std::cout << std::endl;
|
||||
// prints: 1 1 1
|
||||
std::ostringstream os2;
|
||||
for (auto xi : hr0.axis()) os2 << hr0.at(xi) << " ";
|
||||
assert(os2.str() == "1 1 1 ");
|
||||
|
||||
for (auto yi : hr1.axis()) std::cout << hr1.at(yi).value() << " ";
|
||||
std::cout << std::endl;
|
||||
// prints: 1 0 1 1
|
||||
std::ostringstream os3;
|
||||
for (auto yi : hr1.axis()) os3 << hr1.at(yi) << " ";
|
||||
assert(os3.str() == "1 0 1 1 ");
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
#include <boost/histogram.hpp>
|
||||
#include <boost/histogram/serialization.hpp> // includes serialization code
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
auto a = bh::make_static_histogram(bh::axis::regular<>(3, -1, 1, "r"),
|
||||
bh::axis::integer<>(0, 2, "i"));
|
||||
auto a = bh::make_histogram(bh::axis::regular<>(3, -1, 1, "axis 0"),
|
||||
bh::axis::integer<>(0, 2, "axis 1"));
|
||||
a(0.5, 1);
|
||||
|
||||
std::string buf; // holds persistent representation
|
||||
std::string buf; // to hold persistent representation
|
||||
|
||||
// store histogram
|
||||
{
|
||||
@@ -25,8 +26,7 @@ int main() {
|
||||
|
||||
auto b = decltype(a)(); // create a default-constructed second histogram
|
||||
|
||||
std::cout << "before restore " << (a == b) << std::endl;
|
||||
// prints: before restore 0
|
||||
assert(b != a); // b is empty, a is not
|
||||
|
||||
// load histogram
|
||||
{
|
||||
@@ -35,8 +35,7 @@ int main() {
|
||||
ia >> b;
|
||||
}
|
||||
|
||||
std::cout << "after restore " << (a == b) << std::endl;
|
||||
// prints: after restore 1
|
||||
assert(b == a); // now b is equal to a
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -2,37 +2,38 @@
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <boost/histogram/ostream_operators.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
namespace axis = boost::histogram::axis;
|
||||
namespace axis = bh::axis;
|
||||
|
||||
enum { A, B, C };
|
||||
auto h = bh::make_histogram(
|
||||
axis::regular<>(2, -1, 1),
|
||||
axis::regular<axis::transform::log<>>(2, 1, 10, "axis 1"),
|
||||
axis::circular<double, axis::empty_metadata_type>(4, 0.1, 1.0), // axis without metadata
|
||||
axis::variable<>({-1, 0, 1}, "axis 3", axis::option_type::none),
|
||||
axis::category<>({2, 1, 3}, "axis 4"),
|
||||
axis::integer<>(-1, 1, "axis 5")
|
||||
);
|
||||
|
||||
auto h = bh::make_static_histogram(
|
||||
axis::regular<>(2, -1, 1, "regular1", axis::uoflow_type::off),
|
||||
axis::regular<axis::transform::log>(2, 1, 10, "regular2"),
|
||||
axis::circular<>(4, 0.1, 1.0, "polar"),
|
||||
axis::variable<>({-1, 0, 1}, "variable", axis::uoflow_type::off),
|
||||
axis::category<>({A, B, C}, "category"),
|
||||
axis::integer<>(-1, 1, "integer", axis::uoflow_type::off));
|
||||
std::ostringstream os;
|
||||
os << h;
|
||||
|
||||
std::cout << h << std::endl;
|
||||
std::cout << os.str() << std::endl;
|
||||
|
||||
/* prints:
|
||||
|
||||
histogram(
|
||||
regular(2, -1, 1, label='regular1', uoflow=False),
|
||||
regular_log(2, 1, 10, label='regular2'),
|
||||
circular(4, phase=0.1, perimeter=1, label='polar'),
|
||||
variable(-1, 0, 1, label='variable', uoflow=False),
|
||||
category(0, 1, 2, label='category'),
|
||||
integer(-1, 1, label='integer', uoflow=False),
|
||||
)
|
||||
|
||||
*/
|
||||
assert(os.str() ==
|
||||
"histogram(\n"
|
||||
" regular(2, -1, 1, options=underflow_and_overflow),\n"
|
||||
" regular_log(2, 1, 10, metadata=\"axis 1\", options=underflow_and_overflow),\n"
|
||||
" circular(4, 0.1, 1.1, options=overflow),\n"
|
||||
" variable(-1, 0, 1, metadata=\"axis 3\", options=none),\n"
|
||||
" category(2, 1, 3, metadata=\"axis 4\", options=overflow),\n"
|
||||
" integer(-1, 1, metadata=\"axis 5\", options=underflow_and_overflow),\n"
|
||||
")"
|
||||
);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -2,27 +2,25 @@
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
// create vector of axes, axis::any is a polymorphic axis type
|
||||
auto v = std::vector<bh::axis::any_std>();
|
||||
auto v = std::vector<bh::axis::variant<
|
||||
bh::axis::regular<>, bh::axis::integer<>
|
||||
>>();
|
||||
v.push_back(bh::axis::regular<>(100, -1, 1));
|
||||
v.push_back(bh::axis::integer<>(1, 7));
|
||||
|
||||
// create dynamic histogram (make_static_histogram cannot be used with iterators)
|
||||
auto h = bh::make_dynamic_histogram(v.begin(), v.end());
|
||||
// create dynamic histogram from iterator range
|
||||
auto h = bh::make_histogram(v.begin(), v.end());
|
||||
assert(h.rank() == 2);
|
||||
|
||||
// do something with h
|
||||
|
||||
|
||||
|
||||
// make_dynamic_histogram copies axis objects; to instead move the whole axis
|
||||
// vector into the histogram, create a histogram instance directly
|
||||
auto h2 = bh::histogram<decltype(v)>(std::move(v));
|
||||
|
||||
// do something with h2
|
||||
// create dynamic histogram by moving the vector (this avoids copies)
|
||||
auto h2 = bh::make_histogram(std::move(v));
|
||||
assert(h2.rank() == 2);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
//[ guide_make_static_histogram
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
|
||||
int main() {
|
||||
/*
|
||||
create a 1d-histogram in default configuration which
|
||||
covers the real line from -1 to 1 in 100 bins, the same
|
||||
call with `make_dynamic_histogram` would also work
|
||||
create a static 1d-histogram in default configuration
|
||||
which covers the real line from -1 to 1 in 100 bins
|
||||
*/
|
||||
auto h = bh::make_static_histogram(bh::axis::regular<>(100, -1, 1));
|
||||
|
||||
// do something with h
|
||||
auto h = bh::make_histogram(bh::axis::regular<>(100, -1, 1));
|
||||
assert(h.rank() == 1);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
@@ -9,7 +9,21 @@
|
||||
{
|
||||
"ClangFormat":
|
||||
{
|
||||
"style": "File",
|
||||
"format_on_save": true
|
||||
},
|
||||
|
||||
"trim_trailing_white_space_on_save": false,
|
||||
"ensure_newline_at_eof_on_save": false,
|
||||
},
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"name": "CMake",
|
||||
"working_dir": "${project_path}/build",
|
||||
"cmd": ["make && ctest"],
|
||||
"shell": true,
|
||||
"file_regex": "/([^/:]+):(\\d+):(\\d+): ",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
#ifndef BOOST_HISTOGRAM_HPP
|
||||
#define BOOST_HISTOGRAM_HPP
|
||||
|
||||
#include <boost/histogram/axis/any.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/histogram/storage/adaptive_storage.hpp>
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_ANY_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_ANY_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
// forward declaration for serialization
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
class access;
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// this visitation may be collapsed by the compiler almost into a direct cast,
|
||||
// works with gcc-8 -O2 -DNDEBUG on Compiler Explorer at least
|
||||
template <typename T>
|
||||
struct static_cast_visitor : public boost::static_visitor<T> {
|
||||
template <typename A>
|
||||
T operator()(A&& a) const {
|
||||
return static_cast<T>(std::forward<A>(a));
|
||||
}
|
||||
};
|
||||
|
||||
struct index_visitor : public boost::static_visitor<int> {
|
||||
const double x;
|
||||
explicit index_visitor(const double arg) : x(arg) {}
|
||||
template <typename Axis>
|
||||
int operator()(const Axis& a) const {
|
||||
return impl(std::is_convertible<double, typename Axis::value_type>(), a);
|
||||
}
|
||||
template <typename Axis>
|
||||
int impl(std::true_type, const Axis& a) const {
|
||||
return a.index(static_cast<typename Axis::value_type>(x));
|
||||
}
|
||||
template <typename Axis>
|
||||
int impl(std::false_type, const Axis&) const {
|
||||
throw std::runtime_error(boost::histogram::detail::cat(
|
||||
"cannot convert double to ",
|
||||
boost::typeindex::type_id<typename Axis::value_type>().pretty_name(), " for ",
|
||||
boost::typeindex::type_id<Axis>().pretty_name()));
|
||||
}
|
||||
};
|
||||
|
||||
struct lower_visitor : public boost::static_visitor<double> {
|
||||
int idx;
|
||||
lower_visitor(int i) : idx(i) {}
|
||||
template <typename Axis>
|
||||
double operator()(const Axis& a) const {
|
||||
return impl(
|
||||
std::integral_constant<
|
||||
bool, (std::is_convertible<typename Axis::value_type, double>::value &&
|
||||
std::is_same<typename Axis::bin_type, interval_view<Axis>>::value)>(),
|
||||
a);
|
||||
}
|
||||
template <typename Axis>
|
||||
double impl(std::true_type, const Axis& a) const {
|
||||
return a.lower(idx);
|
||||
}
|
||||
template <typename Axis>
|
||||
double impl(std::false_type, const Axis&) const {
|
||||
throw std::runtime_error(boost::histogram::detail::cat(
|
||||
"cannot use ", boost::typeindex::type_id<Axis>().pretty_name(),
|
||||
" with generic boost::histogram::axis::any interface, use"
|
||||
" a static_cast to access the underlying axis type"));
|
||||
}
|
||||
};
|
||||
|
||||
struct bicmp_visitor : public boost::static_visitor<bool> {
|
||||
template <typename T, typename U>
|
||||
bool operator()(const T&, const U&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& a, const T& b) const {
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct assign_visitor : public boost::static_visitor<void> {
|
||||
T& t;
|
||||
assign_visitor(T& tt) : t(tt) {}
|
||||
template <typename U>
|
||||
void operator()(const U& u) const {
|
||||
impl(mp11::mp_contains<typename T::types, U>(), u);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void impl(mp11::mp_true, const U& u) const {
|
||||
t = u;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void impl(mp11::mp_false, const U&) const {
|
||||
throw std::invalid_argument(boost::histogram::detail::cat(
|
||||
"argument ", boost::typeindex::type_id<U>().pretty_name(),
|
||||
" is not a bounded type of ", boost::typeindex::type_id<T>().pretty_name()));
|
||||
}
|
||||
};
|
||||
|
||||
struct get_label_visitor : public boost::static_visitor<boost::string_view> {
|
||||
template <typename T>
|
||||
boost::string_view operator()(const T& t) const {
|
||||
return t.label();
|
||||
}
|
||||
};
|
||||
|
||||
struct set_label_visitor : public boost::static_visitor<void> {
|
||||
boost::string_view view;
|
||||
set_label_visitor(boost::string_view v) : view(v) {}
|
||||
template <typename T>
|
||||
void operator()(T& t) const {
|
||||
t.label(view);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
struct get_allocator_visitor : public boost::static_visitor<U> {
|
||||
template <typename T>
|
||||
U operator()(const T& t) const {
|
||||
return t.get_allocator();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Polymorphic axis type
|
||||
template <typename... Ts>
|
||||
class any : public boost::variant<Ts...> {
|
||||
using base_type = boost::variant<Ts...>;
|
||||
|
||||
public:
|
||||
using types = mp11::mp_list<Ts...>;
|
||||
using value_type = double;
|
||||
using bin_type = interval_view<any>;
|
||||
using const_iterator = iterator_over<any>;
|
||||
using const_reverse_iterator = reverse_iterator_over<any>;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
using requires_bounded_type =
|
||||
mp11::mp_if<mp11::mp_contains<types, boost::histogram::detail::rm_cv_ref<T>>, void>;
|
||||
|
||||
public:
|
||||
any() = default;
|
||||
any(const any&) = default;
|
||||
any& operator=(const any&) = default;
|
||||
any(any&&) = default;
|
||||
any& operator=(any&&) = default;
|
||||
|
||||
template <typename T, typename = requires_bounded_type<T>>
|
||||
any(T&& t) : base_type(std::forward<T>(t)) {}
|
||||
|
||||
template <typename T, typename = requires_bounded_type<T>>
|
||||
any& operator=(T&& t) {
|
||||
base_type::operator=(std::forward<T>(t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
any(const any<Us...>& u) {
|
||||
boost::apply_visitor(detail::assign_visitor<any>(*this), u);
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
any& operator=(const any<Us...>& u) {
|
||||
boost::apply_visitor(detail::assign_visitor<any>(*this), u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int size() const { return static_cast<const base&>(*this).size(); }
|
||||
|
||||
int shape() const { return static_cast<const base&>(*this).shape(); }
|
||||
|
||||
bool uoflow() const { return static_cast<const base&>(*this).uoflow(); }
|
||||
|
||||
// note: this only works for axes with compatible value type
|
||||
int index(const value_type x) const {
|
||||
return boost::apply_visitor(detail::index_visitor(x), *this);
|
||||
}
|
||||
|
||||
string_view label() const {
|
||||
return boost::apply_visitor(detail::get_label_visitor(), *this);
|
||||
}
|
||||
|
||||
void label(const string_view x) {
|
||||
boost::apply_visitor(detail::set_label_visitor(x), *this);
|
||||
}
|
||||
|
||||
// this only works for axes with compatible bin type
|
||||
// and will throw a runtime_error otherwise
|
||||
double lower(int idx) const {
|
||||
return boost::apply_visitor(detail::lower_visitor(idx), *this);
|
||||
}
|
||||
|
||||
bin_type operator[](const int idx) const { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const any& rhs) const {
|
||||
return base_type::operator==(static_cast<const base_type&>(rhs));
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
bool operator==(const any<Us...>& u) const {
|
||||
return boost::apply_visitor(detail::bicmp_visitor(), *this, u);
|
||||
}
|
||||
|
||||
template <typename T, typename = requires_bounded_type<T>>
|
||||
bool operator==(const T& t) const {
|
||||
// variant::operator==(T) is implemented only to fail, we cannot use it
|
||||
auto tp = boost::get<T>(this);
|
||||
return tp && *tp == t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const T& t) const {
|
||||
return !operator==(t);
|
||||
}
|
||||
|
||||
explicit operator const base&() const {
|
||||
return boost::apply_visitor(detail::static_cast_visitor<const base&>(), *this);
|
||||
}
|
||||
|
||||
explicit operator base&() {
|
||||
return boost::apply_visitor(detail::static_cast_visitor<base&>(), *this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
explicit operator const T&() const {
|
||||
return boost::strict_get<T>(*this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
explicit operator T&() {
|
||||
return boost::strict_get<T>(*this);
|
||||
}
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, 0); }
|
||||
const_iterator end() const { return const_iterator(*this, size()); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(*this, size()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(*this, 0); }
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <typename Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
@@ -7,128 +7,75 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_BASE_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_BASE_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container/string.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
// forward declaration for serialization
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
class access;
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
enum class uoflow_type { off = 0, oflow = 1, on = 2 };
|
||||
|
||||
/// Base class for all axes
|
||||
template <typename MetaData>
|
||||
class base {
|
||||
using metadata_type = MetaData;
|
||||
|
||||
public:
|
||||
/// Returns the number of bins without overflow/underflow.
|
||||
int size() const noexcept { return size_; }
|
||||
/// Returns the number of bins, including overflow/underflow if enabled.
|
||||
int shape() const noexcept { return shape_; }
|
||||
/// Returns number of extra bins to count under- or overflow.
|
||||
int uoflow() const noexcept { return shape_ - size_; }
|
||||
/// Returns the number of bins, without extra bins.
|
||||
unsigned size() const noexcept { return size_meta_.first(); }
|
||||
/// Returns the options.
|
||||
option_type options() const noexcept { return opt_; }
|
||||
/// Returns the metadata.
|
||||
metadata_type& metadata() noexcept { return size_meta_.second(); }
|
||||
/// Returns the metadata (const version).
|
||||
const metadata_type& metadata() const noexcept { return size_meta_.second(); }
|
||||
|
||||
friend void swap(base& a, base& b) noexcept // ADL works with friend functions
|
||||
{
|
||||
using std::swap;
|
||||
swap(a.size_meta_, b.size_meta_);
|
||||
swap(a.opt_, b.opt_);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
protected:
|
||||
base(unsigned size, axis::uoflow_type uo)
|
||||
: size_(size), shape_(size + static_cast<int>(uo)) {
|
||||
if (size_ == 0) { throw std::invalid_argument("bins > 0 required"); }
|
||||
base(unsigned n, metadata_type&& m, option_type opt)
|
||||
: size_meta_(n, std::move(m)), opt_(opt) {
|
||||
if (size() == 0) { throw std::invalid_argument("bins > 0 required"); }
|
||||
const auto max_index =
|
||||
static_cast<unsigned>(std::numeric_limits<int>::max() - static_cast<int>(opt_));
|
||||
if (size() > max_index)
|
||||
throw std::invalid_argument(detail::cat("bins <= ", max_index, " required"));
|
||||
}
|
||||
|
||||
base() = default;
|
||||
base() : size_meta_(0), opt_(option_type::none) {}
|
||||
base(const base&) = default;
|
||||
base& operator=(const base&) = default;
|
||||
base(base&& rhs) : size_(rhs.size_), shape_(rhs.shape_) {
|
||||
rhs.size_ = 0;
|
||||
rhs.shape_ = 0;
|
||||
}
|
||||
base(base&& rhs) : size_meta_(std::move(rhs.size_meta_)), opt_(rhs.opt_) {}
|
||||
base& operator=(base&& rhs) {
|
||||
if (this != &rhs) {
|
||||
size_ = rhs.size_;
|
||||
shape_ = rhs.shape_;
|
||||
rhs.size_ = 0;
|
||||
rhs.shape_ = 0;
|
||||
size_meta_ = std::move(rhs.size_meta_);
|
||||
opt_ = rhs.opt_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const base& rhs) const noexcept {
|
||||
return size_ == rhs.size_ && shape_ == rhs.shape_;
|
||||
return size() == rhs.size() && opt_ == rhs.opt_ &&
|
||||
detail::static_if<detail::is_equal_comparable<metadata_type>>(
|
||||
[&rhs](const auto& m) { return m == rhs.metadata(); },
|
||||
[](const auto&) { return true; }, metadata());
|
||||
}
|
||||
|
||||
private:
|
||||
int size_ = 0, shape_ = 0;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/// Base class with a label
|
||||
template <typename Allocator>
|
||||
class labeled_base : public base {
|
||||
public:
|
||||
using allocator_type = Allocator;
|
||||
|
||||
allocator_type get_allocator() const { return label_.get_allocator(); }
|
||||
|
||||
/// Returns the axis label, which is a name or description.
|
||||
boost::string_view label() const noexcept { return label_; }
|
||||
/// Change the label of an axis.
|
||||
void label(boost::string_view label) { label_.assign(label.begin(), label.end()); }
|
||||
|
||||
bool operator==(const labeled_base& rhs) const noexcept {
|
||||
return base::operator==(rhs) && label_ == rhs.label_;
|
||||
}
|
||||
|
||||
protected:
|
||||
labeled_base() = default;
|
||||
labeled_base(const labeled_base&) = default;
|
||||
labeled_base& operator=(const labeled_base&) = default;
|
||||
labeled_base(labeled_base&& rhs) = default;
|
||||
labeled_base& operator=(labeled_base&& rhs) = default;
|
||||
|
||||
labeled_base(unsigned size, axis::uoflow_type uo, string_view label,
|
||||
const allocator_type& a)
|
||||
: base(size, uo), label_(label.begin(), label.end(), a) {}
|
||||
|
||||
private:
|
||||
boost::container::basic_string<char, std::char_traits<char>, allocator_type> label_;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/// Iterator mixin, uses CRTP to inject iterator logic into Derived.
|
||||
template <typename Derived>
|
||||
class iterator_mixin {
|
||||
public:
|
||||
using const_iterator = iterator_over<Derived>;
|
||||
using const_reverse_iterator = reverse_iterator_over<Derived>;
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this), 0);
|
||||
}
|
||||
const_iterator end() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this),
|
||||
static_cast<const Derived*>(this)->size());
|
||||
}
|
||||
const_reverse_iterator rbegin() const noexcept {
|
||||
return const_reverse_iterator(*static_cast<const Derived*>(this),
|
||||
static_cast<const Derived*>(this)->size());
|
||||
}
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return const_reverse_iterator(*static_cast<const Derived*>(this), 0);
|
||||
}
|
||||
};
|
||||
detail::compressed_pair<int, metadata_type> size_meta_;
|
||||
option_type opt_;
|
||||
}; // namespace axis
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
|
||||
155
include/boost/histogram/axis/category.hpp
Normal file
155
include/boost/histogram/axis/category.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/value_bin_view.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
/** Axis which maps unique values to bins (one on one).
|
||||
*
|
||||
* The axis maps a set of values to bins, following the order of
|
||||
* arguments in the constructor. There is an optional overflow bin
|
||||
* for this axis, which counts values that are not part of the set.
|
||||
* Binning is a O(n) operation for n values in the worst case and O(1) in
|
||||
* the best case. The value types must be equal-comparable.
|
||||
*/
|
||||
template <typename T, typename Allocator, typename MetaData>
|
||||
class category : public base<MetaData>,
|
||||
public iterator_mixin<category<T, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using metadata_type = MetaData;
|
||||
using value_type = T;
|
||||
using allocator_type = Allocator;
|
||||
|
||||
public:
|
||||
/** Construct from iterator range of unique values.
|
||||
*
|
||||
* \param begin begin of category range of unique values.
|
||||
* \param end end of category range of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename It, typename = detail::requires_iterator<It>>
|
||||
category(It begin, It end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: base_type(std::distance(begin, end), std::move(m), o), x_(nullptr, std::move(a)) {
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), base_type::size(), begin);
|
||||
}
|
||||
|
||||
/** Construct axis from iterable sequence of unique values.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename C, typename = detail::requires_iterable<C>>
|
||||
category(const C& iterable, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(std::begin(iterable), std::end(iterable), std::move(m), o,
|
||||
std::move(a)) {}
|
||||
|
||||
/** Construct axis from an initializer list of unique values.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U>
|
||||
category(std::initializer_list<U> l, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::overflow, allocator_type a = allocator_type())
|
||||
: category(l.begin(), l.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
category() : x_(nullptr) {}
|
||||
|
||||
category(const category& o) : base_type(o), x_(o.x_) {
|
||||
x_.first() =
|
||||
detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first());
|
||||
}
|
||||
|
||||
category& operator=(const category& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
detail::destroy_buffer(x_.second(), x_.first(), base_type::size());
|
||||
base_type::operator=(o);
|
||||
x_ = o.x_;
|
||||
x_.first() =
|
||||
detail::create_buffer_from_iter(x_.second(), base_type::size(), o.x_.first());
|
||||
} else {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.x_.first(), o.x_.first() + base_type::size(), x_.first());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
category(category&& o) : category() {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
|
||||
category& operator=(category&& o) {
|
||||
if (this != &o) {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~category() { detail::destroy_buffer(x_.second(), x_.first(), base_type::size()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(const value_type& x) const noexcept {
|
||||
const auto begin = x_.first();
|
||||
const auto end = begin + base_type::size();
|
||||
return std::distance(begin, std::find(begin, end, x));
|
||||
}
|
||||
|
||||
/// Returns the value for the bin index (performs a range check).
|
||||
const value_type& value(unsigned idx) const {
|
||||
if (idx >= base_type::size()) throw std::out_of_range("category index out of range");
|
||||
return x_.first()[idx];
|
||||
}
|
||||
|
||||
auto operator[](int idx) const noexcept { return value_bin_view<category>(idx, *this); }
|
||||
|
||||
bool operator==(const category& o) const noexcept {
|
||||
return base_type::operator==(o) &&
|
||||
std::equal(x_.first(), x_.first() + base_type::size(), o.x_.first());
|
||||
}
|
||||
|
||||
bool operator!=(const category& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
detail::compressed_pair<pointer, allocator_type> x_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
92
include/boost/histogram/axis/circular.hpp
Normal file
92
include/boost/histogram/axis/circular.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_bin_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
// two_pi can be found in boost/math, but it is defined here to reduce deps
|
||||
constexpr double two_pi = 6.283185307179586;
|
||||
|
||||
/** Axis for real values on a circle.
|
||||
*
|
||||
* The axis is circular and wraps around reaching the perimeter value.
|
||||
* It has no underflow bin and the overflow bin merely counts special
|
||||
* values like NaN and infinity. Binning is a O(1) operation.
|
||||
*/
|
||||
template <typename RealType, typename MetaData>
|
||||
class circular : public base<MetaData>,
|
||||
public iterator_mixin<circular<RealType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = RealType;
|
||||
using metadata_type = MetaData;
|
||||
|
||||
public:
|
||||
/** Construct n bins with an optional offset.
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param phase starting phase.
|
||||
* \param perimeter range after which value wraps around.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
circular(unsigned n, RealType phase = 0, RealType perimeter = two_pi,
|
||||
MetaData m = MetaData(), option_type o = option_type::overflow)
|
||||
: base_type(n, std::move(m),
|
||||
o == option_type::underflow_and_overflow ? option_type::overflow : o)
|
||||
, phase_(phase)
|
||||
, delta_(perimeter / n) {
|
||||
if (!std::isfinite(phase) || !(perimeter > 0))
|
||||
throw std::invalid_argument("invalid phase or perimeter");
|
||||
}
|
||||
|
||||
circular() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const auto z = std::floor((x - phase_) / delta_);
|
||||
if (std::isfinite(z)) {
|
||||
const auto i = static_cast<int>(z) % base_type::size();
|
||||
return i + (i < 0) * base_type::size();
|
||||
}
|
||||
return base_type::size();
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
value_type value(value_type i) const noexcept { return phase_ + i * delta_; }
|
||||
|
||||
auto operator[](int idx) const noexcept { return interval_bin_view<circular>(idx, *this); }
|
||||
|
||||
bool operator==(const circular& o) const noexcept {
|
||||
return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_;
|
||||
}
|
||||
|
||||
bool operator!=(const circular<>& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type phase_ = 0.0, delta_ = 1.0;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
98
include/boost/histogram/axis/integer.hpp
Normal file
98
include/boost/histogram/axis/integer.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_INTEGER_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/value_bin_view.hpp>
|
||||
#include <boost/histogram/axis/interval_bin_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
/** Axis for an interval of integer values with unit steps.
|
||||
*
|
||||
* Binning is a O(1) operation. This axis operates
|
||||
* faster than a regular axis.
|
||||
*/
|
||||
template <typename IntType, typename MetaData>
|
||||
class integer : public base<MetaData>, public iterator_mixin<integer<IntType, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using value_type = IntType;
|
||||
using metadata_type = MetaData;
|
||||
using bin_view = std::conditional_t<std::is_integral<value_type>::value,
|
||||
value_bin_view<integer>, interval_bin_view<integer>>;
|
||||
|
||||
public:
|
||||
/** Construct over semi-open integer interval [start, stop).
|
||||
*
|
||||
* \param start first integer of covered range.
|
||||
* \param stop one past last integer of covered range.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
integer(value_type start, value_type stop, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow)
|
||||
: base_type(stop - start, std::move(m), o), min_(start) {
|
||||
if (start >= stop) { throw std::invalid_argument("start < stop required"); }
|
||||
}
|
||||
|
||||
integer() = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
x = std::floor(x - min_);
|
||||
return x >= 0 ? (x > static_cast<value_type>(base_type::size()) ? base_type::size()
|
||||
: static_cast<int>(x))
|
||||
: -1;
|
||||
}
|
||||
|
||||
/// Returns axis value for index.
|
||||
value_type value(value_type i) const noexcept {
|
||||
if (i < 0) {
|
||||
return detail::static_if<std::is_integral<value_type>>(
|
||||
[](auto) { return std::numeric_limits<value_type>::min(); },
|
||||
[](auto) { return -std::numeric_limits<value_type>::infinity(); },
|
||||
0
|
||||
);
|
||||
}
|
||||
if (i > static_cast<int>(base_type::size())) {
|
||||
return detail::static_if<std::is_integral<value_type>>(
|
||||
[](auto) { return std::numeric_limits<value_type>::max(); },
|
||||
[](auto) { return std::numeric_limits<value_type>::infinity(); },
|
||||
0
|
||||
);
|
||||
}
|
||||
return min_ + i;
|
||||
}
|
||||
|
||||
decltype(auto) operator[](int idx) const noexcept { return bin_view(idx, *this); }
|
||||
|
||||
bool operator==(const integer& o) const noexcept {
|
||||
return base_type::operator==(o) && min_ == o.min_;
|
||||
}
|
||||
|
||||
bool operator!=(const integer& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
value_type min_ = 0;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
42
include/boost/histogram/axis/interval_bin_view.hpp
Normal file
42
include/boost/histogram/axis/interval_bin_view.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class interval_bin_view {
|
||||
public:
|
||||
interval_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
decltype(auto) lower() const noexcept { return axis_.value(idx_); }
|
||||
decltype(auto) upper() const noexcept { return axis_.value(idx_ + 1); }
|
||||
decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); }
|
||||
decltype(auto) width() const noexcept { return upper() - lower(); }
|
||||
|
||||
bool operator==(const interval_bin_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const interval_bin_view& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
private:
|
||||
const int idx_;
|
||||
const Axis& axis_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class interval_view {
|
||||
public:
|
||||
interval_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
interval_view(const interval_view&) = default;
|
||||
interval_view& operator=(const interval_view&) = default;
|
||||
interval_view(interval_view&&) = default;
|
||||
interval_view& operator=(interval_view&&) = default;
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
auto lower() const noexcept -> decltype(std::declval<Axis&>().lower(0)) {
|
||||
return axis_.lower(idx_);
|
||||
}
|
||||
auto upper() const noexcept -> decltype(std::declval<Axis&>().lower(0)) {
|
||||
return axis_.lower(idx_ + 1);
|
||||
}
|
||||
typename Axis::value_type width() const noexcept {
|
||||
return upper() - lower();
|
||||
}
|
||||
|
||||
bool operator==(const interval_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const interval_view& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
private:
|
||||
const int idx_;
|
||||
const Axis& axis_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#define BOOST_HISTOGRAM_AXIS_ITERATOR_HPP
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/iterator/reverse_iterator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
@@ -15,12 +16,11 @@ namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class iterator_over
|
||||
: public iterator_facade<iterator_over<Axis>, typename Axis::bin_type,
|
||||
: public iterator_facade<iterator_over<Axis>, decltype(std::declval<Axis&>()[0]),
|
||||
random_access_traversal_tag,
|
||||
typename Axis::bin_type> {
|
||||
decltype(std::declval<Axis&>()[0]), int> {
|
||||
public:
|
||||
explicit iterator_over(const Axis& axis, int idx)
|
||||
: axis_(axis), idx_(idx) {}
|
||||
explicit iterator_over(const Axis& axis, int idx) : axis_(axis), idx_(idx) {}
|
||||
|
||||
iterator_over(const iterator_over&) = default;
|
||||
iterator_over& operator=(const iterator_over&) = default;
|
||||
@@ -29,46 +29,38 @@ protected:
|
||||
void increment() noexcept { ++idx_; }
|
||||
void decrement() noexcept { --idx_; }
|
||||
void advance(int n) noexcept { idx_ += n; }
|
||||
int distance_to(const iterator_over& other) const noexcept {
|
||||
return other.idx_ - idx_;
|
||||
}
|
||||
int distance_to(const iterator_over& other) const noexcept { return other.idx_ - idx_; }
|
||||
bool equal(const iterator_over& other) const noexcept {
|
||||
return &axis_ == &other.axis_ && idx_ == other.idx_;
|
||||
}
|
||||
typename Axis::bin_type dereference() const { return axis_[idx_]; }
|
||||
decltype(std::declval<Axis&>()[0]) dereference() const { return axis_[idx_]; }
|
||||
|
||||
friend class ::boost::iterator_core_access;
|
||||
|
||||
const Axis& axis_;
|
||||
int idx_;
|
||||
};
|
||||
|
||||
template <typename Axis>
|
||||
class reverse_iterator_over
|
||||
: public iterator_facade<
|
||||
reverse_iterator_over<Axis>, typename Axis::bin_type,
|
||||
random_access_traversal_tag, typename Axis::bin_type> {
|
||||
/// Uses CRTP to inject iterator logic into Derived.
|
||||
template <typename Derived>
|
||||
class iterator_mixin {
|
||||
public:
|
||||
explicit reverse_iterator_over(const Axis& axis, int idx)
|
||||
: axis_(axis), idx_(idx) {}
|
||||
using const_iterator = iterator_over<Derived>;
|
||||
using const_reverse_iterator = boost::reverse_iterator<const_iterator>;
|
||||
|
||||
reverse_iterator_over(const reverse_iterator_over&) = default;
|
||||
reverse_iterator_over& operator=(const reverse_iterator_over&) = default;
|
||||
|
||||
protected:
|
||||
void increment() noexcept { --idx_; }
|
||||
void decrement() noexcept { ++idx_; }
|
||||
void advance(int n) noexcept { idx_ -= n; }
|
||||
int distance_to(const reverse_iterator_over& other) const noexcept {
|
||||
return other.idx_ - idx_;
|
||||
const_iterator begin() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this), 0);
|
||||
}
|
||||
bool equal(const reverse_iterator_over& other) const noexcept {
|
||||
return &axis_ == &other.axis_ && idx_ == other.idx_;
|
||||
const_iterator end() const noexcept {
|
||||
return const_iterator(*static_cast<const Derived*>(this),
|
||||
static_cast<const Derived*>(this)->size());
|
||||
}
|
||||
const_reverse_iterator rbegin() const noexcept {
|
||||
return boost::make_reverse_iterator(end());
|
||||
}
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return boost::make_reverse_iterator(begin());
|
||||
}
|
||||
typename Axis::bin_type dereference() const { return axis_[idx_ - 1]; }
|
||||
friend class ::boost::iterator_core_access;
|
||||
|
||||
const Axis& axis_;
|
||||
int idx_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
|
||||
@@ -9,144 +9,183 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_OSTREAM_OPERATORS_HPP
|
||||
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/value_view.hpp>
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/interval_bin_view.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/value_bin_view.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
namespace detail {
|
||||
inline string_view to_string(const transform::identity&) { return {}; }
|
||||
inline string_view to_string(const transform::log&) { return {"_log", 4}; }
|
||||
inline string_view to_string(const transform::sqrt&) { return {"_sqrt", 5}; }
|
||||
template <typename T>
|
||||
const char* to_string(const axis::transform::identity<T>&) {
|
||||
return "";
|
||||
}
|
||||
template <typename T>
|
||||
const char* to_string(const axis::transform::log<T>&) {
|
||||
return "_log";
|
||||
}
|
||||
template <typename T>
|
||||
const char* to_string(const axis::transform::sqrt<T>&) {
|
||||
return "_sqrt";
|
||||
}
|
||||
template <typename T>
|
||||
const char* to_string(const axis::transform::pow<T>&) {
|
||||
return "_pow";
|
||||
}
|
||||
|
||||
template <typename OStream, typename T>
|
||||
void stream_metadata(OStream& os, const T& t) {
|
||||
detail::static_if<detail::is_streamable<T>>(
|
||||
[&os](const auto& t) {
|
||||
std::ostringstream oss;
|
||||
oss << t;
|
||||
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
|
||||
},
|
||||
[&os](const auto&) {
|
||||
using U = detail::rm_cvref<T>;
|
||||
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(U));
|
||||
},
|
||||
t);
|
||||
}
|
||||
|
||||
template <typename OStream>
|
||||
void escape_string(OStream& os, const string_view s) {
|
||||
os << '\'';
|
||||
for (auto sit = s.begin(); sit != s.end(); ++sit) {
|
||||
if (*sit == '\'' && (sit == s.begin() || *(sit - 1) != '\\')) {
|
||||
os << "\\\'";
|
||||
} else {
|
||||
os << *sit;
|
||||
}
|
||||
}
|
||||
os << '\'';
|
||||
void stream_options(OStream& os, const axis::option_type o) {
|
||||
os << ", options=" << o;
|
||||
}
|
||||
|
||||
template <typename OStream, typename T>
|
||||
void stream_transform(OStream&, const T&) {}
|
||||
|
||||
template <typename OStream, typename T>
|
||||
void stream_transform(OStream& os, const axis::transform::pow<T>& t) {
|
||||
os << ", power=" << t.power;
|
||||
}
|
||||
|
||||
template <typename OStream, typename T>
|
||||
void stream_value(OStream& os, const T& t) {
|
||||
os << t;
|
||||
}
|
||||
|
||||
template <typename OStream, typename... Ts>
|
||||
void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
|
||||
os << std::quoted(t);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename CharT, typename Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const interval_view<T>& i) {
|
||||
namespace axis {
|
||||
|
||||
template <typename C, typename T>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const axis::option_type o) {
|
||||
switch (o) {
|
||||
case axis::option_type::none:
|
||||
os << "none";
|
||||
break;
|
||||
case axis::option_type::overflow:
|
||||
os << "overflow";
|
||||
break;
|
||||
case axis::option_type::underflow_and_overflow:
|
||||
os << "underflow_and_overflow";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename C, typename T>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const empty_metadata_type&) {
|
||||
return os; // do nothing
|
||||
}
|
||||
|
||||
template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const interval_bin_view<U>& i) {
|
||||
os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const value_view<T>& i) {
|
||||
template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const value_bin_view<U>& i) {
|
||||
os << i.value();
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename U, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const regular<T, U, A>& a) {
|
||||
template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const polymorphic_bin_view<U>& i) {
|
||||
if (i.is_continuous())
|
||||
os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
else
|
||||
os << i.value();
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const regular<Ts...>& a) {
|
||||
os << "regular" << detail::to_string(a.transform()) << "(" << a.size() << ", "
|
||||
<< a[0].lower() << ", " << a[a.size()].lower();
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
if (!a.uoflow()) { os << ", uoflow=False"; }
|
||||
<< a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
detail::stream_transform(os, a.transform());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, const regular<axis::transform::pow, T, A>& a) {
|
||||
os << "regular_pow(" << a.size() << ", " << a[0].lower() << ", " << a[a.size()].lower()
|
||||
<< ", " << a.transform().power;
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
if (!a.uoflow()) { os << ", uoflow=False"; }
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const circular<Ts...>& a) {
|
||||
os << "circular(" << a.size() << ", " << a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const circular<T, A>& a) {
|
||||
os << "circular(" << a.size();
|
||||
if (a.phase() != 0.0) { os << ", phase=" << a.phase(); }
|
||||
if (a.perimeter() != circular<T, A>::two_pi()) {
|
||||
os << ", perimeter=" << a.perimeter();
|
||||
}
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const variable<Ts...>& a) {
|
||||
os << "variable(" << a.value(0);
|
||||
for (unsigned i = 1; i <= a.size(); ++i) { os << ", " << a.value(i); }
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const variable<T, A>& a) {
|
||||
os << "variable(" << a[0].lower();
|
||||
for (int i = 1; i <= a.size(); ++i) { os << ", " << a[i].lower(); }
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
if (!a.uoflow()) { os << ", uoflow=False"; }
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const integer<Ts...>& a) {
|
||||
os << "integer(" << a.value(0) << ", " << a.value(a.size());
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const integer<T, A>& a) {
|
||||
os << "integer(" << a[0].lower() << ", " << a[a.size()].lower();
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
if (!a.uoflow()) { os << ", uoflow=False"; }
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename T, typename A>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const category<T, A>& a) {
|
||||
template <typename C, typename T, typename... Ts>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const category<Ts...>& a) {
|
||||
os << "category(";
|
||||
for (int i = 0; i < a.size(); ++i) { os << a[i] << (i == (a.size() - 1) ? "" : ", "); }
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename A>
|
||||
inline std::basic_ostream<CharT, Traits>& operator<<(
|
||||
std::basic_ostream<CharT, Traits>& os, const category<std::string, A>& a) {
|
||||
os << "category(";
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
detail::escape_string(os, a.value(i));
|
||||
for (unsigned i = 0; i < a.size(); ++i) {
|
||||
detail::stream_value(os, a.value(i));
|
||||
os << (i == (a.size() - 1) ? "" : ", ");
|
||||
}
|
||||
if (!a.label().empty()) {
|
||||
os << ", label=";
|
||||
detail::escape_string(os, a.label());
|
||||
}
|
||||
detail::stream_metadata(os, a.metadata());
|
||||
detail::stream_options(os, a.options());
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
67
include/boost/histogram/axis/polymorphic_bin_view.hpp
Normal file
67
include/boost/histogram/axis/polymorphic_bin_view.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP
|
||||
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class polymorphic_bin_view {
|
||||
using value_type = detail::return_type<decltype(&Axis::value)>;
|
||||
public:
|
||||
polymorphic_bin_view(int idx, const Axis& axis, bool is_continuous)
|
||||
: idx_(idx), axis_(axis), is_continuous_(is_continuous) {}
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
value_type value() const {
|
||||
if (is_continuous_)
|
||||
throw std::runtime_error("calling value() for continuous axis is ambiguous");
|
||||
return axis_.value(idx_);
|
||||
}
|
||||
value_type lower() const {
|
||||
if (!is_continuous_)
|
||||
throw std::runtime_error("cannot call lower() for discontinuous axis");
|
||||
return axis_.value(idx_);
|
||||
}
|
||||
value_type upper() const {
|
||||
if (!is_continuous_)
|
||||
throw std::runtime_error("cannot call upper() for discontinuous axis");
|
||||
return axis_.value(idx_ + 1);
|
||||
}
|
||||
value_type center() const {
|
||||
if (!is_continuous_)
|
||||
throw std::runtime_error("cannot call center() for discontinuous axis");
|
||||
return axis_.value(idx_ + 0.5);
|
||||
}
|
||||
template <typename U=value_type, typename = decltype(std::declval<U&>() - std::declval<U&>())>
|
||||
value_type width() const { return upper() - lower(); }
|
||||
|
||||
bool operator==(const polymorphic_bin_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const polymorphic_bin_view& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
bool is_continuous() const noexcept { return is_continuous_; }
|
||||
|
||||
private:
|
||||
const int idx_;
|
||||
const Axis& axis_;
|
||||
const bool is_continuous_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
179
include/boost/histogram/axis/regular.hpp
Normal file
179
include/boost/histogram/axis/regular.hpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_REGULAR_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_REGULAR_HPP
|
||||
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_bin_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
namespace transform {
|
||||
template <typename T>
|
||||
struct identity {
|
||||
static T forward(T x) { return x; }
|
||||
static T inverse(T x) { return x; }
|
||||
|
||||
bool operator==(const identity&) const noexcept { return true; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned) {} // noop
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct log : identity<T> {
|
||||
static T forward(T x) { return std::log(x); }
|
||||
static T inverse(T x) { return std::exp(x); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct sqrt : identity<T> {
|
||||
static T forward(T x) { return std::sqrt(x); }
|
||||
static T inverse(T x) { return x * x; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct pow {
|
||||
using U = mp11::mp_if<std::is_integral<T>, double, T>;
|
||||
U power = 1.0;
|
||||
|
||||
pow(U p) : power(p) {}
|
||||
pow() = default;
|
||||
|
||||
U forward(U v) const { return std::pow(v, power); }
|
||||
U inverse(U v) const { return std::pow(v, 1.0 / power); }
|
||||
|
||||
bool operator==(const pow& o) const noexcept { return power == o.power; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
template <typename Q>
|
||||
struct unit {
|
||||
using T = typename Q::value_type;
|
||||
using U = typename Q::unit_type;
|
||||
T forward(Q x) const { return x / U(); }
|
||||
Q inverse(T x) const { return x * U(); }
|
||||
};
|
||||
} // namespace transform
|
||||
|
||||
/** Axis for equidistant intervals on the real line.
|
||||
*
|
||||
* The most common binning strategy.
|
||||
* Very fast. Binning is a O(1) operation.
|
||||
*/
|
||||
template <typename Transform, typename MetaData>
|
||||
class regular : public base<MetaData>,
|
||||
public iterator_mixin<regular<Transform, MetaData>>,
|
||||
protected Transform {
|
||||
using base_type = base<MetaData>;
|
||||
using transform_type = Transform;
|
||||
using external_type = detail::return_type<decltype(&Transform::inverse)>;
|
||||
using internal_type = detail::return_type<decltype(&Transform::forward)>;
|
||||
static_assert(!std::is_integral<internal_type>::value,
|
||||
"type returned by forward transform cannot be integral");
|
||||
using metadata_type = MetaData;
|
||||
|
||||
public:
|
||||
/** Construct n bins over real transformed range [begin, end).
|
||||
*
|
||||
* \param trans transform instance to use.
|
||||
* \param n number of bins.
|
||||
* \param start low edge of first bin.
|
||||
* \param stop high edge of last bin.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
regular(transform_type trans, unsigned n, external_type start, external_type stop,
|
||||
metadata_type m = {}, option_type o = option_type::underflow_and_overflow)
|
||||
: base_type(n, std::move(m), o)
|
||||
, transform_type(std::move(trans))
|
||||
, min_(this->forward(start))
|
||||
, delta_((this->forward(stop) - this->forward(start)) / n) {
|
||||
if (!std::isfinite(min_) || !std::isfinite(delta_))
|
||||
throw std::invalid_argument("forward transform of start or stop invalid");
|
||||
if (delta_ == 0)
|
||||
throw std::invalid_argument("range of forward transformed axis is zero");
|
||||
}
|
||||
|
||||
/** Construct n bins over real range [begin, end).
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param start low edge of first bin.
|
||||
* \param stop high edge of last bin.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
*/
|
||||
regular(unsigned n, external_type start, external_type stop, metadata_type m = {},
|
||||
option_type o = option_type::underflow_and_overflow)
|
||||
: regular({}, n, start, stop, std::move(m), o) {}
|
||||
|
||||
regular() = default;
|
||||
|
||||
/// Returns instance of the transform type
|
||||
const transform_type& transform() const noexcept { return *this; }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(external_type x) const noexcept {
|
||||
// Runs in hot loop, please measure impact of changes
|
||||
const auto z = (this->forward(x) - min_) / delta_;
|
||||
if (z < base_type::size()) {
|
||||
if (z >= 0)
|
||||
return static_cast<int>(z);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return base_type::size(); // also returned if z is NaN
|
||||
|
||||
// const auto lt_max = z < base_type::size();
|
||||
// const auto ge_zero = z >= 0;
|
||||
// return lt_max * (ge_zero * static_cast<int>(z) - !ge_zero) + !lt_max *
|
||||
// base_type::size();
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
external_type value(internal_type i) const noexcept {
|
||||
i /= base_type::size();
|
||||
if (i < 0)
|
||||
i = std::copysign(std::numeric_limits<internal_type>::infinity(), -delta_);
|
||||
else if (i > 1)
|
||||
i = std::copysign(std::numeric_limits<internal_type>::infinity(), delta_);
|
||||
else {
|
||||
i = (1 - i) * min_ + i * (min_ + delta_ * base_type::size());
|
||||
}
|
||||
return this->inverse(i);
|
||||
}
|
||||
|
||||
/// Access bin at index
|
||||
auto operator[](int idx) const noexcept { return interval_bin_view<regular>(idx, *this); }
|
||||
|
||||
bool operator==(const regular& o) const noexcept {
|
||||
return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ &&
|
||||
delta_ == o.delta_;
|
||||
}
|
||||
|
||||
bool operator!=(const regular& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
internal_type min_, delta_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
47
include/boost/histogram/axis/traits.hpp
Normal file
47
include/boost/histogram/axis/traits.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_TRAITS_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
|
||||
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
namespace traits {
|
||||
template <typename T>
|
||||
using args = detail::args_type<decltype(&T::operator())>;
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) metadata(T&& t) noexcept {
|
||||
return detail::static_if<detail::has_method_metadata<T>>(
|
||||
[](auto&& x) -> decltype(auto) { return x.metadata(); },
|
||||
[](auto&&) -> detail::copy_qualifiers<T, axis::empty_metadata_type> {
|
||||
static axis::empty_metadata_type m; return m;
|
||||
},
|
||||
t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
option_type options(const T& t) noexcept {
|
||||
return detail::static_if<detail::has_method_options<T>>(
|
||||
[](const auto& x) { return x.options(); },
|
||||
[](const T&) { return axis::option_type::none; },
|
||||
t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
unsigned extend(const T& t) noexcept {
|
||||
return t.size() + static_cast<unsigned>(options(t));
|
||||
}
|
||||
} // namespace traits
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -1,578 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_TYPES_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_TYPES_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/value_view.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
// forward declaration for serialization
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
class access;
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
namespace transform {
|
||||
namespace detail {
|
||||
struct stateless {
|
||||
bool operator==(const stateless&) const noexcept { return true; }
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned) {}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
struct identity : public detail::stateless {
|
||||
template <typename T>
|
||||
static T&& forward(T&& v) {
|
||||
return std::forward<T>(v);
|
||||
}
|
||||
template <typename T>
|
||||
static T&& inverse(T&& v) {
|
||||
return std::forward<T>(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct log : public detail::stateless {
|
||||
template <typename T>
|
||||
static T forward(T v) {
|
||||
return std::log(v);
|
||||
}
|
||||
template <typename T>
|
||||
static T inverse(T v) {
|
||||
return std::exp(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct sqrt : public detail::stateless {
|
||||
template <typename T>
|
||||
static T forward(T v) {
|
||||
return std::sqrt(v);
|
||||
}
|
||||
template <typename T>
|
||||
static T inverse(T v) {
|
||||
return v * v;
|
||||
}
|
||||
};
|
||||
|
||||
// struct cos : public detail::stateless {
|
||||
// template <typename T> static T forward(T v) { return std::cos(v); }
|
||||
// template <typename T> static T inverse(T v) { return std::acos(v); }
|
||||
// };
|
||||
|
||||
struct pow {
|
||||
double power = 1.0;
|
||||
|
||||
pow() = default;
|
||||
pow(double p) : power(p) {}
|
||||
template <typename T>
|
||||
T forward(T v) const {
|
||||
return std::pow(v, power);
|
||||
}
|
||||
template <typename T>
|
||||
T inverse(T v) const {
|
||||
return std::pow(v, 1.0 / power);
|
||||
}
|
||||
bool operator==(const pow& other) const noexcept { return power == other.power; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
} // namespace transform
|
||||
|
||||
/** Axis for equidistant intervals on the real line.
|
||||
*
|
||||
* The most common binning strategy.
|
||||
* Very fast. Binning is a O(1) operation.
|
||||
*/
|
||||
// private inheritance from Transform wastes no space if it is stateless
|
||||
template <typename Transform, typename RealType, typename Allocator>
|
||||
class regular : public labeled_base<Allocator>,
|
||||
public iterator_mixin<regular<Transform, RealType, Allocator>>,
|
||||
Transform {
|
||||
using base_type = labeled_base<Allocator>;
|
||||
|
||||
public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using transform_type = Transform;
|
||||
using value_type = RealType;
|
||||
using bin_type = interval_view<regular>;
|
||||
|
||||
static_assert(std::is_floating_point<value_type>::value,
|
||||
"type returned by forward transform must be floating point");
|
||||
|
||||
/** Construct axis with n bins over real range [lower, upper).
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param lower low edge of first bin.
|
||||
* \param upper high edge of last bin.
|
||||
* \param label description of the axis.
|
||||
* \param uoflow whether to add under-/overflow bins.
|
||||
* \param trans arguments passed to the transform.
|
||||
*/
|
||||
regular(unsigned n, value_type lower, value_type upper, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, transform_type trans = transform_type(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: base_type(n, uo, label, a)
|
||||
, transform_type(std::move(trans))
|
||||
, min_(transform_type::forward(lower))
|
||||
, delta_((transform_type::forward(upper) - transform_type::forward(lower)) / n) {
|
||||
if (lower < upper) {
|
||||
BOOST_ASSERT(!std::isnan(min_));
|
||||
BOOST_ASSERT(!std::isnan(delta_));
|
||||
} else {
|
||||
throw std::invalid_argument("lower < upper required");
|
||||
}
|
||||
}
|
||||
|
||||
regular() = default;
|
||||
regular(const regular&) = default;
|
||||
regular& operator=(const regular&) = default;
|
||||
regular(regular&&) = default;
|
||||
regular& operator=(regular&&) = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
// Optimized code, measure impact of changes
|
||||
const value_type z = (transform_type::forward(x) - min_) / delta_;
|
||||
return z < base_type::size() ? (z >= 0.0 ? static_cast<int>(z) : -1)
|
||||
: base_type::size();
|
||||
}
|
||||
|
||||
/// Returns lower edge of bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
const auto n = base_type::size();
|
||||
value_type x;
|
||||
if (i < 0)
|
||||
x = -std::numeric_limits<value_type>::infinity();
|
||||
else if (i > n)
|
||||
x = std::numeric_limits<value_type>::infinity();
|
||||
else {
|
||||
const auto z = value_type(i) / n;
|
||||
x = (1.0 - z) * min_ + z * (min_ + delta_ * n);
|
||||
}
|
||||
return transform_type::inverse(x);
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const regular& o) const noexcept {
|
||||
return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ &&
|
||||
delta_ == o.delta_;
|
||||
}
|
||||
|
||||
/// Access properties of the transform.
|
||||
const transform_type& transform() const noexcept {
|
||||
return static_cast<const transform_type&>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
value_type min_ = 0, delta_ = 1;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/** Axis for real values on a circle.
|
||||
*
|
||||
* The axis is circular and wraps around reaching the
|
||||
* perimeter value. Therefore, there are no overflow/underflow
|
||||
* bins for this axis. Binning is a O(1) operation.
|
||||
*/
|
||||
template <typename RealType, typename Allocator>
|
||||
class circular : public labeled_base<Allocator>,
|
||||
public iterator_mixin<circular<RealType, Allocator>> {
|
||||
using base_type = labeled_base<Allocator>;
|
||||
|
||||
public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using value_type = RealType;
|
||||
using bin_type = interval_view<circular>;
|
||||
|
||||
// two_pi can be found in boost/math, but it is defined here to reduce deps
|
||||
static value_type two_pi() { return 6.283185307179586; }
|
||||
|
||||
/** Constructor for n bins with an optional offset.
|
||||
*
|
||||
* \param n number of bins.
|
||||
* \param phase starting phase.
|
||||
* \param perimeter range after which value wraps around.
|
||||
* \param label description of the axis.
|
||||
*/
|
||||
explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(),
|
||||
string_view label = {}, const allocator_type& a = allocator_type())
|
||||
: base_type(n, uoflow_type::off, label, a), phase_(phase), perimeter_(perimeter) {
|
||||
if (perimeter <= 0) throw std::invalid_argument("perimeter must be positive");
|
||||
}
|
||||
|
||||
circular() = default;
|
||||
circular(const circular&) = default;
|
||||
circular& operator=(const circular&) = default;
|
||||
circular(circular&&) = default;
|
||||
circular& operator=(circular&&) = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
const value_type z = (x - phase_) / perimeter_;
|
||||
const int i = static_cast<int>(std::floor(z * base_type::size())) % base_type::size();
|
||||
return i + (i < 0) * base_type::size();
|
||||
}
|
||||
|
||||
/// Returns lower edge of bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
const value_type z = value_type(i) / base_type::size();
|
||||
return z * perimeter_ + phase_;
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const circular& o) const noexcept {
|
||||
return base_type::operator==(o) && phase_ == o.phase_ && perimeter_ == o.perimeter_;
|
||||
}
|
||||
|
||||
value_type perimeter() const { return perimeter_; }
|
||||
value_type phase() const { return phase_; }
|
||||
|
||||
private:
|
||||
value_type phase_ = 0.0, perimeter_ = 1.0;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/** Axis for non-equidistant bins on the real line.
|
||||
*
|
||||
* Binning is a O(log(N)) operation. If speed matters and the problem
|
||||
* domain allows it, prefer a regular axis, possibly with a transform.
|
||||
*/
|
||||
template <typename RealType, typename Allocator>
|
||||
class variable : public labeled_base<Allocator>,
|
||||
public iterator_mixin<variable<RealType, Allocator>> {
|
||||
using base_type = labeled_base<Allocator>;
|
||||
|
||||
public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using value_type = RealType;
|
||||
using bin_type = interval_view<variable>;
|
||||
|
||||
private:
|
||||
using value_allocator_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<value_type>;
|
||||
using value_pointer_type =
|
||||
typename std::allocator_traits<value_allocator_type>::pointer;
|
||||
|
||||
public:
|
||||
/** Construct an axis from bin edges.
|
||||
*
|
||||
* \param x sequence of bin edges.
|
||||
* \param label description of the axis.
|
||||
* \param uoflow whether to add under-/overflow bins.
|
||||
*/
|
||||
variable(std::initializer_list<value_type> x, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type())
|
||||
: variable(x.begin(), x.end(), label, uo, a) {}
|
||||
|
||||
template <typename Iterator,
|
||||
typename = boost::histogram::detail::requires_iterator<Iterator>>
|
||||
variable(Iterator begin, Iterator end, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type())
|
||||
: base_type(begin == end ? 0 : std::distance(begin, end) - 1, uo, label, a) {
|
||||
value_allocator_type a2(a);
|
||||
using AT = std::allocator_traits<value_allocator_type>;
|
||||
x_ = AT::allocate(a2, nx());
|
||||
auto xit = x_;
|
||||
try {
|
||||
AT::construct(a2, xit, *begin++);
|
||||
while (begin != end) {
|
||||
if (*begin <= *xit) {
|
||||
++xit; // to make sure catch code works
|
||||
throw std::invalid_argument("input sequence must be strictly ascending");
|
||||
}
|
||||
++xit;
|
||||
AT::construct(a2, xit, *begin++);
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
while (xit != x_) AT::destroy(a2, --xit);
|
||||
AT::deallocate(a2, x_, nx());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
variable() = default;
|
||||
|
||||
variable(const variable& o) : base_type(o) {
|
||||
value_allocator_type a(o.get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_);
|
||||
}
|
||||
|
||||
variable& operator=(const variable& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
this->~variable();
|
||||
base::operator=(o);
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_);
|
||||
} else {
|
||||
base::operator=(o);
|
||||
std::copy(o.x_, o.x_ + o.nx(), x_);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable(variable&& o) : base_type(std::move(o)) {
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
}
|
||||
|
||||
variable& operator=(variable&& o) {
|
||||
this->~variable();
|
||||
base::operator=(std::move(o));
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variable() {
|
||||
if (x_) { // nothing to do for empty state
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
boost::histogram::detail::destroy_buffer(a, x_, nx());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
return std::upper_bound(x_, x_ + nx(), x) - x_ - 1;
|
||||
}
|
||||
|
||||
/// Returns the starting edge of the bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
if (i < 0) { return -std::numeric_limits<value_type>::infinity(); }
|
||||
if (i > base_type::size()) { return std::numeric_limits<value_type>::infinity(); }
|
||||
return x_[i];
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const variable& o) const noexcept {
|
||||
if (!base::operator==(o)) { return false; }
|
||||
return std::equal(x_, x_ + nx(), o.x_);
|
||||
}
|
||||
|
||||
private:
|
||||
int nx() const { return base_type::size() + 1; }
|
||||
|
||||
value_pointer_type x_ = nullptr;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/** Axis for an interval of integral values with unit steps.
|
||||
*
|
||||
* Binning is a O(1) operation. This axis operates
|
||||
* faster than a regular.
|
||||
*/
|
||||
template <typename IntType, typename Allocator>
|
||||
class integer : public labeled_base<Allocator>,
|
||||
public iterator_mixin<integer<IntType, Allocator>> {
|
||||
using base_type = labeled_base<Allocator>;
|
||||
|
||||
public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using value_type = IntType;
|
||||
using bin_type = interval_view<integer>;
|
||||
|
||||
/** Construct axis over a semi-open integer interval [lower, upper).
|
||||
*
|
||||
* \param lower smallest integer of the covered range.
|
||||
* \param upper largest integer of the covered range.
|
||||
* \param label description of the axis.
|
||||
* \param uoflow whether to add under-/overflow bins.
|
||||
*/
|
||||
integer(value_type lower, value_type upper, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type())
|
||||
: base_type(upper - lower, uo, label, a), min_(lower) {
|
||||
if (!(lower < upper)) { throw std::invalid_argument("lower < upper required"); }
|
||||
}
|
||||
|
||||
integer() = default;
|
||||
integer(const integer&) = default;
|
||||
integer& operator=(const integer&) = default;
|
||||
integer(integer&&) = default;
|
||||
integer& operator=(integer&&) = default;
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
const int z = x - min_;
|
||||
return z >= 0 ? (z > base_type::size() ? base_type::size() : z) : -1;
|
||||
}
|
||||
|
||||
/// Returns lower edge of the integral bin.
|
||||
value_type lower(int i) const noexcept {
|
||||
if (i < 0) { return -std::numeric_limits<value_type>::max(); }
|
||||
if (i > base_type::size()) { return std::numeric_limits<value_type>::max(); }
|
||||
return min_ + i;
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const integer& o) const noexcept {
|
||||
return base_type::operator==(o) && min_ == o.min_;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type min_ = 0;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/** Axis which maps unique values to bins (one on one).
|
||||
*
|
||||
* The axis maps a set of values to bins, following the order of
|
||||
* arguments in the constructor. There is an optional overflow bin
|
||||
* for this axis, which counts values that are not part of the set.
|
||||
* Binning is a O(n) operation for n values in the worst case and O(1) in
|
||||
* the best case. The value types must be equal-comparable.
|
||||
*/
|
||||
template <typename T, typename Allocator>
|
||||
class category : public labeled_base<Allocator>,
|
||||
public iterator_mixin<category<T, Allocator>> {
|
||||
using base_type = labeled_base<Allocator>;
|
||||
|
||||
public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using value_type = T;
|
||||
using bin_type = value_view<category>;
|
||||
|
||||
private:
|
||||
using value_allocator_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<value_type>;
|
||||
using value_pointer_type =
|
||||
typename std::allocator_traits<value_allocator_type>::pointer;
|
||||
|
||||
public:
|
||||
/** Construct from an initializer list of strings.
|
||||
*
|
||||
* \param seq sequence of unique values.
|
||||
* \param label description of the axis.
|
||||
* \param uoflow whether to add under-/overflow bins.
|
||||
*/
|
||||
category(std::initializer_list<value_type> seq, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::oflow,
|
||||
const allocator_type& a = allocator_type())
|
||||
: category(seq.begin(), seq.end(), label, uo, a) {}
|
||||
|
||||
template <typename Iterator,
|
||||
typename = boost::histogram::detail::requires_iterator<Iterator>>
|
||||
category(Iterator begin, Iterator end, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::oflow,
|
||||
const allocator_type& a = allocator_type())
|
||||
: base_type(std::distance(begin, end),
|
||||
uo == uoflow_type::on ? uoflow_type::oflow : uo, label, a) {
|
||||
value_allocator_type a2(a);
|
||||
x_ = boost::histogram::detail::create_buffer_from_iter(a2, nx(), begin);
|
||||
}
|
||||
|
||||
category() = default;
|
||||
|
||||
category(const category& o) : base_type(o) {
|
||||
value_allocator_type a(o.get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer_from_iter(a, o.nx(), o.x_);
|
||||
}
|
||||
|
||||
category& operator=(const category& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() != o.size()) {
|
||||
this->~category();
|
||||
base_type::operator=(o);
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer_from_iter(a, nx(), o.x_);
|
||||
} else {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.x_, o.x_ + o.nx(), x_);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
category(category&& o) : base_type(std::move(o)) {
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
}
|
||||
|
||||
category& operator=(category&& o) {
|
||||
this->~category();
|
||||
base_type::operator=(std::move(o));
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~category() {
|
||||
if (x_) { // nothing to do for empty state
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
boost::histogram::detail::destroy_buffer(a, x_, nx());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(const value_type& x) const noexcept {
|
||||
if (last_ < nx() && x_[last_] == x) return last_;
|
||||
last_ = 0;
|
||||
for (auto xit = x_, xe = x_ + nx(); xit != xe && !(*xit == x); ++xit) ++last_;
|
||||
return last_;
|
||||
}
|
||||
|
||||
/// Returns the value for the bin index (performs a range check).
|
||||
const value_type& value(int idx) const {
|
||||
if (idx < 0 || idx >= base_type::size())
|
||||
throw std::out_of_range("category index out of range");
|
||||
return x_[idx];
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const category& o) const noexcept {
|
||||
return base_type::operator==(o) && std::equal(x_, x_ + nx(), o.x_);
|
||||
}
|
||||
|
||||
private:
|
||||
int nx() const { return base_type::size(); }
|
||||
|
||||
value_pointer_type x_ = nullptr;
|
||||
mutable int last_ = 0;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
41
include/boost/histogram/axis/value_bin_view.hpp
Normal file
41
include/boost/histogram/axis/value_bin_view.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class value_bin_view {
|
||||
public:
|
||||
value_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
decltype(auto) value() const { return axis_.value(idx_); }
|
||||
|
||||
bool operator==(const value_bin_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const value_bin_view& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
private:
|
||||
const int idx_;
|
||||
const Axis& axis_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
|
||||
template <typename Axis>
|
||||
class value_view {
|
||||
public:
|
||||
value_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {}
|
||||
|
||||
value_view(const value_view&) = default;
|
||||
value_view& operator=(const value_view&) = default;
|
||||
value_view(value_view&&) = default;
|
||||
value_view& operator=(value_view&&) = default;
|
||||
|
||||
int idx() const noexcept { return idx_; }
|
||||
|
||||
auto value() const -> decltype(std::declval<Axis&>().value(0)) {
|
||||
return axis_.value(idx_);
|
||||
}
|
||||
|
||||
bool operator==(const value_view& rhs) const noexcept {
|
||||
return idx_ == rhs.idx_ && axis_ == rhs.axis_;
|
||||
}
|
||||
bool operator!=(const value_view& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
explicit operator int() const noexcept { return idx_; }
|
||||
|
||||
private:
|
||||
const int idx_;
|
||||
const Axis& axis_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
182
include/boost/histogram/axis/variable.hpp
Normal file
182
include/boost/histogram/axis/variable.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/interval_bin_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/compressed_pair.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace axis {
|
||||
/** Axis for non-equidistant bins on the real line.
|
||||
*
|
||||
* Binning is a O(log(N)) operation. If speed matters and the problem
|
||||
* domain allows it, prefer a regular axis, possibly with a transform.
|
||||
*/
|
||||
template <typename RealType, typename Allocator, typename MetaData>
|
||||
class variable : public base<MetaData>,
|
||||
public iterator_mixin<variable<RealType, Allocator, MetaData>> {
|
||||
using base_type = base<MetaData>;
|
||||
using allocator_type = Allocator;
|
||||
using metadata_type = MetaData;
|
||||
using value_type = RealType;
|
||||
|
||||
public:
|
||||
/** Construct from iterator range of bin edges.
|
||||
*
|
||||
* \param begin begin of edge sequence.
|
||||
* \param end end of edge sequence.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename It, typename = detail::requires_iterator<It>>
|
||||
variable(It begin, It end, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o)
|
||||
, x_(nullptr, std::move(a)) {
|
||||
using AT = std::allocator_traits<allocator_type>;
|
||||
x_.first() = AT::allocate(x_.second(), nx());
|
||||
try {
|
||||
auto xit = x_.first();
|
||||
try {
|
||||
AT::construct(x_.second(), xit, *begin++);
|
||||
while (begin != end) {
|
||||
if (*begin <= *xit) {
|
||||
++xit; // to make sure catch code works
|
||||
throw std::invalid_argument("input sequence must be strictly ascending");
|
||||
}
|
||||
++xit;
|
||||
AT::construct(x_.second(), xit, *begin++);
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
while (xit != x_.first()) AT::destroy(x_.second(), --xit);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
// release resources that were already acquired before rethrowing
|
||||
AT::deallocate(x_.second(), x_.first(), nx());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/** Construct variable axis from iterable range of bin edges.
|
||||
*
|
||||
* \param iterable iterable range of bin edges.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U, typename = detail::requires_iterable<U>>
|
||||
variable(const U& iterable, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(std::begin(iterable), std::end(iterable), std::move(m), o,
|
||||
std::move(a)) {}
|
||||
|
||||
/** Construct variable axis from initializer list of bin edges.
|
||||
*
|
||||
* \param edgelist list of of bin edges.
|
||||
* \param metadata description of the axis.
|
||||
* \param options extra bin options.
|
||||
* \param allocator allocator instance to use.
|
||||
*/
|
||||
template <typename U>
|
||||
variable(const std::initializer_list<U>& l, metadata_type m = metadata_type(),
|
||||
option_type o = option_type::underflow_and_overflow,
|
||||
allocator_type a = allocator_type())
|
||||
: variable(l.begin(), l.end(), std::move(m), o, std::move(a)) {}
|
||||
|
||||
variable() : x_(nullptr) {}
|
||||
|
||||
variable(const variable& o) : base_type(o), x_(o.x_) {
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first());
|
||||
}
|
||||
|
||||
variable& operator=(const variable& o) {
|
||||
if (this != &o) {
|
||||
if (base_type::size() == o.size()) {
|
||||
base_type::operator=(o);
|
||||
std::copy(o.x_.first(), o.x_.first() + nx(), x_.first());
|
||||
} else {
|
||||
detail::destroy_buffer(x_.second(), x_.first(), nx());
|
||||
base_type::operator=(o);
|
||||
x_.second() = o.x_.second();
|
||||
x_.first() = detail::create_buffer_from_iter(x_.second(), nx(), o.x_.first());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable(variable&& o) : variable() {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
|
||||
variable& operator=(variable&& o) {
|
||||
if (this != &o) {
|
||||
using std::swap;
|
||||
swap(static_cast<base_type&>(*this), static_cast<base_type&>(o));
|
||||
swap(x_, o.x_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variable() { detail::destroy_buffer(x_.second(), x_.first(), nx()); }
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int operator()(value_type x) const noexcept {
|
||||
const auto p = x_.first();
|
||||
return std::upper_bound(p, p + nx(), x) - p - 1;
|
||||
}
|
||||
|
||||
/// Returns axis value for fractional index.
|
||||
value_type value(value_type i) const noexcept {
|
||||
if (i < 0) { return -std::numeric_limits<value_type>::infinity(); }
|
||||
if (i > base_type::size()) { return std::numeric_limits<value_type>::infinity(); }
|
||||
value_type z;
|
||||
const int k = std::modf(i, &z);
|
||||
return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1];
|
||||
}
|
||||
|
||||
auto operator[](int idx) const noexcept { return interval_bin_view<variable>(idx, *this); }
|
||||
|
||||
bool operator==(const variable& o) const noexcept {
|
||||
return base_type::operator==(o) &&
|
||||
std::equal(x_.first(), x_.first() + nx(), o.x_.first());
|
||||
}
|
||||
|
||||
bool operator!=(const variable<>& o) const noexcept { return !operator==(o); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
int nx() const { return base_type::size() + 1; }
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
detail::compressed_pair<pointer, allocator_type> x_;
|
||||
};
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
331
include/boost/histogram/axis/variant.hpp
Normal file
331
include/boost/histogram/axis/variant.hpp
Normal file
@@ -0,0 +1,331 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_AXIS_VARIANT_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_VARIANT_HPP
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/polymorphic_bin_view.hpp>
|
||||
#include <boost/histogram/axis/iterator.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
struct is_continuous : public boost::static_visitor<bool> {
|
||||
template <typename A>
|
||||
bool operator()(const A&) const {
|
||||
using T = detail::arg_type<decltype(&A::value)>;
|
||||
return !std::is_integral<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename R>
|
||||
struct functor_wrapper : public boost::static_visitor<R> {
|
||||
F& fcn;
|
||||
functor_wrapper(F& f) : fcn(f) {}
|
||||
|
||||
template <typename T>
|
||||
R operator()(T&& t) const {
|
||||
return fcn(std::forward<T>(t));
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
|
||||
/// Polymorphic axis type
|
||||
template <typename... Ts>
|
||||
class variant : private boost::variant<Ts...>, public iterator_mixin<variant<Ts...>> {
|
||||
using base_type = boost::variant<Ts...>;
|
||||
using first_bounded_type = mp11::mp_first<base_type>;
|
||||
using metadata_type =
|
||||
detail::rm_cvref<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
|
||||
|
||||
template <typename T>
|
||||
using requires_bounded_type =
|
||||
mp11::mp_if<mp11::mp_contains<base_type, detail::rm_cvref<T>>, void>;
|
||||
|
||||
public:
|
||||
variant() = default;
|
||||
variant(const variant&) = default;
|
||||
variant& operator=(const variant&) = default;
|
||||
variant(variant&&) = default;
|
||||
variant& operator=(variant&&) = default;
|
||||
|
||||
template <typename T, typename = requires_bounded_type<T>>
|
||||
variant(T&& t) : base_type(std::forward<T>(t)) {}
|
||||
|
||||
template <typename T, typename = requires_bounded_type<T>>
|
||||
variant& operator=(T&& t) {
|
||||
base_type::operator=(std::forward<T>(t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
variant(const variant<Us...>& u) {
|
||||
this->operator=(u);
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
variant& operator=(const variant<Us...>& u) {
|
||||
visit(
|
||||
[this](const auto& u) {
|
||||
using U = detail::rm_cvref<decltype(u)>;
|
||||
detail::static_if<mp11::mp_contains<base_type, U>>(
|
||||
[this](const auto& u) { this->operator=(u); },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" is not a bounded type of ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(variant))));
|
||||
},
|
||||
u);
|
||||
},
|
||||
u);
|
||||
return *this;
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return visit([](const auto& x) { return x.size(); }, *this);
|
||||
}
|
||||
|
||||
option_type options() const {
|
||||
return visit([](const auto& x) { return axis::traits::options(x); }, *this);
|
||||
}
|
||||
|
||||
const metadata_type& metadata() const {
|
||||
return visit(
|
||||
[](const auto& x) -> const metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, const metadata_type&>>(
|
||||
[](const auto& x) -> const metadata_type& { return traits::metadata(x); },
|
||||
[](const auto&) -> const metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(const metadata_type&)),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
metadata_type& metadata() {
|
||||
return visit(
|
||||
[](auto& x) -> metadata_type& {
|
||||
using U = decltype(traits::metadata(x));
|
||||
return detail::static_if<std::is_same<U, metadata_type&>>(
|
||||
[](auto& x) -> metadata_type& { return traits::metadata(x); },
|
||||
[](auto&) -> metadata_type& {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"cannot return metadata of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" through axis::variant interface which uses type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(metadata_type&)),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
// Will throw invalid_argument exception if axis has incompatible call signature
|
||||
template <typename... Us>
|
||||
int operator()(Us... x) const {
|
||||
auto&& args = std::forward_as_tuple(std::forward<Us>(x)...);
|
||||
return visit(
|
||||
[&args](const auto& a) {
|
||||
using A = detail::rm_cvref<decltype(a)>;
|
||||
using args_t = std::tuple<Us...>;
|
||||
using expected_args_t = axis::traits::args<A>;
|
||||
return detail::static_if<std::is_convertible<args_t, expected_args_t>>(
|
||||
[&args](const auto& a) -> int { return mp11::tuple_apply(a, args); },
|
||||
[](const auto&) -> int {
|
||||
throw std::invalid_argument(detail::cat(
|
||||
"cannot convert ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(args_t)),
|
||||
" to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(expected_args_t)), " for ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
|
||||
"; use boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
// Only works for axes with value method that returns something convertible to
|
||||
// double and will throw a runtime_error otherwise
|
||||
double value(double idx) const {
|
||||
return visit(
|
||||
[idx](const auto& a) {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
return detail::static_if<detail::has_method_value<T>>(
|
||||
[idx](const auto& a) -> double {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
using U = detail::return_type<decltype(&T::value)>;
|
||||
return detail::static_if<std::is_convertible<U, double>>(
|
||||
[idx](const auto& a) -> double {
|
||||
return static_cast<double>(a.value(idx));
|
||||
},
|
||||
[](const auto&) -> double {
|
||||
throw std::runtime_error(detail::cat(
|
||||
"return value ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " of ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
"::value(double) is not convertible to double; use "
|
||||
"boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
[](const auto&) -> double {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
" has no value method; use "
|
||||
"boost::histogram::axis::get to obtain a reference "
|
||||
"of this axis type"));
|
||||
},
|
||||
a);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
decltype(auto) operator[](const int idx) const {
|
||||
// using visit here causes internal error in MSVC 2017
|
||||
const bool is_continuous = boost::apply_visitor(detail::is_continuous(),
|
||||
static_cast<const base_type&>(*this));
|
||||
return polymorphic_bin_view<variant>(idx, *this, is_continuous);
|
||||
}
|
||||
|
||||
bool operator==(const variant& rhs) const {
|
||||
return base_type::operator==(static_cast<const base_type&>(rhs));
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
bool operator==(const variant<Us...>& u) const {
|
||||
return visit([&u](const auto& x) { return u == x; }, *this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const T& t) const {
|
||||
// boost::variant::operator==(T) implemented only to fail, cannot use it
|
||||
auto tp = boost::relaxed_get<T>(this);
|
||||
return tp && *tp == t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const T& t) const {
|
||||
return !operator==(t);
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, unsigned);
|
||||
|
||||
template <typename Functor, typename Variant>
|
||||
friend auto visit(Functor&& f, Variant&& v)
|
||||
-> detail::visitor_return_type<Functor, Variant>;
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend T& get(variant<Us...>& v);
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend const T& get(const variant<Us...>& v);
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend T&& get(variant<Us...>&& v);
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend T* get(variant<Us...>* v);
|
||||
|
||||
template <typename T, typename... Us>
|
||||
friend const T* get(const variant<Us...>* v);
|
||||
}; // namespace histogram
|
||||
|
||||
template <typename Functor, typename Variant>
|
||||
auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type<Functor, Variant> {
|
||||
using R = detail::visitor_return_type<Functor, Variant>;
|
||||
return boost::apply_visitor(
|
||||
detail::functor_wrapper<Functor, R>(f),
|
||||
static_cast<detail::copy_qualifiers<Variant,
|
||||
typename detail::rm_cvref<Variant>::base_type>>(
|
||||
v));
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits, typename... Ts>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const variant<Ts...>& v) {
|
||||
visit(
|
||||
[&os](const auto& x) {
|
||||
using T = detail::rm_cvref<decltype(x)>;
|
||||
detail::static_if<detail::is_streamable<T>>(
|
||||
[&os](const auto& x) { os << x; },
|
||||
[](const auto&) {
|
||||
throw std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
" is not streamable"));
|
||||
},
|
||||
x);
|
||||
},
|
||||
v);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
T& get(variant<Us...>& v) {
|
||||
return boost::get<T>(static_cast<typename variant<Us...>::base_type&>(v));
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
const T& get(const variant<Us...>& v) {
|
||||
return boost::get<T>(static_cast<const typename variant<Us...>::base_type&>(v));
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
T&& get(variant<Us...>&& v) {
|
||||
return boost::get<T>(static_cast<typename variant<Us...>::base_type&&>(v));
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
T* get(variant<Us...>* v) {
|
||||
return boost::relaxed_get<T>(static_cast<typename variant<Us...>::base_type*>(v));
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
const T* get(const variant<Us...>* v) {
|
||||
return boost::relaxed_get<T>(static_cast<const typename variant<Us...>::base_type*>(v));
|
||||
}
|
||||
|
||||
// pass-through if T is an axis instead of a variant
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::rm_cvref<U>>,
|
||||
typename = detail::requires_same<T, detail::rm_cvref<U>>>
|
||||
U get(U&& u) {
|
||||
return std::forward<U>(u);
|
||||
}
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -10,12 +10,11 @@
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/histogram/axis/any.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -37,7 +36,7 @@ struct axes_equal_static_dynamic_impl {
|
||||
template <typename N>
|
||||
void operator()(N) const {
|
||||
using T = mp11::mp_at<Tuple, N>;
|
||||
auto tp = boost::relaxed_get<T>(&v[N::value]);
|
||||
auto tp = axis::get<T>(&v[N::value]);
|
||||
equal &= (tp && *tp == std::get<N::value>(t));
|
||||
}
|
||||
};
|
||||
@@ -60,7 +59,7 @@ struct axes_assign_static_dynamic_impl {
|
||||
template <typename N>
|
||||
void operator()(N) const {
|
||||
using T = mp11::mp_at<Tuple, N>;
|
||||
std::get<N::value>(t) = static_cast<const T&>(v[N::value]);
|
||||
std::get<N::value>(t) = axis::get<T>(v[N::value]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,42 +149,19 @@ void range_check(const std::tuple<Ts...>&) {
|
||||
static_assert(N < sizeof...(Ts), "index out of range");
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <int N, typename T>
|
||||
struct axis_at_impl {};
|
||||
template <typename T, int N>
|
||||
using axis_at = mp_at_c<T, is_static_container<T>::value * N>;
|
||||
|
||||
template <int N, typename... Ts>
|
||||
struct axis_at_impl<N, std::tuple<Ts...>> {
|
||||
using type = mp11::mp_at_c<std::tuple<Ts...>, N>;
|
||||
};
|
||||
|
||||
template <int N, typename Any, typename A>
|
||||
struct axis_at_impl<N, std::vector<Any, A>> {
|
||||
using type = Any;
|
||||
};
|
||||
template <int N, typename T,
|
||||
typename = requires_static_container<T>>
|
||||
auto axis_get(T&& axes) -> decltype(std::get<N>(std::forward<T>(axes))) {
|
||||
return std::get<N>(std::forward<T>(axes));
|
||||
}
|
||||
|
||||
template <int N, typename T>
|
||||
using axis_at = typename axis_at_impl<N, T>::type;
|
||||
|
||||
template <int N, typename... Ts>
|
||||
auto axis_get(std::tuple<Ts...>& axes) -> axis_at<N, std::tuple<Ts...>>& {
|
||||
return std::get<N>(axes);
|
||||
}
|
||||
|
||||
template <int N, typename... Ts>
|
||||
auto axis_get(const std::tuple<Ts...>& axes) -> const axis_at<N, std::tuple<Ts...>>& {
|
||||
return std::get<N>(axes);
|
||||
}
|
||||
|
||||
template <int N, typename Any, typename A>
|
||||
Any& axis_get(std::vector<Any, A>& axes) {
|
||||
return axes[N];
|
||||
}
|
||||
|
||||
template <int N, typename Any, typename A>
|
||||
const Any& axis_get(const std::vector<Any, A>& axes) {
|
||||
return axes[N];
|
||||
template <int N, typename T,
|
||||
typename = requires_axis_vector<T>>
|
||||
auto axis_get(T&& axes) -> decltype(std::forward<T>(axes)[N]) {
|
||||
return std::forward<T>(axes)[N];
|
||||
}
|
||||
|
||||
template <typename F, typename... Ts>
|
||||
@@ -193,22 +169,10 @@ void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
|
||||
mp11::tuple_for_each(axes, std::forward<F>(f));
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename Unary>
|
||||
struct unary_adaptor : public boost::static_visitor<void> {
|
||||
Unary&& unary;
|
||||
unary_adaptor(Unary&& u) : unary(std::forward<Unary>(u)) {}
|
||||
template <typename T>
|
||||
void operator()(const T& a) const {
|
||||
unary(a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename F, typename Any, typename A>
|
||||
void for_each_axis(const std::vector<Any, A>& axes, F&& f) {
|
||||
template <typename F, typename T, typename A>
|
||||
void for_each_axis(const std::vector<T, A>& axes, F&& f) {
|
||||
for (const auto& x : axes) {
|
||||
boost::apply_visitor(unary_adaptor<F>(std::forward<F>(f)), x);
|
||||
axis::visit(std::forward<F>(f), x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +181,7 @@ struct field_counter {
|
||||
std::size_t value = 1;
|
||||
template <typename T>
|
||||
void operator()(const T& t) {
|
||||
value *= t.shape();
|
||||
value *= axis::traits::extend(t);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -256,8 +220,8 @@ struct shape_collector {
|
||||
std::vector<unsigned>::iterator iter;
|
||||
shape_collector(std::vector<unsigned>::iterator i) : iter(i) {}
|
||||
template <typename T>
|
||||
void operator()(const T& a) {
|
||||
*iter++ = a.shape();
|
||||
void operator()(const T& t) {
|
||||
*iter++ = axis::traits::extend(t);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -359,8 +323,8 @@ template <std::size_t D, typename Axes, typename... Us>
|
||||
void indices_to_index(optional_index& idx, const Axes& axes, const int j,
|
||||
const Us... us) {
|
||||
const auto& a = axis_get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto a_size = static_cast<int>(a.size());
|
||||
const auto a_shape = static_cast<int>(axis::traits::extend(a));
|
||||
idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
indices_to_index<(D + 1)>(idx, axes, us...);
|
||||
@@ -375,8 +339,8 @@ void indices_to_index_iter(mp11::mp_size_t<N>, optional_index& idx,
|
||||
const std::tuple<Ts...>& axes, Iterator iter) {
|
||||
constexpr auto D = mp11::mp_size_t<sizeof...(Ts)>() - N;
|
||||
const auto& a = std::get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto a_size = static_cast<int>(a.size());
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = static_cast<int>(*iter);
|
||||
idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
@@ -387,8 +351,8 @@ template <typename Any, typename A, typename Iterator>
|
||||
void indices_to_index_iter(optional_index& idx, const std::vector<Any, A>& axes,
|
||||
Iterator iter) {
|
||||
for (const auto& a : axes) {
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto a_size = static_cast<int>(a.size());
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = static_cast<int>(*iter++);
|
||||
idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
@@ -403,8 +367,8 @@ void indices_to_index_get(mp11::mp_size_t<N>, optional_index& idx, const Axes& a
|
||||
const T& t) {
|
||||
constexpr std::size_t D = mp_size<T>() - N;
|
||||
const auto& a = axis_get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto a_size = static_cast<int>(a.size());
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = static_cast<int>(std::get<D>(t));
|
||||
idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
@@ -417,9 +381,10 @@ void args_to_index(optional_index&, const std::tuple<Ts...>&) noexcept {}
|
||||
template <std::size_t D, typename... Ts, typename U, typename... Us>
|
||||
void args_to_index(optional_index& idx, const std::tuple<Ts...>& axes, const U& u,
|
||||
const Us&... us) {
|
||||
const auto a_size = std::get<D>(axes).size();
|
||||
const auto a_shape = std::get<D>(axes).shape();
|
||||
const auto j = std::get<D>(axes).index(u);
|
||||
const auto& a = std::get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = a(u);
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
args_to_index<(D + 1)>(idx, axes, us...);
|
||||
}
|
||||
@@ -434,8 +399,8 @@ void args_to_index_iter(mp11::mp_size_t<N>, optional_index& idx,
|
||||
constexpr std::size_t D = sizeof...(Ts)-N;
|
||||
const auto& a = axis_get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto j = a.index(*iter);
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = a(*iter);
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
args_to_index_iter(mp11::mp_size_t<(N - 1)>(), idx, axes, ++iter);
|
||||
}
|
||||
@@ -448,38 +413,43 @@ template <std::size_t N, typename... Ts, typename T>
|
||||
void args_to_index_get(mp11::mp_size_t<N>, optional_index& idx,
|
||||
const std::tuple<Ts...>& axes, const T& t) {
|
||||
constexpr std::size_t D = mp_size<T>::value - N;
|
||||
const auto a_size = std::get<D>(axes).size();
|
||||
const auto a_shape = std::get<D>(axes).shape();
|
||||
const auto j = std::get<D>(axes).index(std::get<D>(t));
|
||||
const auto& a = std::get<D>(axes);
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = a(std::get<D>(t));
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t);
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
struct args_to_index_visitor : public boost::static_visitor<void> {
|
||||
struct args_to_index_visitor {
|
||||
optional_index& idx;
|
||||
const T& val;
|
||||
args_to_index_visitor(optional_index& i, const T& v) : idx(i), val(v) {}
|
||||
template <typename Axis>
|
||||
void operator()(const Axis& a) const {
|
||||
impl(std::is_convertible<T, typename Axis::value_type>(), a);
|
||||
template <typename U>
|
||||
void operator()(const U& a) const {
|
||||
using arg_type = mp11::mp_first<axis::traits::args<U>>;
|
||||
impl(std::is_convertible<T, arg_type>(), a);
|
||||
}
|
||||
|
||||
template <typename Axis>
|
||||
void impl(std::true_type, const Axis& a) const {
|
||||
template <typename U>
|
||||
void impl(std::true_type, const U& a) const {
|
||||
using arg_type = mp11::mp_first<axis::traits::args<U>>;
|
||||
const auto a_size = a.size();
|
||||
const auto a_shape = a.shape();
|
||||
const auto j = a.index(static_cast<typename Axis::value_type>(val));
|
||||
const auto a_shape = axis::traits::extend(a);
|
||||
const auto j = a(static_cast<arg_type>(val));
|
||||
linearize(idx, a_size, a_shape, j);
|
||||
}
|
||||
|
||||
template <typename Axis>
|
||||
void impl(std::false_type, const Axis&) const {
|
||||
template <typename U>
|
||||
void impl(std::false_type, const U&) const {
|
||||
using arg_type = mp11::mp_first<axis::traits::args<U>>;
|
||||
throw std::invalid_argument(detail::cat(
|
||||
"axis ", boost::typeindex::type_id<Axis>().pretty_name(), ": argument ",
|
||||
boost::typeindex::type_id<T>().pretty_name(), " not convertible to value_type ",
|
||||
boost::typeindex::type_id<typename Axis::value_type>().pretty_name()));
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(U) ),
|
||||
": cannot convert argument of type ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " to ",
|
||||
boost::core::demangled_name( BOOST_CORE_TYPEID(arg_type) )));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -490,7 +460,7 @@ void args_to_index(optional_index&, const std::vector<Any, A>&) {}
|
||||
template <std::size_t D, typename Any, typename A, typename U, typename... Us>
|
||||
void args_to_index(optional_index& idx, const std::vector<Any, A>& axes, const U& u,
|
||||
const Us&... us) {
|
||||
boost::apply_visitor(args_to_index_visitor<U>(idx, u), axes[D]);
|
||||
axis::visit(args_to_index_visitor<U>(idx, u), axes[D]);
|
||||
args_to_index<(D + 1)>(idx, axes, us...);
|
||||
}
|
||||
|
||||
@@ -499,7 +469,7 @@ void args_to_index_iter(optional_index& idx, const std::vector<Any, A>& axes,
|
||||
Iterator iter) {
|
||||
for (const auto& a : axes) {
|
||||
// iter could be a plain pointer, so we cannot use nested value_type here
|
||||
boost::apply_visitor(args_to_index_visitor<decltype(*iter)>(idx, *iter++), a);
|
||||
axis::visit(args_to_index_visitor<decltype(*iter)>(idx, *iter++), a);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,7 +482,7 @@ void args_to_index_get(mp11::mp_size_t<N>, optional_index& idx,
|
||||
const std::vector<Any, A>& axes, const T& t) {
|
||||
constexpr std::size_t D = mp_size<T>::value - N;
|
||||
using U = decltype(std::get<D>(t));
|
||||
boost::apply_visitor(args_to_index_visitor<U>(idx, std::get<D>(t)), axes[D]);
|
||||
axis::visit(args_to_index_visitor<U>(idx, std::get<D>(t)), axes[D]);
|
||||
args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t);
|
||||
}
|
||||
|
||||
@@ -544,7 +514,7 @@ optional_index call_impl(static_container_tag, const std::tuple<T1, T2, Ts...>&
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename... Ts, typename U>
|
||||
optional_index call_impl(dynamic_container_tag, const std::tuple<T1, T2, Ts...>& axes,
|
||||
optional_index call_impl(iterable_container_tag, const std::tuple<T1, T2, Ts...>& axes,
|
||||
const U& u) {
|
||||
dimension_check(axes, u.size());
|
||||
optional_index i;
|
||||
@@ -573,7 +543,7 @@ optional_index call_impl(static_container_tag, const std::vector<Any, A>& axes,
|
||||
}
|
||||
|
||||
template <typename Any, typename A, typename U>
|
||||
optional_index call_impl(dynamic_container_tag, const std::vector<Any, A>& axes,
|
||||
optional_index call_impl(iterable_container_tag, const std::vector<Any, A>& axes,
|
||||
const U& u) {
|
||||
if (axes.size() == 1) // do not unpack for 1d histograms, it is ambiguous
|
||||
return call_impl(no_container_tag(), axes, u);
|
||||
@@ -608,7 +578,7 @@ std::size_t at_impl(detail::static_container_tag, const A& axes, const U& u) {
|
||||
}
|
||||
|
||||
template <typename... Ts, typename U>
|
||||
std::size_t at_impl(detail::dynamic_container_tag, const std::tuple<Ts...>& axes,
|
||||
std::size_t at_impl(detail::iterable_container_tag, const std::tuple<Ts...>& axes,
|
||||
const U& u) {
|
||||
dimension_check(axes, std::distance(std::begin(u), std::end(u)));
|
||||
auto index = detail::optional_index();
|
||||
@@ -619,7 +589,7 @@ std::size_t at_impl(detail::dynamic_container_tag, const std::tuple<Ts...>& axes
|
||||
}
|
||||
|
||||
template <typename Any, typename A, typename U>
|
||||
std::size_t at_impl(detail::dynamic_container_tag, const std::vector<Any, A>& axes,
|
||||
std::size_t at_impl(detail::iterable_container_tag, const std::vector<Any, A>& axes,
|
||||
const U& u) {
|
||||
dimension_check(axes, std::distance(std::begin(u), std::end(u)));
|
||||
auto index = detail::optional_index();
|
||||
|
||||
@@ -91,6 +91,8 @@ typename std::allocator_traits<Allocator>::pointer create_buffer_from_iter(
|
||||
template <typename Allocator>
|
||||
void destroy_buffer(Allocator& a, typename std::allocator_traits<Allocator>::pointer p,
|
||||
std::size_t n) {
|
||||
if (!p)
|
||||
return;
|
||||
using AT = std::allocator_traits<Allocator>;
|
||||
auto it = p + n;
|
||||
const auto end = p;
|
||||
|
||||
74
include/boost/histogram/detail/compressed_pair.hpp
Normal file
74
include/boost/histogram/detail/compressed_pair.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2018 Hans Dembinski
|
||||
//
|
||||
// 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 BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_COMPRESSED_PAIR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <typename T1, typename T2, bool B>
|
||||
class compressed_pair_impl;
|
||||
|
||||
template <typename T1, typename T2>
|
||||
class compressed_pair_impl<T1, T2, true> : protected T2 {
|
||||
public:
|
||||
template <typename U1, typename U2>
|
||||
compressed_pair_impl(U1&& u1, U2&& u2)
|
||||
: T2(std::forward<U2>(u2)), first_(std::forward<U1>(u1)) {}
|
||||
template <typename U1>
|
||||
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
T1& first() { return first_; }
|
||||
T2& second() { return static_cast<T2&>(*this); }
|
||||
const T1& first() const { return first_; }
|
||||
const T2& second() const { return static_cast<const T2&>(*this); }
|
||||
|
||||
private:
|
||||
T1 first_;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
class compressed_pair_impl<T1, T2, false> {
|
||||
public:
|
||||
template <typename U1, typename U2>
|
||||
compressed_pair_impl(U1&& u1, U2&& u2)
|
||||
: first_(std::forward<U1>(u1)), second_(std::forward<U2>(u2)) {}
|
||||
template <typename U1>
|
||||
compressed_pair_impl(U1&& u1) : first_(std::forward<U1>(u1)) {}
|
||||
compressed_pair_impl() = default;
|
||||
|
||||
T1& first() { return first_; }
|
||||
T2& second() { return second_; }
|
||||
const T1& first() const { return first_; }
|
||||
const T2& second() const { return second_; }
|
||||
|
||||
private:
|
||||
T1 first_;
|
||||
T2 second_;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
void swap(compressed_pair_impl<Ts...>& a, compressed_pair_impl<Ts...>& b) {
|
||||
using std::swap;
|
||||
swap(a.first(), b.first());
|
||||
swap(a.second(), b.second());
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
using compressed_pair =
|
||||
compressed_pair_impl<T1, T2, (!std::is_final<T2>::value && std::is_empty<T2>::value)>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
@@ -35,19 +36,19 @@ struct index_cache : public std::unique_ptr<block_t[]> {
|
||||
struct dim_visitor {
|
||||
mutable std::size_t stride;
|
||||
mutable block_t* b;
|
||||
template <typename Axis>
|
||||
void operator()(const Axis& a) const noexcept {
|
||||
b->dim = dim_t{0, a.size(), stride};
|
||||
template <typename T>
|
||||
void operator()(const T& a) const noexcept {
|
||||
b->dim = dim_t{0, static_cast<int>(a.size()), stride};
|
||||
++b;
|
||||
stride *= a.shape();
|
||||
stride *= axis::traits::extend(a);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename H>
|
||||
void set(const H& h) {
|
||||
if (!(*this) || h.dim() != ptr_t::get()->state.dim) {
|
||||
ptr_t::reset(new block_t[h.dim() + 1]);
|
||||
ptr_t::get()->state.dim = h.dim();
|
||||
if (!(*this) || h.rank() != ptr_t::get()->state.dim) {
|
||||
ptr_t::reset(new block_t[h.rank() + 1]);
|
||||
ptr_t::get()->state.dim = h.rank();
|
||||
ptr_t::get()->state.idx = 0;
|
||||
}
|
||||
h.for_each_axis(dim_visitor{1, ptr_t::get() + 1});
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
|
||||
#include <boost/callable_traits/args.hpp>
|
||||
#include <boost/callable_traits/return_type.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
@@ -19,6 +22,62 @@ namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
using rm_cvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
template <class T>
|
||||
using mp_size = mp11::mp_size<rm_cvref<T>>;
|
||||
|
||||
template <typename T, unsigned N>
|
||||
using mp_at_c = mp11::mp_at_c<rm_cvref<T>, N>;
|
||||
|
||||
template <typename T1, typename T2>
|
||||
using copy_qualifiers = mp11::mp_if<
|
||||
std::is_rvalue_reference<T1>, T2&&,
|
||||
mp11::mp_if<std::is_lvalue_reference<T1>,
|
||||
mp11::mp_if<std::is_const<typename std::remove_reference<T1>::type>,
|
||||
const T2&, T2&>,
|
||||
mp11::mp_if<std::is_const<T1>, const T2, T2>>>;
|
||||
|
||||
template <typename S, typename L>
|
||||
using mp_set_union = mp11::mp_apply_q<mp11::mp_bind_front<mp11::mp_set_push_back, S>, L>;
|
||||
|
||||
template <typename L>
|
||||
using mp_last = mp11::mp_at_c<L, (mp_size<L>::value - 1)>;
|
||||
|
||||
template <typename T>
|
||||
using container_element_type = mp11::mp_first<rm_cvref<T>>;
|
||||
|
||||
template <typename T>
|
||||
using iterator_value_type =
|
||||
typename std::iterator_traits<T>::value_type;
|
||||
|
||||
template <typename T>
|
||||
using return_type = typename boost::callable_traits::return_type<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using args_type = mp11::mp_if<std::is_member_function_pointer<T>,
|
||||
mp11::mp_pop_front<boost::callable_traits::args_t<T>>,
|
||||
boost::callable_traits::args_t<T>>;
|
||||
|
||||
template <typename T, std::size_t N = 0>
|
||||
using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
|
||||
|
||||
template <typename F, typename V>
|
||||
using visitor_return_type =
|
||||
decltype(std::declval<F>()(std::declval<copy_qualifiers<V, mp_at_c<V, 0>>>()));
|
||||
|
||||
template <bool B, typename T, typename F, typename... Ts>
|
||||
constexpr decltype(auto) static_if_c(T&& t, F&& f, Ts&&... ts) {
|
||||
return std::get<(B ? 0 : 1)>(std::forward_as_tuple(
|
||||
std::forward<T>(t), std::forward<F>(f)))(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <typename B, typename... Ts>
|
||||
constexpr decltype(auto) static_if(Ts&&... ts) {
|
||||
return static_if_c<B::value>(std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
#define BOOST_HISTOGRAM_MAKE_SFINAE(name, cond) \
|
||||
template <typename U> \
|
||||
struct name##_impl { \
|
||||
@@ -36,46 +95,60 @@ namespace detail {
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_variance_support,
|
||||
(std::declval<T&>().value(), std::declval<T&>().variance()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, (std::declval<T&>().lower(0)));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, (std::declval<T&>().value(0)));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, (std::begin(std::declval<T&>())));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(
|
||||
has_method_options, (static_cast<axis::option_type>(std::declval<T&>().options())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval<T&>().metadata()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_transform, (&T::forward, &T::inverse));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container,
|
||||
(std::declval<T&>()[0], std::declval<T&>().size()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, (std::get<0>(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_castable_to_int, (static_cast<int>(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_string, (std::declval<T&>().c_str()));
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_equal_comparable,
|
||||
(std::declval<T&>() == std::declval<T&>()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_axis, (std::declval<T&>().size(), &T::operator()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_iterable, (std::begin(std::declval<T&>()),
|
||||
std::end(std::declval<T&>())));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(is_streamable,
|
||||
(std::declval<std::ostream&>() << std::declval<T&>()));
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
struct is_axis_variant_impl : std::false_type {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
using is_axis_variant = typename is_axis_variant_impl<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using is_axis_or_axis_variant = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_axis_vector = mp11::mp_all<is_random_access_container<T>,
|
||||
is_axis_or_axis_variant<container_element_type<rm_cvref<T>>>>;
|
||||
|
||||
struct static_container_tag {};
|
||||
struct dynamic_container_tag {};
|
||||
struct iterable_container_tag {};
|
||||
struct no_container_tag {};
|
||||
|
||||
template <typename T>
|
||||
using classify_container = typename std::conditional<
|
||||
is_static_container<T>::value, static_container_tag,
|
||||
typename std::conditional<(is_dynamic_container<T>::value &&
|
||||
!std::is_convertible<T, const char*>::value &&
|
||||
!is_string<T>::value),
|
||||
dynamic_container_tag, no_container_tag>::type>::type;
|
||||
|
||||
template <typename T,
|
||||
typename = decltype(std::declval<T&>().size(), std::declval<T&>().increase(0),
|
||||
std::declval<T&>()[0])>
|
||||
struct requires_storage {};
|
||||
|
||||
template <typename T, typename = decltype(*std::declval<T&>(), ++std::declval<T&>())>
|
||||
struct requires_iterator {};
|
||||
|
||||
template <typename T, typename = decltype(std::declval<T&>()[0])>
|
||||
struct requires_vector {};
|
||||
|
||||
template <typename T, typename = decltype(std::get<0>(std::declval<T&>()))>
|
||||
struct requires_tuple {};
|
||||
|
||||
template <typename T>
|
||||
using requires_axis = decltype(std::declval<T&>().size(), std::declval<T&>().shape(),
|
||||
std::declval<T&>().uoflow(), std::declval<T&>().label(),
|
||||
std::declval<T&>()[0]);
|
||||
is_iterable<T>::value, iterable_container_tag,
|
||||
typename std::conditional<is_static_container<T>::value, static_container_tag,
|
||||
no_container_tag>::type>::type;
|
||||
|
||||
namespace {
|
||||
struct bool_mask_impl {
|
||||
@@ -86,7 +159,7 @@ struct bool_mask_impl {
|
||||
b[Int::value] = v;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename... Ns>
|
||||
std::vector<bool> bool_mask(unsigned n, bool v) {
|
||||
@@ -95,28 +168,40 @@ std::vector<bool> bool_mask(unsigned n, bool v) {
|
||||
return b;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using rm_cv_ref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
// poor-mans concept checks
|
||||
template <typename T,
|
||||
typename = decltype(std::declval<T&>().size(), std::declval<T&>().increase(0),
|
||||
std::declval<T&>()[0])>
|
||||
struct requires_storage {};
|
||||
|
||||
template <class T>
|
||||
using mp_size = mp11::mp_size<rm_cv_ref<T>>;
|
||||
template <typename T, typename = decltype(*std::declval<T&>(), ++std::declval<T&>())>
|
||||
struct requires_iterator {};
|
||||
|
||||
template <typename T, unsigned D>
|
||||
using mp_at_c = mp11::mp_at_c<rm_cv_ref<T>, D>;
|
||||
template <typename T, typename = mp11::mp_if<is_iterable<T>, void>>
|
||||
struct requires_iterable {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
using copy_qualifiers = mp11::mp_if<
|
||||
std::is_rvalue_reference<T1>, T2&&,
|
||||
mp11::mp_if<std::is_lvalue_reference<T1>,
|
||||
mp11::mp_if<std::is_const<typename std::remove_reference<T1>::type>,
|
||||
const T2&, T2&>,
|
||||
mp11::mp_if<std::is_const<T1>, const T2, T2>>>;
|
||||
template <typename T, typename = mp11::mp_if<is_static_container<T>, void>>
|
||||
struct requires_static_container {};
|
||||
|
||||
template <typename S, typename L>
|
||||
using mp_set_union = mp11::mp_apply_q<mp11::mp_bind_front<mp11::mp_set_push_back, S>, L>;
|
||||
template <typename T, typename = mp11::mp_if<is_axis<T>, void>>
|
||||
struct requires_axis {};
|
||||
|
||||
template <typename L>
|
||||
using mp_last = mp11::mp_at_c<L, (mp11::mp_size<L>::value - 1)>;
|
||||
template <typename T,
|
||||
typename =
|
||||
mp11::mp_if_c<(is_axis<T>::value || is_axis_variant<T>::value), void>>
|
||||
struct requires_axis_or_axis_variant {};
|
||||
|
||||
template <typename T, typename U = container_element_type<T>,
|
||||
typename = mp11::mp_if_c<(is_random_access_container<T>::value &&
|
||||
(is_axis<U>::value || is_axis_variant<U>::value)),
|
||||
void>>
|
||||
struct requires_axis_vector {};
|
||||
|
||||
template <typename T, typename U, typename = mp11::mp_if<std::is_same<T, U>, void>>
|
||||
struct requires_same {};
|
||||
|
||||
template <typename T, typename = mp11::mp_if<is_transform<T>, void>>
|
||||
struct requires_transform {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/histogram/iterator.hpp>
|
||||
#include <boost/histogram/storage/adaptive_storage.hpp>
|
||||
#include <boost/histogram/storage/operators.hpp>
|
||||
#include <boost/histogram/storage/weight_counter.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
@@ -23,13 +24,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
// forward declaration for serialization
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
class access;
|
||||
}
|
||||
}
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
@@ -42,7 +36,6 @@ public:
|
||||
using storage_type = Storage;
|
||||
using element_type = typename storage_type::element_type;
|
||||
using const_reference = typename storage_type::const_reference;
|
||||
using scale_type = typename storage_type::scale_type;
|
||||
using const_iterator = iterator_over<histogram>;
|
||||
|
||||
histogram() = default;
|
||||
@@ -91,20 +84,18 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
histogram& operator*=(const scale_type& rhs) {
|
||||
storage_ *= rhs;
|
||||
histogram& operator*=(const double x) {
|
||||
storage_ *= x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
histogram& operator/=(const scale_type& rhs) {
|
||||
static_assert(std::is_floating_point<scale_type>::value,
|
||||
"division requires a floating point type");
|
||||
storage_ *= scale_type(1) / rhs;
|
||||
histogram& operator/=(const double x) {
|
||||
storage_ *= 1.0 / x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Number of axes (dimensions) of histogram
|
||||
std::size_t dim() const noexcept { return detail::axes_size(axes_); }
|
||||
std::size_t rank() const noexcept { return detail::axes_size(axes_); }
|
||||
|
||||
/// Total number of bins in the histogram (including underflow/overflow)
|
||||
std::size_t size() const noexcept { return storage_.size(); }
|
||||
@@ -114,34 +105,36 @@ public:
|
||||
|
||||
/// Get N-th axis (const version)
|
||||
template <std::size_t N>
|
||||
auto axis(mp11::mp_size_t<N>) const -> const detail::axis_at<N, axes_type>& {
|
||||
decltype(auto) axis(mp11::mp_size_t<N>) const {
|
||||
detail::range_check<N>(axes_);
|
||||
return detail::axis_get<N>(axes_);
|
||||
}
|
||||
|
||||
/// Get N-th axis
|
||||
template <std::size_t N>
|
||||
auto axis(mp11::mp_size_t<N>) -> detail::axis_at<N, axes_type>& {
|
||||
decltype(auto) axis(mp11::mp_size_t<N>) {
|
||||
detail::range_check<N>(axes_);
|
||||
return detail::axis_get<N>(axes_);
|
||||
}
|
||||
|
||||
/// Get first axis (convenience for 1-d histograms, const version)
|
||||
const detail::axis_at<0, axes_type>& axis() const { return axis(mp11::mp_size_t<0>()); }
|
||||
decltype(auto) axis() const { return axis(mp11::mp_size_t<0>()); }
|
||||
|
||||
/// Get first axis (convenience for 1-d histograms)
|
||||
detail::axis_at<0, axes_type>& axis() { return axis(mp11::mp_size_t<0>()); }
|
||||
decltype(auto) axis() { return axis(mp11::mp_size_t<0>()); }
|
||||
|
||||
/// Get N-th axis with runtime index (const version)
|
||||
template <typename U = axes_type, typename = detail::requires_vector<U>>
|
||||
const detail::axis_at<0, U>& axis(std::size_t i) const {
|
||||
template <typename U = axes_type,
|
||||
typename = detail::requires_axis_vector<U>>
|
||||
decltype(auto) axis(std::size_t i) const {
|
||||
BOOST_ASSERT_MSG(i < axes_.size(), "index out of range");
|
||||
return axes_[i];
|
||||
}
|
||||
|
||||
/// Get N-th axis with runtime index
|
||||
template <typename U = axes_type, typename = detail::requires_vector<U>>
|
||||
detail::axis_at<0, U>& axis(std::size_t i) {
|
||||
template <typename U = axes_type,
|
||||
typename = detail::requires_axis_vector<U>>
|
||||
decltype(auto) axis(std::size_t i) {
|
||||
BOOST_ASSERT_MSG(i < axes_.size(), "index out of range");
|
||||
return axes_[i];
|
||||
}
|
||||
@@ -162,7 +155,7 @@ public:
|
||||
|
||||
/// Fill histogram with a weight and a value tuple
|
||||
template <typename U, typename... Ts>
|
||||
void operator()(detail::weight_type<U>&& w, const Ts&... ts) {
|
||||
void operator()(weight_type<U>&& w, const Ts&... ts) {
|
||||
// case with one argument needs special treatment, specialized below
|
||||
const auto index = detail::call_impl(detail::no_container_tag(), axes_, ts...);
|
||||
if (index) storage_.add(*index, w);
|
||||
@@ -185,7 +178,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename U, typename T>
|
||||
void operator()(detail::weight_type<U>&& w, const T& t) {
|
||||
void operator()(weight_type<U>&& w, const T& t) {
|
||||
// check whether we need to unpack argument
|
||||
const auto index = detail::call_impl(detail::classify_container<T>(), axes_, t);
|
||||
if (index) storage_.add(*index, w);
|
||||
@@ -215,8 +208,8 @@ public:
|
||||
using HR = histogram<sub_axes_type, storage_type>;
|
||||
auto sub_axes = detail::make_sub_axes(axes_, N(), Ns()...);
|
||||
auto hr = HR(std::move(sub_axes), storage_type(storage_.get_allocator()));
|
||||
const auto b = detail::bool_mask<N, Ns...>(dim(), true);
|
||||
std::vector<unsigned> shape(dim());
|
||||
const auto b = detail::bool_mask<N, Ns...>(rank(), true);
|
||||
std::vector<unsigned> shape(rank());
|
||||
for_each_axis(detail::shape_collector(shape.begin()));
|
||||
detail::index_mapper m(shape, b);
|
||||
do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next());
|
||||
@@ -226,22 +219,22 @@ public:
|
||||
/// Returns a lower-dimensional histogram
|
||||
// precondition: sequence must be strictly ascending axis indices
|
||||
template <typename Iterator, typename U = axes_type,
|
||||
typename = detail::requires_vector<U>,
|
||||
typename = detail::requires_axis_vector<U>,
|
||||
typename = detail::requires_iterator<Iterator>>
|
||||
histogram reduce_to(Iterator begin, Iterator end) const {
|
||||
BOOST_ASSERT_MSG(std::is_sorted(begin, end, std::less_equal<decltype(*begin)>()),
|
||||
"integer sequence must be strictly ascending");
|
||||
BOOST_ASSERT_MSG(begin == end || static_cast<unsigned>(*(end - 1)) < dim(),
|
||||
BOOST_ASSERT_MSG(begin == end || static_cast<unsigned>(*(end - 1)) < rank(),
|
||||
"index out of range");
|
||||
auto sub_axes = histogram::axes_type(axes_.get_allocator());
|
||||
sub_axes.reserve(std::distance(begin, end));
|
||||
auto b = std::vector<bool>(dim(), false);
|
||||
auto b = std::vector<bool>(rank(), false);
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
sub_axes.push_back(axes_[*it]);
|
||||
b[*it] = true;
|
||||
}
|
||||
auto hr = histogram(std::move(sub_axes), storage_type(storage_.get_allocator()));
|
||||
std::vector<unsigned> shape(dim());
|
||||
std::vector<unsigned> shape(rank());
|
||||
for_each_axis(detail::shape_collector(shape.begin()));
|
||||
detail::index_mapper m(shape, b);
|
||||
do { hr.storage_.add(m.second, storage_[m.first]); } while (m.next());
|
||||
@@ -252,6 +245,9 @@ public:
|
||||
|
||||
const_iterator end() const noexcept { return const_iterator(*this, size()); }
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
axes_type axes_;
|
||||
Storage storage_;
|
||||
@@ -260,76 +256,69 @@ private:
|
||||
friend class histogram;
|
||||
template <typename H>
|
||||
friend class iterator_over;
|
||||
friend class python_access;
|
||||
friend class ::boost::serialization::access;
|
||||
template <typename Archive>
|
||||
void serialize(Archive&, unsigned);
|
||||
};
|
||||
|
||||
/// static type factory with custom storage type
|
||||
template <typename Storage, typename... Ts>
|
||||
histogram<std::tuple<detail::rm_cv_ref<Ts>...>, detail::rm_cv_ref<Storage>>
|
||||
make_static_histogram_with(Storage&& s, Ts&&... axis) {
|
||||
using H = histogram<std::tuple<detail::rm_cv_ref<Ts>...>, detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename H::axes_type(std::forward<Ts>(axis)...);
|
||||
return H(std::move(axes), std::forward<Storage>(s));
|
||||
template <typename S, typename T, typename... Ts, typename = detail::requires_axis<T>>
|
||||
histogram<
|
||||
std::tuple<detail::rm_cvref<T>, detail::rm_cvref<Ts>...>,
|
||||
detail::rm_cvref<S>
|
||||
>
|
||||
make_histogram_with(S&& s, T&& axis0, Ts&&... axis) {
|
||||
auto axes = std::make_tuple(std::forward<T>(axis0), std::forward<Ts>(axis)...);
|
||||
return histogram<decltype(axes), detail::rm_cvref<S>>(
|
||||
std::move(axes), std::forward<S>(s)
|
||||
);
|
||||
}
|
||||
|
||||
/// static type factory with standard storage type
|
||||
template <typename... Ts>
|
||||
histogram<std::tuple<detail::rm_cv_ref<Ts>...>> make_static_histogram(Ts&&... axis) {
|
||||
using S = typename histogram<std::tuple<detail::rm_cv_ref<Ts>...>>::storage_type;
|
||||
return make_static_histogram_with(S(), std::forward<Ts>(axis)...);
|
||||
template <typename T, typename... Ts, typename = detail::requires_axis<T>>
|
||||
auto make_histogram(T&& axis0, Ts&&... axis)
|
||||
-> decltype(make_histogram_with(default_storage(),
|
||||
std::forward<T>(axis0),
|
||||
std::forward<Ts>(axis)...))
|
||||
{
|
||||
return make_histogram_with(default_storage(),
|
||||
std::forward<T>(axis0),
|
||||
std::forward<Ts>(axis)...);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename S, typename Any>
|
||||
using srebind = typename std::allocator_traits<
|
||||
typename rm_cv_ref<S>::allocator_type>::template rebind_alloc<Any>;
|
||||
/// dynamic type factory from vector-like with custom storage type
|
||||
template <typename S, typename T,
|
||||
typename = detail::requires_axis_vector<T>>
|
||||
histogram<detail::rm_cvref<T>, detail::rm_cvref<S>>
|
||||
make_histogram_with(S&& s, T&& t) {
|
||||
return histogram<detail::rm_cvref<T>, detail::rm_cvref<S>>(
|
||||
std::forward<T>(t), std::forward<S>(s)
|
||||
);
|
||||
}
|
||||
|
||||
/// dynamic type factory with custom storage type
|
||||
template <typename Any = axis::any_std, typename Storage, typename T, typename... Ts>
|
||||
histogram<std::vector<Any, detail::srebind<Storage, Any>>, detail::rm_cv_ref<Storage>>
|
||||
make_dynamic_histogram_with(Storage&& s, T&& axis0, Ts&&... axis) {
|
||||
using H = histogram<std::vector<Any, detail::srebind<Storage, Any>>,
|
||||
detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename H::axes_type(
|
||||
{Any(std::forward<T>(axis0)), Any(std::forward<Ts>(axis))...}, s.get_allocator());
|
||||
return H(std::move(axes), std::forward<Storage>(s));
|
||||
/// dynamic type factory from vector-like with standard storage type
|
||||
template <typename T,
|
||||
typename = detail::requires_axis_vector<T>>
|
||||
auto make_histogram(T&& t)
|
||||
-> decltype(make_histogram_with(default_storage(), std::forward<T>(t)))
|
||||
{
|
||||
return make_histogram_with(default_storage(), std::forward<T>(t));
|
||||
}
|
||||
|
||||
/// dynamic type factory with standard storage type
|
||||
template <typename Any = axis::any_std, typename T, typename... Ts>
|
||||
histogram<std::vector<Any>> make_dynamic_histogram(T&& axis0, Ts&&... axis) {
|
||||
using S = typename histogram<std::vector<Any>>::storage_type;
|
||||
return make_dynamic_histogram_with<Any>(S(), std::forward<T>(axis0),
|
||||
std::forward<Ts>(axis)...);
|
||||
}
|
||||
|
||||
/// dynamic type factory with custom storage type
|
||||
/// dynamic type factory from iterator range with custom storage type
|
||||
template <typename Storage, typename Iterator,
|
||||
typename = detail::requires_iterator<Iterator>>
|
||||
histogram<std::vector<typename Iterator::value_type,
|
||||
detail::srebind<Storage, typename Iterator::value_type>>,
|
||||
detail::rm_cv_ref<Storage>>
|
||||
make_dynamic_histogram_with(Storage&& s, Iterator begin, Iterator end) {
|
||||
using H =
|
||||
histogram<std::vector<typename Iterator::value_type,
|
||||
detail::srebind<Storage, typename Iterator::value_type>>,
|
||||
detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename H::axes_type(s.get_allocator());
|
||||
axes.reserve(std::distance(begin, end));
|
||||
while (begin != end) axes.emplace_back(*begin++);
|
||||
return H(std::move(axes), std::forward<Storage>(s));
|
||||
histogram<std::vector<detail::iterator_value_type<Iterator>>,
|
||||
detail::rm_cvref<Storage>>
|
||||
make_histogram_with(Storage&& s, Iterator begin, Iterator end) {
|
||||
using T = detail::iterator_value_type<Iterator>;
|
||||
auto axes = std::vector<T>(begin, end);
|
||||
return make_histogram_with(std::forward<Storage>(s), std::move(axes));
|
||||
}
|
||||
|
||||
/// dynamic type factory with standard storage type
|
||||
/// dynamic type factory from iterator range with standard storage type
|
||||
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
|
||||
histogram<std::vector<typename Iterator::value_type>> make_dynamic_histogram(
|
||||
Iterator begin, Iterator end) {
|
||||
using S = typename histogram<std::vector<typename Iterator::value_type>>::storage_type;
|
||||
return make_dynamic_histogram_with(S(), begin, end);
|
||||
auto make_histogram(Iterator begin, Iterator end)
|
||||
-> decltype(make_histogram_with(default_storage(), begin, end))
|
||||
{
|
||||
return make_histogram_with(default_storage(), begin, end);
|
||||
}
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
@@ -8,54 +8,62 @@
|
||||
#define BOOST_HISTOGRAM_HISTOGRAM_FWD_HPP
|
||||
|
||||
#include <memory> // for std::allocator
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace axis {
|
||||
struct empty_metadata_type {};
|
||||
|
||||
enum class option_type {
|
||||
none = 0,
|
||||
overflow = 1,
|
||||
underflow_and_overflow = 2,
|
||||
};
|
||||
|
||||
namespace transform {
|
||||
template <typename T = double>
|
||||
struct identity;
|
||||
template <typename T = double>
|
||||
struct log;
|
||||
template <typename T = double>
|
||||
struct sqrt;
|
||||
template <typename T = double>
|
||||
struct pow;
|
||||
} // namespace transform
|
||||
|
||||
template <typename Transform = transform::identity, typename T = double,
|
||||
typename Allocator = std::allocator<char>>
|
||||
template <typename Transform = transform::identity<double>,
|
||||
typename MetaData = std::string>
|
||||
class regular;
|
||||
template <typename T = double, typename Allocator = std::allocator<char>>
|
||||
|
||||
template <typename RealType = double, typename MetaData = std::string>
|
||||
class circular;
|
||||
template <typename T = double, typename Allocator = std::allocator<char>>
|
||||
|
||||
template <typename RealType = double, typename Allocator = std::allocator<RealType>,
|
||||
typename MetaData = std::string>
|
||||
class variable;
|
||||
template <typename T = int, typename Allocator = std::allocator<char>>
|
||||
|
||||
template <typename IntType = double, typename MetaData = std::string>
|
||||
class integer;
|
||||
template <typename T = int, typename Allocator = std::allocator<char>>
|
||||
|
||||
template <typename T = int, typename Allocator = std::allocator<T>,
|
||||
typename MetaData = std::string>
|
||||
class category;
|
||||
|
||||
template <typename... Ts>
|
||||
class any;
|
||||
using any_std =
|
||||
any<regular<transform::identity, double, std::allocator<char>>,
|
||||
regular<transform::log, double, std::allocator<char>>,
|
||||
regular<transform::sqrt, double, std::allocator<char>>,
|
||||
regular<transform::pow, double, std::allocator<char>>,
|
||||
circular<double, std::allocator<char>>, variable<double, std::allocator<char>>,
|
||||
integer<int, std::allocator<char>>, category<int, std::allocator<char>>,
|
||||
category<std::string, std::allocator<char>>>;
|
||||
|
||||
class variant;
|
||||
} // namespace axis
|
||||
|
||||
template <typename Allocator = std::allocator<char>>
|
||||
struct adaptive_storage;
|
||||
template <typename T, typename ScaleType = T, typename Allocator = std::allocator<T>>
|
||||
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
struct array_storage;
|
||||
|
||||
template <class Axes = std::vector<axis::any_std>, class Storage = adaptive_storage<>>
|
||||
class histogram;
|
||||
using default_storage = adaptive_storage<>;
|
||||
|
||||
template <class Axes, class Storage = default_storage>
|
||||
class histogram;
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
cache_.reset();
|
||||
}
|
||||
|
||||
std::size_t dim() const noexcept { return histogram_.dim(); }
|
||||
std::size_t rank() const noexcept { return histogram_.rank(); }
|
||||
|
||||
int idx(std::size_t dim = 0) const noexcept {
|
||||
if (!cache_) { cache_.set(histogram_); }
|
||||
|
||||
@@ -20,8 +20,8 @@ template <typename OStream>
|
||||
struct axis_ostream_visitor {
|
||||
OStream& os_;
|
||||
explicit axis_ostream_visitor(OStream& os) : os_(os) {}
|
||||
template <typename Axis>
|
||||
void operator()(const Axis& a) const {
|
||||
template <typename T>
|
||||
void operator()(const T& a) const {
|
||||
os_ << "\n " << a << ",";
|
||||
}
|
||||
};
|
||||
@@ -33,7 +33,7 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
using OS = std::basic_ostream<CharT, Traits>;
|
||||
os << "histogram(";
|
||||
h.for_each_axis(detail::axis_ostream_visitor<OS>(os));
|
||||
os << (h.dim() ? "\n)" : ")");
|
||||
os << (h.rank() ? "\n)" : ")");
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,40 +7,53 @@
|
||||
#ifndef BOOST_HISTOGRAM_SERIALIZATION_HPP
|
||||
#define BOOST_HISTOGRAM_SERIALIZATION_HPP
|
||||
|
||||
#include <boost/container/string.hpp>
|
||||
#include <boost/histogram/axis/any.hpp>
|
||||
#include <boost/histogram/axis/base.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/storage/adaptive_storage.hpp>
|
||||
#include <boost/histogram/storage/array_storage.hpp>
|
||||
#include <boost/histogram/storage/weight_counter.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/variant.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
/** \file boost/histogram/serialization.hpp
|
||||
* \brief Defines the serialization functions, to use with boost.serialize.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace std {
|
||||
template <class Archive, typename... Ts>
|
||||
void serialize(Archive& ar, tuple<Ts...>& t, unsigned /* version */) {
|
||||
::boost::mp11::tuple_for_each(t, [&ar](auto& x) { ar& x; });
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
template <typename RealType>
|
||||
template <class Archive>
|
||||
void weight_counter<RealType>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& w;
|
||||
ar& w2;
|
||||
}
|
||||
|
||||
template <class Archive, typename T, typename A>
|
||||
void serialize(Archive& ar, array_storage<T, A>& s, unsigned /* version */) {
|
||||
ar& s.buffer;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Archive>
|
||||
struct serialize_t {
|
||||
Archive& ar_;
|
||||
explicit serialize_t(Archive& ar) : ar_(ar) {}
|
||||
template <typename T>
|
||||
void operator()(T& t) const {
|
||||
ar_& t;
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer {
|
||||
template <typename T, typename Buffer, typename Archive>
|
||||
void operator()(T*, Buffer& b, Archive& ar) {
|
||||
@@ -56,21 +69,8 @@ struct serializer {
|
||||
if (Archive::is_loading::value) { b.ptr = nullptr; }
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename RealType>
|
||||
template <class Archive>
|
||||
void weight_counter<RealType>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& w;
|
||||
ar& w2;
|
||||
}
|
||||
|
||||
template <class Archive, typename T, typename A>
|
||||
void serialize(Archive& ar, array_storage<T, A>& s, unsigned /* version */) {
|
||||
ar& s.buffer;
|
||||
}
|
||||
|
||||
template <class Archive, typename A>
|
||||
void serialize(Archive& ar, adaptive_storage<A>& s, unsigned /* version */) {
|
||||
using S = adaptive_storage<A>;
|
||||
@@ -80,113 +80,85 @@ void serialize(Archive& ar, adaptive_storage<A>& s, unsigned /* version */) {
|
||||
S::apply(detail::serializer(), s.buffer, ar);
|
||||
}
|
||||
|
||||
template <typename A, typename S>
|
||||
template <class Archive>
|
||||
void histogram<A, S>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& axes_;
|
||||
ar& storage_;
|
||||
}
|
||||
|
||||
namespace axis {
|
||||
|
||||
template <class Archive>
|
||||
void base::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& size_;
|
||||
ar& shape_;
|
||||
void serialize(Archive&, empty_metadata_type&, unsigned /* version */) {} // noop
|
||||
|
||||
template <typename M>
|
||||
template <class Archive>
|
||||
void base<M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& size_meta_.first();
|
||||
ar& size_meta_.second();
|
||||
ar& opt_;
|
||||
}
|
||||
|
||||
template <class A>
|
||||
template <typename T>
|
||||
template <class Archive>
|
||||
void labeled_base<A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<base>(*this);
|
||||
auto size = label_.size();
|
||||
ar& size;
|
||||
if (Archive::is_loading::value) { label_.resize(size); }
|
||||
ar& serialization::make_array(label_.data(), size);
|
||||
}
|
||||
|
||||
namespace transform {
|
||||
template <class Archive>
|
||||
void pow::serialize(Archive& ar, unsigned /* version */) {
|
||||
void transform::pow<T>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& power;
|
||||
}
|
||||
} // namespace transform
|
||||
|
||||
template <typename T, typename U, typename A>
|
||||
template <typename T, typename M>
|
||||
template <class Archive>
|
||||
void regular<T, U, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
ar& boost::serialization::base_object<T>(*this);
|
||||
void regular<T, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& static_cast<transform_type&>(*this);
|
||||
ar& min_;
|
||||
ar& delta_;
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
template <typename R, typename M>
|
||||
template <class Archive>
|
||||
void circular<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
void circular<R, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& phase_;
|
||||
ar& perimeter_;
|
||||
ar& delta_;
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
template <typename R, typename A, typename M>
|
||||
template <class Archive>
|
||||
void variable<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
if (Archive::is_loading::value) { this->~variable(); }
|
||||
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
|
||||
if (Archive::is_loading::value) {
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer(a, nx());
|
||||
}
|
||||
|
||||
ar& boost::serialization::make_array(x_, base_type::size() + 1);
|
||||
void variable<R, A, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
// destroy must happen before base serialization with old size
|
||||
if (Archive::is_loading::value) detail::destroy_buffer(x_.second(), x_.first(), nx());
|
||||
ar& static_cast<base_type&>(*this);
|
||||
if (Archive::is_loading::value)
|
||||
x_.first() = boost::histogram::detail::create_buffer(x_.second(), nx());
|
||||
ar& boost::serialization::make_array(x_.first(), nx());
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
template <typename I, typename M>
|
||||
template <class Archive>
|
||||
void integer<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
void integer<I, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& static_cast<base_type&>(*this);
|
||||
ar& min_;
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
template <typename V, typename A, typename M>
|
||||
template <class Archive>
|
||||
void category<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
if (Archive::is_loading::value) { this->~category(); }
|
||||
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
|
||||
if (Archive::is_loading::value) {
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
x_ = boost::histogram::detail::create_buffer(a, nx());
|
||||
}
|
||||
|
||||
ar& boost::serialization::make_array(x_, base_type::size());
|
||||
void category<V, A, M>::serialize(Archive& ar, unsigned /* version */) {
|
||||
// destroy must happen before base serialization with old size
|
||||
if (Archive::is_loading::value)
|
||||
detail::destroy_buffer(x_.second(), x_.first(), base_type::size());
|
||||
ar& static_cast<base_type&>(*this);
|
||||
if (Archive::is_loading::value)
|
||||
x_.first() = boost::histogram::detail::create_buffer(x_.second(), base_type::size());
|
||||
ar& boost::serialization::make_array(x_.first(), base_type::size());
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <class Archive>
|
||||
void any<Ts...>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& boost::serialization::base_object<boost::variant<Ts...>>(*this);
|
||||
void variant<Ts...>::serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& static_cast<base_type&>(*this);
|
||||
}
|
||||
|
||||
} // namespace axis
|
||||
|
||||
namespace {
|
||||
template <class Archive, typename... Ts>
|
||||
void serialize_axes(Archive& ar, std::tuple<Ts...>& axes) {
|
||||
detail::serialize_t<Archive> sh(ar);
|
||||
mp11::tuple_for_each(axes, sh);
|
||||
}
|
||||
|
||||
template <class Archive, typename Any, typename A>
|
||||
void serialize_axes(Archive& ar, std::vector<Any, A>& axes) {
|
||||
ar& axes;
|
||||
}
|
||||
}
|
||||
|
||||
template <class A, class S>
|
||||
template <class Archive>
|
||||
void histogram<A, S>::serialize(Archive& ar, unsigned /* version */) {
|
||||
serialize_axes(ar, axes_);
|
||||
ar& storage_;
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -9,15 +9,21 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/histogram/detail/buffer.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/storage/weight_counter.hpp>
|
||||
#include <boost/histogram/weight.hpp>
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
// warning-ignore required in Boost-1.66 for cpp_int.hpp:822
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -45,8 +51,36 @@ bool safe_assign(T& t, const U& u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename B>
|
||||
struct make_unsigned_impl;
|
||||
|
||||
template <typename T>
|
||||
struct make_unsigned_impl<T, std::true_type> {
|
||||
using type = typename std::make_unsigned<T>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct make_unsigned_impl<T, std::false_type> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using make_unsigned =
|
||||
typename make_unsigned_impl<T, typename std::is_signed<T>::type>::type;
|
||||
|
||||
template <typename T, typename U>
|
||||
bool safe_radd(T& t, const U& u) {
|
||||
BOOST_ASSERT(t >= 0);
|
||||
BOOST_ASSERT(u >= 0);
|
||||
using V = make_unsigned<U>;
|
||||
// static_cast converts back from signed to unsigned integer
|
||||
if (static_cast<T>(std::numeric_limits<T>::max() - t) < static_cast<V>(u)) return false;
|
||||
t += static_cast<T>(u); // static_cast to suppress conversion warning
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool safe_radd(T& t, const boost::multiprecision::number<U>& u) {
|
||||
BOOST_ASSERT(t >= 0);
|
||||
BOOST_ASSERT(u >= 0);
|
||||
// static_cast converts back from signed to unsigned integer
|
||||
@@ -54,7 +88,7 @@ bool safe_radd(T& t, const U& u) {
|
||||
t += static_cast<T>(u); // static_cast to suppress conversion warning
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <class Allocator>
|
||||
struct adaptive_storage {
|
||||
@@ -64,23 +98,16 @@ struct adaptive_storage {
|
||||
"adaptive_storage requires allocator with trivial pointer type");
|
||||
|
||||
using allocator_type = Allocator;
|
||||
using element_type = weight_counter<double>;
|
||||
using scale_type = double;
|
||||
using const_reference = element_type;
|
||||
using element_type = double;
|
||||
using const_reference = double;
|
||||
|
||||
using wcount = weight_counter<double>;
|
||||
using mp_int = boost::multiprecision::number<
|
||||
boost::multiprecision::cpp_int_backend<
|
||||
0, 0, boost::multiprecision::signed_magnitude,
|
||||
boost::multiprecision::unchecked,
|
||||
using mp_int = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
|
||||
0, 0, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked,
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<
|
||||
boost::multiprecision::limb_type
|
||||
>
|
||||
>
|
||||
>;
|
||||
boost::multiprecision::limb_type>>>;
|
||||
|
||||
using types = mp11::mp_list<void, uint8_t, uint16_t, uint32_t,uint64_t,
|
||||
mp_int, wcount>;
|
||||
using types =
|
||||
mp11::mp_list<void, uint8_t, uint16_t, uint32_t, uint64_t, mp_int, double>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr char type_index() {
|
||||
@@ -92,27 +119,28 @@ struct adaptive_storage {
|
||||
char type;
|
||||
std::size_t size;
|
||||
void* ptr;
|
||||
|
||||
buffer_type(std::size_t s = 0, const allocator_type& a = allocator_type())
|
||||
: alloc(a), type(0), size(s), ptr(nullptr) {}
|
||||
|
||||
template <typename T, typename U>
|
||||
T* create_impl(T*, const U* init) {
|
||||
using alloc_type = typename std::allocator_traits<
|
||||
allocator_type>::template rebind_alloc<T>;
|
||||
using alloc_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
|
||||
alloc_type a(alloc); // rebind allocator
|
||||
return init ? detail::create_buffer_from_iter(a, size, init) :
|
||||
detail::create_buffer(a, size, 0);
|
||||
return init ? detail::create_buffer_from_iter(a, size, init)
|
||||
: detail::create_buffer(a, size, 0);
|
||||
}
|
||||
|
||||
template <typename U = mp_int>
|
||||
mp_int* create_impl(mp_int*, const U* init) {
|
||||
using alloc_type = typename std::allocator_traits<
|
||||
allocator_type>::template rebind_alloc<mp_int>;
|
||||
using alloc_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<mp_int>;
|
||||
alloc_type a(alloc); // rebound allocator for buffer
|
||||
// mp_int has no ctor with an allocator instance, cannot pass state :(
|
||||
// typename mp_int::backend_type::allocator_type a2(alloc);
|
||||
return init ? detail::create_buffer_from_iter(a, size, init) :
|
||||
detail::create_buffer(a, size, 0);
|
||||
return init ? detail::create_buffer_from_iter(a, size, init)
|
||||
: detail::create_buffer(a, size, 0);
|
||||
}
|
||||
|
||||
void* create_impl(void*, const void* init) {
|
||||
@@ -133,11 +161,29 @@ struct adaptive_storage {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename B, typename... Ts>
|
||||
static decltype(auto) apply(F&& f, B&& b, Ts&&... ts) {
|
||||
// this is intentionally not a switch, the if-chain is faster in benchmarks
|
||||
if (b.type == type_index<uint8_t>())
|
||||
return f(reinterpret_cast<uint8_t*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint16_t>())
|
||||
return f(reinterpret_cast<uint16_t*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint32_t>())
|
||||
return f(reinterpret_cast<uint32_t*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint64_t>())
|
||||
return f(reinterpret_cast<uint64_t*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<mp_int>())
|
||||
return f(reinterpret_cast<mp_int*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<double>())
|
||||
return f(reinterpret_cast<double*>(b.ptr), b, std::forward<Ts>(ts)...);
|
||||
// b.type == 0 is intentionally the last in the chain,
|
||||
// because it is rarely triggered
|
||||
return f(b.ptr, b, std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
~adaptive_storage() { apply(destroyer(), buffer); }
|
||||
|
||||
adaptive_storage(const adaptive_storage& o) {
|
||||
apply(replacer(), o.buffer, buffer);
|
||||
}
|
||||
adaptive_storage(const adaptive_storage& o) { apply(replacer(), o.buffer, buffer); }
|
||||
|
||||
adaptive_storage& operator=(const adaptive_storage& o) {
|
||||
if (this != &o) { apply(replacer(), o.buffer, buffer); }
|
||||
@@ -157,8 +203,8 @@ struct adaptive_storage {
|
||||
|
||||
template <typename S, typename = detail::requires_storage<S>>
|
||||
explicit adaptive_storage(const S& s) : buffer(s.size(), s.get_allocator()) {
|
||||
buffer.set(buffer.template create<wcount>());
|
||||
auto it = static_cast<wcount*>(buffer.ptr);
|
||||
buffer.set(buffer.template create<double>());
|
||||
auto it = static_cast<double*>(buffer.ptr);
|
||||
const auto end = it + size();
|
||||
std::size_t i = 0;
|
||||
while (it != end) *it++ = s[i++];
|
||||
@@ -200,10 +246,14 @@ struct adaptive_storage {
|
||||
apply(adder(), buffer, i, x);
|
||||
}
|
||||
|
||||
const_reference operator[](std::size_t i) const {
|
||||
return apply(getter(), buffer, i);
|
||||
template <typename T>
|
||||
void add(std::size_t i, const weight_type<T>& x) {
|
||||
BOOST_ASSERT(i < size());
|
||||
apply(adder(), buffer, i, x.value);
|
||||
}
|
||||
|
||||
const_reference operator[](std::size_t i) const { return apply(getter(), buffer, i); }
|
||||
|
||||
bool operator==(const adaptive_storage& o) const {
|
||||
if (size() != o.size()) return false;
|
||||
return apply(comparer(), buffer, o.buffer);
|
||||
@@ -251,8 +301,8 @@ struct adaptive_storage {
|
||||
struct destroyer {
|
||||
template <typename T, typename Buffer>
|
||||
void operator()(T* tp, Buffer& b) {
|
||||
using alloc_type = typename std::allocator_traits<
|
||||
allocator_type>::template rebind_alloc<T>;
|
||||
using alloc_type =
|
||||
typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
|
||||
alloc_type a(b.alloc); // rebind allocator
|
||||
detail::destroy_buffer(a, tp, b.size);
|
||||
}
|
||||
@@ -261,32 +311,6 @@ struct adaptive_storage {
|
||||
void operator()(void*, Buffer&) {}
|
||||
};
|
||||
|
||||
template <typename F, typename B, typename... Ts>
|
||||
static typename std::result_of<F(void*, B&&, Ts&&...)>::type apply(F&& f, B&& b, Ts&&... ts) {
|
||||
// this is intentionally not a switch, the if-chain is faster in benchmarks
|
||||
if (b.type == type_index<uint8_t>())
|
||||
return f(reinterpret_cast<uint8_t*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint16_t>())
|
||||
return f(reinterpret_cast<uint16_t*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint32_t>())
|
||||
return f(reinterpret_cast<uint32_t*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<uint64_t>())
|
||||
return f(reinterpret_cast<uint64_t*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<mp_int>())
|
||||
return f(reinterpret_cast<mp_int*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
if (b.type == type_index<wcount>())
|
||||
return f(reinterpret_cast<wcount*>(b.ptr), std::forward<B>(b),
|
||||
std::forward<Ts>(ts)...);
|
||||
// b.type == 0 is intentionally the last in the chain, because it is rarely
|
||||
// triggered
|
||||
return f(b.ptr, std::forward<B>(b), std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
struct replacer {
|
||||
template <typename T, typename OBuffer, typename Buffer>
|
||||
void operator()(T* optr, const OBuffer& ob, Buffer& b) {
|
||||
@@ -333,14 +357,15 @@ struct adaptive_storage {
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
void operator()(wcount* tp, Buffer&, std::size_t i) {
|
||||
void operator()(double* tp, Buffer&, std::size_t i) {
|
||||
++tp[i];
|
||||
}
|
||||
};
|
||||
|
||||
struct adder {
|
||||
template <typename Buffer, typename U>
|
||||
void if_U_is_integral(std::true_type, mp_int* tp, Buffer&, std::size_t i, const U& x) {
|
||||
void if_U_is_integral(std::true_type, mp_int* tp, Buffer&, std::size_t i,
|
||||
const U& x) {
|
||||
tp[i] += static_cast<mp_int>(x);
|
||||
}
|
||||
|
||||
@@ -357,17 +382,17 @@ struct adaptive_storage {
|
||||
|
||||
template <typename T, typename Buffer, typename U>
|
||||
void if_U_is_integral(std::false_type, T* tp, Buffer& b, std::size_t i, const U& x) {
|
||||
auto ptr = b.template create<wcount>(tp);
|
||||
auto ptr = b.template create<double>(tp);
|
||||
destroyer()(tp, b);
|
||||
b.set(ptr);
|
||||
operator()(static_cast<wcount*>(b.ptr), b, i, x);
|
||||
operator()(static_cast<double*>(b.ptr), b, i, x);
|
||||
}
|
||||
|
||||
template <typename T, typename Buffer, typename U>
|
||||
void operator()(T* tp, Buffer& b, std::size_t i, const U& x) {
|
||||
if_U_is_integral(std::integral_constant<bool,
|
||||
(std::is_integral<U>::value ||
|
||||
std::is_same<U, mp_int>::value)>(), tp, b, i, x);
|
||||
if_U_is_integral(
|
||||
mp11::mp_bool<(std::is_integral<U>::value || std::is_same<U, mp_int>::value)>(),
|
||||
tp, b, i, x);
|
||||
}
|
||||
|
||||
template <typename Buffer, typename U>
|
||||
@@ -378,12 +403,12 @@ struct adaptive_storage {
|
||||
}
|
||||
|
||||
template <typename Buffer, typename U>
|
||||
void operator()(wcount* tp, Buffer&, std::size_t i, const U& x) {
|
||||
void operator()(double* tp, Buffer&, std::size_t i, const U& x) {
|
||||
tp[i] += x;
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
void operator()(wcount* tp, Buffer&, std::size_t i, const mp_int& x) {
|
||||
void operator()(double* tp, Buffer&, std::size_t i, const mp_int& x) {
|
||||
tp[i] += static_cast<double>(x);
|
||||
}
|
||||
};
|
||||
@@ -400,22 +425,35 @@ struct adaptive_storage {
|
||||
|
||||
struct getter {
|
||||
template <typename T, typename Buffer>
|
||||
wcount operator()(T* tp, Buffer&, std::size_t i) {
|
||||
return static_cast<wcount>(tp[i]);
|
||||
double operator()(T* tp, Buffer&, std::size_t i) {
|
||||
return static_cast<double>(tp[i]);
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
wcount operator()(void*, Buffer&, std::size_t) {
|
||||
return static_cast<wcount>(0);
|
||||
double operator()(void*, Buffer&, std::size_t) {
|
||||
return 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
// precondition: buffers already have same size
|
||||
struct comparer {
|
||||
struct inner {
|
||||
struct cmp {
|
||||
template <typename T, typename U>
|
||||
bool operator()(const T& t, const U& u) {
|
||||
return t == u;
|
||||
}
|
||||
bool operator()(const mp_int& t, const double& u) {
|
||||
return static_cast<double>(t) == u;
|
||||
}
|
||||
bool operator()(const double& t, const mp_int& u) {
|
||||
return t == static_cast<double>(u);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename U, typename OBuffer, typename T>
|
||||
bool operator()(const U* optr, const OBuffer& ob, const T* tp) {
|
||||
return std::equal(optr, optr + ob.size, tp);
|
||||
return std::equal(optr, optr + ob.size, tp, cmp());
|
||||
}
|
||||
|
||||
template <typename U, typename OBuffer>
|
||||
@@ -444,17 +482,25 @@ struct adaptive_storage {
|
||||
struct multiplier {
|
||||
template <typename T, typename Buffer>
|
||||
void operator()(T* tp, Buffer& b, const double x) {
|
||||
auto ptr = b.template create<wcount>(tp);
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244) // possible loss of data
|
||||
#endif
|
||||
// potential lossy conversion that cannot be avoided
|
||||
auto ptr = b.template create<double>(tp);
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
destroyer()(tp, b);
|
||||
b.set(ptr);
|
||||
operator()(reinterpret_cast<wcount*>(b.ptr), b, x);
|
||||
operator()(reinterpret_cast<double*>(b.ptr), b, x);
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
void operator()(void*, Buffer&, const double) {}
|
||||
|
||||
template <typename Buffer>
|
||||
void operator()(wcount* tp, Buffer& b, const double x) {
|
||||
void operator()(double* tp, Buffer& b, const double x) {
|
||||
for (auto end = tp + b.size; tp != end; ++tp) *tp *= x;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
template <typename T, typename ScaleType, typename Allocator>
|
||||
template <typename T, typename Allocator>
|
||||
struct array_storage {
|
||||
using allocator_type = Allocator;
|
||||
using element_type = T;
|
||||
using scale_type = ScaleType;
|
||||
using const_reference = const T&;
|
||||
|
||||
using buffer_type = std::vector<element_type, allocator_type>;
|
||||
@@ -90,7 +89,7 @@ struct array_storage {
|
||||
return *this;
|
||||
}
|
||||
|
||||
array_storage& operator*=(const scale_type& x) noexcept {
|
||||
array_storage& operator*=(const double x) noexcept {
|
||||
for (std::size_t i = 0; i < size(); ++i) buffer[i] *= x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -11,15 +11,10 @@
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace serialization {
|
||||
class access;
|
||||
} // namespace serialization
|
||||
|
||||
namespace histogram {
|
||||
|
||||
/// Double counter which holds a sum of weights and a sum of squared weights
|
||||
template <typename RealType>
|
||||
template <typename RealType = double>
|
||||
class weight_counter {
|
||||
public:
|
||||
/// Beware: For performance reasons counters are not initialized
|
||||
@@ -30,8 +25,7 @@ public:
|
||||
weight_counter& operator=(weight_counter&&) = default;
|
||||
|
||||
weight_counter(const RealType& value, const RealType& variance) noexcept
|
||||
: w(value),
|
||||
w2(variance) {}
|
||||
: w(value), w2(variance) {}
|
||||
|
||||
explicit weight_counter(const RealType& value) noexcept : w(value), w2(value) {}
|
||||
|
||||
@@ -56,7 +50,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
weight_counter& operator+=(const detail::weight_type<T>& rhs) {
|
||||
weight_counter& operator+=(const weight_type<T>& rhs) {
|
||||
const auto x = static_cast<RealType>(rhs.value);
|
||||
w += x;
|
||||
w2 += x * x;
|
||||
@@ -105,12 +99,10 @@ public:
|
||||
return static_cast<T>(w);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ::boost::serialization::access;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive&, unsigned /* version */);
|
||||
|
||||
private:
|
||||
RealType w, w2;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,31 +7,23 @@
|
||||
#ifndef BOOST_HISTOGRAM_WEIGHT_HPP
|
||||
#define BOOST_HISTOGRAM_WEIGHT_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
/// Type wrapper to make T as weight
|
||||
template <typename T>
|
||||
struct weight_type {
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct sample_type {
|
||||
T value;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// Helper function to mark argument as a weight
|
||||
template <typename T>
|
||||
detail::weight_type<T> weight(T&& t) {
|
||||
return {t};
|
||||
weight_type<T&&> weight(T&& t) {
|
||||
return {std::forward<T>(t)};
|
||||
}
|
||||
|
||||
/// Helper function to mark argument as a sample
|
||||
template <typename T>
|
||||
detail::sample_type<T> sample(T&& t) {
|
||||
return {t};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
test/Jamfile
13
test/Jamfile
@@ -15,28 +15,30 @@ project histogram-test
|
||||
|
||||
alias run-tests :
|
||||
[ run adaptive_storage_serialization_test.cpp /boost/serialization//boost_serialization/<link>static ]
|
||||
[ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/<link>static ]
|
||||
[ run adaptive_storage_test.cpp ]
|
||||
[ run array_storage_test.cpp ]
|
||||
[ run axis_test.cpp ]
|
||||
[ run axis_regular_test.cpp ]
|
||||
[ run axis_circular_test.cpp ]
|
||||
[ run axis_variable_test.cpp ]
|
||||
[ run axis_integer_test.cpp ]
|
||||
[ run axis_category_test.cpp ]
|
||||
[ run axis_variant_test.cpp ]
|
||||
[ run detail_test.cpp ]
|
||||
[ run histogram_dynamic_test.cpp ]
|
||||
[ run histogram_mixed_test.cpp ]
|
||||
[ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/<link>static ]
|
||||
[ run histogram_test.cpp ]
|
||||
[ run index_mapper_test.cpp ]
|
||||
[ run meta_test.cpp ]
|
||||
[ link speed_cpp.cpp : : speed_test ]
|
||||
[ run utility_test.cpp ]
|
||||
[ run weight_counter_test.cpp ]
|
||||
;
|
||||
|
||||
alias run-fail-tests :
|
||||
[ run-fail histogram_dynamic_add_fail.cpp ]
|
||||
[ run-fail histogram_dynamic_at_tuple_wrong_dimension_fail.cpp ]
|
||||
[ run-fail histogram_dynamic_at_vector_wrong_dimension_fail.cpp ]
|
||||
[ run-fail histogram_dynamic_at_wrong_dimension_fail.cpp ]
|
||||
[ run-fail histogram_dynamic_reduce_wrong_order_fail.cpp ]
|
||||
[ run-fail histogram_static_add_fail.cpp ]
|
||||
[ run-fail histogram_static_at_vector_wrong_dimension_fail.cpp ]
|
||||
;
|
||||
|
||||
@@ -46,4 +48,5 @@ alias run-speed-tests :
|
||||
[ run speed_root.cpp ]
|
||||
;
|
||||
|
||||
explicit run-fail-tests ;
|
||||
explicit run-speed-tests ;
|
||||
|
||||
@@ -82,7 +82,7 @@ int main() {
|
||||
serialization_impl<uint32_t>();
|
||||
serialization_impl<uint64_t>();
|
||||
serialization_impl<adaptive_storage_type::mp_int>();
|
||||
serialization_impl<adaptive_storage_type::wcount>();
|
||||
serialization_impl<double>();
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace bh = boost::histogram;
|
||||
using adaptive_storage_type = bh::adaptive_storage<std::allocator<char>>;
|
||||
template <typename T> using array_storage = bh::array_storage<T, T, std::allocator<T>>;
|
||||
template <typename T> using array_storage = bh::array_storage<T, std::allocator<T>>;
|
||||
using bh::weight;
|
||||
|
||||
template <typename T>
|
||||
@@ -50,31 +50,22 @@ void copy_impl() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void equal_impl() {
|
||||
void equal_1_impl() {
|
||||
auto a = prepare<void>(1);
|
||||
auto b = prepare(1, T(0));
|
||||
BOOST_TEST_EQ(a[0].value(), 0.0);
|
||||
BOOST_TEST_EQ(a[0].variance(), 0.0);
|
||||
BOOST_TEST_EQ(a[0], 0.0);
|
||||
BOOST_TEST(a == b);
|
||||
b.increase(0);
|
||||
BOOST_TEST(!(a == b));
|
||||
|
||||
array_storage<unsigned> c;
|
||||
c.reset(1);
|
||||
auto d = prepare(1, T(0));
|
||||
BOOST_TEST(c == d);
|
||||
c.increase(0);
|
||||
BOOST_TEST(!(c == d));
|
||||
}
|
||||
|
||||
template <>
|
||||
void equal_impl<void>() {
|
||||
void equal_1_impl<void>() {
|
||||
auto a = prepare<void>(1);
|
||||
auto b = prepare<uint8_t>(1, 0);
|
||||
auto c = prepare<uint8_t>(2, 0);
|
||||
auto d = prepare<unsigned>(1);
|
||||
BOOST_TEST_EQ(a[0].value(), 0.0);
|
||||
BOOST_TEST_EQ(a[0].variance(), 0.0);
|
||||
BOOST_TEST_EQ(a[0], 0.0);
|
||||
BOOST_TEST(a == b);
|
||||
BOOST_TEST(b == a);
|
||||
BOOST_TEST(a == d);
|
||||
@@ -89,6 +80,16 @@ void equal_impl<void>() {
|
||||
BOOST_TEST(!(d == a));
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void equal_2_impl() {
|
||||
auto a = prepare<T>(1);
|
||||
array_storage<U> b;
|
||||
b.reset(1);
|
||||
BOOST_TEST(a == b);
|
||||
b.increase(0);
|
||||
BOOST_TEST(!(a == b));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void increase_and_grow_impl() {
|
||||
auto tmax = std::numeric_limits<T>::max();
|
||||
@@ -100,24 +101,24 @@ void increase_and_grow_impl() {
|
||||
|
||||
auto x = prepare<void>(2);
|
||||
x.increase(0);
|
||||
n2.add(0, x[0].value());
|
||||
n2.add(0, x[0]);
|
||||
|
||||
double v = tmax;
|
||||
++v;
|
||||
BOOST_TEST_EQ(n[0].value(), v);
|
||||
BOOST_TEST_EQ(n2[0].value(), v);
|
||||
BOOST_TEST_EQ(n[1].value(), 0.0);
|
||||
BOOST_TEST_EQ(n2[1].value(), 0.0);
|
||||
BOOST_TEST_EQ(n[0], v);
|
||||
BOOST_TEST_EQ(n2[0], v);
|
||||
BOOST_TEST_EQ(n[1], 0.0);
|
||||
BOOST_TEST_EQ(n2[1], 0.0);
|
||||
}
|
||||
|
||||
template <>
|
||||
void increase_and_grow_impl<void>() {
|
||||
auto s = prepare<void>(2);
|
||||
BOOST_TEST_EQ(s[0].value(), 0);
|
||||
BOOST_TEST_EQ(s[1].value(), 0);
|
||||
BOOST_TEST_EQ(s[0], 0);
|
||||
BOOST_TEST_EQ(s[1], 0);
|
||||
s.increase(0);
|
||||
BOOST_TEST_EQ(s[0].value(), 1);
|
||||
BOOST_TEST_EQ(s[1].value(), 0);
|
||||
BOOST_TEST_EQ(s[0], 1);
|
||||
BOOST_TEST_EQ(s[1], 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -129,20 +130,20 @@ void convert_array_storage_impl() {
|
||||
|
||||
auto a = aref;
|
||||
a = s;
|
||||
BOOST_TEST_EQ(a[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(a[0], 1.0);
|
||||
BOOST_TEST(a == s);
|
||||
a.increase(0);
|
||||
BOOST_TEST(!(a == s));
|
||||
|
||||
adaptive_storage_type b(s);
|
||||
BOOST_TEST_EQ(b[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(b[0], 1.0);
|
||||
BOOST_TEST(b == s);
|
||||
b.increase(0);
|
||||
BOOST_TEST(!(b == s));
|
||||
|
||||
auto c = aref;
|
||||
c.add(0, s[0]);
|
||||
BOOST_TEST_EQ(c[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(c[0], 1.0);
|
||||
BOOST_TEST(c == s);
|
||||
BOOST_TEST(s == c);
|
||||
|
||||
@@ -156,20 +157,20 @@ void convert_array_storage_impl() {
|
||||
|
||||
auto e = aref;
|
||||
e = s;
|
||||
BOOST_TEST_EQ(e[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(e[0], 1.0);
|
||||
BOOST_TEST(e == s);
|
||||
e.increase(0);
|
||||
BOOST_TEST(!(e == s));
|
||||
|
||||
adaptive_storage_type f(s);
|
||||
BOOST_TEST_EQ(f[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(f[0], 1.0);
|
||||
BOOST_TEST(f == s);
|
||||
f.increase(0);
|
||||
BOOST_TEST(!(f == s));
|
||||
|
||||
auto g = aref;
|
||||
g.add(0, s[0]);
|
||||
BOOST_TEST_EQ(g[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(g[0], 1.0);
|
||||
BOOST_TEST(g == s);
|
||||
BOOST_TEST(s == g);
|
||||
|
||||
@@ -185,21 +186,21 @@ void convert_array_storage_impl() {
|
||||
template <>
|
||||
void convert_array_storage_impl<void>() {
|
||||
const auto aref = prepare<void>(1);
|
||||
BOOST_TEST_EQ(aref[0].value(), 0.0);
|
||||
BOOST_TEST_EQ(aref[0], 0.0);
|
||||
array_storage<uint8_t> s;
|
||||
s.reset(1);
|
||||
s.increase(0);
|
||||
|
||||
auto a = aref;
|
||||
a = s;
|
||||
BOOST_TEST_EQ(a[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(a[0], 1.0);
|
||||
BOOST_TEST(a == s);
|
||||
a.increase(0);
|
||||
BOOST_TEST(!(a == s));
|
||||
|
||||
auto c = aref;
|
||||
c.add(0, s[0]);
|
||||
BOOST_TEST_EQ(c[0].value(), 1.0);
|
||||
BOOST_TEST_EQ(c[0], 1.0);
|
||||
BOOST_TEST(c == s);
|
||||
BOOST_TEST(s == c);
|
||||
|
||||
@@ -216,15 +217,14 @@ void add_impl() {
|
||||
auto b = prepare<RHS>(2);
|
||||
if (std::is_same<RHS, void>::value) {
|
||||
a += b;
|
||||
BOOST_TEST_EQ(a[0].value(), 0);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
} else {
|
||||
b.increase(0);
|
||||
b.increase(0);
|
||||
a += b;
|
||||
BOOST_TEST_EQ(a[0].value(), 2);
|
||||
BOOST_TEST_EQ(a[0].variance(), 2);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ void add_impl_all_rhs() {
|
||||
add_impl<LHS, uint32_t>();
|
||||
add_impl<LHS, uint64_t>();
|
||||
add_impl<LHS, adaptive_storage_type::mp_int>();
|
||||
add_impl<LHS, adaptive_storage_type::wcount>();
|
||||
add_impl<LHS, double>();
|
||||
}
|
||||
|
||||
int main() {
|
||||
@@ -270,7 +270,7 @@ int main() {
|
||||
|
||||
// copy
|
||||
{
|
||||
copy_impl<adaptive_storage_type::wcount>();
|
||||
copy_impl<double>();
|
||||
copy_impl<void>();
|
||||
copy_impl<uint8_t>();
|
||||
copy_impl<uint16_t>();
|
||||
@@ -281,13 +281,29 @@ int main() {
|
||||
|
||||
// equal_operator
|
||||
{
|
||||
equal_impl<void>();
|
||||
equal_impl<uint8_t>();
|
||||
equal_impl<uint16_t>();
|
||||
equal_impl<uint32_t>();
|
||||
equal_impl<uint64_t>();
|
||||
equal_impl<adaptive_storage_type::mp_int>();
|
||||
equal_impl<adaptive_storage_type::wcount>();
|
||||
equal_1_impl<void>();
|
||||
equal_1_impl<uint8_t>();
|
||||
equal_1_impl<uint16_t>();
|
||||
equal_1_impl<uint32_t>();
|
||||
equal_1_impl<uint64_t>();
|
||||
equal_1_impl<adaptive_storage_type::mp_int>();
|
||||
equal_1_impl<double>();
|
||||
|
||||
equal_2_impl<void, unsigned>();
|
||||
equal_2_impl<uint8_t, unsigned>();
|
||||
equal_2_impl<uint16_t, unsigned>();
|
||||
equal_2_impl<uint32_t, unsigned>();
|
||||
equal_2_impl<uint64_t, unsigned>();
|
||||
equal_2_impl<adaptive_storage_type::mp_int, unsigned>();
|
||||
equal_2_impl<double, unsigned>();
|
||||
|
||||
equal_2_impl<adaptive_storage_type::mp_int, double>();
|
||||
|
||||
auto a = prepare<double>(1);
|
||||
auto b = prepare<adaptive_storage_type::mp_int>(1);
|
||||
BOOST_TEST(a == b);
|
||||
a.increase(0);
|
||||
BOOST_TEST_NOT(a == b);
|
||||
}
|
||||
|
||||
// increase_and_grow
|
||||
@@ -300,11 +316,11 @@ int main() {
|
||||
|
||||
// only increase for mp_int
|
||||
auto a = prepare<adaptive_storage_type::mp_int>(2, 1);
|
||||
BOOST_TEST_EQ(a[0].value(), 1);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 1);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
a.increase(0);
|
||||
BOOST_TEST_EQ(a[0].value(), 2);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 2);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
}
|
||||
|
||||
// add
|
||||
@@ -315,38 +331,33 @@ int main() {
|
||||
add_impl_all_rhs<uint32_t>();
|
||||
add_impl_all_rhs<uint64_t>();
|
||||
add_impl_all_rhs<adaptive_storage_type::mp_int>();
|
||||
add_impl_all_rhs<adaptive_storage_type::wcount>();
|
||||
add_impl_all_rhs<double>();
|
||||
}
|
||||
|
||||
// add_and_grow
|
||||
{
|
||||
auto a = prepare<void>(1);
|
||||
a += a;
|
||||
BOOST_TEST_EQ(a[0].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
a.increase(0);
|
||||
double x = 1;
|
||||
auto b = prepare<void>(1);
|
||||
b.increase(0);
|
||||
BOOST_TEST_EQ(b[0].value(), x);
|
||||
BOOST_TEST_EQ(b[0], x);
|
||||
for (unsigned i = 0; i < 80; ++i) {
|
||||
x += x;
|
||||
a.add(0, a[0].value());
|
||||
a.add(0, a[0]);
|
||||
b += b;
|
||||
BOOST_TEST_EQ(a[0].value(), x);
|
||||
BOOST_TEST_EQ(a[0].variance(), x);
|
||||
BOOST_TEST_EQ(b[0].value(), x);
|
||||
BOOST_TEST_EQ(b[0].variance(), x);
|
||||
BOOST_TEST_EQ(a[0], x);
|
||||
BOOST_TEST_EQ(b[0], x);
|
||||
auto c = prepare<void>(1);
|
||||
c.add(0, a[0].value());
|
||||
BOOST_TEST_EQ(c[0].value(), x);
|
||||
BOOST_TEST_EQ(c[0].variance(), x);
|
||||
c.add(0, a[0]);
|
||||
BOOST_TEST_EQ(c[0], x);
|
||||
c.add(0, weight(0));
|
||||
BOOST_TEST_EQ(c[0].value(), x);
|
||||
BOOST_TEST_EQ(c[0].variance(), x);
|
||||
BOOST_TEST_EQ(c[0], x);
|
||||
auto d = prepare<void>(1);
|
||||
d.add(0, weight(x));
|
||||
BOOST_TEST_EQ(d[0].value(), x);
|
||||
BOOST_TEST_EQ(d[0].variance(), x * x);
|
||||
BOOST_TEST_EQ(d[0], x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,24 +365,18 @@ int main() {
|
||||
{
|
||||
auto a = prepare<void>(2);
|
||||
a *= 2;
|
||||
BOOST_TEST_EQ(a[0].value(), 0);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[0], 0);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
a.increase(0);
|
||||
a *= 3;
|
||||
BOOST_TEST_EQ(a[0].value(), 3);
|
||||
BOOST_TEST_EQ(a[0].variance(), 9);
|
||||
BOOST_TEST_EQ(a[1].value(), 0);
|
||||
BOOST_TEST_EQ(a[1].variance(), 0);
|
||||
a.add(1, adaptive_storage_type::element_type(2, 5));
|
||||
BOOST_TEST_EQ(a[0].value(), 3);
|
||||
BOOST_TEST_EQ(a[0].variance(), 9);
|
||||
BOOST_TEST_EQ(a[1].value(), 2);
|
||||
BOOST_TEST_EQ(a[1].variance(), 5);
|
||||
BOOST_TEST_EQ(a[0], 3);
|
||||
BOOST_TEST_EQ(a[1], 0);
|
||||
a.add(1, 2);
|
||||
BOOST_TEST_EQ(a[0], 3);
|
||||
BOOST_TEST_EQ(a[1], 2);
|
||||
a *= 3;
|
||||
BOOST_TEST_EQ(a[0].value(), 9);
|
||||
BOOST_TEST_EQ(a[0].variance(), 81);
|
||||
BOOST_TEST_EQ(a[1].value(), 6);
|
||||
BOOST_TEST_EQ(a[1].variance(), 45);
|
||||
BOOST_TEST_EQ(a[0], 9);
|
||||
BOOST_TEST_EQ(a[1], 6);
|
||||
}
|
||||
|
||||
// convert_array_storage
|
||||
@@ -382,7 +387,7 @@ int main() {
|
||||
convert_array_storage_impl<uint32_t>();
|
||||
convert_array_storage_impl<uint64_t>();
|
||||
convert_array_storage_impl<adaptive_storage_type::mp_int>();
|
||||
convert_array_storage_impl<adaptive_storage_type::wcount>();
|
||||
convert_array_storage_impl<double>();
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
|
||||
65
test/axis_category_test.cpp
Normal file
65
test/axis_category_test.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// bad_ctors
|
||||
{
|
||||
auto empty = std::vector<double>(0);
|
||||
BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::category
|
||||
{
|
||||
std::string A("A"), B("B"), C("C"), other;
|
||||
axis::category<std::string> a({A, B, C});
|
||||
axis::category<std::string> b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = axis::category<std::string>{{B, A, C}};
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::category<std::string> c = std::move(b);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
BOOST_TEST_NE(b, a);
|
||||
axis::category<std::string> d;
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.size(), 3);
|
||||
BOOST_TEST_EQ(a(A), 0);
|
||||
BOOST_TEST_EQ(a(B), 1);
|
||||
BOOST_TEST_EQ(a(C), 2);
|
||||
BOOST_TEST_EQ(a(other), 3);
|
||||
BOOST_TEST_EQ(a.value(0), A);
|
||||
BOOST_TEST_EQ(a.value(1), B);
|
||||
BOOST_TEST_EQ(a.value(2), C);
|
||||
BOOST_TEST_THROWS(a.value(3), std::out_of_range);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::category<>({3, 1, 2}, ""), 0, 3);
|
||||
test_axis_iterator(axis::category<std::string>({"A", "B"}, ""), 0, 2);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
56
test/axis_circular_test.cpp
Normal file
56
test/axis_circular_test.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <limits>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// bad_ctor
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::circular<>(2, 0, -1), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::circular
|
||||
{
|
||||
axis::circular<> a{4, 0, 1};
|
||||
BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1);
|
||||
axis::circular<> b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::circular<> c = std::move(b);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::circular<> d;
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-1.0 * 3), 0);
|
||||
BOOST_TEST_EQ(a(0.0), 0);
|
||||
BOOST_TEST_EQ(a(0.25), 1);
|
||||
BOOST_TEST_EQ(a(0.5), 2);
|
||||
BOOST_TEST_EQ(a(0.75), 3);
|
||||
BOOST_TEST_EQ(a(1.0), 0);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::infinity()), 4);
|
||||
BOOST_TEST_EQ(a(-std::numeric_limits<double>::infinity()), 4);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::quiet_NaN()), 4);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
79
test/axis_integer_test.cpp
Normal file
79
test/axis_integer_test.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// bad_ctor
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::integer
|
||||
{
|
||||
axis::integer<> a{-1, 2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::integer<> b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::integer<> c = std::move(b);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::integer<> d;
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
BOOST_TEST_EQ(a(-2), -1);
|
||||
BOOST_TEST_EQ(a(-1), 0);
|
||||
BOOST_TEST_EQ(a(0), 1);
|
||||
BOOST_TEST_EQ(a(1), 2);
|
||||
BOOST_TEST_EQ(a(2), 3);
|
||||
BOOST_TEST_EQ(a(10), 3);
|
||||
}
|
||||
|
||||
// axis::integer with int type
|
||||
{
|
||||
axis::integer<int> a{-1, 2};
|
||||
BOOST_TEST_EQ(a[-2].value(), std::numeric_limits<int>::min());
|
||||
BOOST_TEST_EQ(a[4].value(), std::numeric_limits<int>::max());
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
BOOST_TEST_EQ(a(-2), -1);
|
||||
BOOST_TEST_EQ(a(-1), 0);
|
||||
BOOST_TEST_EQ(a(0), 1);
|
||||
BOOST_TEST_EQ(a(1), 2);
|
||||
BOOST_TEST_EQ(a(2), 3);
|
||||
BOOST_TEST_EQ(a(10), 3);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
158
test/axis_regular_test.cpp
Normal file
158
test/axis_regular_test.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/units/quantity.hpp>
|
||||
#include <boost/units/systems/si/length.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// bad_ctors
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::regular<>(1, 0, 0), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::regular
|
||||
{
|
||||
axis::regular<> a{4, -2, 2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::regular<> b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::regular<> c = std::move(b);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
axis::regular<> d;
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a(-10.), -1);
|
||||
BOOST_TEST_EQ(a(-2.1), -1);
|
||||
BOOST_TEST_EQ(a(-2.0), 0);
|
||||
BOOST_TEST_EQ(a(-1.1), 0);
|
||||
BOOST_TEST_EQ(a(0.0), 2);
|
||||
BOOST_TEST_EQ(a(0.9), 2);
|
||||
BOOST_TEST_EQ(a(1.0), 3);
|
||||
BOOST_TEST_EQ(a(10.), 4);
|
||||
BOOST_TEST_EQ(a(-std::numeric_limits<double>::infinity()), -1);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::infinity()), 4);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::quiet_NaN()), 4);
|
||||
}
|
||||
|
||||
// regular axis with inverted range
|
||||
{
|
||||
axis::regular<> a{2, 1, -2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[0].lower(), 1);
|
||||
BOOST_TEST_EQ(a[1].lower(), -0.5);
|
||||
BOOST_TEST_EQ(a[2].lower(), -2);
|
||||
BOOST_TEST_EQ(a[2].upper(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a(2), -1);
|
||||
BOOST_TEST_EQ(a(1.001), -1);
|
||||
BOOST_TEST_EQ(a(1), 0);
|
||||
BOOST_TEST_EQ(a(0), 0);
|
||||
BOOST_TEST_EQ(a(-0.499), 0);
|
||||
BOOST_TEST_EQ(a(-0.5), 1);
|
||||
BOOST_TEST_EQ(a(-1), 1);
|
||||
BOOST_TEST_EQ(a(-2), 2);
|
||||
BOOST_TEST_EQ(a(-20), 2);
|
||||
}
|
||||
|
||||
// axis::regular with log transform
|
||||
{
|
||||
axis::regular<axis::transform::log<>> b{2, 1e0, 1e2};
|
||||
BOOST_TEST_EQ(b[-1].lower(), 0.0);
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower(), 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower(), 10.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower(), 100.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper(), std::numeric_limits<double>::infinity());
|
||||
|
||||
BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b(0), -1);
|
||||
BOOST_TEST_EQ(b(1), 0);
|
||||
BOOST_TEST_EQ(b(9), 0);
|
||||
BOOST_TEST_EQ(b(10), 1);
|
||||
BOOST_TEST_EQ(b(90), 1);
|
||||
BOOST_TEST_EQ(b(100), 2);
|
||||
BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity()), 2);
|
||||
}
|
||||
|
||||
// axis::regular with sqrt transform
|
||||
{
|
||||
axis::regular<axis::transform::sqrt<>> b{2, 0, 4};
|
||||
// this is weird: -inf * -inf = inf, thus the lower bound
|
||||
BOOST_TEST_EQ(b[-1].lower(), std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower(), 0.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower(), 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower(), 4.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper(), std::numeric_limits<double>::infinity());
|
||||
|
||||
BOOST_TEST_EQ(b(-1), 2); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b(0), 0);
|
||||
BOOST_TEST_EQ(b(0.99), 0);
|
||||
BOOST_TEST_EQ(b(1), 1);
|
||||
BOOST_TEST_EQ(b(3.99), 1);
|
||||
BOOST_TEST_EQ(b(4), 2);
|
||||
BOOST_TEST_EQ(b(100), 2);
|
||||
BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity()), 2);
|
||||
}
|
||||
|
||||
// axis::regular with quantity
|
||||
{
|
||||
using namespace boost::units;
|
||||
using namespace boost::units::si;
|
||||
using Q = quantity<length>;
|
||||
|
||||
axis::regular<axis::transform::unit<Q>> b(2, 0 * meter, 2 * meter);
|
||||
BOOST_TEST_EQ(b[-1].lower() / meter, -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower() / meter, 0.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower() / meter, 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower() / meter, 2.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper() / meter, std::numeric_limits<double>::infinity());
|
||||
|
||||
BOOST_TEST_EQ(b(-1 * meter), -1); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b(0 * meter), 0);
|
||||
BOOST_TEST_EQ(b(0.99 * meter), 0);
|
||||
BOOST_TEST_EQ(b(1 * meter), 1);
|
||||
BOOST_TEST_EQ(b(1.99 * meter), 1);
|
||||
BOOST_TEST_EQ(b(2 * meter), 2);
|
||||
BOOST_TEST_EQ(b(100 * meter), 2);
|
||||
BOOST_TEST_EQ(b(std::numeric_limits<double>::infinity() * meter), 2);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::option_type::none), 0, 5);
|
||||
test_axis_iterator(
|
||||
axis::regular<>(5, 0, 1, "", axis::option_type::underflow_and_overflow), 0, 5);
|
||||
}
|
||||
|
||||
// bin_type streamable
|
||||
{
|
||||
auto test = [](const auto& x, const char* ref) {
|
||||
std::ostringstream os;
|
||||
os << x;
|
||||
BOOST_TEST_EQ(os.str(), std::string(ref));
|
||||
};
|
||||
|
||||
auto a = axis::regular<>(2, 0, 1);
|
||||
test(a[0], "[0, 0.5)");
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -1,496 +0,0 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/any.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/types.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps)
|
||||
|
||||
template <typename Axis>
|
||||
void test_axis_iterator(const Axis& a, int begin, int end) {
|
||||
for (auto bin : a) {
|
||||
BOOST_TEST_EQ(bin.idx(), begin);
|
||||
BOOST_TEST_EQ(bin, a[begin]);
|
||||
++begin;
|
||||
}
|
||||
BOOST_TEST_EQ(begin, end);
|
||||
auto rit = a.rbegin();
|
||||
for (; rit != a.rend(); ++rit) {
|
||||
BOOST_TEST_EQ(rit->idx(), --begin);
|
||||
BOOST_TEST_EQ(*rit, a[begin]);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// bad_ctors
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::regular<>(1, 1, -1), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::circular<>(0), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::variable<>({}), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::variable<>({1.0}), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::integer<>(1, -1), std::logic_error);
|
||||
BOOST_TEST_THROWS(axis::category<>({}), std::logic_error);
|
||||
}
|
||||
|
||||
// axis::regular
|
||||
{
|
||||
axis::regular<> a{4, -2, 2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::regular<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::regular<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
axis::regular<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.index(-10.), -1);
|
||||
BOOST_TEST_EQ(a.index(-2.1), -1);
|
||||
BOOST_TEST_EQ(a.index(-2.0), 0);
|
||||
BOOST_TEST_EQ(a.index(-1.1), 0);
|
||||
BOOST_TEST_EQ(a.index(0.0), 2);
|
||||
BOOST_TEST_EQ(a.index(0.9), 2);
|
||||
BOOST_TEST_EQ(a.index(1.0), 3);
|
||||
BOOST_TEST_EQ(a.index(10.), 4);
|
||||
BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), -1);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 4);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 4);
|
||||
}
|
||||
|
||||
// axis::regular with log transform
|
||||
{
|
||||
axis::regular<axis::transform::log> b{2, 1e0, 1e2};
|
||||
BOOST_TEST_EQ(b[-1].lower(), 0.0);
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower(), 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower(), 10.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower(), 100.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper(), std::numeric_limits<double>::infinity());
|
||||
|
||||
BOOST_TEST_EQ(b.index(-1), 2); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b.index(0), -1);
|
||||
BOOST_TEST_EQ(b.index(1), 0);
|
||||
BOOST_TEST_EQ(b.index(9), 0);
|
||||
BOOST_TEST_EQ(b.index(10), 1);
|
||||
BOOST_TEST_EQ(b.index(90), 1);
|
||||
BOOST_TEST_EQ(b.index(100), 2);
|
||||
BOOST_TEST_EQ(b.index(std::numeric_limits<double>::infinity()), 2);
|
||||
}
|
||||
|
||||
// axis::regular with sqrt transform
|
||||
{
|
||||
axis::regular<axis::transform::sqrt> b{2, 0, 4};
|
||||
// this is weird: -inf * -inf = inf, thus the lower bound
|
||||
BOOST_TEST_EQ(b[-1].lower(), std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_IS_CLOSE(b[0].lower(), 0.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[1].lower(), 1.0, 1e-9);
|
||||
BOOST_TEST_IS_CLOSE(b[2].lower(), 4.0, 1e-9);
|
||||
BOOST_TEST_EQ(b[2].upper(), std::numeric_limits<double>::infinity());
|
||||
|
||||
BOOST_TEST_EQ(b.index(-1), 2); // produces NaN in conversion
|
||||
BOOST_TEST_EQ(b.index(0), 0);
|
||||
BOOST_TEST_EQ(b.index(0.99), 0);
|
||||
BOOST_TEST_EQ(b.index(1), 1);
|
||||
BOOST_TEST_EQ(b.index(3.99), 1);
|
||||
BOOST_TEST_EQ(b.index(4), 2);
|
||||
BOOST_TEST_EQ(b.index(100), 2);
|
||||
BOOST_TEST_EQ(b.index(std::numeric_limits<double>::infinity()), 2);
|
||||
}
|
||||
|
||||
// axis::circular
|
||||
{
|
||||
axis::circular<> a{4};
|
||||
BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - a.perimeter());
|
||||
axis::circular<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::circular<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
axis::circular<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.index(-1.0 * a.perimeter()), 0);
|
||||
BOOST_TEST_EQ(a.index(0.0), 0);
|
||||
BOOST_TEST_EQ(a.index(0.25 * a.perimeter()), 1);
|
||||
BOOST_TEST_EQ(a.index(0.5 * a.perimeter()), 2);
|
||||
BOOST_TEST_EQ(a.index(0.75 * a.perimeter()), 3);
|
||||
BOOST_TEST_EQ(a.index(1.00 * a.perimeter()), 0);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 0);
|
||||
BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), 0);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 0);
|
||||
}
|
||||
|
||||
// axis::variable
|
||||
{
|
||||
axis::variable<> a{-1, 0, 1};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::variable<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::variable<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
axis::variable<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
axis::variable<> e{-2, 0, 2};
|
||||
BOOST_TEST_NOT(a == e);
|
||||
BOOST_TEST_EQ(a.index(-10.), -1);
|
||||
BOOST_TEST_EQ(a.index(-1.), 0);
|
||||
BOOST_TEST_EQ(a.index(0.), 1);
|
||||
BOOST_TEST_EQ(a.index(1.), 2);
|
||||
BOOST_TEST_EQ(a.index(10.), 2);
|
||||
BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), -1);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
|
||||
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 2);
|
||||
}
|
||||
|
||||
// axis::integer
|
||||
{
|
||||
axis::integer<> a{-1, 2};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<int>::max());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<int>::max());
|
||||
axis::integer<> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::integer<> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
axis::integer<> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.index(-10), -1);
|
||||
BOOST_TEST_EQ(a.index(-2), -1);
|
||||
BOOST_TEST_EQ(a.index(-1), 0);
|
||||
BOOST_TEST_EQ(a.index(0), 1);
|
||||
BOOST_TEST_EQ(a.index(1), 2);
|
||||
BOOST_TEST_EQ(a.index(2), 3);
|
||||
BOOST_TEST_EQ(a.index(10), 3);
|
||||
}
|
||||
|
||||
// axis::category
|
||||
{
|
||||
std::string A("A"), B("B"), C("C"), other;
|
||||
axis::category<std::string> a{{A, B, C}};
|
||||
axis::category<std::string> b;
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = axis::category<std::string>{{B, A, C}};
|
||||
BOOST_TEST_NOT(a == b);
|
||||
b = a;
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::category<std::string> c = std::move(b);
|
||||
BOOST_TEST(c == a);
|
||||
BOOST_TEST_NOT(b == a);
|
||||
axis::category<std::string> d;
|
||||
BOOST_TEST_NOT(c == d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
BOOST_TEST_EQ(a.size(), 3);
|
||||
BOOST_TEST_EQ(a.index(A), 0);
|
||||
BOOST_TEST_EQ(a.index(B), 1);
|
||||
BOOST_TEST_EQ(a.index(C), 2);
|
||||
BOOST_TEST_EQ(a.index(other), 3);
|
||||
BOOST_TEST_EQ(a.value(0), A);
|
||||
BOOST_TEST_EQ(a.value(1), B);
|
||||
BOOST_TEST_EQ(a.value(2), C);
|
||||
BOOST_TEST_THROWS(a.value(3), std::out_of_range);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
enum { A, B, C };
|
||||
test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::uoflow_type::off), 0, 5);
|
||||
test_axis_iterator(axis::regular<>(5, 0, 1, "", axis::uoflow_type::on), 0, 5);
|
||||
test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5);
|
||||
test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2);
|
||||
test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4);
|
||||
test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3);
|
||||
test_axis_iterator(axis::any_std(axis::regular<>(5, 0, 1)), 0, 5);
|
||||
BOOST_TEST_THROWS(axis::any_std(axis::category<>({A, B, C})).lower(0),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
// axis::any copyable
|
||||
{
|
||||
axis::any_std a1(axis::regular<>(2, -1, 1));
|
||||
axis::any_std a2(a1);
|
||||
BOOST_TEST_EQ(a1, a2);
|
||||
axis::any_std a3;
|
||||
BOOST_TEST_NE(a3, a1);
|
||||
a3 = a1;
|
||||
BOOST_TEST_EQ(a3, a1);
|
||||
axis::any<axis::regular<>> a4(axis::regular<>(3, -2, 2));
|
||||
axis::any_std a5(a4);
|
||||
BOOST_TEST_EQ(a4, a5);
|
||||
axis::any<axis::regular<>> a6;
|
||||
a6 = a1;
|
||||
BOOST_TEST_EQ(a6, a1);
|
||||
axis::any<axis::regular<>, axis::integer<>> a7(axis::integer<>(0, 2));
|
||||
BOOST_TEST_THROWS(axis::any<axis::regular<>> a8(a7), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(a4 = a7, std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::any movable
|
||||
{
|
||||
axis::any_std a(axis::regular<>(2, -1, 1));
|
||||
axis::any_std r(a);
|
||||
axis::any_std b(std::move(a));
|
||||
BOOST_TEST_EQ(b, r);
|
||||
axis::any_std c;
|
||||
BOOST_TEST_NOT(a == c);
|
||||
c = std::move(b);
|
||||
BOOST_TEST(c == r);
|
||||
}
|
||||
|
||||
// axis::any streamable
|
||||
{
|
||||
enum { A, B, C };
|
||||
std::string a = "A";
|
||||
std::string b = "B";
|
||||
std::vector<axis::any_std> axes;
|
||||
axes.push_back(axis::regular<>{2, -1, 1, "regular1"});
|
||||
axes.push_back(axis::regular<axis::transform::log>(2, 1, 10, "regular2",
|
||||
axis::uoflow_type::off));
|
||||
axes.push_back(axis::regular<axis::transform::pow>(2, 1, 10, "regular3",
|
||||
axis::uoflow_type::on, 0.5));
|
||||
axes.push_back(axis::regular<axis::transform::pow>(2, 1, 10, "regular4",
|
||||
axis::uoflow_type::off, -0.5));
|
||||
axes.push_back(axis::circular<>(4, 0.1, 1.0, "polar"));
|
||||
axes.push_back(axis::variable<>({-1, 0, 1}, "variable", axis::uoflow_type::off));
|
||||
axes.push_back(axis::category<>({A, B, C}, "category"));
|
||||
axes.push_back(axis::category<std::string>({a, b}, "category2"));
|
||||
axes.push_back(axis::integer<>(-1, 1, "integer", axis::uoflow_type::off));
|
||||
std::ostringstream os;
|
||||
for (const auto& a : axes) { os << a << "\n"; }
|
||||
os << axes.back()[0];
|
||||
const std::string ref =
|
||||
"regular(2, -1, 1, label='regular1')\n"
|
||||
"regular_log(2, 1, 10, label='regular2', uoflow=False)\n"
|
||||
"regular_pow(2, 1, 10, 0.5, label='regular3')\n"
|
||||
"regular_pow(2, 1, 10, -0.5, label='regular4', uoflow=False)\n"
|
||||
"circular(4, phase=0.1, perimeter=1, label='polar')\n"
|
||||
"variable(-1, 0, 1, label='variable', uoflow=False)\n"
|
||||
"category(0, 1, 2, label='category')\n"
|
||||
"category('A', 'B', label='category2')\n"
|
||||
"integer(-1, 1, label='integer', uoflow=False)\n"
|
||||
"[-1, 0)";
|
||||
BOOST_TEST_EQ(os.str(), ref);
|
||||
}
|
||||
|
||||
// axis::any equal_comparable
|
||||
{
|
||||
enum { A, B, C };
|
||||
std::vector<axis::any_std> axes;
|
||||
axes.push_back(axis::regular<>{2, -1, 1});
|
||||
axes.push_back(
|
||||
axis::regular<axis::transform::pow>(2, 1, 4, "", axis::uoflow_type::on, 0.5));
|
||||
axes.push_back(axis::circular<>{4});
|
||||
axes.push_back(axis::variable<>{-1, 0, 1});
|
||||
axes.push_back(axis::category<>{A, B, C});
|
||||
axes.push_back(axis::integer<>{-1, 1});
|
||||
for (const auto& a : axes) {
|
||||
BOOST_TEST(!(a == axis::any_std()));
|
||||
BOOST_TEST_EQ(a, axis::any_std(a));
|
||||
}
|
||||
BOOST_TEST_NOT(axes == std::vector<axis::any_std>());
|
||||
BOOST_TEST(axes == std::vector<axis::any_std>(axes));
|
||||
}
|
||||
|
||||
// axis::any value_to_index_failure
|
||||
{
|
||||
std::string a = "A", b = "B";
|
||||
axis::any_std x = axis::category<std::string>({a, b}, "category");
|
||||
BOOST_TEST_THROWS(x.index(1.5), std::runtime_error);
|
||||
auto cx = static_cast<const axis::category<std::string>&>(x);
|
||||
BOOST_TEST_EQ(cx.index(b), 1);
|
||||
}
|
||||
|
||||
// sequence equality
|
||||
{
|
||||
enum { A, B, C };
|
||||
std::vector<
|
||||
axis::any<axis::regular<>, axis::variable<>, axis::category<>, axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
std::vector<axis::any<axis::regular<>, axis::variable<>, axis::category<>>>
|
||||
std_vector2 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B, C}}};
|
||||
|
||||
std::vector<axis::any<axis::regular<>, axis::variable<>>> std_vector3 = {
|
||||
axis::variable<>{-1, 0, 1}, axis::regular<>{2, -1, 1}};
|
||||
|
||||
std::vector<axis::any<axis::variable<>, axis::regular<>>> std_vector4 = {
|
||||
axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}};
|
||||
|
||||
BOOST_TEST(detail::axes_equal(std_vector1, std_vector2));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector2, std_vector3));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, std_vector4));
|
||||
|
||||
auto tuple1 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B, C}});
|
||||
|
||||
auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B}});
|
||||
|
||||
auto tuple3 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1});
|
||||
|
||||
BOOST_TEST(detail::axes_equal(std_vector1, tuple1));
|
||||
BOOST_TEST(detail::axes_equal(tuple1, std_vector1));
|
||||
BOOST_TEST_NOT(detail::axes_equal(tuple1, tuple2));
|
||||
BOOST_TEST_NOT(detail::axes_equal(tuple2, tuple3));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple3));
|
||||
}
|
||||
|
||||
// sequence assign
|
||||
{
|
||||
enum { A, B, C, D };
|
||||
std::vector<
|
||||
axis::any<axis::regular<>, axis::variable<>, axis::category<>, axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
std::vector<axis::any<axis::regular<>, axis::variable<>, axis::category<>>>
|
||||
std_vector2 = {axis::regular<>{2, -2, 2}, axis::variable<>{-2, 0, 2},
|
||||
axis::category<>{A, B}};
|
||||
|
||||
detail::axes_assign(std_vector2, std_vector1);
|
||||
BOOST_TEST(detail::axes_equal(std_vector2, std_vector1));
|
||||
|
||||
auto tuple1 = std::make_tuple(axis::regular<>{2, -3, 3}, axis::variable<>{-3, 0, 3},
|
||||
axis::category<>{A, B, C, D});
|
||||
|
||||
detail::axes_assign(tuple1, std_vector1);
|
||||
BOOST_TEST(detail::axes_equal(tuple1, std_vector1));
|
||||
|
||||
decltype(std_vector1) std_vector3;
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple1));
|
||||
detail::axes_assign(std_vector3, tuple1);
|
||||
BOOST_TEST(detail::axes_equal(std_vector3, tuple1));
|
||||
|
||||
auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B});
|
||||
|
||||
detail::axes_assign(tuple2, tuple1);
|
||||
BOOST_TEST(detail::axes_equal(tuple2, tuple1));
|
||||
}
|
||||
|
||||
// sub_axes
|
||||
{
|
||||
using ra = axis::regular<>;
|
||||
using ia = axis::integer<>;
|
||||
using ca = axis::category<>;
|
||||
using T = std::tuple<ra, ia, ca>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i0>, std::tuple<ra>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i1>, std::tuple<ia>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i2>, std::tuple<ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i1, i2>, std::tuple<ra, ia, ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i1>, std::tuple<ra, ia>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i2>, std::tuple<ra, ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i1, i2>, std::tuple<ia, ca>>));
|
||||
}
|
||||
|
||||
// make_sub_tuple
|
||||
{
|
||||
using ia = axis::integer<>;
|
||||
using T = std::tuple<ia, ia, ia>;
|
||||
auto axes = T(ia(0, 1), ia(1, 2), ia(2, 3));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i1(), i2()),
|
||||
(std::tuple<ia, ia>(ia(1, 2), ia(2, 3))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1()),
|
||||
(std::tuple<ia, ia>(ia(0, 1), ia(1, 2))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i1()), (std::tuple<ia>(ia(1, 2))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1(), i2()), axes);
|
||||
}
|
||||
|
||||
// vector of axes with custom allocators
|
||||
{
|
||||
using T1 = axis::regular<axis::transform::identity, double, tracing_allocator<char>>;
|
||||
using T2 = axis::circular<double, tracing_allocator<char>>;
|
||||
using T3 = axis::variable<double, tracing_allocator<char>>;
|
||||
using T4 = axis::integer<int, tracing_allocator<char>>;
|
||||
using T5 = axis::category<long, tracing_allocator<char>>;
|
||||
using axis_type = axis::any<T1, T2, T3, T4, T5>; // no heap allocation
|
||||
using axes_type = std::vector<axis_type, tracing_allocator<axis_type>>;
|
||||
|
||||
tracing_allocator_db db;
|
||||
{
|
||||
auto a = tracing_allocator<char>(db);
|
||||
const auto label = std::string(512, 'c');
|
||||
axes_type axes(a);
|
||||
axes.reserve(5);
|
||||
axes.emplace_back(T1(1, 0, 1, label, axis::uoflow_type::on, {}, a));
|
||||
axes.emplace_back(T2(2, 0, T2::two_pi(), label, a));
|
||||
axes.emplace_back(T3({0., 1., 2.}, label, axis::uoflow_type::on, a));
|
||||
axes.emplace_back(T4(0, 4, label, axis::uoflow_type::on, a));
|
||||
axes.emplace_back(T5({1, 2, 3, 4, 5}, label, axis::uoflow_type::on, a));
|
||||
}
|
||||
// 5 axis::any objects
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second);
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, 5);
|
||||
|
||||
// 5 labels
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second);
|
||||
BOOST_TEST_GE(db[typeid(char)].first, 5 * 512u);
|
||||
|
||||
// nothing to allocate for T1
|
||||
// nothing to allocate for T2
|
||||
// T3 allocates storage for bin edges
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second);
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, 3u);
|
||||
// nothing to allocate for T4
|
||||
// T5 allocates storage for long array
|
||||
BOOST_TEST_EQ(db[typeid(long)].first, db[typeid(long)].second);
|
||||
BOOST_TEST_EQ(db[typeid(long)].first, 5u);
|
||||
|
||||
#if (BOOST_MSVC)
|
||||
BOOST_TEST_EQ(db.size(), 5); // axis_type, char, double, long + ???
|
||||
#else
|
||||
BOOST_TEST_EQ(db.size(), 4); // axis_type, char, double, long
|
||||
#endif
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
60
test/axis_variable_test.cpp
Normal file
60
test/axis_variable_test.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015-2017 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <limits>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// bad_ctors
|
||||
{
|
||||
auto empty = std::vector<double>(0);
|
||||
BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument);
|
||||
BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis::variable
|
||||
{
|
||||
axis::variable<> a{-1, 0, 1};
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
axis::variable<> b;
|
||||
BOOST_TEST_NE(a, b);
|
||||
b = a;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
b = b;
|
||||
BOOST_TEST_EQ(a, b);
|
||||
axis::variable<> c = std::move(b);
|
||||
BOOST_TEST_EQ(c, a);
|
||||
BOOST_TEST_NE(b, a);
|
||||
axis::variable<> d;
|
||||
BOOST_TEST_NE(c, d);
|
||||
d = std::move(c);
|
||||
BOOST_TEST_EQ(d, a);
|
||||
axis::variable<> e{-2, 0, 2};
|
||||
BOOST_TEST_NE(a, e);
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
BOOST_TEST_EQ(a(-1), 0);
|
||||
BOOST_TEST_EQ(a(0), 1);
|
||||
BOOST_TEST_EQ(a(1), 2);
|
||||
BOOST_TEST_EQ(a(10), 2);
|
||||
BOOST_TEST_EQ(a(-std::numeric_limits<double>::infinity()), -1);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::infinity()), 2);
|
||||
BOOST_TEST_EQ(a(std::numeric_limits<double>::quiet_NaN()), 2);
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
239
test/axis_variant_test.cpp
Normal file
239
test/axis_variant_test.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright 2015-2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/circular.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
{
|
||||
BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument);
|
||||
}
|
||||
|
||||
{
|
||||
axis::variant<
|
||||
axis::integer<>,
|
||||
axis::category<std::string>
|
||||
> a{axis::integer<>(0, 2, "int")};
|
||||
BOOST_TEST_EQ(a(-10), -1);
|
||||
BOOST_TEST_EQ(a(-1), -1);
|
||||
BOOST_TEST_EQ(a(0), 0);
|
||||
BOOST_TEST_EQ(a(0.5), 0);
|
||||
BOOST_TEST_EQ(a(1), 1);
|
||||
BOOST_TEST_EQ(a(2), 2);
|
||||
BOOST_TEST_EQ(a(10), 2);
|
||||
BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits<double>::infinity());
|
||||
BOOST_TEST_EQ(a.metadata(), std::string("int"));
|
||||
BOOST_TEST_EQ(a.options(), axis::option_type::underflow_and_overflow);
|
||||
|
||||
a = axis::category<std::string>({"A", "B"}, "cat");
|
||||
BOOST_TEST_EQ(a("A"), 0);
|
||||
BOOST_TEST_EQ(a("B"), 1);
|
||||
BOOST_TEST_EQ(a.metadata(), std::string("cat"));
|
||||
BOOST_TEST_EQ(a.options(), axis::option_type::overflow);
|
||||
}
|
||||
|
||||
// axis::variant support for minimal_axis
|
||||
{
|
||||
struct minimal_axis {
|
||||
int operator()(double) const { return 0; }
|
||||
unsigned size() const { return 1; }
|
||||
};
|
||||
|
||||
axis::variant<minimal_axis> axis;
|
||||
BOOST_TEST_EQ(axis(0), 0);
|
||||
BOOST_TEST_EQ(axis(10), 0);
|
||||
BOOST_TEST_EQ(axis.size(), 1);
|
||||
BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error);
|
||||
BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<decltype(axis.metadata()), axis::empty_metadata_type&>));
|
||||
}
|
||||
|
||||
// axis::variant copyable
|
||||
{
|
||||
axis::variant<axis::regular<>> a1(axis::regular<>(2, -1, 1));
|
||||
axis::variant<axis::regular<>> a2(a1);
|
||||
BOOST_TEST_EQ(a1, a2);
|
||||
axis::variant<axis::regular<>> a3;
|
||||
BOOST_TEST_NE(a3, a1);
|
||||
a3 = a1;
|
||||
BOOST_TEST_EQ(a3, a1);
|
||||
axis::variant<axis::regular<>> a4(axis::regular<>(3, -2, 2));
|
||||
axis::variant<axis::regular<>, axis::integer<>> a5(a4);
|
||||
BOOST_TEST_EQ(a4, a5);
|
||||
axis::variant<axis::regular<>> a6;
|
||||
a6 = a1;
|
||||
BOOST_TEST_EQ(a6, a1);
|
||||
axis::variant<axis::regular<>, axis::integer<>> a7(axis::integer<>(0, 2));
|
||||
BOOST_TEST_THROWS(axis::variant<axis::regular<>> a8(a7), std::runtime_error);
|
||||
BOOST_TEST_THROWS(a4 = a7, std::runtime_error);
|
||||
}
|
||||
|
||||
// axis::variant movable
|
||||
{
|
||||
axis::variant<axis::regular<>> a(axis::regular<>(2, -1, 1));
|
||||
axis::variant<axis::regular<>> r(a);
|
||||
axis::variant<axis::regular<>> b(std::move(a));
|
||||
BOOST_TEST_EQ(b, r);
|
||||
axis::variant<axis::regular<>> c;
|
||||
BOOST_TEST_NOT(a == c);
|
||||
c = std::move(b);
|
||||
BOOST_TEST(c == r);
|
||||
}
|
||||
|
||||
// axis::variant streamable
|
||||
{
|
||||
auto test = [](auto&& a, const char* ref) {
|
||||
using T = detail::rm_cvref<decltype(a)>;
|
||||
axis::variant<T> axis(std::move(a));
|
||||
std::ostringstream os;
|
||||
os << axis;
|
||||
BOOST_TEST_EQ(os.str(), std::string(ref));
|
||||
};
|
||||
|
||||
struct user_defined {};
|
||||
|
||||
namespace tr = axis::transform;
|
||||
test(axis::regular<>(2, -1, 1, "regular1"),
|
||||
"regular(2, -1, 1, metadata=\"regular1\", options=underflow_and_overflow)");
|
||||
test(axis::regular<tr::log<>>(2, 1, 10, "regular2", axis::option_type::none),
|
||||
"regular_log(2, 1, 10, metadata=\"regular2\", options=none)");
|
||||
test(axis::regular<tr::pow<>>(1.5, 2, 1, 10, "regular3", axis::option_type::overflow),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular3\", options=overflow, power=1.5)");
|
||||
test(axis::regular<tr::pow<>>(-1.5, 2, 1, 10, "regular4", axis::option_type::none),
|
||||
"regular_pow(2, 1, 10, metadata=\"regular4\", options=none, power=-1.5)");
|
||||
test(axis::circular<double, axis::empty_metadata_type>(4, 0.1, 1.0),
|
||||
"circular(4, 0.1, 1.1, options=overflow)");
|
||||
test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none),
|
||||
"variable(-1, 0, 1, metadata=\"variable\", options=none)");
|
||||
test(axis::category<>({0, 1, 2}, "category"),
|
||||
"category(0, 1, 2, metadata=\"category\", options=overflow)");
|
||||
test(axis::category<std::string>({"A", "B"}, "category2"),
|
||||
"category(\"A\", \"B\", metadata=\"category2\", options=overflow)");
|
||||
#ifndef _MSC_VER // fails on MSVC because demagnled name for user_defined looks different
|
||||
test(axis::integer<int, user_defined>(-1, 1, {}, axis::option_type::none),
|
||||
"integer(-1, 1, metadata=main::user_defined, options=none)");
|
||||
#endif
|
||||
}
|
||||
|
||||
// bin_type streamable
|
||||
{
|
||||
auto test = [](const auto& x, const char* ref) {
|
||||
std::ostringstream os;
|
||||
os << x;
|
||||
BOOST_TEST_EQ(os.str(), std::string(ref));
|
||||
};
|
||||
|
||||
auto b = axis::category<>({1, 2});
|
||||
test(b[0], "1");
|
||||
}
|
||||
|
||||
// axis::variant equal_comparable
|
||||
{
|
||||
enum { A, B, C };
|
||||
using variant = axis::variant<axis::regular<>, axis::regular<axis::transform::pow<>>,
|
||||
axis::circular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>;
|
||||
std::vector<variant> axes;
|
||||
axes.push_back(axis::regular<>{2, -1, 1});
|
||||
axes.push_back(axis::regular<axis::transform::pow<>>(
|
||||
0.5, 2, 1, 4, "", axis::option_type::underflow_and_overflow));
|
||||
axes.push_back(axis::circular<>{4});
|
||||
axes.push_back(axis::variable<>{-1, 0, 1});
|
||||
axes.push_back(axis::category<>({A, B, C}));
|
||||
axes.push_back(axis::integer<>{-1, 1});
|
||||
for (const auto& a : axes) {
|
||||
BOOST_TEST(!(a == variant()));
|
||||
BOOST_TEST_EQ(a, variant(a));
|
||||
}
|
||||
BOOST_TEST_NOT(axes == std::vector<variant>());
|
||||
BOOST_TEST(axes == std::vector<variant>(axes));
|
||||
}
|
||||
|
||||
// axis::variant with unusual args
|
||||
{
|
||||
axis::variant<axis::category<std::string>> x =
|
||||
axis::category<std::string>({"A", "B"}, "category");
|
||||
BOOST_TEST_EQ(x("B"), 1);
|
||||
}
|
||||
|
||||
{
|
||||
auto a = axis::variant<axis::category<>>(axis::category<>({2, 1, 3}));
|
||||
BOOST_TEST_THROWS(a[0].lower(), std::runtime_error);
|
||||
}
|
||||
|
||||
// vector of axes with custom allocators
|
||||
{
|
||||
struct null {};
|
||||
using M = std::vector<char, tracing_allocator<char>>;
|
||||
using T1 = axis::regular<axis::transform::identity<>, M>;
|
||||
using T2 = axis::circular<double, null>;
|
||||
using T3 = axis::variable<double, tracing_allocator<double>, null>;
|
||||
using T4 = axis::integer<int, null>;
|
||||
using T5 = axis::category<long, tracing_allocator<long>, null>;
|
||||
using axis_type = axis::variant<T1, T2, T3, T4, T5>; // no heap allocation
|
||||
using axes_type = std::vector<axis_type, tracing_allocator<axis_type>>;
|
||||
|
||||
tracing_allocator_db db;
|
||||
{
|
||||
auto a = tracing_allocator<char>(db);
|
||||
axes_type axes(a);
|
||||
axes.reserve(5);
|
||||
axes.emplace_back(T1(1, 0, 1, M(3, 'c', a)));
|
||||
axes.emplace_back(T2(2));
|
||||
axes.emplace_back(
|
||||
T3({0., 1., 2.}, {}, axis::option_type::underflow_and_overflow, a));
|
||||
axes.emplace_back(T4(0, 4));
|
||||
axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a));
|
||||
}
|
||||
// 5 axis::variant objects
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second);
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, 5);
|
||||
|
||||
// label
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second);
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, 3u);
|
||||
|
||||
// nothing to allocate for T1
|
||||
// nothing to allocate for T2
|
||||
// T3 allocates storage for bin edges
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second);
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, 3u);
|
||||
// nothing to allocate for T4
|
||||
// T5 allocates storage for long array
|
||||
BOOST_TEST_EQ(db[typeid(long)].first, db[typeid(long)].second);
|
||||
BOOST_TEST_EQ(db[typeid(long)].first, 5u);
|
||||
|
||||
#if (BOOST_MSVC)
|
||||
BOOST_TEST_EQ(db.size(), 5); // axis_type, char, double, long + ???
|
||||
#else
|
||||
BOOST_TEST_EQ(db.size(), 4); // axis_type, char, double, long
|
||||
#endif
|
||||
}
|
||||
|
||||
// iterators
|
||||
{
|
||||
test_axis_iterator(axis::variant<axis::regular<>>(axis::regular<>(5, 0, 1)), 0, 5);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -5,39 +5,125 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variable.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/ostream_operators.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace bhd = boost::histogram::detail;
|
||||
namespace bhad = boost::histogram::axis::detail;
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
// escape0
|
||||
BOOST_TEST_EQ(detail::cat("foo", 1, "bar"), "foo1bar");
|
||||
|
||||
// sequence equality
|
||||
{
|
||||
std::ostringstream os;
|
||||
bhad::escape_string(os, std::string("abc"));
|
||||
BOOST_TEST_EQ(os.str(), std::string("'abc'"));
|
||||
enum { A, B, C };
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>>>
|
||||
std_vector2 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B, C}}};
|
||||
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>>> std_vector3 = {
|
||||
axis::variable<>{-1, 0, 1}, axis::regular<>{2, -1, 1}};
|
||||
|
||||
std::vector<axis::variant<axis::variable<>, axis::regular<>>> std_vector4 = {
|
||||
axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1}};
|
||||
|
||||
BOOST_TEST(detail::axes_equal(std_vector1, std_vector2));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector2, std_vector3));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, std_vector4));
|
||||
|
||||
auto tuple1 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B, C}});
|
||||
|
||||
auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B}});
|
||||
|
||||
auto tuple3 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1});
|
||||
|
||||
BOOST_TEST(detail::axes_equal(std_vector1, tuple1));
|
||||
BOOST_TEST(detail::axes_equal(tuple1, std_vector1));
|
||||
BOOST_TEST_NOT(detail::axes_equal(tuple1, tuple2));
|
||||
BOOST_TEST_NOT(detail::axes_equal(tuple2, tuple3));
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple3));
|
||||
}
|
||||
|
||||
// escape1
|
||||
// sequence assign
|
||||
{
|
||||
std::ostringstream os;
|
||||
bhad::escape_string(os, std::string("abc\n"));
|
||||
BOOST_TEST_EQ(os.str(), std::string("'abc\n'"));
|
||||
enum { A, B, C, D };
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>,
|
||||
axis::integer<>>>
|
||||
std_vector1 = {axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B, C}};
|
||||
|
||||
std::vector<axis::variant<axis::regular<>, axis::variable<>, axis::category<>>>
|
||||
std_vector2 = {axis::regular<>{2, -2, 2}, axis::variable<>{-2, 0, 2},
|
||||
axis::category<>{A, B}};
|
||||
|
||||
detail::axes_assign(std_vector2, std_vector1);
|
||||
BOOST_TEST(detail::axes_equal(std_vector2, std_vector1));
|
||||
|
||||
auto tuple1 = std::make_tuple(axis::regular<>{2, -3, 3}, axis::variable<>{-3, 0, 3},
|
||||
axis::category<>{A, B, C, D});
|
||||
|
||||
detail::axes_assign(tuple1, std_vector1);
|
||||
BOOST_TEST(detail::axes_equal(tuple1, std_vector1));
|
||||
|
||||
decltype(std_vector1) std_vector3;
|
||||
BOOST_TEST_NOT(detail::axes_equal(std_vector3, tuple1));
|
||||
detail::axes_assign(std_vector3, tuple1);
|
||||
BOOST_TEST(detail::axes_equal(std_vector3, tuple1));
|
||||
|
||||
auto tuple2 = std::make_tuple(axis::regular<>{2, -1, 1}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{A, B});
|
||||
|
||||
detail::axes_assign(tuple2, tuple1);
|
||||
BOOST_TEST(detail::axes_equal(tuple2, tuple1));
|
||||
}
|
||||
|
||||
// escape2
|
||||
// sub_axes
|
||||
{
|
||||
std::ostringstream os;
|
||||
bhad::escape_string(os, std::string("'abc'"));
|
||||
BOOST_TEST_EQ(os.str(), std::string("'\\\'abc\\\''"));
|
||||
using ra = axis::regular<>;
|
||||
using ia = axis::integer<>;
|
||||
using ca = axis::category<>;
|
||||
using T = std::tuple<ra, ia, ca>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i0>, std::tuple<ra>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i1>, std::tuple<ia>>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<detail::sub_axes<T, i2>, std::tuple<ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i1, i2>, std::tuple<ra, ia, ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i1>, std::tuple<ra, ia>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i0, i2>, std::tuple<ra, ca>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<detail::sub_axes<T, i1, i2>, std::tuple<ia, ca>>));
|
||||
}
|
||||
|
||||
// cat
|
||||
{ BOOST_TEST_EQ(bhd::cat("foo", 1, "bar"), std::string("foo1bar")); }
|
||||
// make_sub_tuple
|
||||
{
|
||||
using ia = axis::integer<>;
|
||||
using T = std::tuple<ia, ia, ia>;
|
||||
auto axes = T(ia(0, 1), ia(1, 2), ia(2, 3));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i1(), i2()),
|
||||
(std::tuple<ia, ia>(ia(1, 2), ia(2, 3))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1()),
|
||||
(std::tuple<ia, ia>(ia(0, 1), ia(1, 2))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i1()), (std::tuple<ia>(ia(1, 2))));
|
||||
BOOST_TEST_EQ(detail::make_sub_axes(axes, i0(), i1(), i2()), axes);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto a = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
auto b = make_dynamic_histogram(axis::integer<>(0, 3));
|
||||
a += b;
|
||||
}
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
auto v = std::vector<axis::variant<axis::integer<>>>();
|
||||
v.push_back(axis::integer<>(0, 2));
|
||||
auto h = make_histogram(v);
|
||||
h.at(std::make_pair(0, 0));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
auto v = std::vector<axis::variant<axis::integer<>>>();
|
||||
v.push_back(axis::integer<>(0, 2));
|
||||
auto h = make_histogram(v);
|
||||
h.at(std::vector<int>({0, 0}));
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
auto v = std::vector<axis::variant<axis::integer<>>>();
|
||||
v.push_back(axis::integer<>(0, 2));
|
||||
auto h = make_histogram(v);
|
||||
h.at(0, 0);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto a = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
std::vector<axis::variant<axis::integer<>>> v(1, axis::integer<>(0, 2));
|
||||
auto a = make_histogram(v);
|
||||
a(std::make_tuple(1)); // fails, because tuple is intentionally not unpacked
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto a = make_dynamic_histogram(axis::integer<>(0, 2));
|
||||
std::vector<axis::variant<axis::integer<>>> v(1, axis::integer<>(0, 2));
|
||||
auto a = make_histogram(v);
|
||||
a(std::vector<int>(1)); // fails, because tuple is intentionally not unpacked
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 1), axis::integer<>(1, 2));
|
||||
auto a = std::vector<axis::variant<axis::integer<>>>();
|
||||
a.push_back(axis::integer<>(0, 1));
|
||||
a.push_back(axis::integer<>(1, 2));
|
||||
auto h = make_histogram(a);
|
||||
auto v = {0, 0};
|
||||
h.reduce_to(v.begin(), v.end());
|
||||
}
|
||||
|
||||
@@ -30,36 +30,35 @@ int main() {
|
||||
|
||||
// init
|
||||
{
|
||||
auto v = std::vector<axis::any<axis::regular<>, axis::integer<>>>();
|
||||
auto v = std::vector<axis::variant<axis::regular<>, axis::integer<>>>();
|
||||
v.push_back(axis::regular<>(4, -1, 1));
|
||||
v.push_back(axis::integer<>(1, 7));
|
||||
auto h = make_dynamic_histogram(v.begin(), v.end());
|
||||
BOOST_TEST_EQ(h.dim(), 2);
|
||||
auto h = make_histogram(v.begin(), v.end());
|
||||
BOOST_TEST_EQ(h.rank(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0), v[0]);
|
||||
BOOST_TEST_EQ(h.axis(1), v[1]);
|
||||
|
||||
auto h2 = make_dynamic_histogram_with(array_storage<int>(), v.begin(), v.end());
|
||||
BOOST_TEST_EQ(h2.dim(), 2);
|
||||
auto h2 = make_histogram_with(array_storage<int>(), v.begin(), v.end());
|
||||
BOOST_TEST_EQ(h2.rank(), 2);
|
||||
BOOST_TEST_EQ(h2.axis(0), v[0]);
|
||||
BOOST_TEST_EQ(h2.axis(1), v[1]);
|
||||
}
|
||||
|
||||
// bad fill argument
|
||||
{
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 3));
|
||||
auto h = make(dynamic_tag(), axis::integer<>(0, 3));
|
||||
BOOST_TEST_THROWS(h(std::string()), std::invalid_argument);
|
||||
}
|
||||
|
||||
// axis methods
|
||||
{
|
||||
enum { A, B };
|
||||
auto c = make_dynamic_histogram(axis::category<>({A, B}));
|
||||
BOOST_TEST_THROWS(c.axis().lower(0), std::runtime_error);
|
||||
auto c = make(dynamic_tag(), axis::category<std::string>({"A", "B"}));
|
||||
BOOST_TEST_THROWS(c.axis().value(0), std::runtime_error);
|
||||
}
|
||||
|
||||
// reduce
|
||||
{
|
||||
auto h1 = make_dynamic_histogram(axis::integer<>(0, 2), axis::integer<>(0, 3));
|
||||
auto h1 = make(dynamic_tag(), axis::integer<>(0, 2), axis::integer<>(0, 3));
|
||||
h1(0, 0);
|
||||
h1(0, 1);
|
||||
h1(1, 0);
|
||||
@@ -70,7 +69,7 @@ int main() {
|
||||
|
||||
x = {0};
|
||||
auto h1_0 = h1.reduce_to(x.begin(), x.end());
|
||||
BOOST_TEST_EQ(h1_0.dim(), 1);
|
||||
BOOST_TEST_EQ(h1_0.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h1_0), 5);
|
||||
BOOST_TEST_EQ(h1_0.at(0), 2);
|
||||
BOOST_TEST_EQ(h1_0.at(1), 3);
|
||||
@@ -78,7 +77,7 @@ int main() {
|
||||
|
||||
x = {1};
|
||||
auto h1_1 = h1.reduce_to(x.begin(), x.end());
|
||||
BOOST_TEST_EQ(h1_1.dim(), 1);
|
||||
BOOST_TEST_EQ(h1_1.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h1_1), 5);
|
||||
BOOST_TEST_EQ(h1_1.at(0), 2);
|
||||
BOOST_TEST_EQ(h1_1.at(1), 2);
|
||||
@@ -88,13 +87,14 @@ int main() {
|
||||
|
||||
// histogram iterator
|
||||
{
|
||||
auto h = make_dynamic_histogram(axis::integer<>(0, 3));
|
||||
auto h =
|
||||
make_s(dynamic_tag(), array_storage<weight_counter<>>(), axis::integer<>(0, 3));
|
||||
const auto& a = h.axis();
|
||||
h(weight(2), 0);
|
||||
h(1);
|
||||
h(1);
|
||||
auto it = h.begin();
|
||||
BOOST_TEST_EQ(it.dim(), 1);
|
||||
BOOST_TEST_EQ(it.rank(), 1);
|
||||
|
||||
BOOST_TEST_EQ(it.idx(0), 0);
|
||||
BOOST_TEST_EQ(it.bin(0), a[0]);
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
#include <boost/archive/text_oarchive.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
#include <boost/archive/text_oarchive.hpp>
|
||||
#include <boost/histogram/serialization.hpp>
|
||||
#include <boost/histogram/ostream_operators.hpp>
|
||||
#include <string>
|
||||
#include <boost/histogram/serialization.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
@@ -21,13 +21,14 @@ template <typename Tag>
|
||||
void run_tests() {
|
||||
// histogram_serialization
|
||||
{
|
||||
enum { A, B, C };
|
||||
auto a = make(
|
||||
Tag(), axis::regular<>(3, -1, 1, "r"), axis::circular<>(4, 0.0, 1.0, "p"),
|
||||
axis::regular<axis::transform::log>(3, 1, 100, "lr"),
|
||||
axis::regular<axis::transform::pow>(3, 1, 100, "pr", axis::uoflow_type::on, 0.5),
|
||||
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "v"), axis::category<>{A, B, C},
|
||||
axis::integer<>(0, 2, "i"));
|
||||
auto a = make(Tag(), axis::regular<>(3, -1, 1, "axis 0"),
|
||||
axis::circular<>(4, 0.0, 1.0, "axis 1"),
|
||||
axis::regular<axis::transform::log<>>(3, 1, 100, "axis 2"),
|
||||
axis::regular<axis::transform::pow<>>(0.5, 3, 1, 100, "axis 3",
|
||||
axis::option_type::overflow),
|
||||
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"),
|
||||
axis::category<>{3, 1, 2},
|
||||
axis::integer<int, axis::empty_metadata_type>(0, 2));
|
||||
a(0.5, 0.2, 20, 20, 0.25, 1, 1);
|
||||
std::string buf;
|
||||
{
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2018 Hans Dembinski
|
||||
//
|
||||
// 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)
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
|
||||
using namespace boost::histogram;
|
||||
int main() {
|
||||
auto a = make_static_histogram(axis::integer<>(0, 2));
|
||||
auto b = make_static_histogram(axis::integer<>(0, 3));
|
||||
a += b;
|
||||
}
|
||||
@@ -5,9 +5,11 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/histogram.hpp>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
int main() {
|
||||
auto h = make_static_histogram(axis::integer<>(0, 2));
|
||||
auto h = make_histogram(axis::integer<>(0, 2));
|
||||
h.at(std::vector<int>({0, 0}));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <boost/histogram/storage/adaptive_storage.hpp>
|
||||
#include <boost/histogram/storage/array_storage.hpp>
|
||||
#include <boost/histogram/storage/weight_counter.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
@@ -38,60 +39,59 @@ void run_tests() {
|
||||
// init_1
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1});
|
||||
BOOST_TEST_EQ(h.dim(), 1);
|
||||
BOOST_TEST_EQ(h.rank(), 1);
|
||||
BOOST_TEST_EQ(h.size(), 5);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 5);
|
||||
BOOST_TEST_EQ(h.axis().shape(), 5);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 3);
|
||||
BOOST_TEST_EQ(h.axis().size(), 3);
|
||||
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1});
|
||||
BOOST_TEST_EQ(h2, h);
|
||||
}
|
||||
|
||||
// init_2
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2});
|
||||
BOOST_TEST_EQ(h.dim(), 2);
|
||||
BOOST_TEST_EQ(h.size(), 25);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 5);
|
||||
BOOST_TEST_EQ(h.axis(1_c).shape(), 5);
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 3});
|
||||
BOOST_TEST_EQ(h.rank(), 2);
|
||||
BOOST_TEST_EQ(h.size(), 30);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 3);
|
||||
BOOST_TEST_EQ(h.axis(1_c).size(), 4);
|
||||
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
|
||||
axis::integer<>{-1, 2});
|
||||
axis::integer<>{-1, 3});
|
||||
BOOST_TEST_EQ(h2, h);
|
||||
}
|
||||
|
||||
// init_3
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2},
|
||||
axis::circular<>{3});
|
||||
BOOST_TEST_EQ(h.dim(), 3);
|
||||
BOOST_TEST_EQ(h.size(), 75);
|
||||
axis::circular<>{2});
|
||||
BOOST_TEST_EQ(h.rank(), 3);
|
||||
BOOST_TEST_EQ(h.size(), 5 * 5 * 3);
|
||||
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
|
||||
axis::integer<>{-1, 2}, axis::circular<>{3});
|
||||
axis::integer<>{-1, 2}, axis::circular<>{2});
|
||||
BOOST_TEST_EQ(h2, h);
|
||||
}
|
||||
|
||||
// init_4
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2},
|
||||
axis::circular<>{3}, axis::variable<>{-1, 0, 1});
|
||||
BOOST_TEST_EQ(h.dim(), 4);
|
||||
BOOST_TEST_EQ(h.size(), 300);
|
||||
axis::circular<>{2}, axis::variable<>{-1, 0, 1});
|
||||
BOOST_TEST_EQ(h.rank(), 4);
|
||||
BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4);
|
||||
auto h2 =
|
||||
make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
|
||||
axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1});
|
||||
axis::integer<>{-1, 2}, axis::circular<>{2}, axis::variable<>{-1, 0, 1});
|
||||
BOOST_TEST_EQ(h2, h);
|
||||
}
|
||||
|
||||
// init_5
|
||||
{
|
||||
enum { A, B, C };
|
||||
auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2},
|
||||
axis::circular<>{3}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{A, B, C}});
|
||||
BOOST_TEST_EQ(h.dim(), 5);
|
||||
BOOST_TEST_EQ(h.size(), 1200);
|
||||
axis::circular<>{2}, axis::variable<>{-1, 0, 1},
|
||||
axis::category<>{{3, 1, 2}});
|
||||
BOOST_TEST_EQ(h.rank(), 5);
|
||||
BOOST_TEST_EQ(h.size(), 5 * 5 * 3 * 4 * 4);
|
||||
auto h2 = make_s(Tag(), array_storage<unsigned>(), axis::regular<>{3, -1, 1},
|
||||
axis::integer<>{-1, 2}, axis::circular<>{3},
|
||||
axis::variable<>{-1, 0, 1}, axis::category<>{{A, B, C}});
|
||||
axis::integer<>{-1, 2}, axis::circular<>{2},
|
||||
axis::variable<>{-1, 0, 1}, axis::category<>{{3, 1, 2}});
|
||||
BOOST_TEST_EQ(h2, h);
|
||||
}
|
||||
|
||||
@@ -131,51 +131,49 @@ void run_tests() {
|
||||
const auto href = h;
|
||||
decltype(h) h2(std::move(h));
|
||||
// static axes cannot shrink to zero
|
||||
BOOST_TEST_EQ(h.dim(), expected_moved_from_dim(Tag(), 2));
|
||||
BOOST_TEST_EQ(sum(h).value(), 0);
|
||||
BOOST_TEST_EQ(h.rank(), expected_moved_from_dim(Tag(), 2));
|
||||
BOOST_TEST_EQ(sum(h), 0);
|
||||
BOOST_TEST_EQ(h.size(), 0);
|
||||
BOOST_TEST_EQ(h2, href);
|
||||
decltype(h) h3;
|
||||
h3 = std::move(h2);
|
||||
// static axes cannot shrink to zero
|
||||
BOOST_TEST_EQ(h2.dim(), expected_moved_from_dim(Tag(), 2));
|
||||
BOOST_TEST_EQ(sum(h2).value(), 0);
|
||||
BOOST_TEST_EQ(h2.rank(), expected_moved_from_dim(Tag(), 2));
|
||||
BOOST_TEST_EQ(sum(h2), 0);
|
||||
BOOST_TEST_EQ(h2.size(), 0);
|
||||
BOOST_TEST_EQ(h3, href);
|
||||
}
|
||||
|
||||
// axis methods
|
||||
{
|
||||
enum { A = 3, B = 5 };
|
||||
auto a = make(Tag(), axis::regular<>(1, 1, 2, "foo"));
|
||||
BOOST_TEST_EQ(a.axis().size(), 1);
|
||||
BOOST_TEST_EQ(a.axis().shape(), 3);
|
||||
BOOST_TEST_EQ(a.axis().index(1), 0);
|
||||
BOOST_TEST_EQ(a.axis()[0].lower(), 1);
|
||||
BOOST_TEST_EQ(a.axis()[0].upper(), 2);
|
||||
BOOST_TEST_EQ(a.axis().label(), "foo");
|
||||
a.axis().label("bar");
|
||||
BOOST_TEST_EQ(a.axis().label(), "bar");
|
||||
BOOST_TEST_EQ(a.axis().metadata(), "foo");
|
||||
a.axis().metadata() = "bar";
|
||||
BOOST_TEST_EQ(a.axis().metadata(), "bar");
|
||||
|
||||
auto b = make(Tag(), axis::integer<>(1, 2));
|
||||
BOOST_TEST_EQ(b.axis().size(), 1);
|
||||
BOOST_TEST_EQ(b.axis().shape(), 3);
|
||||
BOOST_TEST_EQ(b.axis().index(1), 0);
|
||||
BOOST_TEST_EQ(b.axis()[0].lower(), 1);
|
||||
BOOST_TEST_EQ(b.axis()[0].upper(), 2);
|
||||
b.axis().label("foo");
|
||||
BOOST_TEST_EQ(b.axis().label(), "foo");
|
||||
auto b = make(Tag(), axis::regular<>(1, 1, 2, "foo"),
|
||||
axis::integer<>(1, 3));
|
||||
BOOST_TEST_EQ(b.axis(0_c).size(), 1);
|
||||
BOOST_TEST_EQ(b.axis(0_c)[0].lower(), 1);
|
||||
BOOST_TEST_EQ(b.axis(0_c)[0].upper(), 2);
|
||||
BOOST_TEST_EQ(b.axis(1_c).size(), 2);
|
||||
BOOST_TEST_EQ(b.axis(1_c)[0].lower(), 1);
|
||||
BOOST_TEST_EQ(b.axis(1_c)[0].upper(), 2);
|
||||
b.axis(1_c).metadata() = "bar";
|
||||
BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo");
|
||||
BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar");
|
||||
|
||||
auto c = make(Tag(), axis::category<>({A, B}));
|
||||
enum class C { A = 3, B = 5 };
|
||||
auto c = make(Tag(), axis::category<C>({C::A, C::B}));
|
||||
BOOST_TEST_EQ(c.axis().size(), 2);
|
||||
BOOST_TEST_EQ(c.axis().shape(), 3);
|
||||
BOOST_TEST_EQ(c.axis().index(A), 0);
|
||||
BOOST_TEST_EQ(c.axis().index(B), 1);
|
||||
c.axis().label("foo");
|
||||
BOOST_TEST_EQ(c.axis().label(), "foo");
|
||||
c.axis().metadata() = "foo";
|
||||
BOOST_TEST_EQ(c.axis().metadata(), "foo");
|
||||
// need to cast here for this to work with Tag == dynamic_tag, too
|
||||
auto ca = static_cast<const axis::category<>&>(c.axis());
|
||||
BOOST_TEST_EQ(ca[0].value(), A);
|
||||
auto ca = axis::get<axis::category<C>>(c.axis());
|
||||
BOOST_TEST(ca[0].value() == C::A);
|
||||
}
|
||||
|
||||
// equal_compare
|
||||
@@ -211,9 +209,8 @@ void run_tests() {
|
||||
h(-1);
|
||||
h(10);
|
||||
|
||||
BOOST_TEST_EQ(h.dim(), 1);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 4);
|
||||
BOOST_TEST_EQ(h.rank(), 1);
|
||||
BOOST_TEST_EQ(h.axis().size(), 2);
|
||||
BOOST_TEST_EQ(sum(h), 4);
|
||||
|
||||
BOOST_TEST_EQ(h.at(-1), 1);
|
||||
@@ -224,15 +221,14 @@ void run_tests() {
|
||||
|
||||
// d1_2
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off));
|
||||
auto h = make(Tag(), axis::integer<>(0, 2, "", axis::option_type::none));
|
||||
h(0);
|
||||
h(-0);
|
||||
h(-1);
|
||||
h(10);
|
||||
|
||||
BOOST_TEST_EQ(h.dim(), 1);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 2);
|
||||
BOOST_TEST_EQ(h.rank(), 1);
|
||||
BOOST_TEST_EQ(h.axis().size(), 2);
|
||||
BOOST_TEST_EQ(sum(h), 2);
|
||||
|
||||
BOOST_TEST_EQ(h.at(0), 2);
|
||||
@@ -247,18 +243,19 @@ void run_tests() {
|
||||
h("D");
|
||||
h("E");
|
||||
|
||||
BOOST_TEST_EQ(h.dim(), 1);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 3);
|
||||
BOOST_TEST_EQ(h.rank(), 1);
|
||||
BOOST_TEST_EQ(h.axis().size(), 2);
|
||||
BOOST_TEST_EQ(sum(h), 4);
|
||||
|
||||
BOOST_TEST_EQ(h.at(0), 1);
|
||||
BOOST_TEST_EQ(h.at(1), 1);
|
||||
BOOST_TEST_EQ(h.at(2), 2); // overflow bin
|
||||
}
|
||||
|
||||
// d1w
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 2));
|
||||
auto h = make_s(Tag(), array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 2));
|
||||
h(-1);
|
||||
h(0);
|
||||
h(weight(0.5), 0);
|
||||
@@ -282,17 +279,15 @@ void run_tests() {
|
||||
// d2
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>(2, -1, 1),
|
||||
axis::integer<>(-1, 2, "", axis::uoflow_type::off));
|
||||
axis::integer<>(-1, 2, {}, axis::option_type::none));
|
||||
h(-1, -1);
|
||||
h(-1, 0);
|
||||
h(-1, -10);
|
||||
h(-10, 0);
|
||||
|
||||
BOOST_TEST_EQ(h.dim(), 2);
|
||||
BOOST_TEST_EQ(h.rank(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0_c).size(), 2);
|
||||
BOOST_TEST_EQ(h.axis(0_c).shape(), 4);
|
||||
BOOST_TEST_EQ(h.axis(1_c).size(), 3);
|
||||
BOOST_TEST_EQ(h.axis(1_c).shape(), 3);
|
||||
BOOST_TEST_EQ(sum(h), 3);
|
||||
|
||||
BOOST_TEST_EQ(h.at(-1, 0), 0);
|
||||
@@ -314,8 +309,10 @@ void run_tests() {
|
||||
|
||||
// d2w
|
||||
{
|
||||
auto h = make(Tag(), axis::regular<>(2, -1, 1),
|
||||
axis::integer<>(-1, 2, "", axis::uoflow_type::off));
|
||||
auto h = make_s(Tag(),
|
||||
array_storage<weight_counter<>>(),
|
||||
axis::regular<>(2, -1, 1),
|
||||
axis::integer<>(-1, 2, {}, axis::option_type::none));
|
||||
h(-1, 0); // -> 0, 1
|
||||
h(weight(10), -1, -1); // -> 0, 0
|
||||
h(weight(5), -1, -10); // is ignored
|
||||
@@ -359,17 +356,20 @@ void run_tests() {
|
||||
|
||||
// d3w
|
||||
{
|
||||
auto h =
|
||||
make(Tag(), axis::integer<>(0, 3), axis::integer<>(0, 4), axis::integer<>(0, 5));
|
||||
for (auto i = 0; i < h.axis(0_c).size(); ++i) {
|
||||
for (auto j = 0; j < h.axis(1_c).size(); ++j) {
|
||||
for (auto k = 0; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); }
|
||||
auto h = make_s(Tag(),
|
||||
array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 3),
|
||||
axis::integer<>(0, 4),
|
||||
axis::integer<>(0, 5));
|
||||
for (auto i = 0u; i < h.axis(0_c).size(); ++i) {
|
||||
for (auto j = 0u; j < h.axis(1_c).size(); ++j) {
|
||||
for (auto k = 0u; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); }
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = 0; i < h.axis(0_c).size(); ++i) {
|
||||
for (auto j = 0; j < h.axis(1_c).size(); ++j) {
|
||||
for (auto k = 0; k < h.axis(2_c).size(); ++k) {
|
||||
for (auto i = 0u; i < h.axis(0_c).size(); ++i) {
|
||||
for (auto j = 0u; j < h.axis(1_c).size(); ++j) {
|
||||
for (auto k = 0u; k < h.axis(2_c).size(); ++k) {
|
||||
BOOST_TEST_EQ(h.at(i, j, k).value(), i + j + k);
|
||||
BOOST_TEST_EQ(h.at(i, j, k).variance(), (i + j + k) * (i + j + k));
|
||||
}
|
||||
@@ -402,8 +402,10 @@ void run_tests() {
|
||||
|
||||
// add_2
|
||||
{
|
||||
auto a = make(Tag(), axis::integer<>(0, 2));
|
||||
auto b = make(Tag(), axis::integer<>(0, 2));
|
||||
auto a = make_s(Tag(), array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 2));
|
||||
auto b = make_s(Tag(), array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 2));
|
||||
|
||||
a(0);
|
||||
BOOST_TEST_EQ(a.at(0).variance(), 1);
|
||||
@@ -449,7 +451,20 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(d.at(3), 0);
|
||||
}
|
||||
|
||||
// functional programming
|
||||
// bad add
|
||||
{
|
||||
auto va = std::vector<axis::variant<axis::integer<>>>();
|
||||
va.push_back(axis::integer<>(0, 2));
|
||||
auto a = make_histogram(va);
|
||||
|
||||
auto vb = std::vector<axis::variant<axis::integer<>>>();
|
||||
vb.push_back(axis::integer<>(0, 3));
|
||||
auto b = make_histogram(vb);
|
||||
|
||||
BOOST_TEST_THROWS(a += b, std::invalid_argument);
|
||||
}
|
||||
|
||||
// STL support
|
||||
{
|
||||
auto v = std::vector<int>{0, 1, 2};
|
||||
auto h = std::for_each(v.begin(), v.end(), make(Tag(), axis::integer<>(0, 3)));
|
||||
@@ -457,6 +472,12 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(h.at(1), 1);
|
||||
BOOST_TEST_EQ(h.at(2), 1);
|
||||
BOOST_TEST_EQ(sum(h), 3);
|
||||
|
||||
auto a = std::vector<double>();
|
||||
std::partial_sum(h.begin(), h.end(), std::back_inserter(a));
|
||||
BOOST_TEST_EQ(a[0], 1);
|
||||
BOOST_TEST_EQ(a[1], 2);
|
||||
BOOST_TEST_EQ(a[2], 3);
|
||||
}
|
||||
|
||||
// operators
|
||||
@@ -476,20 +497,20 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(d.at(1), 3);
|
||||
auto e = 3 * a;
|
||||
auto f = b * 2;
|
||||
BOOST_TEST_EQ(e.at(0).value(), 3);
|
||||
BOOST_TEST_EQ(e.at(1).value(), 0);
|
||||
BOOST_TEST_EQ(f.at(0).value(), 0);
|
||||
BOOST_TEST_EQ(f.at(1).value(), 2);
|
||||
BOOST_TEST_EQ(e.at(0), 3);
|
||||
BOOST_TEST_EQ(e.at(1), 0);
|
||||
BOOST_TEST_EQ(f.at(0), 0);
|
||||
BOOST_TEST_EQ(f.at(1), 2);
|
||||
auto r = a;
|
||||
r += b;
|
||||
r += e;
|
||||
BOOST_TEST_EQ(r.at(0).value(), 4);
|
||||
BOOST_TEST_EQ(r.at(1).value(), 1);
|
||||
BOOST_TEST_EQ(r.at(0), 4);
|
||||
BOOST_TEST_EQ(r.at(1), 1);
|
||||
BOOST_TEST_EQ(r, a + b + 3 * a);
|
||||
auto s = r / 4;
|
||||
r /= 4;
|
||||
BOOST_TEST_EQ(r.at(0).value(), 1);
|
||||
BOOST_TEST_EQ(r.at(1).value(), 0.25);
|
||||
BOOST_TEST_EQ(r.at(0), 1);
|
||||
BOOST_TEST_EQ(r.at(1), 0.25);
|
||||
BOOST_TEST_EQ(r, s);
|
||||
}
|
||||
|
||||
@@ -499,15 +520,17 @@ void run_tests() {
|
||||
std::ostringstream os;
|
||||
os << a;
|
||||
BOOST_TEST_EQ(os.str(),
|
||||
"histogram("
|
||||
"\n regular(3, -1, 1, label='r'),"
|
||||
"\n integer(0, 2, label='i'),"
|
||||
"\n)");
|
||||
std::string(
|
||||
"histogram(\n"
|
||||
" regular(3, -1, 1, metadata=\"r\", options=underflow_and_overflow),\n"
|
||||
" integer(0, 2, metadata=\"i\", options=underflow_and_overflow),\n"
|
||||
")"
|
||||
));
|
||||
}
|
||||
|
||||
// histogram_reset
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off));
|
||||
auto h = make(Tag(), axis::integer<>(0, 2, {}, axis::option_type::none));
|
||||
h(0);
|
||||
h(1);
|
||||
BOOST_TEST_EQ(h.at(0), 1);
|
||||
@@ -540,16 +563,14 @@ void run_tests() {
|
||||
*/
|
||||
|
||||
auto h1_0 = h1.reduce_to(0_c);
|
||||
BOOST_TEST_EQ(h1_0.dim(), 1);
|
||||
BOOST_TEST_EQ(h1_0.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h1_0), 5);
|
||||
BOOST_TEST_EQ(h1_0.at(0), 2);
|
||||
BOOST_TEST_EQ(h1_0.at(1), 3);
|
||||
BOOST_TEST_EQ(h1_0.axis()[0].lower(), 0);
|
||||
BOOST_TEST_EQ(h1_0.axis()[1].lower(), 1);
|
||||
BOOST_TEST(h1_0.axis() == h1.axis(0_c));
|
||||
|
||||
auto h1_1 = h1.reduce_to(1_c);
|
||||
BOOST_TEST_EQ(h1_1.dim(), 1);
|
||||
BOOST_TEST_EQ(h1_1.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h1_1), 5);
|
||||
BOOST_TEST_EQ(h1_1.at(0), 2);
|
||||
BOOST_TEST_EQ(h1_1.at(1), 2);
|
||||
@@ -565,21 +586,21 @@ void run_tests() {
|
||||
h2(1, 0, 2);
|
||||
|
||||
auto h2_0 = h2.reduce_to(0_c);
|
||||
BOOST_TEST_EQ(h2_0.dim(), 1);
|
||||
BOOST_TEST_EQ(h2_0.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h2_0), 5);
|
||||
BOOST_TEST_EQ(h2_0.at(0), 4);
|
||||
BOOST_TEST_EQ(h2_0.at(1), 1);
|
||||
BOOST_TEST(h2_0.axis() == axis::integer<>(0, 2));
|
||||
|
||||
auto h2_1 = h2.reduce_to(1_c);
|
||||
BOOST_TEST_EQ(h2_1.dim(), 1);
|
||||
BOOST_TEST_EQ(h2_1.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h2_1), 5);
|
||||
BOOST_TEST_EQ(h2_1.at(0), 3);
|
||||
BOOST_TEST_EQ(h2_1.at(1), 2);
|
||||
BOOST_TEST(h2_1.axis() == axis::integer<>(0, 3));
|
||||
|
||||
auto h2_2 = h2.reduce_to(2_c);
|
||||
BOOST_TEST_EQ(h2_2.dim(), 1);
|
||||
BOOST_TEST_EQ(h2_2.rank(), 1);
|
||||
BOOST_TEST_EQ(sum(h2_2), 5);
|
||||
BOOST_TEST_EQ(h2_2.at(0), 2);
|
||||
BOOST_TEST_EQ(h2_2.at(1), 1);
|
||||
@@ -587,7 +608,7 @@ void run_tests() {
|
||||
BOOST_TEST(h2_2.axis() == axis::integer<>(0, 4));
|
||||
|
||||
auto h2_01 = h2.reduce_to(0_c, 1_c);
|
||||
BOOST_TEST_EQ(h2_01.dim(), 2);
|
||||
BOOST_TEST_EQ(h2_01.rank(), 2);
|
||||
BOOST_TEST_EQ(sum(h2_01), 5);
|
||||
BOOST_TEST_EQ(h2_01.at(0, 0), 2);
|
||||
BOOST_TEST_EQ(h2_01.at(0, 1), 2);
|
||||
@@ -596,7 +617,7 @@ void run_tests() {
|
||||
BOOST_TEST(h2_01.axis(1_c) == axis::integer<>(0, 3));
|
||||
|
||||
auto h2_02 = h2.reduce_to(0_c, 2_c);
|
||||
BOOST_TEST_EQ(h2_02.dim(), 2);
|
||||
BOOST_TEST_EQ(h2_02.rank(), 2);
|
||||
BOOST_TEST_EQ(sum(h2_02), 5);
|
||||
BOOST_TEST_EQ(h2_02.at(0, 0), 2);
|
||||
BOOST_TEST_EQ(h2_02.at(0, 1), 1);
|
||||
@@ -606,7 +627,7 @@ void run_tests() {
|
||||
BOOST_TEST(h2_02.axis(1_c) == axis::integer<>(0, 4));
|
||||
|
||||
auto h2_12 = h2.reduce_to(1_c, 2_c);
|
||||
BOOST_TEST_EQ(h2_12.dim(), 2);
|
||||
BOOST_TEST_EQ(h2_12.rank(), 2);
|
||||
BOOST_TEST_EQ(sum(h2_12), 5);
|
||||
BOOST_TEST_EQ(h2_12.at(0, 0), 1);
|
||||
BOOST_TEST_EQ(h2_12.at(1, 0), 1);
|
||||
@@ -619,14 +640,12 @@ void run_tests() {
|
||||
// custom axis
|
||||
{
|
||||
struct custom_axis : public axis::integer<> {
|
||||
using value_type = const char*; // type that is fed to the axis
|
||||
|
||||
using integer::integer; // inherit ctors of base
|
||||
|
||||
// the customization point
|
||||
// - accept const char* and convert to int
|
||||
// - then call index method of base class
|
||||
int index(value_type s) const { return integer::index(std::atoi(s)); }
|
||||
// customization point: convert argument and call base class
|
||||
int operator()(const char* s) const {
|
||||
return integer::operator()(std::atoi(s));
|
||||
}
|
||||
};
|
||||
|
||||
auto h = make(Tag(), custom_axis(0, 3));
|
||||
@@ -635,7 +654,7 @@ void run_tests() {
|
||||
h("1");
|
||||
h("9");
|
||||
|
||||
BOOST_TEST_EQ(h.dim(), 1);
|
||||
BOOST_TEST_EQ(h.rank(), 1);
|
||||
BOOST_TEST_EQ(h.axis(), custom_axis(0, 3));
|
||||
BOOST_TEST_EQ(h.at(0), 1);
|
||||
BOOST_TEST_EQ(h.at(1), 1);
|
||||
@@ -644,13 +663,14 @@ void run_tests() {
|
||||
|
||||
// histogram iterator 1D
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 3));
|
||||
auto h = make_s(Tag(), array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 3));
|
||||
const auto& a = h.axis();
|
||||
h(weight(2), 0);
|
||||
h(1);
|
||||
h(1);
|
||||
auto it = h.begin();
|
||||
BOOST_TEST_EQ(it.dim(), 1);
|
||||
BOOST_TEST_EQ(it.rank(), 1);
|
||||
|
||||
BOOST_TEST_EQ(it.idx(), 0);
|
||||
BOOST_TEST_EQ(it.bin(), a[0]);
|
||||
@@ -678,8 +698,10 @@ void run_tests() {
|
||||
|
||||
// histogram iterator 2D
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 1),
|
||||
axis::integer<>(2, 4, "", axis::uoflow_type::off));
|
||||
auto h = make_s(Tag(),
|
||||
array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 1),
|
||||
axis::integer<>(2, 4, "", axis::option_type::none));
|
||||
const auto& a0 = h.axis(0_c);
|
||||
const auto& a1 = h.axis(1_c);
|
||||
h(weight(2), 0, 2);
|
||||
@@ -687,7 +709,7 @@ void run_tests() {
|
||||
h(1, 3);
|
||||
|
||||
auto it = h.begin();
|
||||
BOOST_TEST_EQ(it.dim(), 2);
|
||||
BOOST_TEST_EQ(it.rank(), 2);
|
||||
|
||||
BOOST_TEST_EQ(it.idx(0), 0);
|
||||
BOOST_TEST_EQ(it.idx(1), 0);
|
||||
@@ -738,20 +760,11 @@ void run_tests() {
|
||||
BOOST_TEST_EQ(v.variance(), 6);
|
||||
}
|
||||
|
||||
// STL compatibility
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 3));
|
||||
for (int i = 0; i < 3; ++i) h(i);
|
||||
auto a = std::vector<weight_counter<double>>();
|
||||
std::partial_sum(h.begin(), h.end(), std::back_inserter(a));
|
||||
BOOST_TEST_EQ(a[0].value(), 1);
|
||||
BOOST_TEST_EQ(a[1].value(), 2);
|
||||
BOOST_TEST_EQ(a[2].value(), 3);
|
||||
}
|
||||
|
||||
// using STL containers
|
||||
{
|
||||
auto h = make(Tag(), axis::integer<>(0, 2), axis::regular<>(2, 2, 4));
|
||||
auto h = make_s(Tag(),
|
||||
array_storage<weight_counter<>>(),
|
||||
axis::integer<>(0, 2), axis::regular<>(2, 2, 4));
|
||||
// vector in
|
||||
h(std::vector<int>({0, 2}));
|
||||
// pair in
|
||||
@@ -805,24 +818,19 @@ void run_tests() {
|
||||
tracing_allocator_db db;
|
||||
{
|
||||
tracing_allocator<char> a(db);
|
||||
auto h = make_s(Tag(), array_storage<int, int, tracing_allocator<int>>(a),
|
||||
axis::integer<int, tracing_allocator<char>>(
|
||||
0, 1024, std::string(512, 'c'), axis::uoflow_type::on, a));
|
||||
auto h = make_s(Tag(), array_storage<int, tracing_allocator<int>>(a),
|
||||
axis::integer<>(0, 1000));
|
||||
h(0);
|
||||
}
|
||||
|
||||
// int allocation for array_storage
|
||||
BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second);
|
||||
BOOST_TEST_GE(db[typeid(int)].first, 1024u);
|
||||
BOOST_TEST_EQ(db[typeid(int)].first, 1002u);
|
||||
|
||||
// char allocation for axis label
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second);
|
||||
BOOST_TEST_GE(db[typeid(char)].first, 512u);
|
||||
|
||||
if (Tag()) { // axis::any allocation, only for dynamic histogram
|
||||
using T = axis::any<axis::integer<int, tracing_allocator<char>>>;
|
||||
if (Tag()) { // axis::variant allocation, only for dynamic histogram
|
||||
using T = axis::variant<axis::integer<>>;
|
||||
BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second);
|
||||
BOOST_TEST_GE(db[typeid(T)].first, 1u);
|
||||
BOOST_TEST_LE(db[typeid(T)].first, 1u); // zero if vector uses small-vector-optimisation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,28 @@
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace boost::histogram::detail;
|
||||
using namespace boost::histogram::literals;
|
||||
namespace bh = boost::histogram;
|
||||
using namespace bh::detail;
|
||||
using namespace bh::literals;
|
||||
namespace mp11 = boost::mp11;
|
||||
|
||||
struct VisitorTestFunctor {
|
||||
template <typename T>
|
||||
T operator()(T&&);
|
||||
};
|
||||
|
||||
int main() {
|
||||
// literals
|
||||
{
|
||||
@@ -34,67 +43,166 @@ int main() {
|
||||
|
||||
// has_variance_support
|
||||
{
|
||||
struct no_methods {};
|
||||
struct A {};
|
||||
|
||||
struct value_method {
|
||||
struct B {
|
||||
void value() {}
|
||||
};
|
||||
|
||||
struct variance_method {
|
||||
struct C {
|
||||
void variance() {}
|
||||
};
|
||||
|
||||
struct value_and_variance_methods {
|
||||
struct D {
|
||||
void value() {}
|
||||
void variance() {}
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<no_methods>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<value_method>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<variance_method>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_variance_support<value_and_variance_methods>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<B>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_variance_support<C>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_variance_support<D>));
|
||||
}
|
||||
|
||||
// has_method_lower
|
||||
// has_method_value
|
||||
{
|
||||
struct no_methods {};
|
||||
struct lower_method {
|
||||
void lower(int) {}
|
||||
struct A {};
|
||||
struct B {
|
||||
void value(int) {}
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_lower<no_methods>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_lower<lower_method>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<B>));
|
||||
}
|
||||
|
||||
// has_method_options
|
||||
{
|
||||
struct NotOptions {};
|
||||
struct A {};
|
||||
struct B {
|
||||
NotOptions options();
|
||||
};
|
||||
struct C {
|
||||
bh::axis::option_type options();
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_options<A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_options<B>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_options<C>));
|
||||
}
|
||||
|
||||
// has_method_metadata
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
void metadata();
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_metadata<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_metadata<B>));
|
||||
}
|
||||
|
||||
// is_transform
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
double forward(double);
|
||||
double inverse(double);
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_transform<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_transform<B>));
|
||||
}
|
||||
|
||||
// is_equal_comparable
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
bool operator==(const B&);
|
||||
};
|
||||
BOOST_TEST_TRAIT_TRUE((is_equal_comparable<int>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_equal_comparable<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_equal_comparable<B>));
|
||||
}
|
||||
|
||||
// is_axis
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
int operator()(double);
|
||||
unsigned size() const;
|
||||
};
|
||||
struct C {
|
||||
int operator()(double);
|
||||
};
|
||||
struct D {
|
||||
unsigned size();
|
||||
};
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis<B>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis<C>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis<D>));
|
||||
}
|
||||
|
||||
// is_iterable
|
||||
{
|
||||
using A = std::vector<int>;
|
||||
using B = int[3];
|
||||
using C = std::initializer_list<int>;
|
||||
BOOST_TEST_TRAIT_FALSE((is_iterable<int>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_iterable<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_iterable<B>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_iterable<C>));
|
||||
}
|
||||
|
||||
// is_streamable
|
||||
{
|
||||
struct Foo {};
|
||||
BOOST_TEST_TRAIT_TRUE((is_streamable<int>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_streamable<std::string>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_streamable<Foo>));
|
||||
}
|
||||
|
||||
// is_axis_variant
|
||||
{
|
||||
struct A {};
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis_variant<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis_variant<bh::axis::variant<>>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis_variant<bh::axis::variant<bh::axis::regular<>>>));
|
||||
}
|
||||
|
||||
// classify_container
|
||||
{
|
||||
using result1 = classify_container<int>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result1, no_container_tag>));
|
||||
using A = classify_container<int>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<A, no_container_tag>));
|
||||
|
||||
using result1a = classify_container<int&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result1a, no_container_tag>));
|
||||
using B = classify_container<int&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<B, no_container_tag>));
|
||||
|
||||
using result2 = classify_container<std::vector<int>>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result2, dynamic_container_tag>));
|
||||
using C = classify_container<std::vector<int>>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<C, iterable_container_tag>));
|
||||
|
||||
using result2a = classify_container<std::vector<int>&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result2a, dynamic_container_tag>));
|
||||
using D = classify_container<std::vector<int>&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<D, iterable_container_tag>));
|
||||
|
||||
using result3 = classify_container<std::pair<int, int>>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result3, static_container_tag>));
|
||||
using E = classify_container<std::pair<int, int>>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<E, static_container_tag>));
|
||||
|
||||
using result3a = classify_container<std::pair<int, int>&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result3a, static_container_tag>));
|
||||
using F = classify_container<std::pair<int, int>&>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<F, static_container_tag>));
|
||||
|
||||
// (c-)strings are not regarded as dynamic containers
|
||||
using result4a = classify_container<decltype("abc")>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result4a, no_container_tag>));
|
||||
using G = classify_container<std::string>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<G, iterable_container_tag>));
|
||||
|
||||
using result4b = classify_container<std::string>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result4b, no_container_tag>));
|
||||
using H = classify_container<int*>; // has no std::end
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<H, no_container_tag>));
|
||||
|
||||
using result5 = classify_container<int*>; // has no std::end
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result5, no_container_tag>));
|
||||
using I = classify_container<std::initializer_list<int>>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<I, iterable_container_tag>));
|
||||
|
||||
auto j = {0, 1};
|
||||
using J = classify_container<decltype(j)>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<J, iterable_container_tag>));
|
||||
}
|
||||
|
||||
// bool mask
|
||||
@@ -106,7 +214,7 @@ int main() {
|
||||
BOOST_TEST_EQ(v2, std::vector<bool>({false, true, false, true}));
|
||||
}
|
||||
|
||||
// rm_cv_ref
|
||||
// rm_cvref
|
||||
{
|
||||
using T1 = int;
|
||||
using T2 = int&&;
|
||||
@@ -116,14 +224,14 @@ int main() {
|
||||
using T6 = volatile int&&;
|
||||
using T7 = volatile const int;
|
||||
using T8 = volatile const int&;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T1>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T2>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T3>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T4>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T5>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T6>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T7>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cv_ref<T8>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T1>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T2>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T3>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T4>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T5>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T6>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T7>, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<rm_cvref<T8>, int>));
|
||||
}
|
||||
|
||||
// mp_size
|
||||
@@ -156,5 +264,81 @@ int main() {
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<result, expected>));
|
||||
}
|
||||
|
||||
// mp_last
|
||||
{
|
||||
using L = mp11::mp_list<int, char, long>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<mp_last<L>, long>));
|
||||
}
|
||||
|
||||
// container_element_type
|
||||
{
|
||||
using T1 = std::vector<int>;
|
||||
using U1 = container_element_type<T1>;
|
||||
using T2 = const std::vector<const int>&;
|
||||
using U2 = container_element_type<T2>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<U1, int>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<U2, const int>));
|
||||
}
|
||||
|
||||
// iterator_value_type
|
||||
{
|
||||
using T1 = const char*;
|
||||
using T2 = std::iterator<std::random_access_iterator_tag, int>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<iterator_value_type<T1>, char>));
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<iterator_value_type<T2>, int>));
|
||||
}
|
||||
|
||||
// args_type
|
||||
{
|
||||
struct Foo {
|
||||
static int f1(char);
|
||||
int f2(long) const;
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<args_type<decltype(&Foo::f1)>, std::tuple<char>>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<args_type<decltype(&Foo::f2)>, std::tuple<long>>));
|
||||
}
|
||||
|
||||
// visitor_return_type
|
||||
{
|
||||
using V1 = bh::axis::variant<char>;
|
||||
using V2 = bh::axis::variant<int>&;
|
||||
using V3 = const bh::axis::variant<long>&;
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V1>, char>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V2>, int&>));
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_same<visitor_return_type<VisitorTestFunctor, V3>, const long&>));
|
||||
}
|
||||
|
||||
// static_if
|
||||
{
|
||||
struct callable {
|
||||
int operator()() { return 1; };
|
||||
};
|
||||
struct not_callable {};
|
||||
auto fcn = [](auto b, auto x) {
|
||||
return static_if<decltype(b)>([](auto x) { return x(); }, [](auto) { return 2; },
|
||||
x);
|
||||
};
|
||||
BOOST_TEST_EQ(fcn(std::true_type(), callable()), 1);
|
||||
BOOST_TEST_EQ(fcn(std::false_type(), not_callable()), 2);
|
||||
}
|
||||
|
||||
// is_axis_vector
|
||||
{
|
||||
using A = std::vector<bh::axis::regular<>>;
|
||||
using B = std::vector<bh::axis::variant<bh::axis::regular<>>>;
|
||||
using C = std::vector<int>;
|
||||
using D = bh::axis::regular<>;
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis_vector<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_axis_vector<B>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis_vector<C>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_axis_vector<D>));
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_HISTOGRAM_TEST_UTILITY_HPP
|
||||
#define BOOST_HISTOGRAM_TEST_UTILITY_HPP
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
@@ -25,7 +26,7 @@ using i2 = boost::mp11::mp_size_t<2>;
|
||||
using i3 = boost::mp11::mp_size_t<3>;
|
||||
|
||||
namespace std {
|
||||
// never add to std, we only do it to get ADL working :(
|
||||
// never add to std, we only do it here to get ADL working :(
|
||||
template <typename T>
|
||||
ostream& operator<<(ostream& os, const vector<T>& v) {
|
||||
os << "[ ";
|
||||
@@ -34,20 +35,10 @@ ostream& operator<<(ostream& os, const vector<T>& v) {
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct ostreamer {
|
||||
ostream& os;
|
||||
template <typename T>
|
||||
void operator()(const T& t) const {
|
||||
os << t << " ";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
ostream& operator<<(ostream& os, const tuple<Ts...>& t) {
|
||||
os << "[ ";
|
||||
::boost::mp11::tuple_for_each(t, detail::ostreamer{os});
|
||||
::boost::mp11::tuple_for_each(t, [&os](const auto& x) { os << x << " "; });
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
@@ -60,31 +51,38 @@ typename Histogram::element_type sum(const Histogram& h) {
|
||||
return std::accumulate(h.begin(), h.end(), typename Histogram::element_type(0));
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
std::vector<axis::variant<detail::rm_cvref<Ts>...>>
|
||||
make_axis_vector(Ts&& ... ts) {
|
||||
using T = axis::variant<detail::rm_cvref<Ts>...>;
|
||||
return std::vector<T>({T(std::forward<Ts>(ts))...});
|
||||
}
|
||||
|
||||
using static_tag = std::false_type;
|
||||
using dynamic_tag = std::true_type;
|
||||
|
||||
template <typename... Axes>
|
||||
auto make(static_tag, Axes&&... axes)
|
||||
-> decltype(make_static_histogram(std::forward<Axes>(axes)...)) {
|
||||
return make_static_histogram(std::forward<Axes>(axes)...);
|
||||
-> decltype(make_histogram(std::forward<Axes>(axes)...)) {
|
||||
return make_histogram(std::forward<Axes>(axes)...);
|
||||
}
|
||||
|
||||
template <typename S, typename... Axes>
|
||||
auto make_s(static_tag, S&& s, Axes&&... axes)
|
||||
-> decltype(make_static_histogram_with(s, std::forward<Axes>(axes)...)) {
|
||||
return make_static_histogram_with(s, std::forward<Axes>(axes)...);
|
||||
-> decltype(make_histogram_with(s, std::forward<Axes>(axes)...)) {
|
||||
return make_histogram_with(s, std::forward<Axes>(axes)...);
|
||||
}
|
||||
|
||||
template <typename... Axes>
|
||||
auto make(dynamic_tag, Axes&&... axes)
|
||||
-> decltype(make_dynamic_histogram<axis::any<detail::rm_cv_ref<Axes>...>>(std::forward<Axes>(axes)...)) {
|
||||
return make_dynamic_histogram<axis::any<detail::rm_cv_ref<Axes>...>>(std::forward<Axes>(axes)...);
|
||||
-> decltype(make_histogram(make_axis_vector(std::forward<Axes>(axes)...))) {
|
||||
return make_histogram(make_axis_vector(std::forward<Axes>(axes)...));
|
||||
}
|
||||
|
||||
template <typename S, typename... Axes>
|
||||
auto make_s(dynamic_tag, S&& s, Axes&&... axes)
|
||||
-> decltype(make_dynamic_histogram_with<axis::any<detail::rm_cv_ref<Axes>...>>(s, std::forward<Axes>(axes)...)) {
|
||||
return make_dynamic_histogram_with<axis::any<detail::rm_cv_ref<Axes>...>>(s, std::forward<Axes>(axes)...);
|
||||
-> decltype(make_histogram_with(s, make_axis_vector(std::forward<Axes>(axes)...))) {
|
||||
return make_histogram_with(s, make_axis_vector(std::forward<Axes>(axes)...));
|
||||
}
|
||||
|
||||
using tracing_allocator_db = std::unordered_map<
|
||||
@@ -132,6 +130,23 @@ constexpr bool operator!=(const tracing_allocator<T>& t,
|
||||
return !operator==(t, u);
|
||||
}
|
||||
|
||||
template <typename Axis>
|
||||
void test_axis_iterator(const Axis& a, int begin, int end) {
|
||||
for (auto bin : a) {
|
||||
BOOST_TEST_EQ(bin.idx(), begin);
|
||||
BOOST_TEST_EQ(bin, a[begin]);
|
||||
++begin;
|
||||
}
|
||||
BOOST_TEST_EQ(begin, end);
|
||||
auto rit = a.rbegin();
|
||||
for (; rit != a.rend(); ++rit) {
|
||||
BOOST_TEST_EQ(rit->idx(), --begin);
|
||||
BOOST_TEST_EQ(*rit, a[begin]);
|
||||
}
|
||||
}
|
||||
|
||||
#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps)
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
||||
Reference in New Issue
Block a user