Merge pull request #126 from HDembinski/feature/axis_generalization

Mega-patch
This commit is contained in:
Hans Dembinski
2018-10-27 19:46:01 +02:00
committed by GitHub
81 changed files with 3749 additions and 2823 deletions

View File

@@ -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
View File

@@ -1,4 +1,5 @@
*build*
histogram.sublime-workspace
CMakeSettings.json
doc/html/.buildinfo
doc/html/.doctrees

View File

@@ -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

View File

@@ -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)

View File

@@ -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 ;

View File

@@ -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]

View File

@@ -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 ;

View File

@@ -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
}
//]

View File

@@ -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);
}
//]

View File

@@ -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"
);
}
//]

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
*/
}
//]

View 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
}
//]

View 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");
}
//]

View File

@@ -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
}

View File

@@ -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);
}
//]

View File

@@ -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);
}
//]

View File

@@ -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 ");
}
//]

View File

@@ -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
}
//]

View File

@@ -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"
")"
);
}
//]

View File

@@ -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);
}
//]

View File

@@ -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);
}
//]

View File

@@ -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+): ",
}
}
]
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View 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

View 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

View 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

View File

@@ -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

View 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

View File

@@ -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

View 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

View 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

View File

@@ -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();

View File

@@ -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;

View 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

View File

@@ -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});

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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_); }

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}
};

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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};
}
}
}

View File

@@ -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 ;

View File

@@ -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();

View File

@@ -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();

View 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();
}

View 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();
}

View 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
View 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();
}

View File

@@ -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();
}

View 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
View 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();
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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}));
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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());
}

View File

@@ -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]);

View File

@@ -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;
{

View File

@@ -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;
}

View File

@@ -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}));
}

View File

@@ -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
}
}
}

View File

@@ -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();
}

View File

@@ -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