diff options
Diffstat (limited to 'src/boost/libs/histogram')
174 files changed, 13142 insertions, 0 deletions
diff --git a/src/boost/libs/histogram/CMakeLists.txt b/src/boost/libs/histogram/CMakeLists.txt new file mode 100644 index 000000000..e924acef7 --- /dev/null +++ b/src/boost/libs/histogram/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright Peter Dimov, Hans Dembinski 2018-2019 +# 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 + +# We support CMake 3.5, but prefer 3.16 policies and behavior +cmake_minimum_required(VERSION 3.5...3.16) + +project(boost_histogram VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) + +add_library(boost_histogram INTERFACE) +add_library(Boost::histogram ALIAS boost_histogram) + +target_include_directories(boost_histogram INTERFACE include) + +target_compile_features(boost_histogram INTERFACE cxx_std_14) + +target_link_libraries(boost_histogram + INTERFACE + Boost::assert + Boost::config + Boost::core + Boost::mp11 + Boost::throw_exception + Boost::variant2) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + + # Fetch support files + + message(STATUS "Fetching BoostFetch.cmake") + + file(DOWNLOAD + "https://raw.githubusercontent.com/boostorg/cmake/develop/include/BoostFetch.cmake" + "${CMAKE_BINARY_DIR}/fetch_and_include/BoostFetch.cmake" + ) + + include("${CMAKE_BINARY_DIR}/fetch_and_include/BoostFetch.cmake") + + boost_fetch(boostorg/cmake TAG develop NO_ADD_SUBDIR) + + FetchContent_GetProperties(boostorg_cmake) + + list(APPEND CMAKE_MODULE_PATH ${boostorg_cmake_SOURCE_DIR}/include) + + # Enable testing + + include(CTest) + add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>) + + if(BUILD_TESTING) + + set(BUILD_TESTING OFF) # hide cache variable + + boost_fetch(boostorg/assert TAG develop EXCLUDE_FROM_ALL) + boost_fetch(boostorg/config TAG develop EXCLUDE_FROM_ALL) + boost_fetch(boostorg/core TAG develop EXCLUDE_FROM_ALL) + boost_fetch(boostorg/mp11 TAG develop EXCLUDE_FROM_ALL) + boost_fetch(boostorg/throw_exception TAG develop EXCLUDE_FROM_ALL) + boost_fetch(boostorg/variant2 TAG develop EXCLUDE_FROM_ALL) + ## No cmake support yet + # boost_fetch(boostorg/accumulators TAG develop) + # boost_fetch(boostorg/range TAG develop) + # boost_fetch(boostorg/serialization TAG develop) + # boost_fetch(boostorg/units TAG develop) + + unset(BUILD_TESTING) + + endif() + +endif() + +if(BOOST_SUPERPROJECT_VERSION) + + include(BoostInstall) + boost_install(TARGETS boost_histogram HEADER_DIRECTORY include/) + +endif() + +if (BUILD_TESTING) + + add_subdirectory(test) + + # do not pollute the superproject with the benchmarks + if(NOT BOOST_SUPERPROJECT_VERSION) + + add_subdirectory(benchmark) + + endif() + +endif() diff --git a/src/boost/libs/histogram/CONTRIBUTING.md b/src/boost/libs/histogram/CONTRIBUTING.md new file mode 100644 index 000000000..455fa2fc9 --- /dev/null +++ b/src/boost/libs/histogram/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing to Boost.Histogram + +## Star the project + +If you like Boost.Histogram, please star the project on Github! We want Boost.Histogram to be the best histogram library out there. If you give it a star, it becomes more visible and will gain more users. More users mean more user feedback to make the library even better. + +## Support + +Feel free to ask questions on https://gitter.im/boostorg/histogram. + +## Reporting Issues + +We value your feedback about issues you encounter. The more information you provide the easier it is for developers to resolve the problem. + +Issues should be reported to the [issue tracker]( +https://github.com/boostorg/histogram/issues?state=open). + +Issues can also be used to submit feature requests. + +Don't be shy: if you are friendly, we are friendly! And we care, issues are usually answered within a working day. + +## Submitting Pull Requests + +Fork the main repository. Base your changes on the `develop` branch. Make a new branch for any feature or bug-fix in your fork. Start developing. You can start a pull request when you feel the change is ready for review. + +Please rebase your branch to the original `develop` branch before submitting (which may have diverged from your fork in the meantime). + +For general advice on how to set up the Boost project for development, see +https://github.com/boostorg/boost/wiki/Getting-Started. + +To build the documentation, you need to install a few extra things, see +https://www.boost.org/doc/libs/1_70_0/doc/html/quickbook/install.html. + +## Running Tests + +To run the tests from the project folder, do `b2 cxxstd=latest warnings-as-errors=on test`. You can also test the examples by executing `b2 cxxstd=latest examples`. To make the tests complete faster, you can use the option `-j4` (or another number) to run builds in parallel. +More details can be found in [travis](https://github.com/boostorg/histogram/blob/develop/.travis.yml) and [appveyor](https://github.com/boostorg/histogram/blob/develop/.appveyor.yml) config files. + +Please report any tests failures to the issue tracker along with the test +output and information on your system: + +* platform (Linux, Windows, OSX, ...) +* compiler and version + +### Test coverage + +Boost.Histogram maintains 100% line coverage. Coverage is automatically checked by CI. To generate a report locally, you need to build the code with gcc-8 and coverage instrumentation enabled, do `b2 toolset=gcc-8 cxxstd=latest coverage=on test`. To generate the coverage report, run `tools/cov.sh` from the project root directory of Boost.Histogram. This will generate a new folder `coverage-report` with a HTML report. Open `coverage-report/index.html` in a browser. + +Notes: Generating coverage data is very fickle. You need to use gcc-5 or gcc-8 and a matching version of gcov, other gcc versions (6, 7, 9) are known to be broken or are not supported by lcov, which is used to process the raw coverage data. Generating coverage data with clang and llvm-cov is not supported by lcov. The best results are obtained with gcc-5. gcc-8 is known to report lines as missed which are impossible to miss. + +## Coding Style + +Follow the [Boost Library Requirements and Guidelines](https://www.boost.org/development/requirements.html) and the established style in Boost.Histogram. + +### Code formatting + +Using `clang-format -style=file` is recommended, which should pick up the `.clang-format` file of the project. All names are written with small letters and `_`. Template parameters are capitalized and in camel-case. + +### Documentation + +Doxygen comments should be added for all user-facing functions and methods. Implementation details are not documented (everything in the `boost::histogram::detail` namespace is an implementation detail that can change at any time). diff --git a/src/boost/libs/histogram/Jamfile b/src/boost/libs/histogram/Jamfile new file mode 100644 index 000000000..63bb5402a --- /dev/null +++ b/src/boost/libs/histogram/Jamfile @@ -0,0 +1,36 @@ +# Copyright Mateusz Loskot 2018 <mateusz@loskot.net> +# Copyright Klemens David Morgenstern, Hans P. Dembinski 2016-2017 +# +# Use, modification and distribution is subject to 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) + +import common ; + +project + : requirements + <implicit-dependency>/boost//headers + <include>$(BOOST_ROOT) + <toolset>clang:<cxxflags>"-pedantic -Wextra -Wsign-compare -Wstrict-aliasing -fstrict-aliasing" + <toolset>gcc:<cxxflags>"-pedantic -Wextra -Wsign-compare -Wstrict-aliasing -fstrict-aliasing" + <toolset>msvc:<cxxflags>"/bigobj" + <toolset>intel-win:<cxxflags>"/bigobj" + : default-build + <warnings>all + ; + +path-constant THIS_PATH : . ; + +# only works with clang because of -fsanitize-blacklist +variant histogram_ubasan : debug : + <cxxflags>"-fno-omit-frame-pointer -O0 -fno-inline -fsanitize=address,leak,undefined -fno-sanitize-recover=all -fsanitize-blacklist=$(THIS_PATH)/tools/blacklist.supp" + <linkflags>"-fsanitize=address,leak,undefined" + ; + +## Special builds: +# +# Generate coverage data: `b2 cxxstd=latest coverage=on` +# Build without exceptions and rtti: `b2 exception-handling=off rtti=off` + +build-project test ; +build-project examples ; diff --git a/src/boost/libs/histogram/LICENSE b/src/boost/libs/histogram/LICENSE new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/src/boost/libs/histogram/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/boost/libs/histogram/README.md b/src/boost/libs/histogram/README.md new file mode 100644 index 000000000..ac3c5901d --- /dev/null +++ b/src/boost/libs/histogram/README.md @@ -0,0 +1,113 @@ +<!-- + Copyright Hans Dembinski 2016 - 2019. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + https://www.boost.org/LICENSE_1_0.txt) +--> + +![](doc/logo/color.svg) + +**Fast multi-dimensional histogram with convenient interface for C++14** + +Coded with ❤. Powered by the [Boost community](https://www.boost.org) and the [Scikit-HEP Project](http://scikit-hep.org). Licensed under the [Boost Software License](http://www.boost.org/LICENSE_1_0.txt). + +**Supported compiler versions** gcc >= 5.5, clang >= 3.8, msvc >= 14.1 + +Branch | Linux and OSX | Windows | Coverage +------- | ------------- |-------- | -------- +develop | [![Build Status Travis](https://travis-ci.org/boostorg/histogram.svg?branch=develop)](https://travis-ci.org/boostorg/histogram/branches) | [![Build status Appveyor](https://ci.appveyor.com/api/projects/status/p27laa26ti1adyf1/branch/develop?svg=true)](https://ci.appveyor.com/project/HDembinski/histogram-d5g5k/branch/develop) | [![Coveralls](https://coveralls.io/repos/github/boostorg/histogram/badge.svg?branch=develop)](https://coveralls.io/github/boostorg/histogram?branch=develop) +master | [![Build Status Travis](https://travis-ci.org/boostorg/histogram.svg?branch=master)](https://travis-ci.org/boostorg/histogram/branches) | [![Build status Appveyor](https://ci.appveyor.com/api/projects/status/p27laa26ti1adyf1/branch/master?svg=true)](https://ci.appveyor.com/project/HDembinski/histogram-d5g5k/branch/master) | [![Coveralls](https://coveralls.io/repos/github/boostorg/histogram/badge.svg?branch=master)](https://coveralls.io/github/boostorg/histogram?branch=master) + +Boost.Histogram is a very fast state-of-the-art multi-dimensional [histogram](https://en.wikipedia.org/wiki/Histogram) class for the beginner and expert alike. + +* Header-only +* Easy to use, easy to customize +* Just count or use arbitrary accumulators to compute means, minimum, maximum, ... +* Supports multi-threading and restricted environments (no heap allocation, exceptions or RTTI) +* Has [Python bindings](https://github.com/scikit-hep/boost-histogram) + +Check out the [full documentation](https://www.boost.org/doc/libs/develop/libs/histogram/doc/html/index.html). + +💡 Boost.Histogram is a mature library, but if you find some issue or find the documentation lacking, tell us about it by [submitting an issue](https://github.com/boostorg/histogram/issues). Chat with us on the [Boost channel on Slack](https://cpplang.slack.com) and [Gitter](https://gitter.im/boostorg/histogram). + +## Code examples + +The following stripped-down example was taken from the [Getting started](https://www.boost.org/doc/libs/develop/libs/histogram/doc/html/histogram/getting_started.html) section in the documentation. Have a look into the docs to see the full version with comments and more examples. + +Example: Make and fill a 1d-histogram ([try it live on Wandbox](https://wandbox.org/permlink/NSM2ZiDyntUi6RDC)). The core of this example [compiles into 53 lines of assembly code](https://godbolt.org/z/632yzE). + +```cpp +#include <boost/histogram.hpp> +#include <boost/format.hpp> // used here for printing +#include <iostream> + +int main() { + using namespace boost::histogram; + + // make 1d histogram with 4 regular bins from 0 to 2 + auto h = make_histogram( axis::regular<>(4, 0.0, 2.0) ); + + // push some values into the histogram + for (auto&& value : { 0.4, 1.1, 0.3, 1.7, 10. }) + h(value); + + // iterate over bins + for (auto&& x : indexed(h)) { + std::cout << boost::format("bin %i [ %.1f, %.1f ): %i\n") + % x.index() % x.bin().lower() % x.bin().upper() % *x; + } + + std::cout << std::flush; + + /* program output: + + bin 0 [ 0.0, 0.5 ): 2 + bin 1 [ 0.5, 1.0 ): 0 + bin 2 [ 1.0, 1.5 ): 1 + bin 3 [ 1.5, 2.0 ): 1 + */ +} +``` + +## Features + +* Extremely customisable multi-dimensional histogram +* Simple, convenient, STL and Boost-compatible interface +* Counters with high dynamic range, cannot overflow or be capped [[1]](#note1) +* Better performance than other libraries (see benchmarks for details) +* Efficient use of memory [[1]](#note1) +* Support for custom axis types: define how input values should map to indices +* Support for under-/overflow bins (can be disabled individually to reduce memory consumption) +* Support for axes that grow automatically with input values [[2]](#note2) +* Support for weighted increments +* Support for profiles and more generally, user-defined accumulators in cells [[3]](#note3) +* Support for completely stack-based histograms +* Support for compilation without exceptions or RTTI [[4]](#note4) +* Support for adding, subtracting, multiplying, dividing, and scaling histograms +* Support for custom allocators +* Support for programming with units [[5]](#note5) +* Optional serialization based on [Boost.Serialization](https://www.boost.org/doc/libs/release/libs/serialization/) + +<b id="note1">Note 1</b> In the standard configuration, if you don't use weighted increments. The counter capacity is increased dynamically as the cell counts grow. When even the largest plain integral type would overflow, the storage switches to a multiprecision integer similar to those in [Boost.Multiprecision](https://www.boost.org/doc/libs/release/libs/multiprecision/), which is only limited by available memory. + +<b id="note2">Note 2</b> An axis can be configured to grow when a value is encountered that is outside of its range. It then grows new bins towards this value so that the value ends up in the new highest or lowest bin. + +<b id="note3">Note 3</b> The histogram can be configured to hold an arbitrary accumulator in each cell instead of a simple counter. Extra values can be passed to the histogram, for example, to compute the mean and variance of values which fall into the same cell. This feature can be used to calculate variance estimates for each cell, which are useful when you need to fit a statistical model to the cell values. + +<b id="note4">Note 4</b> The library throws exceptions when exceptions are enabled. When exceptions are disabled, a user-defined exception handler is called instead upon a throw and the program terminates, see [boost::throw_exception](https://www.boost.org/doc/libs/master/libs/exception/doc/throw_exception.html) for details. Disabling exceptions improves performance by 10 % to 20 % in benchmarks. The library does not use RTTI (only CTTI) so disabling it has no effect. + +<b id="note5">Note 5</b> Builtin axis types can be configured to only accept dimensional quantities, like those from [Boost.Units](https://www.boost.org/doc/libs/release/libs/units/). This means you get a useful error if you accidentally try to fill a length where the histogram axis expects a time, for example. + +## Benchmarks + +Boost.Histogram is more flexible and faster than other C/C++ libraries. It was compared to: + - [GNU Scientific Library](https://www.gnu.org/software/gsl) + - [ROOT framework from CERN](https://root.cern.ch) + +Details on the benchmark are given in the [documentation](https://www.boost.org/doc/libs/develop/libs/histogram/doc/html/histogram/benchmarks.html). + +## What users say + +**John Buonagurio** | Manager at [**E<sup><i>x</i></sup>ponent<sup>®</sup>**](https://www.exponent.com) + +*"I just wanted to say 'thanks' for your awesome Histogram library. I'm working on a software package for processing meteorology data and I'm using it to generate wind roses with the help of Qt and QwtPolar. Looks like you thought of just about everything here – the circular axis type was practically designed for this application, everything 'just worked'."* diff --git a/src/boost/libs/histogram/benchmark/CMakeLists.txt b/src/boost/libs/histogram/benchmark/CMakeLists.txt new file mode 100644 index 000000000..1f2834cdd --- /dev/null +++ b/src/boost/libs/histogram/benchmark/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright 2019 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(BoostFetch) + +# setup for google benchmark +set(CMAKE_BUILD_TYPE Release) # ok, only set in local scope +option(BENCHMARK_ENABLE_TESTING "" OFF) +boost_fetch(google/benchmark) + +function(add_benchmark NAME) + + cmake_parse_arguments(PARSE_ARGV 1 _ "" "" ";INCLUDE_DIRECTORIES;LINK_LIBRARIES;COMPILE_OPTIONS") + + if(__UNPARSED_ARGUMENTS) + message(AUTHOR_WARNING "add_benchmark: extra arguments ignored: ${__UNPARSED_ARGUMENTS}") + endif() + + set(SOURCE ${NAME}) + set(NAME benchmark_${NAME}) + + if(DEFINED BUILD_TESTING AND NOT BUILD_TESTING) + return() + endif() + + add_executable(${NAME} ${SOURCE}) + target_include_directories(${NAME} PRIVATE ${__INCLUDE_DIRECTORIES}) + target_link_libraries(${NAME} PRIVATE Boost::histogram benchmark_main ${__LINK_LIBRARIES}) + target_compile_options(${NAME} PRIVATE -DNDEBUG -O3 -march=native -funsafe-math-optimizations ${__COMPILE_OPTIONS}) + +endfunction() + +add_benchmark(axis_index) +add_benchmark(histogram_filling) +add_benchmark(histogram_iteration) + +find_package(Threads) +if (Threads_FOUND) + add_benchmark(histogram_parallel_filling) +endif() + +find_package(GSL) +if (GSL_FOUND) + add_benchmark( + histogram_filling_gsl + INCLUDE_DIRECTORIES ${GSL_INCLUDE_DIRS} + LINK_LIBRARIES ${GSL_LIBRARIES}) +endif() + +find_package(ROOT QUIET) +if (ROOT_FOUND) + add_benchmark( + histogram_filling_root + INCLUDE_DIRECTORIES ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} + COMPILE_OPTIONS -frtti -fexceptions + ) + # add ROOT linker flags? ${ROOT_EXE_LINKER_FLAGS} +endif() diff --git a/src/boost/libs/histogram/benchmark/axis_index.cpp b/src/boost/libs/histogram/benchmark/axis_index.cpp new file mode 100644 index 000000000..fb8e71f77 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/axis_index.cpp @@ -0,0 +1,70 @@ +// 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 <benchmark/benchmark.h> +#include <boost/histogram/axis.hpp> +#include <numeric> +#include "../test/throw_exception.hpp" +#include "generator.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +using namespace boost::histogram; + +template <class Distribution> +static void regular(benchmark::State& state) { + auto a = axis::regular<>(100, 0.0, 1.0); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(a.index(gen())); +} + +template <class Distribution> +static void circular(benchmark::State& state) { + auto a = axis::circular<>(100, 0.0, 1.0); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(a.index(gen())); +} + +template <class T, class Distribution> +static void integer(benchmark::State& state) { + auto a = axis::integer<T>(0, 1); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(a.index(gen())); +} + +template <class Distribution> +static void variable(benchmark::State& state) { + std::vector<double> v; + for (double x = 0; x <= state.range(0); ++x) { v.push_back(x / state.range(0)); } + auto a = axis::variable<>(v); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(a.index(gen())); +} + +static void category(benchmark::State& state) { + std::vector<int> v(state.range(0)); + std::iota(v.begin(), v.end(), 0); + auto a = axis::category<int>(v); + generator<uniform_int> gen(static_cast<int>(state.range(0))); + for (auto _ : state) benchmark::DoNotOptimize(a.index(gen())); +} + +BENCHMARK_TEMPLATE(regular, uniform); +BENCHMARK_TEMPLATE(regular, normal); +BENCHMARK_TEMPLATE(circular, uniform); +BENCHMARK_TEMPLATE(circular, normal); +BENCHMARK_TEMPLATE(integer, int, uniform); +BENCHMARK_TEMPLATE(integer, int, normal); +BENCHMARK_TEMPLATE(integer, double, uniform); +BENCHMARK_TEMPLATE(integer, double, normal); +BENCHMARK_TEMPLATE(variable, uniform)->RangeMultiplier(10)->Range(10, 10000); +BENCHMARK_TEMPLATE(variable, normal)->RangeMultiplier(10)->Range(10, 10000); +BENCHMARK(category)->RangeMultiplier(10)->Range(10, 10000); diff --git a/src/boost/libs/histogram/benchmark/disable_cpu_scaling.sh b/src/boost/libs/histogram/benchmark/disable_cpu_scaling.sh new file mode 100755 index 000000000..f945e180e --- /dev/null +++ b/src/boost/libs/histogram/benchmark/disable_cpu_scaling.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright Hans Dembinski 2019 +# 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 + +for cpu in /sys/devices/system/cpu/cpu? ; do echo performance > $cpu/cpufreq/scaling_governor; done diff --git a/src/boost/libs/histogram/benchmark/enable_cpu_scaling.sh b/src/boost/libs/histogram/benchmark/enable_cpu_scaling.sh new file mode 100755 index 000000000..d0a463eb0 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/enable_cpu_scaling.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Copyright Hans Dembinski 2019 +# 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 + +for cpu in /sys/devices/system/cpu/cpu? ; do echo powersave > $cpu/cpufreq/scaling_governor; done diff --git a/src/boost/libs/histogram/benchmark/generator.hpp b/src/boost/libs/histogram/benchmark/generator.hpp new file mode 100644 index 000000000..f613ced35 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/generator.hpp @@ -0,0 +1,50 @@ +// Copyright 2019 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 <array> +#include <random> + +using uniform = std::uniform_real_distribution<>; +using uniform_int = std::uniform_int_distribution<>; +using normal = std::normal_distribution<>; + +template <class Distribution, class... Ts> +Distribution init(Ts...); + +template <> +uniform init<uniform>() { + return uniform{0.0, 1.0}; +} + +template <> +normal init<normal>() { + return normal{0.5, 0.3}; +} + +template <> +uniform_int init<uniform_int, int>(int n) { + return uniform_int{0, n}; +} + +template <class Distribution, std::size_t N = 1 << 15> +struct generator : std::array<double, N> { + using base_t = std::array<double, N>; + + template <class... Ts> + generator(Ts... ts) { + std::default_random_engine rng(1); + auto dis = init<Distribution>(ts...); + std::generate(base_t::begin(), base_t::end(), [&] { return dis(rng); }); + } + + const double& operator()() { + ++ptr_; + if (ptr_ == base_t::data() + N) ptr_ = base_t::data(); + return *ptr_; + } + + const double* ptr_ = base_t::data() - 1; +}; diff --git a/src/boost/libs/histogram/benchmark/histogram_filling.cpp b/src/boost/libs/histogram/benchmark/histogram_filling.cpp new file mode 100644 index 000000000..0bc5164b8 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_filling.cpp @@ -0,0 +1,176 @@ +// Copyright 2015-2019 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 <benchmark/benchmark.h> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <memory> +#include "../test/throw_exception.hpp" +#include "../test/utility_histogram.hpp" +#include "generator.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +using SStore = std::vector<double>; + +// make benchmark compatible with older versions of the library +#if __has_include(<boost/histogram/unlimited_storage.hpp>) +#include <boost/histogram/unlimited_storage.hpp> +using DStore = boost::histogram::unlimited_storage<>; +#else +#include <boost/histogram/adaptive_storage.hpp> +using DStore = boost::histogram::adaptive_storage<>; +#endif + +using namespace boost::histogram; +using reg = axis::regular<>; + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_1d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + for (auto _ : state) benchmark::DoNotOptimize(h(gen())); + state.SetItemsProcessed(state.iterations()); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_n_1d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + for (auto _ : state) h.fill(gen); + state.SetItemsProcessed(state.iterations() * gen.size()); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_2d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + for (auto _ : state) benchmark::DoNotOptimize(h(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_n_2d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + auto v = {gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 2 * gen.size()); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_3d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + for (auto _ : state) benchmark::DoNotOptimize(h(gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 3); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_n_3d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator<Distribution>(); + auto v = {gen, gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 3 * gen.size()); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_6d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1), + reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1)); + auto gen = generator<Distribution>(); + for (auto _ : state) + benchmark::DoNotOptimize(h(gen(), gen(), gen(), gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 6); +} + +template <class Distribution, class Tag, class Storage = SStore> +static void fill_n_6d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1), + reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1)); + auto gen = generator<Distribution>(); + auto v = {gen, gen, gen, gen, gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 6 * gen.size()); +} + +BENCHMARK_TEMPLATE(fill_1d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_1d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_1d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_1d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_1d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_n_1d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_2d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_2d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_2d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_2d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_2d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_2d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_2d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_n_2d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_2d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_n_2d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_2d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_2d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_2d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_2d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_3d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_3d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_3d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_3d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_3d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_3d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_3d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_3d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_3d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_n_3d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_3d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_n_3d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_3d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_3d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_3d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_3d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_6d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_6d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_6d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_6d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_6d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_6d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_6d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_6d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_6d, uniform, static_tag); +// BENCHMARK_TEMPLATE(fill_n_6d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, normal, static_tag); +// BENCHMARK_TEMPLATE(fill_n_6d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, uniform, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_6d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, normal, dynamic_tag); +// BENCHMARK_TEMPLATE(fill_n_6d, normal, dynamic_tag, DStore); diff --git a/src/boost/libs/histogram/benchmark/histogram_filling_gsl.cpp b/src/boost/libs/histogram/benchmark/histogram_filling_gsl.cpp new file mode 100644 index 000000000..94d738966 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_filling_gsl.cpp @@ -0,0 +1,45 @@ +// Copyright 2015-2019 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 <benchmark/benchmark.h> +#include <gsl/gsl_histogram.h> +#include <gsl/gsl_histogram2d.h> +#include "../test/throw_exception.hpp" +#include "generator.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +template <class Distribution> +static void fill_1d(benchmark::State& state) { + gsl_histogram* h = gsl_histogram_alloc(100); + gsl_histogram_set_ranges_uniform(h, 0, 1); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(gsl_histogram_increment(h, gen())); + gsl_histogram_free(h); + state.SetItemsProcessed(state.iterations()); +} + +template <class Distribution> +static void fill_2d(benchmark::State& state) { + gsl_histogram2d* h = gsl_histogram2d_alloc(100, 100); + gsl_histogram2d_set_ranges_uniform(h, 0, 1, 0, 1); + generator<Distribution> gen; + for (auto _ : state) + benchmark::DoNotOptimize(gsl_histogram2d_increment(h, gen(), gen())); + gsl_histogram2d_free(h); + state.SetItemsProcessed(state.iterations() * 2); +} + +BENCHMARK_TEMPLATE(fill_1d, uniform); +BENCHMARK_TEMPLATE(fill_2d, uniform); + +BENCHMARK_TEMPLATE(fill_1d, normal); +BENCHMARK_TEMPLATE(fill_2d, normal); diff --git a/src/boost/libs/histogram/benchmark/histogram_filling_numpy.py b/src/boost/libs/histogram/benchmark/histogram_filling_numpy.py new file mode 100644 index 000000000..ce0fa2b73 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_filling_numpy.py @@ -0,0 +1,14 @@ +from __future__ import print_function +import numpy as np +# pip install fast-histogram +from fast_histogram import histogram1d +import timeit + +x = np.random.rand(1 << 20) +nrepeat = 10 + +print(timeit.timeit("np.histogram(x, bins=100, range=(0, 1))", + "from __main__ import x, np", number=nrepeat) / (nrepeat * len(x)) / 1e-9) + +print(timeit.timeit("histogram1d(x, bins=100, range=(0, 1))", + "from __main__ import x, histogram1d", number=nrepeat) / (nrepeat * len(x)) / 1e-9)
\ No newline at end of file diff --git a/src/boost/libs/histogram/benchmark/histogram_filling_root.cpp b/src/boost/libs/histogram/benchmark/histogram_filling_root.cpp new file mode 100644 index 000000000..98d638517 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_filling_root.cpp @@ -0,0 +1,67 @@ +// Copyright 2015-2019 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 <TH1I.h> +#include <TH2I.h> +#include <TH3I.h> +#include <THn.h> +#include <benchmark/benchmark.h> +#include "generator.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +template <class Distribution> +static void fill_1d(benchmark::State& state) { + TH1I h("", "", 100, 0, 1); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen())); + state.SetItemsProcessed(state.iterations()); +} + +template <class Distribution> +static void fill_2d(benchmark::State& state) { + TH2I h("", "", 100, 0, 1, 100, 0, 1); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); +} + +template <class Distribution> +static void fill_3d(benchmark::State& state) { + TH3I h("", "", 100, 0, 1, 100, 0, 1, 100, 0, 1); + generator<Distribution> gen; + for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 3); +} + +template <class Distribution> +static void fill_6d(benchmark::State& state) { + int bin[] = {10, 10, 10, 10, 10, 10}; + double min[] = {0, 0, 0, 0, 0, 0}; + double max[] = {1, 1, 1, 1, 1, 1}; + THnI h("", "", 6, bin, min, max); + generator<Distribution> gen; + for (auto _ : state) { + const double buf[6] = {gen(), gen(), gen(), gen(), gen(), gen()}; + benchmark::DoNotOptimize(h.Fill(buf)); + } + state.SetItemsProcessed(state.iterations() * 6); +} + +BENCHMARK_TEMPLATE(fill_1d, uniform); +BENCHMARK_TEMPLATE(fill_2d, uniform); +BENCHMARK_TEMPLATE(fill_3d, uniform); +BENCHMARK_TEMPLATE(fill_6d, uniform); + +BENCHMARK_TEMPLATE(fill_1d, normal); +BENCHMARK_TEMPLATE(fill_2d, normal); +BENCHMARK_TEMPLATE(fill_3d, normal); +BENCHMARK_TEMPLATE(fill_6d, normal); diff --git a/src/boost/libs/histogram/benchmark/histogram_iteration.cpp b/src/boost/libs/histogram/benchmark/histogram_iteration.cpp new file mode 100644 index 000000000..bb18a0ba6 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_iteration.cpp @@ -0,0 +1,178 @@ +// 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 <benchmark/benchmark.h> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/indexed.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/mp11/integral.hpp> +#include <vector> +#include "../test/throw_exception.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +using namespace boost::histogram; +using namespace boost::histogram::literals; + +struct tuple {}; +struct vector {}; +struct vector_of_variant {}; + +template <unsigned I> +using Dim_t = boost::mp11::mp_int<I>; + +using d1 = Dim_t<1>; +using d2 = Dim_t<2>; +using d3 = Dim_t<3>; + +auto make_histogram(tuple, d1, unsigned n) { + return make_histogram_with(std::vector<unsigned>(), axis::integer<>(0, n)); +} + +auto make_histogram(tuple, d2, unsigned n) { + return make_histogram_with(std::vector<unsigned>(), axis::integer<>(0, n), + axis::integer<>(0, n)); +} + +auto make_histogram(tuple, d3, unsigned n) { + return make_histogram_with(std::vector<unsigned>(), axis::integer<>(0, n), + axis::integer<>(0, n), axis::integer<>(0, n)); +} + +template <int Dim> +auto make_histogram(vector, boost::mp11::mp_int<Dim>, unsigned n) { + std::vector<axis::integer<>> axes; + for (unsigned d = 0; d < Dim; ++d) axes.emplace_back(axis::integer<>(0, n)); + return make_histogram_with(std::vector<unsigned>(), std::move(axes)); +} + +template <int Dim> +auto make_histogram(vector_of_variant, boost::mp11::mp_int<Dim>, unsigned n) { + std::vector<axis::variant<axis::integer<>>> axes; + for (unsigned d = 0; d < Dim; ++d) axes.emplace_back(axis::integer<>(0, n)); + return make_histogram_with(std::vector<unsigned>(), std::move(axes)); +} + +template <class Tag> +static void Naive(benchmark::State& state, Tag, d1, coverage cov) { + auto h = make_histogram(Tag(), d1(), state.range(0)); + const int d = cov == coverage::all; + for (auto _ : state) { + for (int i = -d; i < h.axis().size() + d; ++i) { + benchmark::DoNotOptimize(i); + benchmark::DoNotOptimize(h.at(i)); + } + } +} + +template <class Tag> +static void Naive(benchmark::State& state, Tag, d2, coverage cov) { + auto h = make_histogram(Tag(), d2(), state.range(0)); + const int d = cov == coverage::all; + for (auto _ : state) { + for (int i = -d; i < h.axis(0_c).size() + d; ++i) { + for (int j = -d; j < h.axis(1_c).size() + d; ++j) { + benchmark::DoNotOptimize(i); + benchmark::DoNotOptimize(j); + benchmark::DoNotOptimize(h.at(i, j)); + } + } + } +} + +template <class Tag> +static void Naive(benchmark::State& state, Tag, d3, coverage cov) { + auto h = make_histogram(Tag(), d3(), state.range(0)); + const int d = cov == coverage::all; + for (auto _ : state) { + for (int i = -d; i < h.axis(0_c).size() + d; ++i) { + for (int j = -d; j < h.axis(1_c).size() + d; ++j) { + for (int k = -d; k < h.axis(2_c).size() + d; ++k) { + benchmark::DoNotOptimize(i); + benchmark::DoNotOptimize(j); + benchmark::DoNotOptimize(k); + benchmark::DoNotOptimize(h.at(i, j, k)); + } + } + } + } +} + +template <class Tag> +static void Indexed(benchmark::State& state, Tag, d1, coverage cov) { + auto h = make_histogram(Tag(), d1(), state.range(0)); + for (auto _ : state) { + for (auto&& x : indexed(h, cov)) { + benchmark::DoNotOptimize(*x); + benchmark::DoNotOptimize(x.index()); + } + } +} + +template <class Tag> +static void Indexed(benchmark::State& state, Tag, d2, coverage cov) { + auto h = make_histogram(Tag(), d2(), state.range(0)); + for (auto _ : state) { + for (auto&& x : indexed(h, cov)) { + benchmark::DoNotOptimize(*x); + benchmark::DoNotOptimize(x.index(0)); + benchmark::DoNotOptimize(x.index(1)); + } + } +} + +template <class Tag> +static void Indexed(benchmark::State& state, Tag, d3, coverage cov) { + auto h = make_histogram(Tag(), d3(), state.range(0)); + for (auto _ : state) { + for (auto&& x : indexed(h, cov)) { + benchmark::DoNotOptimize(*x); + benchmark::DoNotOptimize(x.index(0)); + benchmark::DoNotOptimize(x.index(1)); + benchmark::DoNotOptimize(x.index(2)); + } + } +} + +#define BENCH(Type, Tag, Dim, Cov) \ + BENCHMARK_CAPTURE(Type, (Tag, Dim, Cov), Tag{}, Dim_t<Dim>{}, coverage::Cov) \ + ->RangeMultiplier(4) \ + ->Range(4, 256) + +BENCH(Naive, tuple, 1, inner); +BENCH(Indexed, tuple, 1, inner); + +BENCH(Naive, vector, 1, inner); +BENCH(Indexed, vector, 1, inner); + +BENCH(Naive, vector_of_variant, 1, inner); +BENCH(Indexed, vector_of_variant, 1, inner); + +BENCH(Naive, tuple, 2, inner); +BENCH(Indexed, tuple, 2, inner); + +BENCH(Naive, vector, 2, inner); +BENCH(Indexed, vector, 2, inner); + +BENCH(Naive, vector_of_variant, 2, inner); +BENCH(Indexed, vector_of_variant, 2, inner); + +BENCH(Naive, tuple, 3, inner); +BENCH(Indexed, tuple, 3, inner); + +BENCH(Naive, vector, 3, inner); +BENCH(Indexed, vector, 3, inner); + +BENCH(Naive, vector_of_variant, 3, inner); +BENCH(Indexed, vector_of_variant, 3, inner); diff --git a/src/boost/libs/histogram/benchmark/histogram_parallel_filling.cpp b/src/boost/libs/histogram/benchmark/histogram_parallel_filling.cpp new file mode 100644 index 000000000..0ca7eaada --- /dev/null +++ b/src/boost/libs/histogram/benchmark/histogram_parallel_filling.cpp @@ -0,0 +1,149 @@ +// 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 <benchmark/benchmark.h> +#include <boost/histogram/accumulators/thread_safe.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <chrono> +#include <functional> +#include <mutex> +#include <numeric> +#include <random> +#include <thread> +#include <vector> +#include "../test/throw_exception.hpp" + +#include <boost/assert.hpp> +struct assert_check { + assert_check() { + BOOST_ASSERT(false); // don't run with asserts enabled + } +} _; + +using namespace boost::histogram; +using namespace std::chrono_literals; + +using DS = dense_storage<unsigned>; +using DSTS = dense_storage<accumulators::thread_safe<unsigned>>; + +static void NoThreads(benchmark::State& state) { + std::default_random_engine gen(1); + std::uniform_real_distribution<> dis(0, 1); + const unsigned nbins = state.range(0); + auto hist = make_histogram_with(DS(), axis::regular<>(nbins, 0, 1)); + for (auto _ : state) { + // simulate some work + for (volatile unsigned n = 0; n < state.range(1); ++n) + ; + + hist(dis(gen)); + } +} + +std::mutex init; +static auto hist = make_histogram_with(DSTS(), axis::regular<>()); + +static void AtomicStorage(benchmark::State& state) { + init.lock(); + if (state.thread_index == 0) { + const unsigned nbins = state.range(0); + hist = make_histogram_with(DSTS(), axis::regular<>(nbins, 0, 1)); + } + init.unlock(); + std::default_random_engine gen(state.thread_index); + std::uniform_real_distribution<> dis(0, 1); + for (auto _ : state) { + // simulate some work + for (volatile unsigned n = 0; n < state.range(1); ++n) + ; + hist(dis(gen)); + } +} + +BENCHMARK(NoThreads) + ->UseRealTime() + + ->Args({1 << 4, 0}) + ->Args({1 << 6, 0}) + ->Args({1 << 8, 0}) + ->Args({1 << 10, 0}) + ->Args({1 << 14, 0}) + ->Args({1 << 18, 0}) + + ->Args({1 << 4, 5}) + ->Args({1 << 6, 5}) + ->Args({1 << 8, 5}) + ->Args({1 << 10, 5}) + ->Args({1 << 14, 5}) + ->Args({1 << 18, 5}) + + ->Args({1 << 4, 10}) + ->Args({1 << 6, 10}) + ->Args({1 << 8, 10}) + ->Args({1 << 10, 10}) + ->Args({1 << 14, 10}) + ->Args({1 << 18, 10}) + + ->Args({1 << 4, 50}) + ->Args({1 << 6, 50}) + ->Args({1 << 8, 50}) + ->Args({1 << 10, 50}) + ->Args({1 << 14, 50}) + ->Args({1 << 18, 50}) + + ->Args({1 << 4, 100}) + ->Args({1 << 6, 100}) + ->Args({1 << 8, 100}) + ->Args({1 << 10, 100}) + ->Args({1 << 14, 100}) + ->Args({1 << 18, 100}) + + ; + +BENCHMARK(AtomicStorage) + ->UseRealTime() + ->Threads(1) + ->Threads(2) + ->Threads(4) + + ->Args({1 << 4, 0}) + ->Args({1 << 6, 0}) + ->Args({1 << 8, 0}) + ->Args({1 << 10, 0}) + ->Args({1 << 14, 0}) + ->Args({1 << 18, 0}) + + ->Args({1 << 4, 5}) + ->Args({1 << 6, 5}) + ->Args({1 << 8, 5}) + ->Args({1 << 10, 5}) + ->Args({1 << 14, 5}) + ->Args({1 << 18, 5}) + + ->Args({1 << 4, 10}) + ->Args({1 << 6, 10}) + ->Args({1 << 8, 10}) + ->Args({1 << 10, 10}) + ->Args({1 << 14, 10}) + ->Args({1 << 18, 10}) + + ->Args({1 << 4, 50}) + ->Args({1 << 6, 50}) + ->Args({1 << 8, 50}) + ->Args({1 << 10, 50}) + ->Args({1 << 14, 50}) + ->Args({1 << 18, 50}) + + ->Args({1 << 4, 100}) + ->Args({1 << 6, 100}) + ->Args({1 << 8, 100}) + ->Args({1 << 10, 100}) + ->Args({1 << 14, 100}) + ->Args({1 << 18, 100}) + + ; diff --git a/src/boost/libs/histogram/benchmark/plot_benchmarks.py b/src/boost/libs/histogram/benchmark/plot_benchmarks.py new file mode 100755 index 000000000..2661549fd --- /dev/null +++ b/src/boost/libs/histogram/benchmark/plot_benchmarks.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +# Copyright Hans Dembinski 2019 +# 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 + +from matplotlib import pyplot as plt, lines +import shelve +import json +import subprocess as subp +import sys +from collections import defaultdict +from run_benchmarks import get_commits, run +import numpy as np +import threading + +thread = None +current_index = 0 + +commits, comments = get_commits() + +def get_benchmarks(results): + benchmarks = defaultdict(lambda: []) + for hash in commits: + if hash in results and results[hash] is not None: + benchs = results[hash] + for b in benchs["benchmarks"]: + name = b["name"] + time = min(b["cpu_time"], b["real_time"]) + benchmarks[name].append((commits.index(hash), time)) + return benchmarks + +with shelve.open("benchmark_results") as results: + benchmarks = get_benchmarks(results) + +fig, ax = plt.subplots(4, 1, figsize=(10, 10), sharex=True) +plt.subplots_adjust(hspace=0, top=0.98, bottom=0.05, right=0.96) + +plt.sca(ax[0]) +for name, xy in benchmarks.items(): + if "uniform" in name: continue + if "_1d" in name: + x, y = np.transpose(xy) + plt.plot(x, y, ".-", label=name) +plt.legend(fontsize="xx-small") + +plt.sca(ax[1]) +for name, xy in benchmarks.items(): + if "uniform" in name: continue + if "_2d" in name: + x, y = np.transpose(xy) + plt.plot(x, y, ".-", label=name) +plt.legend(fontsize="xx-small") + +plt.sca(ax[2]) +for name, xy in benchmarks.items(): + if "uniform" in name: continue + if "_3d" in name: + x, y = np.transpose(xy) + plt.plot(x, y, ".-", label=name) +plt.legend(fontsize="xx-small") + +plt.sca(ax[3]) +for name, xy in benchmarks.items(): + if "uniform" in name: continue + if "_6d" in name: + x, y = np.transpose(xy) + plt.plot(x, y, ".-", label=name) +plt.legend(fontsize="xx-small") + +plt.figtext(0.01, 0.5, "time per loop / ns [smaller is better]", rotation=90, va="center") + +def format_coord(x, y): + global current_index + current_index = max(0, min(int(x + 0.5), len(commits) - 1)) + hash = commits[current_index] + comment = comments[hash] + return f"{hash} {comment}" + +for axi in ax.flatten(): + axi.format_coord = format_coord + +def on_key_press(event): + global thread + if thread and thread.is_alive(): return + + if event.key != "u": return + + hash = commits[current_index] + + def worker(fig, ax, hash): + with shelve.open("benchmark_results") as results: + run(results, comments, hash, True) + benchmarks = get_benchmarks(results) + + for name in benchmarks: + xy = benchmarks[name] + x, y = np.transpose(xy) + for axi in ax.flatten(): + for artist in axi.get_children(): + if isinstance(artist, lines.Line2D) and artist.get_label() == name: + artist.set_xdata(x) + artist.set_ydata(y) + + fig.canvas.draw() + + thread = threading.Thread(target=worker, args=(fig, ax, hash)) + thread.start() + +fig.canvas.mpl_connect('key_press_event', on_key_press) + +plt.show() diff --git a/src/boost/libs/histogram/benchmark/run_benchmarks.py b/src/boost/libs/histogram/benchmark/run_benchmarks.py new file mode 100755 index 000000000..db1055013 --- /dev/null +++ b/src/boost/libs/histogram/benchmark/run_benchmarks.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +# Copyright Hans Dembinski 2019 +# 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 + +""" +This script runs the benchmarks on previous versions of this library to track changes +in performance. + +Run this from a special build directory that uses the benchmark folder as root + + cd my_build_dir + cmake ../benchmark + ../run_benchmarks.py + +This creates a database, benchmark_results. Plot it: + + ../plot_benchmarks.py + +The script leaves the include folder in a modified state. To clean up, do: + + git checkout HEAD -- ../include + git clean -f -- ../include + +""" +import subprocess as subp +import tempfile +import os +import shelve +import json +import argparse + + +def get_commits(): + commits = [] + comments = {} + for line in subp.check_output(("git", "log", "--oneline")).decode("ascii").split("\n"): + if line: + ispace = line.index(" ") + hash = line[:ispace] + commits.append(hash) + comments[hash] = line[ispace+1:] + commits = commits[::-1] + return commits, comments + + +def recursion(results, commits, comments, ia, ib): + ic = int((ia + ib) / 2) + if ic == ia: + return + run(results, comments, commits[ic], False) + if all([results[commits[i]] is None for i in (ia, ib, ic)]): + return + recursion(results, commits, comments, ic, ib) + recursion(results, commits, comments, ia, ic) + + +def run(results, comments, hash, update): + if not update and hash in results: + return + print(hash, comments[hash]) + subp.call(("rm", "-rf", "../include")) + if subp.call(("git", "checkout", hash, "--", "../include")) != 0: + print("[Benchmark] Cannot checkout include folder\n") + return + print(hash, "make") + with tempfile.TemporaryFile() as out: + if subp.call(("make", "-j4", "histogram_filling"), stdout=out, stderr=out) != 0: + print("[Benchmark] Cannot make benchmarks\n") + out.seek(0) + print(out.read().decode("utf-8") + "\n") + return + print(hash, "run") + s = subp.check_output(("./histogram_filling", "--benchmark_format=json", "--benchmark_filter=normal")) + d = json.loads(s) + if update and hash in results and results[hash] is not None: + d2 = results[hash] + for i, (b, b2) in enumerate(zip(d["benchmarks"], d2["benchmarks"])): + d["benchmarks"][i] = b if b["cpu_time"] < b2["cpu_time"] else b2 + results[hash] = d + for benchmark in d["benchmarks"]: + print(benchmark["name"], min(benchmark["real_time"], benchmark["cpu_time"])) + + +def main(): + commits, comments = get_commits() + + parser = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("first", type=str, default="begin", + help="first commit in range, special value `begin` is allowed") + parser.add_argument("last", type=str, default="end", + help="last commit in range, special value `end` is allowed") + parser.add_argument("-f", action="store_true", + help="override previous results") + + args = parser.parse_args() + + if args.first == "begin": + args.first = commits[0] + if args.last == "end": + args.last = commits[-1] + + with shelve.open("benchmark_results") as results: + a = commits.index(args.first) + b = commits.index(args.last) + if args.f: + for hash in commits[a:b+1]: + del results[hash] + run(results, comments, args.first, False) + run(results, comments, args.last, False) + recursion(results, commits, comments, a, b) + +if __name__ == "__main__": + main() diff --git a/src/boost/libs/histogram/examples/Jamfile b/src/boost/libs/histogram/examples/Jamfile new file mode 100644 index 000000000..c9d620aa8 --- /dev/null +++ b/src/boost/libs/histogram/examples/Jamfile @@ -0,0 +1,71 @@ +# Copyright (c) 2018 Mateusz Loskot <mateusz@loskot.net> +# Copyright (c) 2019 Hans Dembinski +# +# Use, modification and distribution is subject to 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) + +import testing ; +import ../../config/checks/config : requires ; + +project + : requirements + [ requires + cxx14_constexpr cxx14_decltype_auto cxx14_generic_lambdas cxx14_return_type_deduction cxx11_user_defined_literals + # list could go on... + ] + ; + +alias cxx14 : + [ run getting_started_listing_01.cpp ] + [ run getting_started_listing_02.cpp ] + [ run getting_started_listing_03.cpp ] + [ run getting_started_listing_04.cpp ] + [ run getting_started_listing_05.cpp ] + [ run guide_axis_basic_demo.cpp ] + [ run guide_axis_circular.cpp ] + [ run guide_axis_growing.cpp ] + [ run guide_axis_with_labels.cpp ] + [ run guide_axis_with_transform.cpp ] + [ run guide_axis_with_uoflow_off.cpp ] + [ run guide_custom_2d_axis.cpp ] + [ run guide_custom_accumulators_builtin.cpp ] + [ run guide_custom_accumulators_with_metadata.cpp ] + [ run guide_custom_accumulators_simple.cpp ] + [ run guide_custom_accumulators_advanced.cpp ] + [ run guide_custom_accumulators_ouroboros.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_fill_profile.cpp ] + [ run guide_fill_weighted_histogram.cpp ] + [ run guide_fill_weighted_profile.cpp ] + [ run guide_histogram_operators.cpp ] + [ run guide_histogram_reduction.cpp ] + [ run guide_histogram_projection.cpp ] + [ run guide_histogram_streaming.cpp ] + [ run guide_histogram_in_api.cpp ] + [ run guide_indexed_access.cpp ] + [ run guide_make_dynamic_histogram.cpp ] + [ run guide_make_static_histogram.cpp ] + [ run guide_stdlib_algorithms.cpp ] + ; + +alias threading : + [ run guide_parallel_filling.cpp ] : + <threading>multi + ; + +alias libserial : /boost/serialization//boost_serialization : <link>static ; + +alias serial : + [ run guide_histogram_serialization.cpp libserial ] + ; + +alias all : cxx14 threading serial ; + +explicit cxx14 ; +explicit threading ; +explicit libserial ; +explicit serial ; diff --git a/src/boost/libs/histogram/examples/getting_started_listing_01.cpp b/src/boost/libs/histogram/examples/getting_started_listing_01.cpp new file mode 100644 index 000000000..a83df68d7 --- /dev/null +++ b/src/boost/libs/histogram/examples/getting_started_listing_01.cpp @@ -0,0 +1,93 @@ +// 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) + +// clang-format off + +//[ getting_started_listing_01 + +#include <algorithm> // std::for_each +#include <boost/format.hpp> // only needed for printing +#include <boost/histogram.hpp> // make_histogram, regular, weight, indexed +#include <cassert> // assert (used to test this example for correctness) +#include <functional> // std::ref +#include <iostream> // std::cout, std::flush +#include <sstream> // std::ostringstream + +int main() { + using namespace boost::histogram; // strip the boost::histogram prefix + + /* + Create a 1d-histogram with a regular axis that has 6 equidistant bins on + the real line from -1.0 to 2.0, and label it as "x". A family of overloaded + factory functions called `make_histogram` makes creating histograms easy. + + A regular axis is a sequence of semi-open bins. Extra under- and overflow + bins extend the axis by default (this can be turned off). + + index : -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 + bin edges: -inf -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 inf + */ + auto h = make_histogram(axis::regular<>(6, -1.0, 2.0, "x")); + + /* + Let's fill a histogram with data, typically this happens in a loop. + + STL algorithms are supported. std::for_each is very convenient to fill a + histogram from an iterator range. Use std::ref in the call, if you don't + want std::for_each to make a copy of your histogram. + */ + auto data = {-0.5, 1.1, 0.3, 1.7}; + std::for_each(data.begin(), data.end(), std::ref(h)); + // let's fill some more values manually + h(-1.5); // is placed in underflow bin -1 + h(-1.0); // is placed in bin 0, bin interval is semi-open + h(2.0); // is placed in overflow bin 6, bin interval is semi-open + h(20.0); // is placed in overflow bin 6 + + /* + This does a weighted fill using the `weight` function as an additional + argument. It may appear at the beginning or end of the argument list. C++ + doesn't have keyword arguments like Python, this is the next-best thing. + */ + h(0.1, weight(1.0)); + + /* + Iterate over bins with the `indexed` range generator, which provides a + special accessor object, that can be used to obtain the current bin index, + and the current bin value by dereferncing (it acts like a pointer to the + value). Using `indexed` is convenient and gives you better performance than + looping over the histogram cells with hand-written for loops. By default, + under- and overflow bins are skipped. Passing `coverage::all` as the + optional second argument iterates over all bins. + + - Access the value with the dereference operator. + - Access the current index with `index(d)` method of the accessor. + - Access the corresponding bin interval view with `bin(d)`. + + The return type of `bin(d)` depends on the axis type (see the axis reference + for details). It usually is a class that represents a semi-open interval. + Edges can be accessed with methods `lower()` and `upper()`. + */ + + std::ostringstream os; + for (auto&& x : indexed(h, coverage::all)) { + os << boost::format("bin %2i [%4.1f, %4.1f): %i\n") + % x.index() % x.bin().lower() % x.bin().upper() % *x; + } + + std::cout << os.str() << std::flush; + + assert(os.str() == "bin -1 [-inf, -1.0): 1\n" + "bin 0 [-1.0, -0.5): 1\n" + "bin 1 [-0.5, -0.0): 1\n" + "bin 2 [-0.0, 0.5): 2\n" + "bin 3 [ 0.5, 1.0): 0\n" + "bin 4 [ 1.0, 1.5): 1\n" + "bin 5 [ 1.5, 2.0): 1\n" + "bin 6 [ 2.0, inf): 2\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/getting_started_listing_02.cpp b/src/boost/libs/histogram/examples/getting_started_listing_02.cpp new file mode 100644 index 000000000..cf5b3143a --- /dev/null +++ b/src/boost/libs/histogram/examples/getting_started_listing_02.cpp @@ -0,0 +1,83 @@ +// 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) + +// clang-format off + +//[ getting_started_listing_02 + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> +#include <string> + +int main() { + using namespace boost::histogram; + + /* + Create a histogram which can be configured dynamically at run-time. The axis + configuration is first collected in a vector of axis::variant type, which + can hold different axis types (those in its template argument list). Here, + we use a variant that can store a regular and a category axis. + */ + using reg = axis::regular<>; + using cat = axis::category<std::string>; + using variant = axis::variant<axis::regular<>, axis::category<std::string>>; + std::vector<variant> axes; + axes.emplace_back(cat({"red", "blue"})); + axes.emplace_back(reg(3, 0.0, 1.0, "x")); + axes.emplace_back(reg(3, 0.0, 1.0, "y")); + // passing an iterator range also works here + auto h = make_histogram(std::move(axes)); + + // fill histogram with data, usually this happens in a loop + h("red", 0.1, 0.2); + h("blue", 0.7, 0.3); + h("red", 0.3, 0.7); + h("red", 0.7, 0.7); + + /* + Print histogram by iterating over bins. + Since the [bin type] of the category axis cannot be converted into a double, + it cannot be handled by the polymorphic interface of axis::variant. We use + axis::get to "cast" the variant type to the actual category type. + */ + + // get reference to category axis, performs a run-time checked static cast + const auto& cat_axis = axis::get<cat>(h.axis(0)); + std::ostringstream os; + for (auto&& x : indexed(h)) { + os << boost::format("(%i, %i, %i) %4s [%3.1f, %3.1f) [%3.1f, %3.1f) %3.0f\n") + % x.index(0) % x.index(1) % x.index(2) + % cat_axis.bin(x.index(0)) + % x.bin(1).lower() % x.bin(1).upper() + % x.bin(2).lower() % x.bin(2).upper() + % *x; + } + + std::cout << os.str() << std::flush; + assert(os.str() == "(0, 0, 0) red [0.0, 0.3) [0.0, 0.3) 1\n" + "(1, 0, 0) blue [0.0, 0.3) [0.0, 0.3) 0\n" + "(0, 1, 0) red [0.3, 0.7) [0.0, 0.3) 0\n" + "(1, 1, 0) blue [0.3, 0.7) [0.0, 0.3) 0\n" + "(0, 2, 0) red [0.7, 1.0) [0.0, 0.3) 0\n" + "(1, 2, 0) blue [0.7, 1.0) [0.0, 0.3) 1\n" + "(0, 0, 1) red [0.0, 0.3) [0.3, 0.7) 0\n" + "(1, 0, 1) blue [0.0, 0.3) [0.3, 0.7) 0\n" + "(0, 1, 1) red [0.3, 0.7) [0.3, 0.7) 0\n" + "(1, 1, 1) blue [0.3, 0.7) [0.3, 0.7) 0\n" + "(0, 2, 1) red [0.7, 1.0) [0.3, 0.7) 0\n" + "(1, 2, 1) blue [0.7, 1.0) [0.3, 0.7) 0\n" + "(0, 0, 2) red [0.0, 0.3) [0.7, 1.0) 1\n" + "(1, 0, 2) blue [0.0, 0.3) [0.7, 1.0) 0\n" + "(0, 1, 2) red [0.3, 0.7) [0.7, 1.0) 0\n" + "(1, 1, 2) blue [0.3, 0.7) [0.7, 1.0) 0\n" + "(0, 2, 2) red [0.7, 1.0) [0.7, 1.0) 1\n" + "(1, 2, 2) blue [0.7, 1.0) [0.7, 1.0) 0\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/getting_started_listing_03.cpp b/src/boost/libs/histogram/examples/getting_started_listing_03.cpp new file mode 100644 index 000000000..f3ebfcd8c --- /dev/null +++ b/src/boost/libs/histogram/examples/getting_started_listing_03.cpp @@ -0,0 +1,54 @@ +// 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) + +// clang-format off + +//[ getting_started_listing_03 + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + + /* + Create a profile. Profiles does not only count entries in each cell, but + also compute the mean of a sample value in each cell. + */ + auto p = make_profile(axis::regular<>(5, 0.0, 1.0)); + + /* + Fill profile with data, usually this happens in a loop. You pass the sample + with the `sample` helper function. The sample can be the first or last + argument. + */ + p(0.1, sample(1)); + p(0.15, sample(3)); + p(0.2, sample(4)); + p(0.9, sample(5)); + + /* + Iterate over bins and print profile. + */ + std::ostringstream os; + for (auto&& x : indexed(p)) { + os << boost::format("bin %i [%3.1f, %3.1f) count %i mean %g\n") + % x.index() % x.bin().lower() % x.bin().upper() + % x->count() % x->value(); + } + + std::cout << os.str() << std::flush; + assert(os.str() == "bin 0 [0.0, 0.2) count 2 mean 2\n" + "bin 1 [0.2, 0.4) count 1 mean 4\n" + "bin 2 [0.4, 0.6) count 0 mean 0\n" + "bin 3 [0.6, 0.8) count 0 mean 0\n" + "bin 4 [0.8, 1.0) count 1 mean 5\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/getting_started_listing_04.cpp b/src/boost/libs/histogram/examples/getting_started_listing_04.cpp new file mode 100644 index 000000000..fe44b1f51 --- /dev/null +++ b/src/boost/libs/histogram/examples/getting_started_listing_04.cpp @@ -0,0 +1,60 @@ +// Copyright 2019 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) + +// clang-format off + +//[ getting_started_listing_04 + +#include <algorithm> // std::max_element +#include <boost/format.hpp> // only needed for printing +#include <boost/histogram.hpp> // make_histogram, integer, indexed +#include <iostream> // std::cout, std::endl +#include <sstream> // std::ostringstream + +int main() { + using namespace boost::histogram; + using namespace boost::histogram::literals; + + /* + We make a 3d histogram for color values (r, g, b) in an image. We assume the values + are of type char. The value range then is [0, 256). The integer axis is perfect for + color values. + */ + auto h = make_histogram( + axis::integer<>(0, 256, "r"), + axis::integer<>(0, 256, "g"), + axis::integer<>(0, 256, "b") + ); + + /* + We don't have real image data, so fill some fake data. + */ + h(1, 2, 3); + h(1, 2, 3); + h(0, 1, 0); + + /* + Now let's say we want to know which color is most common. We can use std::max_element + on an indexed range for that. + */ + auto ind = indexed(h); + auto max_it = std::max_element(ind.begin(), ind.end()); + + /* + max_it is a special iterator to the histogram cell with the highest count. + This iterator allows one to access the cell value, bin indices, and bin values. + You need to twice dereference it to get to the cell value. + */ + std::ostringstream os; + os << boost::format("count=%i rgb=(%i, %i, %i)") + % **max_it % max_it->bin(0_c) % max_it->bin(1) % max_it->bin(2); + + std::cout << os.str() << std::endl; + + assert(os.str() == "count=2 rgb=(1, 2, 3)"); +} + +//] diff --git a/src/boost/libs/histogram/examples/getting_started_listing_05.cpp b/src/boost/libs/histogram/examples/getting_started_listing_05.cpp new file mode 100644 index 000000000..52706284b --- /dev/null +++ b/src/boost/libs/histogram/examples/getting_started_listing_05.cpp @@ -0,0 +1,65 @@ +// Copyright 2019 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) + +// clang-format off + +//[ getting_started_listing_05 + +//////////////// Begin: put this in header file //////////////////// + +#include <algorithm> // std::max_element +#include <boost/format.hpp> // only needed for printing +#include <boost/histogram.hpp> // make_histogram, integer, indexed +#include <iostream> // std::cout, std::endl +#include <sstream> // std::ostringstream +#include <tuple> +#include <vector> + +// use this when axis configuration is fix to get highest performance +struct HolderOfStaticHistogram { + // put axis types here + using axes_t = std::tuple< + boost::histogram::axis::regular<>, + boost::histogram::axis::integer<> + >; + using hist_t = boost::histogram::histogram<axes_t>; + hist_t hist_; +}; + +// use this when axis configuration should be flexible +struct HolderOfDynamicHistogram { + // put all axis types here that you are going to use + using axis_t = boost::histogram::axis::variant< + boost::histogram::axis::regular<>, + boost::histogram::axis::variable<>, + boost::histogram::axis::integer<> + >; + using axes_t = std::vector<axis_t>; + using hist_t = boost::histogram::histogram<axes_t>; + hist_t hist_; +}; + +//////////////// End: put this in header file //////////////////// + +int main() { + using namespace boost::histogram; + + HolderOfStaticHistogram hs; + hs.hist_ = make_histogram(axis::regular<>(5, 0, 1), axis::integer<>(0, 3)); + // now assign a different histogram + hs.hist_ = make_histogram(axis::regular<>(3, 1, 2), axis::integer<>(4, 6)); + // hs.hist_ = make_histogram(axis::regular<>(5, 0, 1)); does not work; + // the static histogram cannot change the number or order of axis types + + HolderOfDynamicHistogram hd; + hd.hist_ = make_histogram(axis::regular<>(5, 0, 1), axis::integer<>(0, 3)); + // now assign a different histogram + hd.hist_ = make_histogram(axis::regular<>(3, -1, 2)); + // and assign another + hd.hist_ = make_histogram(axis::integer<>(0, 5), axis::integer<>(3, 5)); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_basic_demo.cpp b/src/boost/libs/histogram/examples/guide_axis_basic_demo.cpp new file mode 100644 index 000000000..2612d043c --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_basic_demo.cpp @@ -0,0 +1,82 @@ +// Copyright 2019 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) + +//[ guide_axis_basic_demo + +#include <boost/histogram/axis.hpp> +#include <limits> + +int main() { + using namespace boost::histogram; + + // make a regular axis with 10 bins over interval from 1.5 to 2.5 + auto r = axis::regular<>{10, 1.5, 2.5}; + // `<>` is needed in C++14 because the axis is templated, + // in C++17 you can do: auto r = axis::regular{10, 1.5, 2.5}; + assert(r.size() == 10); + // alternatively, you can define the step size with the `step` marker + auto r_step = axis::regular<>{axis::step(0.1), 1.5, 2.5}; + assert(r_step == r); + + // histogram uses the `index` method to convert values to indices + // note: intervals of builtin axis types are always semi-open [a, b) + assert(r.index(1.5) == 0); + assert(r.index(1.6) == 1); + assert(r.index(2.4) == 9); + // index for a value below the start of the axis is always -1 + assert(r.index(1.0) == -1); + assert(r.index(-std::numeric_limits<double>::infinity()) == -1); + // index for a value below the above the end of the axis is always `size()` + assert(r.index(3.0) == 10); + assert(r.index(std::numeric_limits<double>::infinity()) == 10); + // index for not-a-number is also `size()` by convention + assert(r.index(std::numeric_limits<double>::quiet_NaN()) == 10); + + // make a variable axis with 3 bins [-1.5, 0.1), [0.1, 0.3), [0.3, 10) + auto v = axis::variable<>{-1.5, 0.1, 0.3, 10.}; + assert(v.index(-2.0) == -1); + assert(v.index(-1.5) == 0); + assert(v.index(0.1) == 1); + assert(v.index(0.3) == 2); + assert(v.index(10) == 3); + assert(v.index(20) == 3); + + // make an integer axis with 3 bins at -1, 0, 1 + auto i = axis::integer<>{-1, 2}; + assert(i.index(-2) == -1); + assert(i.index(-1) == 0); + assert(i.index(0) == 1); + assert(i.index(1) == 2); + assert(i.index(2) == 3); + + // make an integer axis called "foo" + auto i_with_label = axis::integer<>{-1, 2, "foo"}; + // all builtin axis types allow you to pass some optional metadata as the last + // argument in the constructor; a string by default, but can be any copyable type + + // two axis do not compare equal if they differ in their metadata + assert(i != i_with_label); + + // integer axis also work well with unscoped enums + enum { red, blue }; + auto i_for_enum = axis::integer<>{red, blue + 1}; + assert(i_for_enum.index(red) == 0); + assert(i_for_enum.index(blue) == 1); + + // make a category axis from a scoped enum and/or if the identifiers are not consecutive + enum class Bar { red = 12, blue = 6 }; + auto c = axis::category<Bar>{Bar::red, Bar::blue}; + assert(c.index(Bar::red) == 0); + assert(c.index(Bar::blue) == 1); + // c.index(12) is a compile-time error, since the argument must be of type `Bar` + + // category axis can be created for any copyable and equal-comparable type + auto c_str = axis::category<std::string>{"red", "blue"}; + assert(c_str.index("red") == 0); + assert(c_str.index("blue") == 1); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_circular.cpp b/src/boost/libs/histogram/examples/guide_axis_circular.cpp new file mode 100644 index 000000000..b4b8b4941 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_circular.cpp @@ -0,0 +1,43 @@ +// Copyright 2019 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) + +//[ guide_axis_circular + +#include <boost/histogram/axis.hpp> +#include <limits> + +int main() { + using namespace boost::histogram; + + // make a circular regular axis ... [0, 180), [180, 360), [0, 180) .... + using opts = decltype(axis::option::overflow | axis::option::circular); + auto r = axis::regular<double, use_default, use_default, opts>{2, 0., 360.}; + assert(r.index(-180) == 1); + assert(r.index(0) == 0); + assert(r.index(180) == 1); + assert(r.index(360) == 0); + assert(r.index(540) == 1); + assert(r.index(720) == 0); + // special values are mapped to the overflow bin index + assert(r.index(std::numeric_limits<double>::infinity()) == 2); + assert(r.index(-std::numeric_limits<double>::infinity()) == 2); + assert(r.index(std::numeric_limits<double>::quiet_NaN()) == 2); + + // since the regular axis is the most common circular axis, there exists an alias + auto c = axis::circular<>{2, 0., 360.}; + assert(r == c); + + // make a circular integer axis + auto i = axis::integer<int, use_default, axis::option::circular_t>{1, 4}; + assert(i.index(0) == 2); + assert(i.index(1) == 0); + assert(i.index(2) == 1); + assert(i.index(3) == 2); + assert(i.index(4) == 0); + assert(i.index(5) == 1); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_growing.cpp b/src/boost/libs/histogram/examples/guide_axis_growing.cpp new file mode 100644 index 000000000..5aed481b3 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_growing.cpp @@ -0,0 +1,62 @@ +// Copyright 2019 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) + +// clang-format off + +//[ guide_axis_growing + +#include <boost/histogram.hpp> +#include <cassert> + +#include <iostream> + +int main() { + using namespace boost::histogram; + + // make a growing regular axis + // - it grows new bins with its constant bin width until the value is covered + auto h1 = make_histogram(axis::regular<double, + use_default, + use_default, + axis::option::growth_t>{2, 0., 1.}); + // nothing special happens here + h1(0.1); + h1(0.9); + // state: [0, 0.5): 1, [0.5, 1.0): 1 + assert(h1.axis().size() == 2); + assert(h1.axis().bin(0).lower() == 0.0); + assert(h1.axis().bin(1).upper() == 1.0); + + // value below range: axis grows new bins until value is in range + h1(-0.3); + // state: [-0.5, 0.0): 1, [0, 0.5): 1, [0.5, 1.0): 1 + assert(h1.axis().size() == 3); + assert(h1.axis().bin(0).lower() == -0.5); + assert(h1.axis().bin(2).upper() == 1.0); + + h1(1.9); + // state: [-0.5, 0.0): 1, [0, 0.5): 1, [0.5, 1.0): 1, [1.0, 1.5): 0 [1.5, 2.0): 1 + assert(h1.axis().size() == 5); + assert(h1.axis().bin(0).lower() == -0.5); + assert(h1.axis().bin(4).upper() == 2.0); + + // make a growing category axis (here strings) + // - empty axis is allowed: very useful if categories are not known at the beginning + auto h2 = make_histogram(axis::category<std::string, + use_default, + axis::option::growth_t>()); + assert(h2.size() == 0); // histogram is empty + h2("foo"); // new bin foo, index 0 + assert(h2.size() == 1); + h2("bar"); // new bin bar, index 1 + assert(h2.size() == 2); + h2("foo"); + assert(h2.size() == 2); + assert(h2[0] == 2); + assert(h2[1] == 1); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_with_labels.cpp b/src/boost/libs/histogram/examples/guide_axis_with_labels.cpp new file mode 100644 index 000000000..3f3ad8b54 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_with_labels.cpp @@ -0,0 +1,21 @@ +// 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) + +//[ guide_axis_with_labels + +#include <boost/histogram.hpp> + +int main() { + using namespace boost::histogram; + + // create a 2d-histogram with an "age" and an "income" axis + auto h = make_histogram(axis::regular<>(20, 0.0, 100.0, "age in years"), + axis::regular<>(20, 0.0, 100.0, "yearly income in Thousands")); + + // do something with h +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_with_transform.cpp b/src/boost/libs/histogram/examples/guide_axis_with_transform.cpp new file mode 100644 index 000000000..9b3412a7c --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_with_transform.cpp @@ -0,0 +1,57 @@ +// Copyright 2019 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) + +//[ guide_axis_with_transform + +#include <boost/histogram/axis/regular.hpp> +#include <limits> + +int main() { + using namespace boost::histogram; + + // make a regular axis with a log transform over [10, 100), [100, 1000), [1000, 10000) + axis::regular<double, axis::transform::log> r_log{3, 10., 10000.}; + // log transform: + // - useful when values vary dramatically in magnitude, like brightness of stars + // - edges are not exactly at 10, 100, 1000, because of finite floating point precision + // - values >= 0 but smaller than the starting value of the axis are mapped to -1 + // - values < 0 are mapped to `size()`, because the result of std::log(value) is NaN + assert(r_log.index(10.1) == 0); + assert(r_log.index(100.1) == 1); + assert(r_log.index(1000.1) == 2); + assert(r_log.index(1) == -1); + assert(r_log.index(0) == -1); + assert(r_log.index(-1) == 3); + + // make a regular axis with a sqrt transform over [4, 9), [9, 16), [16, 25) + axis::regular<double, axis::transform::sqrt> r_sqrt{3, 4., 25.}; + // sqrt transform: + // - bin widths are more mildly increasing compared to log transform + // - axis starting at value == 0 is ok, sqrt(0) == 0 unlike log transform + // - values < 0 are mapped to `size()`, because the result of std::sqrt(value) is NaN + assert(r_sqrt.index(0) == -1); + assert(r_sqrt.index(4.1) == 0); + assert(r_sqrt.index(9.1) == 1); + assert(r_sqrt.index(16.1) == 2); + assert(r_sqrt.index(25.1) == 3); + assert(r_sqrt.index(-1) == 3); + + // make a regular axis with a power transform x^1/3 over [1, 8), [8, 27), [27, 64) + using pow_trans = axis::transform::pow; + axis::regular<double, pow_trans> r_pow(pow_trans{1. / 3.}, 3, 1., 64.); + // pow transform: + // - generalization of the sqrt transform + // - starting the axis at value == 0 is ok for power p > 0, 0^p == 0 for p > 0 + // - values < 0 are mapped to `size()` if power p is not a positive integer + assert(r_pow.index(0) == -1); + assert(r_pow.index(1.1) == 0); + assert(r_pow.index(8.1) == 1); + assert(r_pow.index(27.1) == 2); + assert(r_pow.index(64.1) == 3); + assert(r_pow.index(-1) == 3); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_axis_with_uoflow_off.cpp b/src/boost/libs/histogram/examples/guide_axis_with_uoflow_off.cpp new file mode 100644 index 000000000..7fe96f5d7 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_axis_with_uoflow_off.cpp @@ -0,0 +1,30 @@ +// 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) + +//[ guide_axis_with_uoflow_off + +#include <boost/histogram.hpp> +#include <string> + +int main() { + using namespace boost::histogram; + + // create a 1d-histogram over integer values from 1 to 6 + auto h1 = make_histogram(axis::integer<int>(1, 7)); + // axis has size 6... + assert(h1.axis().size() == 6); + // ... but histogram has size 8, because of overflow and underflow bins + assert(h1.size() == 8); + + // create a 1d-histogram for throws of a six-sided die without extra bins, + // since the values cannot be smaller than 1 or larger than 6 + auto h2 = make_histogram(axis::integer<int, use_default, axis::option::none_t>(1, 7)); + // now size of axis and histogram is equal + assert(h2.axis().size() == 6); + assert(h2.size() == 6); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_2d_axis.cpp b/src/boost/libs/histogram/examples/guide_custom_2d_axis.cpp new file mode 100644 index 000000000..6a952a659 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_2d_axis.cpp @@ -0,0 +1,56 @@ +// 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) + +//[ guide_custom_2d_axis + +#include <boost/histogram.hpp> +#include <cassert> + +int main() { + using namespace boost::histogram; + + // axis which returns 1 if the input falls inside the unit circle and zero otherwise + struct circle_axis { + // accepts a 2D point in form of a std::tuple + axis::index_type index(const std::tuple<double, double>& point) const { + const auto x = std::get<0>(point); + const auto y = std::get<1>(point); + return x * x + y * y <= 1.0; + } + + axis::index_type size() const { return 2; } + }; + + auto h1 = make_histogram(circle_axis()); + + // fill looks normal for a histogram which has only one Nd-axis + h1(0, 0); // in + h1(0, -1); // in + h1(0, 1); // in + h1(-1, 0); // in + h1(1, 0); // in + h1(1, 1); // out + h1(-1, -1); // out + + // 2D histogram, but only 1D index + assert(h1.at(0) == 2); // out + assert(h1.at(1) == 5); // in + + // other axes can be combined with a Nd-axis + auto h2 = make_histogram(circle_axis(), axis::category<std::string>({"red", "blue"})); + + // now we need to pass arguments for Nd-axis explicitly as std::tuple + h2(std::make_tuple(0, 0), "red"); + h2(std::make_tuple(1, 1), "blue"); + + // 3D histogram, but only 2D index + assert(h2.at(0, 0) == 0); // out, red + assert(h2.at(0, 1) == 1); // out, blue + assert(h2.at(1, 0) == 1); // in, red + assert(h2.at(1, 1) == 0); // in, blue +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_accumulators_advanced.cpp b/src/boost/libs/histogram/examples/guide_custom_accumulators_advanced.cpp new file mode 100644 index 000000000..16ab559f2 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_accumulators_advanced.cpp @@ -0,0 +1,52 @@ +// Copyright 2019 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) + +//[ guide_custom_accumulators_advanced + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + + // Accumulator accepts two samples and an optional weight and computes the mean of each. + struct multi_mean { + accumulators::mean<> mx, my; + + // called when no weight is passed + void operator()(double x, double y) { + mx(x); + my(y); + } + + // called when a weight is passed + void operator()(weight_type<double> w, double x, double y) { + mx(w, x); + my(w, y); + } + }; + // Note: The implementation can be made more efficient by sharing the sum of weights. + + // Create a 1D histogram that uses the custom accumulator. + auto h = make_histogram_with(dense_storage<multi_mean>(), axis::integer<>(0, 2)); + h(0, sample(1, 2)); // samples go to first cell + h(0, sample(3, 4)); // samples go to first cell + h(1, sample(5, 6), weight(2)); // samples go to second cell + h(1, sample(7, 8), weight(3)); // samples go to second cell + + std::ostringstream os; + for (auto&& bin : indexed(h)) { + os << boost::format("index %i mean-x %.1f mean-y %.1f\n") % bin.index() % + bin->mx.value() % bin->my.value(); + } + std::cout << os.str() << std::flush; + assert(os.str() == "index 0 mean-x 2.0 mean-y 3.0\n" + "index 1 mean-x 6.2 mean-y 7.2\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_accumulators_builtin.cpp b/src/boost/libs/histogram/examples/guide_custom_accumulators_builtin.cpp new file mode 100644 index 000000000..59c1e1c7a --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_accumulators_builtin.cpp @@ -0,0 +1,43 @@ +// 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) + +//[ guide_custom_accumulators_builtin + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + using mean = accumulators::mean<>; + + // Create a 1D-profile, which computes the mean of samples in each bin. + auto h = make_histogram_with(dense_storage<mean>(), axis::integer<>(0, 2)); + // The factory function `make_profile` is provided as a shorthand for this, so this is + // equivalent to the previous line: auto h = make_profile(axis::integer<>(0, 2)); + + // An argument marked as `sample` is passed to the accumulator. + h(0, sample(1)); // sample goes to first cell + h(0, sample(2)); // sample goes to first cell + h(1, sample(3)); // sample goes to second cell + h(1, sample(4)); // sample goes to second cell + + std::ostringstream os; + for (auto&& x : indexed(h)) { + // Accumulators usually have methods to access their state. Use the arrow + // operator to access them. Here, `count()` gives the number of samples, + // `value()` the mean, and `variance()` the variance estimate of the mean. + os << boost::format("index %i count %i mean %.1f variance %.1f\n") % x.index() % + x->count() % x->value() % x->variance(); + } + std::cout << os.str() << std::flush; + assert(os.str() == "index 0 count 2 mean 1.5 variance 0.5\n" + "index 1 count 2 mean 3.5 variance 0.5\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_accumulators_ouroboros.cpp b/src/boost/libs/histogram/examples/guide_custom_accumulators_ouroboros.cpp new file mode 100644 index 000000000..5d7299e18 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_accumulators_ouroboros.cpp @@ -0,0 +1,51 @@ +// Copyright 2019 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) + +//[ guide_custom_accumulators_ouroboros + +#include <boost/histogram.hpp> +#include <cmath> +#include <iostream> +#include <sstream> +#include <string> + +int main() { + using namespace boost::histogram; + + // First we define the nested histogram type. + using axis_t = axis::category<int, axis::null_type, axis::option::growth_t>; + using base_t = histogram<std::tuple<axis_t>>; + + // Now we make an accumulator out of it by using inheritance. + // We only need to implement operator(). A matching version of operator() is actually + // present in base_t, but it is templated and this is not allowed by the accumulator + // concept. Initialization could also happen here. We don't need to initialize anything + // here, because the default constructor of base_t is called automatically and + // sufficient for this example. + struct hist_t : base_t { + void operator()(const double x) { base_t::operator()(x); } + }; + + auto h = make_histogram_with(dense_storage<hist_t>(), axis::integer<>(1, 4)); + + auto x = {1, 1, 2, 2}; + auto s = {1, 2, 3, 3}; // samples are filled into the nested histograms + h.fill(x, sample(s)); + + std::ostringstream os; + for (auto&& x : indexed(h)) { + os << x.bin() << " "; + for (auto&& y : indexed(*x)) { os << "(" << y.bin() << ": " << *y << ") "; } + os << "\n"; + } + + std::cout << os.str() << std::flush; + assert(os.str() == "1 (1: 1) (2: 1) \n" + "2 (3: 2) \n" + "3 \n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_accumulators_simple.cpp b/src/boost/libs/histogram/examples/guide_custom_accumulators_simple.cpp new file mode 100644 index 000000000..3e8bec478 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_accumulators_simple.cpp @@ -0,0 +1,44 @@ +// 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) + +//[ guide_custom_accumulators_simple + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + + // A custom accumulator which tracks the maximum of the samples. + // It must have a call operator that accepts the argument of the `sample` function. + struct maximum { + // return value is ignored, so we use void + void operator()(double x) { + if (x > value) value = x; + } + double value = 0; // value is public and initialized to zero + }; + + // Create 1D histogram that uses the custom accumulator. + auto h = make_histogram_with(dense_storage<maximum>(), axis::integer<>(0, 2)); + h(0, sample(1.0)); // sample goes to first cell + h(0, sample(2.0)); // sample goes to first cell + h(1, sample(3.0)); // sample goes to second cell + h(1, sample(4.0)); // sample goes to second cell + + std::ostringstream os; + for (auto&& x : indexed(h)) { + os << boost::format("index %i maximum %.1f\n") % x.index() % x->value; + } + std::cout << os.str() << std::flush; + assert(os.str() == "index 0 maximum 2.0\n" + "index 1 maximum 4.0\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_accumulators_with_metadata.cpp b/src/boost/libs/histogram/examples/guide_custom_accumulators_with_metadata.cpp new file mode 100644 index 000000000..478f97842 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_accumulators_with_metadata.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 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) + +//[ guide_custom_accumulators_with_metadata + +#include <boost/histogram.hpp> +#include <cmath> +#include <iostream> +#include <sstream> +#include <string> + +int main() { + using namespace boost::histogram; + + // derive custom accumulator from one of the builtins + struct accumulator_with_metadata : accumulators::count<> { + std::string meta; // custom meta data + + // arbitrary additional data and interface could be added here + }; + + // make 1D histogram with custom accmulator + auto h = make_histogram_with(dense_storage<accumulator_with_metadata>(), + axis::integer<>(1, 4)); + + // fill some weighted entries + auto x = {1, 0, 2, 1}; + h.fill(x); + + // assigning meta data to two bins + h[0].meta = "Foo"; + h[2].meta = "Bar"; + + std::ostringstream os; + for (auto&& x : indexed(h)) + os << x.bin() << " value " << x->value() << " meta " << x->meta << "\n"; + + std::cout << os.str() << std::flush; + assert(os.str() == "1 value 2 meta Foo\n" + "2 value 1 meta \n" + "3 value 0 meta Bar\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_minimal_axis.cpp b/src/boost/libs/histogram/examples/guide_custom_minimal_axis.cpp new file mode 100644 index 000000000..21c5fd5ce --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_minimal_axis.cpp @@ -0,0 +1,41 @@ +// 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) + +//[ guide_custom_minimal_axis + +#include <boost/histogram.hpp> +#include <cassert> + +int main() { + using namespace boost::histogram; + + // stateless axis which returns 1 if the input is even and 0 otherwise + struct even_odd_axis { + axis::index_type index(int x) const { return x % 2; } + axis::index_type size() const { return 2; } + }; + + // threshold axis which returns 1 if the input is above threshold + struct threshold_axis { + threshold_axis(double x) : thr(x) {} + axis::index_type index(double x) const { return x >= thr; } + axis::index_type size() const { return 2; } + double thr; + }; + + auto h = make_histogram(even_odd_axis(), threshold_axis(3.0)); + + h(0, 2.0); + h(1, 4.0); + h(2, 4.0); + + assert(h.at(0, 0) == 1); // even, below threshold + assert(h.at(0, 1) == 1); // even, above threshold + assert(h.at(1, 0) == 0); // odd, below threshold + assert(h.at(1, 1) == 1); // odd, above threshold +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_modified_axis.cpp b/src/boost/libs/histogram/examples/guide_custom_modified_axis.cpp new file mode 100644 index 000000000..87e2aa094 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_modified_axis.cpp @@ -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) + +//[ guide_custom_modified_axis + +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + + // custom axis, which adapts builtin integer 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 + axis::index_type index(value_type s) const { return integer::index(std::atoi(s)); } + }; + + auto h = make_histogram(custom_axis(3, 6)); + h("-10"); + h("3"); + h("4"); + h("9"); + + std::ostringstream os; + for (auto&& b : indexed(h)) { + os << "bin " << b.index() << " [" << b.bin() << "] " << *b << "\n"; + } + + std::cout << os.str() << std::endl; + + assert(os.str() == "bin 0 [3] 1\n" + "bin 1 [4] 1\n" + "bin 2 [5] 0\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_custom_storage.cpp b/src/boost/libs/histogram/examples/guide_custom_storage.cpp new file mode 100644 index 000000000..6737df7e0 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_custom_storage.cpp @@ -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) + +//[ guide_custom_storage + +#include <algorithm> // std::for_each +#include <array> +#include <boost/histogram.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <functional> // std::ref +#include <unordered_map> +#include <vector> + +int main() { + using namespace boost::histogram; + const auto axis = axis::regular<>(10, 0.0, 1.0); + + auto data = {0.1, 0.3, 0.2, 0.7}; + + // Create static histogram with vector<int> as counter storage, you can use + // other arithmetic types as counters, e.g. double. + auto h1 = make_histogram_with(std::vector<int>(), axis); + std::for_each(data.begin(), data.end(), std::ref(h1)); + assert(algorithm::sum(h1) == 4); + + // Create static histogram with array<int, N> as counter storage which is + // allocated completely on the stack (this is very fast). N may be larger than + // the actual number of bins used; an exception is raised if N is too small to + // hold all bins. + auto h2 = make_histogram_with(std::array<int, 12>(), axis); + std::for_each(data.begin(), data.end(), std::ref(h2)); + assert(algorithm::sum(h2) == 4); + + // Create static histogram with unordered_map as counter storage; this + // generates a sparse histogram where only memory is allocated for bins that + // are non-zero. This sounds like a good idea for high-dimensional histograms, + // but maps come with a memory and run-time overhead. The default_storage + // usually performs better in high dimensions. + auto h3 = make_histogram_with(std::unordered_map<std::size_t, int>(), axis); + std::for_each(data.begin(), data.end(), std::ref(h3)); + assert(algorithm::sum(h3) == 4); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_fill_histogram.cpp b/src/boost/libs/histogram/examples/guide_fill_histogram.cpp new file mode 100644 index 000000000..172be66e0 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_fill_histogram.cpp @@ -0,0 +1,61 @@ +// 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) + +//[ guide_fill_histogram + +#include <boost/histogram.hpp> +#include <cassert> +#include <functional> +#include <numeric> +#include <utility> +#include <vector> + +int main() { + using namespace boost::histogram; + + auto h = make_histogram(axis::integer<>(0, 3), axis::regular<>(2, 0.0, 1.0)); + + // fill histogram, number of arguments must be equal to number of axes, + // types must be convertible to axis value type (here integer and double) + h(0, 0.2); // increase a cell value by one + h(2, 0.5); // increase another cell value by one + + // fills from a tuple are also supported; passing a tuple of wrong size + // causes an error at compile-time or an assertion at runtime in debug mode + auto xy = std::make_tuple(1, 0.3); + h(xy); + + // chunk-wise filling is also supported and more efficient, make some data... + std::vector<double> xy2[2] = {{0, 2, 5}, {0.8, 0.4, 0.7}}; + + // ... and call fill method + h.fill(xy2); + + // once histogram is filled, access individual cells using operator[] or at(...) + // - operator[] can only accept a single argument in the current version of C++, + // it is convenient when you have a 1D histogram + // - at(...) can accept several values, so use this by default + // - underflow bins are at index -1, overflow bins at index `size()` + // - passing an invalid index triggers a std::out_of_range exception + assert(h.at(0, 0) == 1); + assert(h.at(0, 1) == 1); + assert(h.at(1, 0) == 1); + assert(h.at(1, 1) == 0); + assert(h.at(2, 0) == 1); + assert(h.at(2, 1) == 1); + assert(h.at(-1, -1) == 0); // underflow for axis 0 and 1 + assert(h.at(-1, 0) == 0); // underflow for axis 0, normal bin for axis 1 + assert(h.at(-1, 2) == 0); // underflow for axis 0, overflow for axis 1 + assert(h.at(3, 1) == 1); // overflow for axis 0, normal bin for axis 1 + + // iteration over values works, but see next example for a better way + // - iteration using begin() and end() includes under- and overflow bins + // - iteration order is an implementation detail and should not be relied upon + const double sum = std::accumulate(h.begin(), h.end(), 0.0); + assert(sum == 6); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_fill_profile.cpp b/src/boost/libs/histogram/examples/guide_fill_profile.cpp new file mode 100644 index 000000000..55d650815 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_fill_profile.cpp @@ -0,0 +1,53 @@ +// Copyright 2019 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) + +//[ guide_fill_profile + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> +#include <tuple> + +int main() { + using namespace boost::histogram; + + // make a profile, it computes the mean of the samples in each histogram cell + auto h = make_profile(axis::integer<>(0, 3)); + + // mean is computed from the values marked with the sample() helper function + h(0, sample(1)); // sample goes to cell 0 + h(0, sample(2)); // sample goes to cell 0 + h(1, sample(3)); // sample goes to cell 1 + h(sample(4), 1); // sample goes to cell 1; sample can be first or last argument + + // fills from tuples are also supported, 5 and 6 go to cell 2 + auto a = std::make_tuple(2, sample(5)); + auto b = std::make_tuple(sample(6), 2); + h(a); + h(b); + + // builtin accumulators have methods to access their state + std::ostringstream os; + for (auto&& x : indexed(h)) { + // use `.` to access methods of accessor, like `index()` + // use `->` to access methods of accumulator + const auto i = x.index(); + const auto n = x->count(); // how many samples are in this bin + const auto vl = x->value(); // mean value + const auto vr = x->variance(); // estimated variance of the mean value + os << boost::format("index %i count %i value %.1f variance %.1f\n") % i % n % vl % vr; + } + + std::cout << os.str() << std::flush; + + assert(os.str() == "index 0 count 2 value 1.5 variance 0.5\n" + "index 1 count 2 value 3.5 variance 0.5\n" + "index 2 count 2 value 5.5 variance 0.5\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_fill_weighted_histogram.cpp b/src/boost/libs/histogram/examples/guide_fill_weighted_histogram.cpp new file mode 100644 index 000000000..487e1c33a --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_fill_weighted_histogram.cpp @@ -0,0 +1,36 @@ +// Copyright 2019 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) + +//[ guide_fill_weighted_histogram + +#include <boost/histogram.hpp> +#include <cassert> + +int main() { + using namespace boost::histogram; + + // Create a histogram with weight counters that keep track of a variance estimate. + auto h = make_weighted_histogram(axis::regular<>(3, 0.0, 1.0)); + + h(0.0, weight(1)); // weight 1 goes to first bin + h(0.1, weight(2)); // weight 2 goes to first bin + h(0.4, weight(3)); // weight 3 goes to second bin + h(0.5, weight(4)); // weight 4 goes to second bin + + // chunk-wise filling is also supported + auto x = {0.2, 0.6}; + auto w = {5, 6}; + h.fill(x, weight(w)); + + // Weight counters have methods to access the value (sum of weights) and the variance + // (sum of weights squared, why this gives the variance is explained in the rationale) + assert(h[0].value() == 1 + 2 + 5); + assert(h[0].variance() == 1 * 1 + 2 * 2 + 5 * 5); + assert(h[1].value() == 3 + 4 + 6); + assert(h[1].variance() == 3 * 3 + 4 * 4 + 6 * 6); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_fill_weighted_profile.cpp b/src/boost/libs/histogram/examples/guide_fill_weighted_profile.cpp new file mode 100644 index 000000000..767bfad86 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_fill_weighted_profile.cpp @@ -0,0 +1,51 @@ +// Copyright 2019 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) + +//[ guide_fill_weighted_profile + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + using namespace boost::histogram::literals; // _c suffix creates compile-time numbers + + // make 2D weighted profile + auto h = make_weighted_profile(axis::integer<>(0, 2), axis::integer<>(0, 2)); + + // The mean is computed from the values marked with the sample() helper function. + // Weights can be passed as well. The `sample` and `weight` arguments can appear in any + // order, but they must be the first or last arguments. + h(0, 0, sample(1)); // sample goes to cell (0, 0); weight is 1 + h(0, 0, sample(2), weight(3)); // sample goes to cell (0, 0); weight is 3 + h(1, 0, sample(3)); // sample goes to cell (1, 0); weight is 1 + h(1, 0, sample(4)); // sample goes to cell (1, 0); weight is 1 + h(0, 1, sample(5)); // sample goes to cell (1, 0); weight is 1 + h(0, 1, sample(6)); // sample goes to cell (1, 0); weight is 1 + h(1, 1, weight(4), sample(7)); // sample goes to cell (1, 1); weight is 4 + h(weight(5), sample(8), 1, 1); // sample goes to cell (1, 1); weight is 5 + + std::ostringstream os; + for (auto&& x : indexed(h)) { + const auto i = x.index(0_c); + const auto j = x.index(1_c); + const auto m = x->value(); // weighted mean + const auto v = x->variance(); // estimated variance of weighted mean + os << boost::format("index %i,%i mean %.1f variance %.1f\n") % i % j % m % v; + } + + std::cout << os.str() << std::flush; + + assert(os.str() == "index 0,0 mean 1.8 variance 0.5\n" + "index 1,0 mean 3.5 variance 0.5\n" + "index 0,1 mean 5.5 variance 0.5\n" + "index 1,1 mean 7.6 variance 0.5\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_in_api.cpp b/src/boost/libs/histogram/examples/guide_histogram_in_api.cpp new file mode 100644 index 000000000..1011e6885 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_in_api.cpp @@ -0,0 +1,57 @@ +// Copyright 2020 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) + +//[ guide_histogram_in_api + +#include <boost/histogram.hpp> +#include <cassert> + +// function accepts any histogram and returns a copy +template <class Axes, class Storage> +boost::histogram::histogram<Axes, Storage> any_histogram( + boost::histogram::histogram<Axes, Storage>& h) { + return h; +} + +// function only accepts histograms with fixed axis types and returns a copy +template <class Storage, class... Axes> +boost::histogram::histogram<std::tuple<Axes...>, Storage> only_static_histogram( + boost::histogram::histogram<std::tuple<Axes...>, Storage>& h) { + return h; +} + +// function only accepts histograms with dynamic axis types and returns a copy +template <class Storage, class... Axes> +boost::histogram::histogram<std::vector<boost::histogram::axis::variant<Axes...>>, + Storage> +only_dynamic_histogram( + boost::histogram::histogram<std::vector<boost::histogram::axis::variant<Axes...>>, + Storage>& h) { + return h; +} + +int main() { + using namespace boost::histogram; + + auto histogram_with_static_axes = make_histogram(axis::regular<>(10, 0, 1)); + + using axis_variant = axis::variant<axis::regular<>, axis::integer<>>; + std::vector<axis_variant> axes; + axes.emplace_back(axis::regular<>(5, 0, 1)); + axes.emplace_back(axis::integer<>(0, 1)); + auto histogram_with_dynamic_axes = make_histogram(axes); + + assert(any_histogram(histogram_with_static_axes) == histogram_with_static_axes); + assert(any_histogram(histogram_with_dynamic_axes) == histogram_with_dynamic_axes); + assert(only_static_histogram(histogram_with_static_axes) == histogram_with_static_axes); + assert(only_dynamic_histogram(histogram_with_dynamic_axes) == + histogram_with_dynamic_axes); + + // does not compile: only_static_histogram(histogram_with_dynamic_axes) + // does not compile: only_dynamic_histogram(histogram_with_static_axes) +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_operators.cpp b/src/boost/libs/histogram/examples/guide_histogram_operators.cpp new file mode 100644 index 000000000..e4059edc5 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_operators.cpp @@ -0,0 +1,62 @@ +// 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) + +//[ guide_histogram_operators + +#include <boost/histogram.hpp> +#include <cassert> +#include <vector> + +int main() { + using namespace boost::histogram; + + // make two histograms + auto h1 = make_histogram(axis::regular<>(2, -1.0, 1.0)); + auto h2 = make_histogram(axis::regular<>(2, -1.0, 1.0)); + + h1(-0.5); // counts are: 1 0 + h2(0.5); // counts are: 0 1 + + // add them + auto h3 = h1; + h3 += h2; // counts are: 1 1 + + // adding multiple histograms at once is likely to be optimized by the compiler so that + // superfluous temporaries avoided, but no guarantees are given; use this equivalent + // code when you want to make sure: h4 = h1; h4 += h2; h4 += h3; + auto h4 = h1 + h2 + h3; // counts are: 2 2 + + assert(h4.at(0) == 2 && h4.at(1) == 2); + + // multiply by number, h4 *= 2 also works + auto h5 = h4 * 2; // counts are: 4 4 + + // divide by number; s4 /= 4 also works + auto h6 = h5 / 4; // counts are: 1 1 + + assert(h6.at(0) == 1 && h6.at(1) == 1); + assert(h6 != h5 && h5 == 4 * h6); + + // note the special effect of multiplication on weight_storage + auto h = make_histogram_with(weight_storage(), axis::regular<>(2, -1.0, 1.0)); + h(-0.5); + + // counts are: 1 0 + assert(h.at(0).value() == 1 && h.at(1).value() == 0); + + auto h_sum = h + h; + auto h_mul = 2 * h; + + // values are the same as expected... + assert(h_sum.at(0).value() == h_mul.at(0).value()); + // ... but variances differ + assert(h_sum.at(0).variance() == 2 && h_mul.at(0).variance() == 4); + + // equality operator checks variances, so histograms are not equal + assert(h_sum != h_mul); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_projection.cpp b/src/boost/libs/histogram/examples/guide_histogram_projection.cpp new file mode 100644 index 000000000..54f452125 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_projection.cpp @@ -0,0 +1,58 @@ +// 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) + +//[ guide_histogram_projection + +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <sstream> + +int main() { + using namespace boost::histogram; + using namespace literals; // enables _c suffix + + // make a 2d histogram + auto h = make_histogram(axis::regular<>(3, -1.0, 1.0), axis::integer<>(0, 2)); + + h(-0.9, 0); + h(0.9, 1); + h(0.1, 0); + + auto hr0 = algorithm::project(h, 0_c); // keep only first axis + auto hr1 = algorithm::project(h, 1_c); // keep only second axis + + // reduce does not remove counts; returned histograms are summed over + // the removed axes, so h, hr0, and hr1 have same number of total counts; + // we compute the sum of counts with the sum algorithm + assert(algorithm::sum(h) == 3 && algorithm::sum(hr0) == 3 && algorithm::sum(hr1) == 3); + + std::ostringstream os1; + for (auto&& x : indexed(h)) + os1 << "(" << x.index(0) << ", " << x.index(1) << "): " << *x << "\n"; + std::cout << os1.str() << std::flush; + assert(os1.str() == "(0, 0): 1\n" + "(1, 0): 1\n" + "(2, 0): 0\n" + "(0, 1): 0\n" + "(1, 1): 0\n" + "(2, 1): 1\n"); + + std::ostringstream os2; + for (auto&& x : indexed(hr0)) os2 << "(" << x.index(0) << ", -): " << *x << "\n"; + std::cout << os2.str() << std::flush; + assert(os2.str() == "(0, -): 1\n" + "(1, -): 1\n" + "(2, -): 1\n"); + + std::ostringstream os3; + for (auto&& x : indexed(hr1)) os3 << "(- ," << x.index(0) << "): " << *x << "\n"; + std::cout << os3.str() << std::flush; + assert(os3.str() == "(- ,0): 2\n" + "(- ,1): 1\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_reduction.cpp b/src/boost/libs/histogram/examples/guide_histogram_reduction.cpp new file mode 100644 index 000000000..677625305 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_reduction.cpp @@ -0,0 +1,48 @@ +// 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) + +//[ guide_histogram_reduction + +#include <boost/histogram.hpp> +#include <cassert> + +int main() { + using namespace boost::histogram; + // import reduce commands into local namespace to save typing + using algorithm::rebin; + using algorithm::shrink; + using algorithm::slice; + + // make a 2d histogram + auto h = make_histogram(axis::regular<>(4, 0.0, 4.0), axis::regular<>(4, -2.0, 2.0)); + + h(0, -0.9); + h(1, 0.9); + h(2, 0.1); + h(3, 0.1); + + // reduce takes positional commands which are applied to the axes in order + // - shrink is applied to the first axis; the new axis range is 0.0 to 3.0 + // - rebin is applied to the second axis; pairs of adjacent bins are merged + auto h2 = algorithm::reduce(h, shrink(0.0, 3.0), rebin(2)); + + assert(h2.axis(0) == axis::regular<>(3, 0.0, 3.0)); + assert(h2.axis(1) == axis::regular<>(2, -2.0, 2.0)); + + // reduce does not change the total count if the histogram has underflow/overflow bins + assert(algorithm::sum(h) == 4 && algorithm::sum(h2) == 4); + + // One can also explicitly specify the index of the axis in the histogram on which the + // command should act, by using this index as the the first parameter. The position of + // the command in the argument list of reduce is then ignored. We use this to slice only + // the second axis (axis has index 1 in the histogram) from bin index 2 to 4. + auto h3 = algorithm::reduce(h, slice(1, 2, 4)); + + assert(h3.axis(0) == h.axis(0)); // unchanged + assert(h3.axis(1) == axis::regular<>(2, 0.0, 2.0)); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_serialization.cpp b/src/boost/libs/histogram/examples/guide_histogram_serialization.cpp new file mode 100644 index 000000000..c00ef3cd6 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_serialization.cpp @@ -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) + +//[ guide_histogram_serialization + +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/histogram.hpp> +#include <boost/histogram/serialization.hpp> // includes serialization code +#include <cassert> +#include <sstream> + +int main() { + using namespace boost::histogram; + + auto a = make_histogram(axis::regular<>(3, -1.0, 1.0, "axis 0"), + axis::integer<>(0, 2, "axis 1")); + a(0.5, 1); + + std::string buf; // to hold persistent representation + + // store histogram + { + std::ostringstream os; + boost::archive::text_oarchive oa(os); + oa << a; + buf = os.str(); + } + + auto b = decltype(a)(); // create a default-constructed second histogram + + assert(b != a); // b is empty, a is not + + // load histogram + { + std::istringstream is(buf); + boost::archive::text_iarchive ia(is); + ia >> b; + } + + assert(b == a); // now b is equal to a +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_histogram_streaming.cpp b/src/boost/libs/histogram/examples/guide_histogram_streaming.cpp new file mode 100644 index 000000000..52bf2b9d4 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_histogram_streaming.cpp @@ -0,0 +1,58 @@ +// 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) + +//[ guide_histogram_streaming + +#include <boost/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <cassert> +#include <iostream> +#include <sstream> +#include <string> + +int main() { + using namespace boost::histogram; + + std::ostringstream os; + + auto h1 = make_histogram(axis::regular<>(5, -1.0, 1.0, "axis 1")); + h1.at(0) = 2; + h1.at(1) = 4; + h1.at(2) = 3; + h1.at(4) = 1; + + // 1D histograms are rendered as an ASCII drawing + os << h1; + + auto h2 = make_histogram(axis::regular<>(2, -1.0, 1.0, "axis 1"), + axis::category<std::string>({"red", "blue"}, "axis 2")); + + // higher dimensional histograms just have their cell counts listed + os << h2; + + std::cout << os.str() << std::endl; + + assert( + os.str() == + "histogram(regular(5, -1, 1, metadata=\"axis 1\", options=underflow | overflow))\n" + " +-------------------------------------------------------------+\n" + "[-inf, -1) 0 | |\n" + "[ -1, -0.6) 2 |============================== |\n" + "[-0.6, -0.2) 4 |============================================================ |\n" + "[-0.2, 0.2) 3 |============================================= |\n" + "[ 0.2, 0.6) 0 | |\n" + "[ 0.6, 1) 1 |=============== |\n" + "[ 1, inf) 0 | |\n" + " +-------------------------------------------------------------+\n" + "histogram(\n" + " regular(2, -1, 1, metadata=\"axis 1\", options=underflow | overflow)\n" + " category(\"red\", \"blue\", metadata=\"axis 2\", options=overflow)\n" + " (-1 0): 0 ( 0 0): 0 ( 1 0): 0 ( 2 0): 0 (-1 1): 0 ( 0 1): 0\n" + " ( 1 1): 0 ( 2 1): 0 (-1 2): 0 ( 0 2): 0 ( 1 2): 0 ( 2 2): 0\n" + ")"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_indexed_access.cpp b/src/boost/libs/histogram/examples/guide_indexed_access.cpp new file mode 100644 index 000000000..5850c1d3c --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_indexed_access.cpp @@ -0,0 +1,78 @@ +// 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) + +//[ guide_indexed_access + +#include <boost/format.hpp> +#include <boost/histogram.hpp> +#include <cassert> +#include <iostream> +#include <numeric> // for std::accumulate +#include <sstream> + +using namespace boost::histogram; + +int main() { + // make histogram with 2 x 2 = 4 bins (not counting under-/overflow bins) + auto h = make_histogram(axis::regular<>(2, -1.0, 1.0), axis::regular<>(2, 2.0, 4.0)); + + h(weight(1), -0.5, 2.5); // bin index 0, 0 + h(weight(2), -0.5, 3.5); // bin index 0, 1 + h(weight(3), 0.5, 2.5); // bin index 1, 0 + h(weight(4), 0.5, 3.5); // bin index 1, 1 + + // use the `indexed` range adaptor to iterate over all bins; + // it is not only more convenient but also faster than a hand-crafted loop! + std::ostringstream os; + for (auto&& x : indexed(h)) { + // x is a special accessor object + const auto i = x.index(0); // current index along first axis + const auto j = x.index(1); // current index along second axis + const auto b0 = x.bin(0); // current bin interval along first axis + const auto b1 = x.bin(1); // current bin interval along second axis + const auto v = *x; // "dereference" to get the bin value + os << boost::format("%i %i [%2i, %i) [%2i, %i): %i\n") % i % j % b0.lower() % + b0.upper() % b1.lower() % b1.upper() % v; + } + + std::cout << os.str() << std::flush; + + assert(os.str() == "0 0 [-1, 0) [ 2, 3): 1\n" + "1 0 [ 0, 1) [ 2, 3): 3\n" + "0 1 [-1, 0) [ 3, 4): 2\n" + "1 1 [ 0, 1) [ 3, 4): 4\n"); + + // `indexed` skips underflow and overflow bins by default, but can be called + // with the second argument `coverage::all` to walk over all bins + std::ostringstream os2; + for (auto&& x : indexed(h, coverage::all)) { + os2 << boost::format("%2i %2i: %i\n") % x.index(0) % x.index(1) % *x; + } + + std::cout << os2.str() << std::flush; + + assert(os2.str() == "-1 -1: 0\n" + " 0 -1: 0\n" + " 1 -1: 0\n" + " 2 -1: 0\n" + + "-1 0: 0\n" + " 0 0: 1\n" + " 1 0: 3\n" + " 2 0: 0\n" + + "-1 1: 0\n" + " 0 1: 2\n" + " 1 1: 4\n" + " 2 1: 0\n" + + "-1 2: 0\n" + " 0 2: 0\n" + " 1 2: 0\n" + " 2 2: 0\n"); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_make_dynamic_histogram.cpp b/src/boost/libs/histogram/examples/guide_make_dynamic_histogram.cpp new file mode 100644 index 000000000..80ab025ee --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_make_dynamic_histogram.cpp @@ -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) + +//[ guide_make_dynamic_histogram + +#include <boost/histogram.hpp> +#include <cassert> +#include <sstream> +#include <vector> + +const char* config = "4 1.0 2.0\n" + "5 3.0 4.0\n"; + +int main() { + using namespace boost::histogram; + + // read axis config from a config file (mocked here with std::istringstream) + // and create vector of regular axes, the number of axis is not known at compile-time + std::istringstream is(config); + auto v1 = std::vector<axis::regular<>>(); + while (is.good()) { + unsigned bins; + double start, stop; + is >> bins >> start >> stop; + v1.emplace_back(bins, start, stop); + } + + // create histogram from iterator range + // (copying or moving the vector also works, move is shown below) + auto h1 = make_histogram(v1.begin(), v1.end()); + assert(h1.rank() == v1.size()); + + // with a vector of axis::variant (polymorphic axis type that can hold any one of the + // template arguments at a time) the types and number of axis can vary at run-time + auto v2 = std::vector<axis::variant<axis::regular<>, axis::integer<>>>(); + v2.emplace_back(axis::regular<>(100, -1.0, 1.0)); + v2.emplace_back(axis::integer<>(1, 7)); + + // create dynamic histogram by moving the vector + auto h2 = make_histogram(std::move(v2)); + assert(h2.rank() == 2); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_make_static_histogram.cpp b/src/boost/libs/histogram/examples/guide_make_static_histogram.cpp new file mode 100644 index 000000000..55eb4b689 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_make_static_histogram.cpp @@ -0,0 +1,23 @@ +// 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) + +//[ guide_make_static_histogram + +#include <boost/histogram.hpp> +#include <cassert> + +int main() { + using namespace boost::histogram; + + // create a 1d-histogram in default configuration which + // covers the real line from -1 to 1 in 100 bins + auto h = make_histogram(axis::regular<>(100, -1.0, 1.0)); + + // rank is the number of axes + assert(h.rank() == 1); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_parallel_filling.cpp b/src/boost/libs/histogram/examples/guide_parallel_filling.cpp new file mode 100644 index 000000000..f4a23c9c7 --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_parallel_filling.cpp @@ -0,0 +1,50 @@ +// Copyright 2018-2019 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) + +//[ guide_parallel_filling + +#include <boost/histogram.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <cassert> +#include <functional> +#include <thread> +#include <vector> + +// dummy fill function, to be executed in parallel by several threads +template <typename Histogram> +void fill(Histogram& h) { + for (unsigned i = 0; i < 1000; ++i) { h(i % 10); } +} + +int main() { + using namespace boost::histogram; + + /* + Create histogram with container of thread-safe counters for parallel filling in + several threads. Only filling is thread-safe, other guarantees are not given. + */ + auto h = make_histogram_with(dense_storage<accumulators::thread_safe<unsigned>>(), + axis::integer<>(0, 10)); + + /* + Run the fill function in parallel from different threads. This is safe when a + thread-safe accumulator and a storage with thread-safe cell access are used. + */ + auto fill_h = [&h]() { fill(h); }; + std::thread t1(fill_h); + std::thread t2(fill_h); + std::thread t3(fill_h); + std::thread t4(fill_h); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + + // Without a thread-safe accumulator, this number may be smaller. + assert(algorithm::sum(h) == 4000); +} + +//] diff --git a/src/boost/libs/histogram/examples/guide_stdlib_algorithms.cpp b/src/boost/libs/histogram/examples/guide_stdlib_algorithms.cpp new file mode 100644 index 000000000..52581787d --- /dev/null +++ b/src/boost/libs/histogram/examples/guide_stdlib_algorithms.cpp @@ -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) + +//[ guide_stdlib_algorithms + +#include <boost/histogram.hpp> +#include <cassert> + +#include <algorithm> // fill, any_of, min_element, max_element +#include <cmath> // sqrt +#include <numeric> // partial_sum, inner_product + +int main() { + using namespace boost::histogram; + + // make histogram that represents a probability density function (PDF) + auto h1 = make_histogram(axis::regular<>(4, 1.0, 3.0)); + + // make indexed range to skip underflow and overflow cells + auto ind = indexed(h1); + + // use std::fill to set all counters to 0.25 (except under- and overflow counters) + std::fill(ind.begin(), ind.end(), 0.25); + + // compute the cumulative density function (CDF), overriding cell values + std::partial_sum(ind.begin(), ind.end(), ind.begin()); + + assert(h1.at(-1) == 0.00); + assert(h1.at(0) == 0.25); + assert(h1.at(1) == 0.50); + assert(h1.at(2) == 0.75); + assert(h1.at(3) == 1.00); + assert(h1.at(4) == 0.00); + + // use any_of to check if any cell values are smaller than 0.1, + auto b = std::any_of(ind.begin(), ind.end(), [](const auto& x) { return x < 0.1; }); + assert(b == false); // under- and overflow cells are zero, but skipped + + // find minimum element + auto min_it = std::min_element(ind.begin(), ind.end()); + assert(*min_it == 0.25); // under- and overflow cells are skipped + + // find maximum element + auto max_it = std::max_element(ind.begin(), ind.end()); + assert(*max_it == 1.0); + + // make second PDF + auto h2 = make_histogram(axis::regular<>(4, 1.0, 4.0)); + h2.at(0) = 0.1; + h2.at(1) = 0.3; + h2.at(2) = 0.2; + h2.at(3) = 0.4; + + // computing cosine similiarity: cos(theta) = A dot B / sqrt((A dot A) * (B dot B)) + auto ind2 = indexed(h2); + const auto aa = std::inner_product(ind.begin(), ind.end(), ind.begin(), 0.0); + const auto bb = std::inner_product(ind2.begin(), ind2.end(), ind2.begin(), 0.0); + const auto ab = std::inner_product(ind.begin(), ind.end(), ind2.begin(), 0.0); + const auto cos_sim = ab / std::sqrt(aa * bb); + + assert(std::abs(cos_sim - 0.967) < 1e-2); +} + +//] diff --git a/src/boost/libs/histogram/index.html b/src/boost/libs/histogram/index.html new file mode 100644 index 000000000..baf179061 --- /dev/null +++ b/src/boost/libs/histogram/index.html @@ -0,0 +1,21 @@ +<html> + <head> + <title>Boost.Histogram</title> + <meta http-equiv="refresh" content="0; URL=./doc/html/index.html"> + </head> + <body> + Automatic redirection failed, please go to + <a href="./doc/html/index.html">./doc/html/index.html</a> + <hr> + <tt> + Boost.Histogram<br> + <br> + Copyright (C) 2015-2019 Hans Dembinski<br> + <br> + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + <a href=http://www.boost.org/LICENSE_1_0.txt>http://www.boost.org/LICENSE_1_0.txt</a>) <br> + <br> + </tt> + </body> +</html> diff --git a/src/boost/libs/histogram/meta/libraries.json b/src/boost/libs/histogram/meta/libraries.json new file mode 100644 index 000000000..349a08456 --- /dev/null +++ b/src/boost/libs/histogram/meta/libraries.json @@ -0,0 +1,16 @@ +{ + "key": "histogram", + "name": "histogram", + "authors": [ + "Hans Dembinski" + ], + "description": "Fast multi-dimensional histogram with convenient interface for C++14", + "category": [ + "Algorithms", + "Data", + "Math" + ], + "maintainers": [ + "Hans Dembinski <hans.dembinski -at- gmail.com>" + ] +} diff --git a/src/boost/libs/histogram/test/CMakeLists.txt b/src/boost/libs/histogram/test/CMakeLists.txt new file mode 100644 index 000000000..24a8cab5b --- /dev/null +++ b/src/boost/libs/histogram/test/CMakeLists.txt @@ -0,0 +1,121 @@ +# Copyright Hans Dembinski 2019 +# 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 + +find_package(PythonInterp) +if (PYTHONINTERP_FOUND) + # checks that b2 and cmake are in sync + add_test(NAME runpy-${PROJECT_NAME}_check_build_system COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_build_system.py) + + # checks that all headers are included in odr test + add_test(NAME runpy-${PROJECT_NAME}_check_odr_test COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_odr_test.py) +endif() + +include(BoostTest OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) +if(NOT HAVE_BOOST_TEST) + return() +endif() + +set(BOOST_TEST_LINK_LIBRARIES Boost::histogram) + +# keep in sync with Jamfile, this should be automatized... +boost_test(TYPE compile-fail SOURCES axis_category_fail0.cpp) +boost_test(TYPE compile-fail SOURCES axis_category_fail1.cpp) +boost_test(TYPE compile-fail SOURCES axis_category_fail2.cpp) +boost_test(TYPE compile-fail SOURCES axis_integer_fail0.cpp) +boost_test(TYPE compile-fail SOURCES axis_integer_fail1.cpp) +boost_test(TYPE compile-fail SOURCES axis_integer_fail2.cpp) +boost_test(TYPE compile-fail SOURCES axis_integer_fail3.cpp) +boost_test(TYPE compile-fail SOURCES axis_integer_fail4.cpp) +boost_test(TYPE compile-fail SOURCES axis_regular_fail0.cpp) +boost_test(TYPE compile-fail SOURCES axis_regular_fail1.cpp) +boost_test(TYPE compile-fail SOURCES axis_variable_fail0.cpp) +boost_test(TYPE compile-fail SOURCES axis_variable_fail1.cpp) +boost_test(TYPE compile-fail SOURCES make_histogram_fail0.cpp) +boost_test(TYPE compile-fail SOURCES make_histogram_fail1.cpp) +boost_test(TYPE compile-fail SOURCES histogram_fail0.cpp) +boost_test(TYPE compile-fail SOURCES histogram_fail1.cpp) +boost_test(TYPE compile-fail SOURCES histogram_fail2.cpp) +boost_test(TYPE compile-fail SOURCES histogram_fail3.cpp) +boost_test(TYPE compile-fail SOURCES histogram_fail4.cpp) + +set(BOOST_TEST_LINK_LIBRARIES Boost::histogram Boost::core) + +boost_test(TYPE run SOURCES accumulators_count_test.cpp) +boost_test(TYPE run SOURCES accumulators_mean_test.cpp) +boost_test(TYPE run SOURCES accumulators_sum_test.cpp) +boost_test(TYPE run SOURCES accumulators_thread_safe_test.cpp) +boost_test(TYPE run SOURCES accumulators_weighted_mean_test.cpp) +boost_test(TYPE run SOURCES accumulators_weighted_sum_test.cpp) +boost_test(TYPE run SOURCES algorithm_project_test.cpp) +boost_test(TYPE run SOURCES algorithm_reduce_test.cpp) +boost_test(TYPE run SOURCES algorithm_sum_test.cpp) +boost_test(TYPE run SOURCES algorithm_empty_test.cpp) +boost_test(TYPE run SOURCES axis_category_test.cpp) +boost_test(TYPE run SOURCES axis_integer_test.cpp) +boost_test(TYPE run SOURCES axis_option_test.cpp) +boost_test(TYPE run SOURCES axis_regular_test.cpp) +boost_test(TYPE run SOURCES axis_size.cpp) +boost_test(TYPE run SOURCES axis_traits_test.cpp) +boost_test(TYPE run SOURCES axis_variable_test.cpp) +boost_test(TYPE run SOURCES axis_variant_test.cpp) +boost_test(TYPE run SOURCES detail_accumulator_traits_test.cpp) +boost_test(TYPE run SOURCES detail_argument_traits_test.cpp) +boost_test(TYPE run SOURCES detail_args_type_test.cpp) +boost_test(TYPE run SOURCES detail_axes_test.cpp) +boost_test(TYPE run SOURCES detail_convert_integer_test.cpp) +boost_test(TYPE run SOURCES detail_detect_test.cpp) +boost_test(TYPE run SOURCES detail_limits_test.cpp) +boost_test(TYPE run SOURCES detail_make_default_test.cpp) +boost_test(TYPE run SOURCES detail_misc_test.cpp) +boost_test(TYPE run SOURCES detail_large_int_test.cpp) +boost_test(TYPE run SOURCES detail_iterator_adaptor_test.cpp) +boost_test(TYPE run SOURCES detail_operators_test.cpp) +boost_test(TYPE run SOURCES detail_relaxed_equal_test.cpp) +boost_test(TYPE run SOURCES detail_replace_type_test.cpp) +boost_test(TYPE run SOURCES detail_safe_comparison_test.cpp) +boost_test(TYPE run SOURCES detail_static_if_test.cpp) +boost_test(TYPE run SOURCES detail_tuple_slice_test.cpp) +boost_test(TYPE run SOURCES histogram_custom_axis_test.cpp) +boost_test(TYPE run SOURCES histogram_dynamic_test.cpp) +boost_test(TYPE run SOURCES histogram_fill_test.cpp + COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj>) +boost_test(TYPE run SOURCES histogram_growing_test.cpp) +boost_test(TYPE run SOURCES histogram_mixed_test.cpp) +boost_test(TYPE run SOURCES histogram_operators_test.cpp) +boost_test(TYPE run SOURCES histogram_ostream_test.cpp) +boost_test(TYPE run SOURCES histogram_test.cpp) +boost_test(TYPE run SOURCES indexed_test.cpp) +boost_test(TYPE run SOURCES storage_adaptor_test.cpp) +boost_test(TYPE run SOURCES unlimited_storage_test.cpp) +boost_test(TYPE run SOURCES utility_test.cpp) + +find_package(Threads) +if (Threads_FOUND) + + boost_test(TYPE run SOURCES histogram_threaded_test.cpp + LINK_LIBRARIES Threads::Threads) + boost_test(TYPE run SOURCES storage_adaptor_threaded_test.cpp + LINK_LIBRARIES Threads::Threads) + +endif() + +## No cmake support yet +# boost_test(TYPE link SOURCES odr_main_test.cpp odr_test.cpp +# LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES boost_accumulators_support_test.cpp +# LINK_LIBRARIES Boost::accumulators) +# boost_test(TYPE run SOURCES boost_range_support_test.cpp +# LINK_LIBRARIES Boost::range) +# boost_test(TYPE run SOURCES boost_units_support_test.cpp +# LINK_LIBRARIES Boost::units) +# boost_test(TYPE run SOURCES detail_array_wrapper_serialization_test.cpp LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES unlimited_storage_serialization_test.cpp LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES storage_adaptor_serialization_test.cpp LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES histogram_serialization_test.cpp LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES axis_variant_serialization_test.cpp +# LINK_LIBRARIES Boost::serialization) +# boost_test(TYPE run SOURCES accumulators_serialization_test.cpp +# LINK_LIBRARIES Boost::serialization) + +boost_test(TYPE run SOURCES deduction_guides_test.cpp COMPILE_FEATURES cxx_std_17) diff --git a/src/boost/libs/histogram/test/Jamfile b/src/boost/libs/histogram/test/Jamfile new file mode 100644 index 000000000..6f5ca9bdd --- /dev/null +++ b/src/boost/libs/histogram/test/Jamfile @@ -0,0 +1,167 @@ +# Copyright 2016-2017 Klemens David Morgenstern +# Copyright 2018 Mateusz Loskot <mateusz@loskot.net> +# Copyright 2018-2019 Hans Dembinski +# +# Use, modification and distribution is subject to 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) + +import python ; +import os ; +import regex ; +import testing ; +import ../../config/checks/config : requires ; + +if ! [ python.configured ] +{ + using python ; +} + +path-constant THIS_PATH : . ; + +project + : requirements + [ requires + cxx14_constexpr cxx14_decltype_auto cxx14_generic_lambdas cxx14_return_type_deduction cxx11_user_defined_literals + # list could go on... + ] + ; + +# Check consistency of build systems +testing.make-test run-pyd : check_build_system.py : + <dependency>Jamfile <dependency>CMakeLists.txt ; + +# Verify One Definition Rule by linking two object files which include everything +testing.make-test run-pyd : check_odr_test.py : <dependency>odr_test.cpp ; +alias odr : + [ link odr_main_test.cpp odr_test.cpp ] + ; + +alias cxx14 : + [ run accumulators_count_test.cpp ] + [ run accumulators_mean_test.cpp ] + [ run accumulators_sum_test.cpp : : : + # make sure sum accumulator works even with -ffast-math and optimizations + <toolset>gcc:<cxxflags>"-O3 -ffast-math" + <toolset>clang:<cxxflags>"-O3 -ffast-math" ] + [ run accumulators_thread_safe_test.cpp ] + [ run accumulators_weighted_mean_test.cpp ] + [ run accumulators_weighted_sum_test.cpp ] + [ run algorithm_project_test.cpp ] + [ run algorithm_reduce_test.cpp ] + [ run algorithm_sum_test.cpp ] + [ run algorithm_empty_test.cpp ] + [ run axis_category_test.cpp ] + [ run axis_integer_test.cpp ] + [ run axis_option_test.cpp ] + [ run axis_regular_test.cpp ] + [ run axis_size.cpp ] + [ run axis_traits_test.cpp ] + [ run axis_variable_test.cpp ] + [ run axis_variant_test.cpp ] + [ run detail_accumulator_traits_test.cpp ] + [ run detail_argument_traits_test.cpp ] + [ run detail_args_type_test.cpp ] + [ run detail_axes_test.cpp ] + [ run detail_convert_integer_test.cpp ] + [ run detail_detect_test.cpp ] + [ run detail_limits_test.cpp ] + [ run detail_make_default_test.cpp ] + [ run detail_misc_test.cpp ] + [ run detail_iterator_adaptor_test.cpp ] + [ run detail_large_int_test.cpp ] + [ run detail_operators_test.cpp ] + [ run detail_relaxed_equal_test.cpp ] + [ run detail_replace_type_test.cpp ] + [ run detail_safe_comparison_test.cpp ] + [ run detail_static_if_test.cpp ] + [ run detail_tuple_slice_test.cpp ] + [ run histogram_custom_axis_test.cpp ] + [ run histogram_dynamic_test.cpp ] + [ run histogram_fill_test.cpp ] + [ run histogram_growing_test.cpp ] + [ run histogram_mixed_test.cpp ] + [ run histogram_operators_test.cpp ] + [ run histogram_ostream_test.cpp ] + [ run histogram_test.cpp ] + [ run indexed_test.cpp ] + [ run storage_adaptor_test.cpp ] + [ run unlimited_storage_test.cpp ] + [ run utility_test.cpp ] + ; + +alias cxx17 : + [ run deduction_guides_test.cpp ] : + [ requires cpp_deduction_guides ] + ; + +# check that useful error messages are produced when library is used incorrectly +alias failure : + [ compile-fail axis_category_fail0.cpp ] + [ compile-fail axis_category_fail1.cpp ] + [ compile-fail axis_category_fail2.cpp ] + [ compile-fail axis_integer_fail0.cpp ] + [ compile-fail axis_integer_fail1.cpp ] + [ compile-fail axis_integer_fail2.cpp ] + [ compile-fail axis_integer_fail3.cpp ] + [ compile-fail axis_integer_fail4.cpp ] + [ compile-fail axis_regular_fail0.cpp ] + [ compile-fail axis_regular_fail1.cpp ] + [ compile-fail axis_variable_fail0.cpp ] + [ compile-fail axis_variable_fail1.cpp ] + [ compile-fail make_histogram_fail0.cpp ] + [ compile-fail make_histogram_fail1.cpp ] + [ compile-fail histogram_fail0.cpp ] + [ compile-fail histogram_fail1.cpp ] + [ compile-fail histogram_fail2.cpp ] + [ compile-fail histogram_fail3.cpp ] + [ compile-fail histogram_fail4.cpp ] + ; + +alias threading : + [ run histogram_threaded_test.cpp ] + [ run storage_adaptor_threaded_test.cpp ] + : + <threading>multi + ; + +# warnings are off for these other boost libraries, which tend to be not warning-free +alias accumulators : [ run boost_accumulators_support_test.cpp ] : <warnings>off ; +alias range : [ run boost_range_support_test.cpp ] : <warnings>off ; +alias units : [ run boost_units_support_test.cpp ] : <warnings>off ; +alias serialization : + [ run accumulators_serialization_test.cpp libserial : $(THIS_PATH) ] + [ run detail_array_wrapper_serialization_test.cpp libserial ] + [ run axis_variant_serialization_test.cpp libserial : $(THIS_PATH) ] + [ run histogram_serialization_test.cpp libserial : $(THIS_PATH) ] + [ run storage_adaptor_serialization_test.cpp libserial : $(THIS_PATH) ] + [ run unlimited_storage_serialization_test.cpp libserial : $(THIS_PATH) ] + ; + +alias libserial : + /boost/serialization//boost_serialization + : + <link>static <warnings>off <rtti>on + ; + +# for builds without optional boost dependencies +alias minimal : cxx14 cxx17 failure threading ; + +# all tests +alias all : minimal odr accumulators range units serialization ; + +# all except "failure", because it is distracting during development +alias develop : cxx14 cxx17 threading odr accumulators range units serialization ; + +explicit minimal ; +explicit all ; +explicit odr ; +explicit cxx14 ; +explicit cxx17 ; +explicit failure ; +explicit threading ; +explicit accumulators ; +explicit range ; +explicit units ; +explicit serialization ; +explicit libserial ; diff --git a/src/boost/libs/histogram/test/accumulators_count_test.cpp b/src/boost/libs/histogram/test/accumulators_count_test.cpp new file mode 100644 index 000000000..abd3fc2fc --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_count_test.cpp @@ -0,0 +1,43 @@ +// 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/accumulators/count.hpp> +#include <boost/histogram/accumulators/ostream.hpp> +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + using c_t = accumulators::count<double>; + + c_t c; + ++c; + BOOST_TEST_EQ(c.value(), 1); + BOOST_TEST_EQ(str(c), "1"s); + BOOST_TEST_EQ(str(c, 2, false), " 1"s); + BOOST_TEST_EQ(str(c, 2, true), "1 "s); + + c += 2; + BOOST_TEST_EQ(str(c), "3"s); + + BOOST_TEST_EQ(c, 3); + BOOST_TEST_NE(c, 2); + + c_t one(1), two(2), one_copy(1); + BOOST_TEST_LT(one, two); + BOOST_TEST_LE(one, two); + BOOST_TEST_LE(one, one_copy); + BOOST_TEST_GT(two, one); + BOOST_TEST_GE(two, one); + BOOST_TEST_GE(one, one_copy); + + BOOST_TEST_EQ(c_t{} += c_t{}, c_t{}); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_mean_test.cpp b/src/boost/libs/histogram/test/accumulators_mean_test.cpp new file mode 100644 index 000000000..d9a3c5d19 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_mean_test.cpp @@ -0,0 +1,69 @@ +// 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/accumulators/mean.hpp> +#include <boost/histogram/accumulators/ostream.hpp> +#include <boost/histogram/weight.hpp> +#include <sstream> +#include "is_close.hpp" +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + using m_t = accumulators::mean<double>; + m_t a; + BOOST_TEST_EQ(a.count(), 0); + BOOST_TEST_EQ(a, m_t{}); + + a(4); + a(7); + a(13); + a(16); + + BOOST_TEST_EQ(a.count(), 4); + BOOST_TEST_EQ(a.value(), 10); + BOOST_TEST_EQ(a.variance(), 30); + + BOOST_TEST_EQ(str(a), "mean(4, 10, 30)"s); + BOOST_TEST_EQ(str(a, 20, false), " mean(4, 10, 30)"s); + BOOST_TEST_EQ(str(a, 20, true), "mean(4, 10, 30) "s); + + m_t b; + b(1e8 + 4); + b(1e8 + 7); + b(1e8 + 13); + b(1e8 + 16); + + BOOST_TEST_EQ(b.count(), 4); + BOOST_TEST_EQ(b.value(), 1e8 + 10); + BOOST_TEST_EQ(b.variance(), 30); + + auto c = a; + c += a; // same as feeding all samples twice + + BOOST_TEST_EQ(c.count(), 8); + BOOST_TEST_EQ(c.value(), 10); + BOOST_TEST_IS_CLOSE(c.variance(), 25.714, 1e-3); + + // also same as feeding all samples twice + m_t d; + d(weight(2), 4); + d(weight(2), 7); + d(weight(2), 13); + d(weight(2), 16); + + BOOST_TEST_EQ(d, c); + + BOOST_TEST_EQ(m_t() += m_t(), m_t()); + BOOST_TEST_EQ(m_t(1, 2, 3) += m_t(), m_t(1, 2, 3)); + BOOST_TEST_EQ(m_t() += m_t(1, 2, 3), m_t(1, 2, 3)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test.cpp b/src/boost/libs/histogram/test/accumulators_serialization_test.cpp new file mode 100644 index 000000000..9a748a253 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test.cpp @@ -0,0 +1,91 @@ +// Copyright 2019 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/assert.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators.hpp> +#include <boost/histogram/serialization.hpp> +#include <boost/histogram/weight.hpp> +#include "throw_exception.hpp" +#include "utility_serialization.hpp" + +using namespace boost::histogram; + +int main(int argc, char** argv) { + BOOST_ASSERT(argc == 2); + + // mean v0 + { + const auto filename = join(argv[1], "accumulators_serialization_test_mean_v0.xml"); + accumulators::mean<> a; + load_xml(filename, a); + BOOST_TEST_EQ(a.count(), 3); + BOOST_TEST_EQ(a.value(), 2); + BOOST_TEST_EQ(a.variance(), 0.5); + } + + // mean + { + const auto filename = join(argv[1], "accumulators_serialization_test_mean.xml"); + accumulators::mean<> a; + a(1); + a(weight(0.5), 2); + a(3); + print_xml(filename, a); + + accumulators::mean<> b; + BOOST_TEST_NOT(a == b); + load_xml(filename, b); + BOOST_TEST(a == b); + } + + // sum + { + const auto filename = join(argv[1], "accumulators_serialization_test_sum.xml"); + accumulators::sum<> a; + a += 1e100; + a += 1; + print_xml(filename, a); + + accumulators::sum<> b; + BOOST_TEST_NOT(a == b); + load_xml(filename, b); + BOOST_TEST(a == b); + } + + // weighted_mean + { + const auto filename = + join(argv[1], "accumulators_serialization_test_weighted_mean.xml"); + accumulators::weighted_mean<> a; + a(1); + a(weight(0.5), 2); + a(3); + print_xml(filename, a); + + accumulators::weighted_mean<> b; + BOOST_TEST_NOT(a == b); + load_xml(filename, b); + BOOST_TEST(a == b); + } + + // weighted_sum + { + const auto filename = + join(argv[1], "accumulators_serialization_test_weighted_sum.xml"); + accumulators::weighted_sum<> a; + a += weight(1); + a += weight(10); + print_xml(filename, a); + + accumulators::weighted_sum<> b; + BOOST_TEST_NOT(a == b); + load_xml(filename, b); + BOOST_TEST(a == b); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test_mean.xml b/src/boost/libs/histogram/test/accumulators_serialization_test_mean.xml new file mode 100644 index 000000000..b63a1b0bf --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test_mean.xml @@ -0,0 +1,17 @@ +<!-- + Copyright 2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="1"> + <sum>2.50000000000000000e+00</sum> + <mean>2.00000000000000000e+00</mean> + <sum_of_deltas_squared>2.00000000000000000e+00</sum_of_deltas_squared> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test_mean_v0.xml b/src/boost/libs/histogram/test/accumulators_serialization_test_mean_v0.xml new file mode 100644 index 000000000..9e3772026 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test_mean_v0.xml @@ -0,0 +1,17 @@ +<!-- + Copyright 2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <sum>3</sum> + <mean>2.00000000000000000e+00</mean> + <sum_of_deltas_squared>1.00000000000000000e+00</sum_of_deltas_squared> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test_sum.xml b/src/boost/libs/histogram/test/accumulators_serialization_test_sum.xml new file mode 100644 index 000000000..1be3926c1 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test_sum.xml @@ -0,0 +1,16 @@ +<!-- + Copyright 2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <large>1.00000000000000002e+100</large> + <small>1.00000000000000000e+00</small> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_mean.xml b/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_mean.xml new file mode 100644 index 000000000..7feefc593 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_mean.xml @@ -0,0 +1,18 @@ +<!-- + Copyright 2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <sum_of_weights>2.50000000000000000e+00</sum_of_weights> + <sum_of_weights_squared>2.25000000000000000e+00</sum_of_weights_squared> + <weighted_mean>2.00000000000000000e+00</weighted_mean> + <sum_of_weighted_deltas_squared>2.00000000000000000e+00</sum_of_weighted_deltas_squared> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_sum.xml b/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_sum.xml new file mode 100644 index 000000000..9412c6261 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_serialization_test_weighted_sum.xml @@ -0,0 +1,16 @@ +<!-- + Copyright 2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <sum_of_weights>1.10000000000000000e+01</sum_of_weights> + <sum_of_weights_squared>1.01000000000000000e+02</sum_of_weights_squared> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/accumulators_sum_test.cpp b/src/boost/libs/histogram/test/accumulators_sum_test.cpp new file mode 100644 index 000000000..31463e7d3 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_sum_test.cpp @@ -0,0 +1,70 @@ +// 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/accumulators/ostream.hpp> +#include <boost/histogram/accumulators/sum.hpp> +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + double bad_sum = 0; + bad_sum += 1; + bad_sum += 1e100; + bad_sum += 1; + bad_sum += -1e100; + BOOST_TEST_EQ(bad_sum, 0); // instead of 2 + + using s_t = accumulators::sum<double>; + s_t sum; + ++sum; + BOOST_TEST_EQ(sum, 1); + BOOST_TEST_EQ(sum.value(), 1); + BOOST_TEST_EQ(sum.large(), 1); + BOOST_TEST_EQ(sum.small(), 0); + BOOST_TEST_EQ(str(sum), "sum(1 + 0)"s); + BOOST_TEST_EQ(str(sum, 15, false), " sum(1 + 0)"s); + BOOST_TEST_EQ(str(sum, 15, true), "sum(1 + 0) "s); + + sum += 1e100; + BOOST_TEST_EQ(sum, (s_t{1e100, 1})); + ++sum; + BOOST_TEST_EQ(sum, (s_t{1e100, 2})); + sum += -1e100; + BOOST_TEST_EQ(sum, (s_t{0, 2})); + BOOST_TEST_EQ(sum, 2); // correct answer + BOOST_TEST_EQ(sum.value(), 2); + BOOST_TEST_EQ(sum.large(), 0); + BOOST_TEST_EQ(sum.small(), 2); + + sum = s_t{1e100, 1}; + sum += s_t{1e100, 1}; + BOOST_TEST_EQ(sum, (s_t{2e100, 2})); + sum = s_t{1e100, 1}; + sum += s_t{1, 0}; + BOOST_TEST_EQ(sum, (s_t{1e100, 2})); + sum = s_t{1, 0}; + sum += s_t{1e100, 1}; + BOOST_TEST_EQ(sum, (s_t{1e100, 2})); + sum = s_t{0, 1}; + sum += s_t{1, 0}; + BOOST_TEST_EQ(sum, (s_t{1, 1})); + + accumulators::sum<double> a{3}, b{2}, c{3}; + BOOST_TEST_LT(b, c); + BOOST_TEST_LE(b, c); + BOOST_TEST_LE(a, c); + BOOST_TEST_GT(a, b); + BOOST_TEST_GE(a, b); + BOOST_TEST_GE(a, c); + + BOOST_TEST_EQ(s_t{} += s_t{}, s_t{}); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_thread_safe_test.cpp b/src/boost/libs/histogram/test/accumulators_thread_safe_test.cpp new file mode 100644 index 000000000..a53c1de15 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_thread_safe_test.cpp @@ -0,0 +1,30 @@ +// 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/accumulators/ostream.hpp> +#include <boost/histogram/accumulators/thread_safe.hpp> +#include <sstream> +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + using ts_t = accumulators::thread_safe<int>; + + ts_t i; + ++i; + i += 1000; + + BOOST_TEST_EQ(i, 1001); + BOOST_TEST_EQ(str(i), "1001"s); + + BOOST_TEST_EQ(ts_t{} += ts_t{}, ts_t{}); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_weighted_mean_test.cpp b/src/boost/libs/histogram/test/accumulators_weighted_mean_test.cpp new file mode 100644 index 000000000..e74d336d4 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_weighted_mean_test.cpp @@ -0,0 +1,50 @@ +// 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/accumulators/ostream.hpp> +#include <boost/histogram/accumulators/weighted_mean.hpp> +#include <boost/histogram/weight.hpp> +#include <sstream> +#include "is_close.hpp" +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + using m_t = accumulators::weighted_mean<double>; + m_t a; + BOOST_TEST_EQ(a.sum_of_weights(), 0); + BOOST_TEST_EQ(a, m_t{}); + + a(weight(0.5), 1); + a(weight(1.0), 2); + a(weight(0.5), 3); + + BOOST_TEST_EQ(a.sum_of_weights(), 2); + BOOST_TEST_EQ(a.sum_of_weights_squared(), 1.5); + BOOST_TEST_EQ(a.value(), 2); + BOOST_TEST_IS_CLOSE(a.variance(), 0.8, 1e-3); + + BOOST_TEST_EQ(str(a), "weighted_mean(2, 2, 0.8)"s); + BOOST_TEST_EQ(str(a, 25, false), " weighted_mean(2, 2, 0.8)"s); + BOOST_TEST_EQ(str(a, 25, true), "weighted_mean(2, 2, 0.8) "s); + + auto b = a; + b += a; // same as feeding all samples twice + + BOOST_TEST_EQ(b.sum_of_weights(), 4); + BOOST_TEST_EQ(b.value(), 2); + BOOST_TEST_IS_CLOSE(b.variance(), 0.615, 1e-3); + + BOOST_TEST_EQ(m_t() += m_t(), m_t()); + BOOST_TEST_EQ(m_t(1, 2, 3, 4) += m_t(), m_t(1, 2, 3, 4)); + BOOST_TEST_EQ(m_t() += m_t(1, 2, 3, 4), m_t(1, 2, 3, 4)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/accumulators_weighted_sum_test.cpp b/src/boost/libs/histogram/test/accumulators_weighted_sum_test.cpp new file mode 100644 index 000000000..25739f5a9 --- /dev/null +++ b/src/boost/libs/histogram/test/accumulators_weighted_sum_test.cpp @@ -0,0 +1,82 @@ +// 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/accumulators/ostream.hpp> +#include <boost/histogram/accumulators/sum.hpp> +#include <boost/histogram/accumulators/weighted_sum.hpp> +#include <boost/histogram/weight.hpp> +#include <sstream> +#include "throw_exception.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +int main() { + { + using w_t = accumulators::weighted_sum<double>; + w_t w; + BOOST_TEST_EQ(str(w), "weighted_sum(0, 0)"s); + BOOST_TEST_EQ(str(w, 20, false), " weighted_sum(0, 0)"s); + BOOST_TEST_EQ(str(w, 20, true), "weighted_sum(0, 0) "s); + BOOST_TEST_EQ(w, w_t{}); + + BOOST_TEST_EQ(w, w_t(0)); + BOOST_TEST_NE(w, w_t(1)); + w = w_t(1); + BOOST_TEST_EQ(w.value(), 1); + BOOST_TEST_EQ(w.variance(), 1); + BOOST_TEST_EQ(w, 1); + BOOST_TEST_NE(w, 2); + + w += weight(2); + BOOST_TEST_EQ(w.value(), 3); + BOOST_TEST_EQ(w.variance(), 5); + BOOST_TEST_EQ(w, w_t(3, 5)); + BOOST_TEST_NE(w, w_t(3)); + + w += w_t(1, 2); + BOOST_TEST_EQ(w.value(), 4); + BOOST_TEST_EQ(w.variance(), 7); + + // consistency: a weighted counter increased by weight 1 multiplied + // by 2 must be the same as a weighted counter increased by weight 2 + w_t u(0); + ++u; + u *= 2; + BOOST_TEST_EQ(u, w_t(2, 4)); + + w_t v(0); + v += weight(2); + BOOST_TEST_EQ(u, v); + + // conversion to RealType + w_t y(1, 2); + BOOST_TEST_NE(y, 1); + BOOST_TEST_EQ(static_cast<double>(y), 1); + + BOOST_TEST_EQ(w_t() += w_t(), w_t()); + } + + // sum nested in weighted_sum + { + using s_t = accumulators::weighted_sum<accumulators::sum<double>>; + s_t w; + + ++w; + w += weight(1e100); + ++w; + w += weight(-1e100); + + BOOST_TEST_EQ(w.value(), 2); + BOOST_TEST_EQ(w.variance(), 2e200); + + BOOST_TEST_EQ(s_t() += s_t(), s_t()); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/algorithm_empty_test.cpp b/src/boost/libs/histogram/test/algorithm_empty_test.cpp new file mode 100644 index 000000000..f3787f041 --- /dev/null +++ b/src/boost/libs/histogram/test/algorithm_empty_test.cpp @@ -0,0 +1,65 @@ +// 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 <array> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators/weighted_mean.hpp> +#include <boost/histogram/algorithm/empty.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <unordered_map> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using boost::histogram::algorithm::empty; + +template <typename Tag> +void run_tests() { + auto ax = axis::integer<>(0, 10); + + { + auto h = make(Tag(), ax); + BOOST_TEST(empty(h, coverage::all)); + BOOST_TEST(empty(h, coverage::inner)); + for (int i = -1; i < 11; ++i) { + h.reset(); + h(i); + BOOST_TEST(!empty(h, coverage::all)); + if (i == -1 || i == 10) { + BOOST_TEST(empty(h, coverage::inner)); + } else { + BOOST_TEST(!empty(h, coverage::inner)); + } + } + } + + { + auto h = make_s(Tag(), std::vector<accumulators::weighted_mean<>>(), + axis::integer<>(0, 10), axis::integer<>(0, 10)); + BOOST_TEST(empty(h, coverage::all)); + BOOST_TEST(empty(h, coverage::inner)); + h.reset(); + h(weight(2), -2, -4, sample(3)); + BOOST_TEST(!empty(h, coverage::all)); + BOOST_TEST(empty(h, coverage::inner)); + h.reset(); + h(weight(1), -4, 2, sample(2)); + BOOST_TEST(!empty(h, coverage::all)); + BOOST_TEST(empty(h, coverage::inner)); + h.reset(); + h(weight(3), 3, 5, sample(1)); + BOOST_TEST(!empty(h, coverage::all)); + BOOST_TEST(!empty(h, coverage::inner)); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/algorithm_project_test.cpp b/src/boost/libs/histogram/test/algorithm_project_test.cpp new file mode 100644 index 000000000..79c5e7935 --- /dev/null +++ b/src/boost/libs/histogram/test/algorithm_project_test.cpp @@ -0,0 +1,193 @@ +// 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/algorithm/project.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/ostream.hpp> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::literals; // to get _c suffix +using namespace boost::histogram::algorithm; + +template <typename Tag> +void run_tests() { + { + auto h = make(Tag(), axis::integer<>(0, 2), axis::integer<>(0, 3)); + h(0, 0); + h(0, 1); + h(1, 0); + h(1, 1); + h(1, 2); + h(1, 2); + + /* + matrix layout: + + x -> + y 1 1 + | 1 1 + v 0 2 + */ + + auto hx = project(h, 0_c); + BOOST_TEST_EQ(hx.rank(), 1); + BOOST_TEST_EQ(sum(hx), 6); + BOOST_TEST_EQ(hx.axis(), h.axis(0_c)); + BOOST_TEST_EQ(hx.at(0), 2); + BOOST_TEST_EQ(hx.at(1), 4); + + auto hy = project(h, 1_c); + BOOST_TEST_EQ(hy.rank(), 1); + BOOST_TEST_EQ(sum(hy), 6); + BOOST_TEST_EQ(hy.axis(), h.axis(1_c)); + BOOST_TEST_EQ(hy.at(0), 2); + BOOST_TEST_EQ(hy.at(1), 2); + BOOST_TEST_EQ(hy.at(2), 2); + + auto hyx = project(h, 1_c, 0_c); + BOOST_TEST_EQ(hyx.rank(), 2); + BOOST_TEST_EQ(sum(hyx), 6); + BOOST_TEST_EQ(hyx.axis(0_c), h.axis(1_c)); + BOOST_TEST_EQ(hyx.axis(1_c), h.axis(0_c)); + BOOST_TEST_EQ(hyx.at(0, 0), 1); + BOOST_TEST_EQ(hyx.at(1, 0), 1); + BOOST_TEST_EQ(hyx.at(2, 0), 0); + BOOST_TEST_EQ(hyx.at(0, 1), 1); + BOOST_TEST_EQ(hyx.at(1, 1), 1); + BOOST_TEST_EQ(hyx.at(2, 1), 2); + } + + { + auto h = + make(Tag(), axis::integer<>(0, 2), axis::integer<>(0, 3), axis::integer<>(0, 4)); + h(0, 0, 0); + h(0, 1, 0); + h(0, 1, 1); + h(0, 0, 2); + h(1, 0, 2); + + auto h_0 = project(h, 0_c); + BOOST_TEST_EQ(h_0.rank(), 1); + BOOST_TEST_EQ(sum(h_0), 5); + BOOST_TEST_EQ(h_0.at(0), 4); + BOOST_TEST_EQ(h_0.at(1), 1); + BOOST_TEST_EQ(h_0.axis(), axis::integer<>(0, 2)); + + auto h_1 = project(h, 1_c); + BOOST_TEST_EQ(h_1.rank(), 1); + BOOST_TEST_EQ(sum(h_1), 5); + BOOST_TEST_EQ(h_1.at(0), 3); + BOOST_TEST_EQ(h_1.at(1), 2); + BOOST_TEST_EQ(h_1.axis(), axis::integer<>(0, 3)); + + auto h_2 = project(h, 2_c); + BOOST_TEST_EQ(h_2.rank(), 1); + BOOST_TEST_EQ(sum(h_2), 5); + BOOST_TEST_EQ(h_2.at(0), 2); + BOOST_TEST_EQ(h_2.at(1), 1); + BOOST_TEST_EQ(h_2.at(2), 2); + BOOST_TEST_EQ(h_2.axis(), axis::integer<>(0, 4)); + + auto h_01 = project(h, 0_c, 1_c); + BOOST_TEST_EQ(h_01.rank(), 2); + BOOST_TEST_EQ(sum(h_01), 5); + BOOST_TEST_EQ(h_01.at(0, 0), 2); + BOOST_TEST_EQ(h_01.at(0, 1), 2); + BOOST_TEST_EQ(h_01.at(1, 0), 1); + BOOST_TEST_EQ(h_01.axis(0_c), axis::integer<>(0, 2)); + BOOST_TEST_EQ(h_01.axis(1_c), axis::integer<>(0, 3)); + + auto h_02 = project(h, 0_c, 2_c); + BOOST_TEST_EQ(h_02.rank(), 2); + BOOST_TEST_EQ(sum(h_02), 5); + BOOST_TEST_EQ(h_02.at(0, 0), 2); + BOOST_TEST_EQ(h_02.at(0, 1), 1); + BOOST_TEST_EQ(h_02.at(0, 2), 1); + BOOST_TEST_EQ(h_02.at(1, 2), 1); + BOOST_TEST_EQ(h_02.axis(0_c), axis::integer<>(0, 2)); + BOOST_TEST_EQ(h_02.axis(1_c), axis::integer<>(0, 4)); + + auto h_12 = project(h, 1_c, 2_c); + BOOST_TEST_EQ(h_12.rank(), 2); + BOOST_TEST_EQ(sum(h_12), 5); + BOOST_TEST_EQ(h_12.at(0, 0), 1); + BOOST_TEST_EQ(h_12.at(1, 0), 1); + BOOST_TEST_EQ(h_12.at(1, 1), 1); + BOOST_TEST_EQ(h_12.at(0, 2), 2); + BOOST_TEST_EQ(h_12.axis(0_c), axis::integer<>(0, 3)); + BOOST_TEST_EQ(h_12.axis(1_c), axis::integer<>(0, 4)); + + auto h_210 = project(h, 2_c, 1_c, 0_c); + BOOST_TEST_EQ(h_210.at(0, 0, 0), 1); + BOOST_TEST_EQ(h_210.at(0, 1, 0), 1); + BOOST_TEST_EQ(h_210.at(1, 1, 0), 1); + BOOST_TEST_EQ(h_210.at(2, 0, 0), 1); + BOOST_TEST_EQ(h_210.at(2, 0, 1), 1); + } + + { + auto h = make(dynamic_tag(), axis::integer<>(0, 2), axis::integer<>(0, 3)); + h(0, 0); + h(0, 1); + h(1, 0); + h(1, 1); + h(1, 2); + h(1, 2); + + std::vector<int> x; + + x = {0}; + auto hx = project(h, x); + BOOST_TEST_EQ(hx.rank(), 1); + BOOST_TEST_EQ(sum(hx), 6); + BOOST_TEST_EQ(hx.at(0), 2); + BOOST_TEST_EQ(hx.at(1), 4); + BOOST_TEST(hx.axis() == h.axis(0_c)); + + x = {1}; + auto hy = project(h, x); + BOOST_TEST_EQ(hy.rank(), 1); + BOOST_TEST_EQ(sum(hy), 6); + BOOST_TEST_EQ(hy.at(0), 2); + BOOST_TEST_EQ(hy.at(1), 2); + BOOST_TEST_EQ(hy.at(2), 2); + BOOST_TEST(hy.axis() == h.axis(1_c)); + + x = {1, 0}; + auto hyx = project(h, x); + BOOST_TEST_EQ(hyx.rank(), 2); + BOOST_TEST_EQ(hyx.axis(0_c), h.axis(1_c)); + BOOST_TEST_EQ(hyx.axis(1_c), h.axis(0_c)); + BOOST_TEST_EQ(sum(hyx), 6); + BOOST_TEST_EQ(hyx.at(0, 0), 1); + BOOST_TEST_EQ(hyx.at(1, 0), 1); + BOOST_TEST_EQ(hyx.at(0, 1), 1); + BOOST_TEST_EQ(hyx.at(1, 1), 1); + BOOST_TEST_EQ(hyx.at(2, 1), 2); + + // indices must be unique + x = {0, 0}; + BOOST_TEST_THROWS((void)project(h, x), std::invalid_argument); + + // indices must be valid + x = {2, 1}; + BOOST_TEST_THROWS((void)project(h, x), std::invalid_argument); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/algorithm_reduce_test.cpp b/src/boost/libs/histogram/test/algorithm_reduce_test.cpp new file mode 100644 index 000000000..961c1314a --- /dev/null +++ b/src/boost/libs/histogram/test/algorithm_reduce_test.cpp @@ -0,0 +1,353 @@ +// 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/core/lightweight_test.hpp> +#include <boost/histogram/algorithm/reduce.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis/category.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/axis/variable.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/unsafe_access.hpp> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::algorithm; + +struct unreducible { + axis::index_type index(int) const { return 0; } + axis::index_type size() const { return 1; } + friend std::ostream& operator<<(std::ostream& os, const unreducible&) { + os << "unreducible"; + return os; + } +}; + +template <typename Tag> +void run_tests() { + // limitations: shrink does not work with arguments not convertible to double + + using R = axis::regular<double, axis::transform::id, axis::null_type>; + using ID = axis::integer<double, axis::null_type>; + using V = axis::variable<double, axis::empty_type>; + using CI = axis::category<int, axis::empty_type>; + + // various failures + { + auto h = make(Tag(), R(4, 1, 5), R(3, -1, 2)); + + // not allowed: invalid axis index + BOOST_TEST_THROWS((void)reduce(h, slice(10, 2, 3)), std::invalid_argument); + // two slice requests for same axis not allowed + BOOST_TEST_THROWS((void)reduce(h, slice(1, 0, 2), slice(1, 1, 3)), + std::invalid_argument); + // two rebin requests for same axis not allowed + BOOST_TEST_THROWS((void)reduce(h, rebin(0, 2), rebin(0, 2)), std::invalid_argument); + // rebin and slice_and_rebin with merge > 1 requests for same axis cannot be fused + BOOST_TEST_THROWS((void)reduce(h, slice_and_rebin(0, 1, 3, 2), rebin(0, 2)), + std::invalid_argument); + BOOST_TEST_THROWS((void)reduce(h, shrink(1, 0, 2), crop(1, 0, 2)), + std::invalid_argument); + // not allowed: slice with begin >= end + BOOST_TEST_THROWS((void)reduce(h, slice(0, 1, 1)), std::invalid_argument); + BOOST_TEST_THROWS((void)reduce(h, slice(0, 2, 1)), std::invalid_argument); + // not allowed: shrink with lower == upper + BOOST_TEST_THROWS((void)reduce(h, shrink(0, 0, 0)), std::invalid_argument); + // not allowed: crop with lower == upper + BOOST_TEST_THROWS((void)reduce(h, crop(0, 0, 0)), std::invalid_argument); + // not allowed: shrink axis to zero size + BOOST_TEST_THROWS((void)reduce(h, shrink(0, 10, 11)), std::invalid_argument); + // not allowed: rebin with zero merge + BOOST_TEST_THROWS((void)reduce(h, rebin(0, 0)), std::invalid_argument); + // not allowed: reducing unreducible axis + BOOST_TEST_THROWS((void)reduce(make(Tag(), unreducible{}), slice(0, 1)), + std::invalid_argument); + } + + // shrink and crop behavior when value on edge and not on edge is inclusive: + // - lower edge of shrink: pick bin which contains edge, lower <= x < upper + // - upper edge of shrink: pick bin which contains edge + 1, lower < x <= upper + { + auto h = make(Tag(), ID(0, 3)); + const auto& ax = h.axis(); + BOOST_TEST_EQ(ax.value(0), 0); + BOOST_TEST_EQ(ax.value(3), 3); + BOOST_TEST_EQ(ax.index(-1), -1); + BOOST_TEST_EQ(ax.index(3), 3); + + BOOST_TEST_EQ(reduce(h, shrink(-1, 5)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, shrink(0, 3)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, shrink(1, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, shrink(1.001, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, shrink(1.999, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, shrink(2, 3)).axis(), ID(2, 3)); + BOOST_TEST_EQ(reduce(h, shrink(0, 2.999)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, shrink(0, 2.001)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, shrink(0, 2)).axis(), ID(0, 2)); + BOOST_TEST_EQ(reduce(h, shrink(0, 1.999)).axis(), ID(0, 2)); + + BOOST_TEST_EQ(reduce(h, crop(-1, 5)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, crop(0, 3)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, crop(1, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, crop(1.001, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, crop(1.999, 3)).axis(), ID(1, 3)); + BOOST_TEST_EQ(reduce(h, crop(2, 3)).axis(), ID(2, 3)); + BOOST_TEST_EQ(reduce(h, crop(0, 2.999)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, crop(0, 2.001)).axis(), ID(0, 3)); + BOOST_TEST_EQ(reduce(h, crop(0, 2)).axis(), ID(0, 2)); + BOOST_TEST_EQ(reduce(h, crop(0, 1.999)).axis(), ID(0, 2)); + } + + { + auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, -1, 2)); + + /* + matrix layout: + x + y 1 0 1 0 + 1 1 0 0 + 0 2 1 3 + */ + h.at(0, 0) = 1; + h.at(0, 1) = 1; + h.at(1, 1) = 1; + h.at(1, 2) = 2; + h.at(2, 0) = 1; + h.at(2, 2) = 1; + h.at(3, 2) = 3; + + // should do nothing, index order does not matter + auto hr = reduce(h, shrink(1, -1, 2), rebin(0, 1)); + BOOST_TEST_EQ(hr.rank(), 2); + BOOST_TEST_EQ(sum(hr), 10); + BOOST_TEST_EQ(hr.axis(0), R(4, 1, 5)); + BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2)); + BOOST_TEST_EQ(hr, h); + + // noop slice + hr = reduce(h, slice(1, 0, 4), slice(0, 0, 4)); + BOOST_TEST_EQ(hr, h); + + // shrinking along first axis + hr = reduce(h, shrink(0, 2, 4)); + BOOST_TEST_EQ(hr.rank(), 2); + BOOST_TEST_EQ(sum(hr), 10); + BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4)); + BOOST_TEST_EQ(hr.axis(1), R(3, -1, 2)); + BOOST_TEST_EQ(hr.at(-1, 0), 1); // underflow + BOOST_TEST_EQ(hr.at(0, 0), 0); + BOOST_TEST_EQ(hr.at(1, 0), 1); + BOOST_TEST_EQ(hr.at(2, 0), 0); // overflow + BOOST_TEST_EQ(hr.at(-1, 1), 1); + BOOST_TEST_EQ(hr.at(0, 1), 1); + BOOST_TEST_EQ(hr.at(1, 1), 0); + BOOST_TEST_EQ(hr.at(2, 1), 0); + BOOST_TEST_EQ(hr.at(-1, 2), 0); + BOOST_TEST_EQ(hr.at(0, 2), 2); + BOOST_TEST_EQ(hr.at(1, 2), 1); + BOOST_TEST_EQ(hr.at(2, 2), 3); + + /* + matrix layout: + x + y 1 0 1 0 + 1 1 0 0 + 0 2 1 3 + */ + + hr = reduce(h, shrink_and_rebin(0, 2, 5, 2), rebin(1, 3)); + BOOST_TEST_EQ(hr.rank(), 2); + BOOST_TEST_EQ(sum(hr), 10); + BOOST_TEST_EQ(hr.axis(0), R(1, 2, 4)); + BOOST_TEST_EQ(hr.axis(1), R(1, -1, 2)); + BOOST_TEST_EQ(hr.at(-1, 0), 2); // underflow + BOOST_TEST_EQ(hr.at(0, 0), 5); + BOOST_TEST_EQ(hr.at(1, 0), 3); // overflow + + // test overload that accepts iterable and test option fusion + std::vector<reduce_command> opts{{shrink(0, 2, 5), rebin(0, 2), rebin(1, 3)}}; + auto hr2 = reduce(h, opts); + BOOST_TEST_EQ(hr2, hr); + reduce_command opts2[3] = {rebin(1, 3), rebin(0, 2), shrink(0, 2, 5)}; + auto hr3 = reduce(h, opts2); + BOOST_TEST_EQ(hr3, hr); + + // test positional args + auto hr4 = reduce(h, shrink_and_rebin(2, 5, 2), rebin(3)); + BOOST_TEST_EQ(hr4, hr); + } + + // crop + { + auto h = make_s(Tag(), std::vector<int>(), R(4, 1, 5), R(3, 1, 4)); + + /* + matrix layout: + x + y 1 0 1 0 + 1 1 0 0 + 0 2 1 3 + */ + h.at(0, 0) = 1; + h.at(0, 1) = 1; + h.at(1, 1) = 1; + h.at(1, 2) = 2; + h.at(2, 0) = 1; + h.at(2, 2) = 1; + h.at(3, 2) = 3; + + /* + crop first and last column in x and y + matrix layout after: + x + y 3 1 + */ + + auto hr = reduce(h, crop(2, 4), crop_and_rebin(2, 4, 2)); + BOOST_TEST_EQ(hr.rank(), 2); + BOOST_TEST_EQ(sum(hr), 4); + BOOST_TEST_EQ(hr.axis(0), R(2, 2, 4)); + BOOST_TEST_EQ(hr.axis(1), R(1, 2, 4)); + BOOST_TEST_EQ(hr.at(0, 0), 3); + BOOST_TEST_EQ(hr.at(1, 0), 1); + + // slice with crop mode + auto hr2 = reduce(h, slice(1, 3, slice_mode::crop), + slice_and_rebin(1, 3, 2, slice_mode::crop)); + BOOST_TEST_EQ(hr, hr2); + + // explicit axis indices + auto hr3 = reduce(h, crop_and_rebin(1, 2, 4, 2), crop(0, 2, 4)); + BOOST_TEST_EQ(hr, hr3); + auto hr4 = reduce(h, slice_and_rebin(1, 1, 3, 2, slice_mode::crop), + slice(0, 1, 3, slice_mode::crop)); + BOOST_TEST_EQ(hr, hr4); + } + + // mixed axis types + { + R r(5, 0.0, 5.0); + V v{{1., 2., 3.}}; + CI c{{1, 2, 3}}; + unreducible u; + + auto h = make(Tag(), r, v, c, u); + auto hr = algorithm::reduce(h, shrink(0, 2, 4), slice(2, 1, 3)); + BOOST_TEST_EQ(hr.axis(0), (R{2, 2, 4})); + BOOST_TEST_EQ(hr.axis(1), (V{{1., 2., 3.}})); + BOOST_TEST_EQ(hr.axis(2), (CI{{2, 3}})); + BOOST_TEST_EQ(hr.axis(3), u); + BOOST_TEST_THROWS((void)algorithm::reduce(h, rebin(2, 2)), std::invalid_argument); + } + + // reduce on integer axis, rebin must fail + { + auto h = make(Tag(), axis::integer<>(1, 4)); + BOOST_TEST_THROWS((void)reduce(h, rebin(2)), std::invalid_argument); + auto hr = reduce(h, shrink(2, 3)); + BOOST_TEST_EQ(hr.axis().size(), 1); + BOOST_TEST_EQ(hr.axis().bin(0), 2); + BOOST_TEST_EQ(hr.axis().bin(1), 3); + } + + // reduce on circular axis, shrink must fail, also rebin with remainder + { + auto h = make(Tag(), axis::circular<>(4, 1, 4)); + BOOST_TEST_THROWS((void)reduce(h, shrink(0, 2)), std::invalid_argument); + BOOST_TEST_THROWS((void)reduce(h, rebin(3)), std::invalid_argument); + auto hr = reduce(h, rebin(2)); + BOOST_TEST_EQ(hr.axis().size(), 2); + BOOST_TEST_EQ(hr.axis().bin(0).lower(), 1); + BOOST_TEST_EQ(hr.axis().bin(1).upper(), 4); + } + + // reduce on variable axis + { + auto h = make(Tag(), V({0, 1, 2, 3, 4, 5, 6})); + auto hr = reduce(h, shrink_and_rebin(1, 5, 2)); + BOOST_TEST_EQ(hr.axis().size(), 2); + BOOST_TEST_EQ(hr.axis().value(0), 1); + BOOST_TEST_EQ(hr.axis().value(1), 3); + BOOST_TEST_EQ(hr.axis().value(2), 5); + } + + // reduce on axis with inverted range + { + auto h = make(Tag(), R(4, 2, -2)); + const auto& ax = h.axis(); + BOOST_TEST_EQ(ax.index(-0.999), 2); + BOOST_TEST_EQ(ax.index(-1.0), 3); + BOOST_TEST_EQ(ax.index(-1.5), 3); + + BOOST_TEST_EQ(reduce(h, shrink(3, -3)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(2, -2)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(1.999, -2)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(1.001, -2)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(1, -2)).axis(), R(3, 1, -2)); + BOOST_TEST_EQ(reduce(h, shrink(2, -1.999)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(2, -1.001)).axis(), R(4, 2, -2)); + BOOST_TEST_EQ(reduce(h, shrink(2, -1)).axis(), R(3, 2, -1)); + } + + // reduce on histogram with axis without flow bins, see GitHub issue #257 + { + auto h = make(Tag(), axis::integer<int, use_default, axis::option::underflow_t>(0, 3), + axis::integer<int, use_default, axis::option::overflow_t>(0, 3)); + + std::fill(h.begin(), h.end(), 1); + + /* + Original histogram: + x + -1 0 1 2 + ------------- + 0| 1 1 1 1 + x 1| 1 1 1 1 + 2| 1 1 1 1 + 3| 1 1 1 1 + + Shrunk histogram: + -1 0 + ------- + 0| 2 1 + 1| 4 2 + */ + + auto hr = reduce(h, slice(0, 1, 2), slice(1, 1, 2)); + BOOST_TEST_EQ(hr.size(), 2 * 2); + BOOST_TEST_EQ(hr.axis(0).size(), 1); + BOOST_TEST_EQ(hr.axis(1).size(), 1); + BOOST_TEST_EQ(hr.axis(0).bin(0), 1); + BOOST_TEST_EQ(hr.axis(1).bin(0), 1); + + BOOST_TEST_EQ(hr.at(-1, 0), 2); + BOOST_TEST_EQ(hr.at(0, 0), 1); + BOOST_TEST_EQ(hr.at(-1, 1), 4); + BOOST_TEST_EQ(hr.at(0, 1), 2); + } + + // reduce on category axis: removed bins are added to overflow bin + { + auto h = make(Tag(), CI{{1, 2, 3}}); + std::fill(h.begin(), h.end(), 1); + // original: [1: 1, 2: 1, 3: 1, overflow: 1] + auto hr = reduce(h, slice(1, 2)); + // reduced: [2: 1, overflow: 3] + BOOST_TEST_EQ(hr[0], 1); + BOOST_TEST_EQ(hr[1], 3); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/algorithm_sum_test.cpp b/src/boost/libs/histogram/test/algorithm_sum_test.cpp new file mode 100644 index 000000000..4f4b8095e --- /dev/null +++ b/src/boost/libs/histogram/test/algorithm_sum_test.cpp @@ -0,0 +1,79 @@ +// 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 <array> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators/weighted_sum.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <unordered_map> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using boost::histogram::algorithm::sum; + +template <typename Tag> +void run_tests() { + auto ax = axis::integer<>(0, 10); + + { + auto h = make(Tag(), ax); + std::fill(h.begin(), h.end(), 1); + BOOST_TEST_EQ(sum(h), 12); + BOOST_TEST_EQ(sum(h, coverage::inner), 10); + } + + { + auto h = make_s(Tag(), std::array<int, 12>(), ax); + std::fill(h.begin(), h.end(), 1); + BOOST_TEST_EQ(sum(h), 12); + BOOST_TEST_EQ(sum(h, coverage::inner), 10); + } + + { + auto h = make_s(Tag(), std::unordered_map<std::size_t, int>(), ax); + std::fill(h.begin(), h.end(), 1); + BOOST_TEST_EQ(sum(h), 12); + BOOST_TEST_EQ(sum(h, coverage::inner), 10); + } + + { + auto h = make_s(Tag(), std::vector<double>(), ax, ax); + std::fill(h.begin(), h.end(), 1); + BOOST_TEST_EQ(sum(h), 12 * 12); + BOOST_TEST_EQ(sum(h, coverage::inner), 10 * 10); + } + + { + using W = accumulators::weighted_sum<>; + auto h = make_s(Tag(), std::vector<W>(), axis::integer<>(0, 2), + axis::integer<int, axis::null_type, axis::option::none_t>(2, 4)); + W w(0, 2); + for (auto&& x : h) { + x = w; + w = W(w.value() + 1, 2); + } + + // x-axis has 4 bins, y-axis has 2 = 8 bins total with 4 inner bins + + const auto v1 = algorithm::sum(h); + BOOST_TEST_EQ(v1.value(), 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7); + BOOST_TEST_EQ(v1.variance(), 8 * 2); + + const auto v2 = algorithm::sum(h, coverage::inner); + BOOST_TEST_EQ(v2.value(), 1 + 2 + 5 + 6); + BOOST_TEST_EQ(v2.variance(), 4 * 2); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_category_fail0.cpp b/src/boost/libs/histogram/test/axis_category_fail0.cpp new file mode 100644 index 000000000..cc6d1cce9 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_category_fail0.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 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/axis/category.hpp> + +using namespace boost::histogram; + +int main() { + // category axis cannot be circular + (void)axis::category<int, boost::use_default, axis::option::circular_t>({1, 2}); +} diff --git a/src/boost/libs/histogram/test/axis_category_fail1.cpp b/src/boost/libs/histogram/test/axis_category_fail1.cpp new file mode 100644 index 000000000..cda21cd6e --- /dev/null +++ b/src/boost/libs/histogram/test/axis_category_fail1.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 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/axis/category.hpp> + +using namespace boost::histogram; + +int main() { + // category axis cannot have underflow + (void)axis::category<int, boost::use_default, axis::option::underflow_t>({1, 2}); +} diff --git a/src/boost/libs/histogram/test/axis_category_fail2.cpp b/src/boost/libs/histogram/test/axis_category_fail2.cpp new file mode 100644 index 000000000..53d2de9d5 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_category_fail2.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/category.hpp> + +using namespace boost::histogram; + +int main() { + // growing category axis cannot have entries in overflow bin + (void)axis::category<int, boost::use_default, + decltype(axis::option::growth | axis::option::overflow)>({1, 2}); +} diff --git a/src/boost/libs/histogram/test/axis_category_test.cpp b/src/boost/libs/histogram/test/axis_category_test.cpp new file mode 100644 index 000000000..66f5b4803 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_category_test.cpp @@ -0,0 +1,158 @@ +// 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/ignore_unused.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/axis/category.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/traits.hpp> +#include <limits> +#include <sstream> +#include <string> +#include <type_traits> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_axis.hpp" +#include "utility_str.hpp" + +int main() { + using namespace boost::histogram; + + BOOST_TEST(std::is_nothrow_move_constructible<axis::category<int>>::value); + BOOST_TEST(std::is_nothrow_move_constructible<axis::category<std::string>>::value); + BOOST_TEST(std::is_nothrow_move_assignable<axis::category<int>>::value); + BOOST_TEST(std::is_nothrow_move_assignable<axis::category<std::string>>::value); + + // bad ctor + { + int x[2]; + boost::ignore_unused(x); + BOOST_TEST_THROWS(axis::category<int>(x + 1, x), std::invalid_argument); + } + + // value should return copy for arithmetic types and const reference otherwise + { + enum class Foo { foo }; + + BOOST_TEST_TRAIT_SAME(axis::traits::value_type<axis::category<std::string>>, + std::string); + BOOST_TEST_TRAIT_SAME(decltype(std::declval<axis::category<std::string>>().value(0)), + const std::string&); + BOOST_TEST_TRAIT_SAME(axis::traits::value_type<axis::category<const char*>>, + const char*); + BOOST_TEST_TRAIT_SAME(decltype(std::declval<axis::category<const char*>>().value(0)), + const char*); + BOOST_TEST_TRAIT_SAME(axis::traits::value_type<axis::category<Foo>>, Foo); + BOOST_TEST_TRAIT_SAME(decltype(std::declval<axis::category<Foo>>().value(0)), Foo); + BOOST_TEST_TRAIT_SAME(axis::traits::value_type<axis::category<int>>, int); + BOOST_TEST_TRAIT_SAME(decltype(std::declval<axis::category<int>>().value(0)), int); + } + + // empty axis::category + { + axis::category<int> a; + axis::category<int> b(std::vector<int>(0)); + BOOST_TEST_EQ(a, b); + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(a.index(-1), 0); + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(1), 0); + } + + // axis::category + { + std::string A("A"), B("B"), C("C"), other; + + axis::category<std::string> a({A, B, C}, "foo"); + BOOST_TEST_EQ(a.metadata(), "foo"); + BOOST_TEST_EQ(static_cast<const axis::category<std::string>&>(a).metadata(), "foo"); + a.metadata() = "bar"; + BOOST_TEST_EQ(static_cast<const axis::category<std::string>&>(a).metadata(), "bar"); + 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); + + BOOST_TEST_EQ(str(a), + "category(\"A\", \"B\", \"C\", metadata=\"bar\", options=overflow)"); + } + + // category<int, axis::null_type>: copy, move + { + using C = axis::category<int, axis::null_type>; + C a({1, 2, 3}); + C a2(a); + BOOST_TEST_EQ(a2, a); + C b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = C{{2, 1, 3}}; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + C c = std::move(b); + BOOST_TEST_EQ(c, a); + C d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + } + + // category<std::string>: copy, move + { + using C = axis::category<std::string>; + + C a({"A", "B", "C"}, "foo"); + C a2(a); + BOOST_TEST_EQ(a2, a); + C b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = C{{"B", "A", "C"}}; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + C c = std::move(b); + BOOST_TEST_EQ(c, a); + C d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + } + + // axis::category with growth + { + using pii_t = std::pair<axis::index_type, axis::index_type>; + axis::category<int, axis::null_type, axis::option::growth_t> a; + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(a.update(5), pii_t(0, -1)); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(1), pii_t(1, -1)); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(a.update(10), pii_t(2, -1)); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a.update(10), pii_t(2, 0)); + BOOST_TEST_EQ(a.size(), 3); + + BOOST_TEST_EQ(str(a), "category(5, 1, 10, options=growth)"); + } + + // 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(); +} diff --git a/src/boost/libs/histogram/test/axis_integer_fail0.cpp b/src/boost/libs/histogram/test/axis_integer_fail0.cpp new file mode 100644 index 000000000..e9eb86143 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_fail0.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/integer.hpp> + +using namespace boost::histogram; + +int main() { + // circular integer axis cannot be growing + (void)axis::integer<int, boost::use_default, + decltype(axis::option::circular | axis::option::growth)>(1, 2); +} diff --git a/src/boost/libs/histogram/test/axis_integer_fail1.cpp b/src/boost/libs/histogram/test/axis_integer_fail1.cpp new file mode 100644 index 000000000..2a1efad54 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_fail1.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/integer.hpp> + +using namespace boost::histogram; + +int main() { + // circular integer axis cannot have entries in underflow or overflow bins + (void)axis::integer<int, boost::use_default, + decltype(axis::option::circular | axis::option::underflow)>(1, 2); +} diff --git a/src/boost/libs/histogram/test/axis_integer_fail2.cpp b/src/boost/libs/histogram/test/axis_integer_fail2.cpp new file mode 100644 index 000000000..fcafc4d4f --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_fail2.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/integer.hpp> + +using namespace boost::histogram; + +int main() { + // circular integer axis cannot have entries in underflow or overflow bins + (void)axis::integer<int, boost::use_default, + decltype(axis::option::circular | axis::option::overflow)>(1, 2); +} diff --git a/src/boost/libs/histogram/test/axis_integer_fail3.cpp b/src/boost/libs/histogram/test/axis_integer_fail3.cpp new file mode 100644 index 000000000..89717794f --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_fail3.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/integer.hpp> + +using namespace boost::histogram; + +int main() { + // growing integer axis cannot have entries in underflow or overflow bins + (void)axis::integer<int, boost::use_default, + decltype(axis::option::growth | axis::option::underflow)>(1, 2); +} diff --git a/src/boost/libs/histogram/test/axis_integer_fail4.cpp b/src/boost/libs/histogram/test/axis_integer_fail4.cpp new file mode 100644 index 000000000..7b25eb304 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_fail4.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/integer.hpp> + +using namespace boost::histogram; + +int main() { + // growing integer axis cannot have entries in underflow or overflow bins + (void)axis::integer<int, boost::use_default, + decltype(axis::option::growth | axis::option::overflow)>(1, 2); +} diff --git a/src/boost/libs/histogram/test/axis_integer_test.cpp b/src/boost/libs/histogram/test/axis_integer_test.cpp new file mode 100644 index 000000000..c6a0b2da2 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_integer_test.cpp @@ -0,0 +1,191 @@ +// 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/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <limits> +#include <sstream> +#include <type_traits> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_axis.hpp" +#include "utility_str.hpp" + +int main() { + using namespace boost::histogram; + + BOOST_TEST(std::is_nothrow_move_assignable<axis::integer<>>::value); + BOOST_TEST(std::is_nothrow_move_constructible<axis::integer<>>::value); + + // bad_ctor + { BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); } + + // axis::integer with double type + { + axis::integer<double> a{-1, 2, "foo"}; + BOOST_TEST_EQ(a.metadata(), "foo"); + BOOST_TEST_EQ(static_cast<const axis::integer<double>&>(a).metadata(), "foo"); + a.metadata() = "bar"; + BOOST_TEST_EQ(static_cast<const axis::integer<double>&>(a).metadata(), "bar"); + BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity()); + 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); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 3); + + BOOST_TEST_EQ(str(a), + "integer(-1, 2, metadata=\"bar\", options=underflow | overflow)"); + + axis::integer<double> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + axis::integer<double> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::integer<double> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + } + + // empty axis::integer + { + axis::integer<> a; + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(a.bin(1), 1); + BOOST_TEST_EQ(a.bin(0), 0); + BOOST_TEST_EQ(a.bin(-1), -1); + BOOST_TEST_EQ(a.index(-10), -1); + BOOST_TEST_EQ(a.index(-1), -1); + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(10), 0); + + BOOST_TEST_EQ(str(a), "integer(0, 0, options=underflow | overflow)"); + + axis::integer<> b{1, 1}; + BOOST_TEST_EQ(b.size(), 0); + BOOST_TEST_EQ(b.bin(1), 2); + BOOST_TEST_EQ(b.bin(0), 1); + BOOST_TEST_EQ(b.bin(-1), 0); + BOOST_TEST_EQ(b.index(-10), -1); + BOOST_TEST_EQ(b.index(-1), -1); + BOOST_TEST_EQ(b.index(0), -1); + BOOST_TEST_EQ(b.index(1), 0); + BOOST_TEST_EQ(b.index(10), 0); + + BOOST_TEST_EQ(str(b), "integer(1, 1, options=underflow | overflow)"); + } + + // axis::integer with int type + { + axis::integer<int> a{-1, 2}; + BOOST_TEST_EQ(a.bin(-2), -3); + BOOST_TEST_EQ(a.bin(4), 3); + 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); + + BOOST_TEST_EQ(str(a), "integer(-1, 2, options=underflow | overflow)"); + } + + // axis::integer int,circular + { + axis::integer<int, axis::null_type, axis::option::circular_t> a(-1, 1); + BOOST_TEST_EQ(a.value(-1), -2); + BOOST_TEST_EQ(a.value(0), -1); + BOOST_TEST_EQ(a.value(1), 0); + BOOST_TEST_EQ(a.value(2), 1); + BOOST_TEST_EQ(a.value(3), 2); + 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), 0); + BOOST_TEST_EQ(a.index(2), 1); + + BOOST_TEST_EQ(str(a), "integer(-1, 1, options=circular)"); + } + + // axis::integer double,circular + { + axis::integer<double, axis::null_type, axis::option::circular_t> a(-1, 1); + BOOST_TEST_EQ(a.value(-1), -2); + BOOST_TEST_EQ(a.value(0), -1); + BOOST_TEST_EQ(a.value(1), 0); + BOOST_TEST_EQ(a.value(2), 1); + BOOST_TEST_EQ(a.value(3), 2); + 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), 0); + BOOST_TEST_EQ(a.index(2), 1); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 2); + } + + // axis::integer with growth + { + using pii_t = std::pair<axis::index_type, axis::index_type>; + axis::integer<double, axis::null_type, axis::option::growth_t> a; + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(a.update(0), pii_t(0, -1)); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(1), pii_t(1, -1)); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(a.update(-1), pii_t(0, 1)); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::infinity()), pii_t(a.size(), 0)); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::quiet_NaN()), pii_t(a.size(), 0)); + BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()), pii_t(-1, 0)); + } + + // iterators + { + test_axis_iterator(axis::integer<int>(0, 4), 0, 4); + test_axis_iterator(axis::integer<double>(0, 4), 0, 4); + test_axis_iterator( + axis::integer<int, axis::null_type, axis::option::circular_t>(0, 4), 0, 4); + } + + // shrink and rebin + { + using A = axis::integer<>; + auto a = A(-1, 5); + auto b = A(a, 2, 5, 1); + BOOST_TEST_EQ(b.size(), 3); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(3), 4); + auto c = A(a, 1, 5, 1); + BOOST_TEST_EQ(c.size(), 4); + BOOST_TEST_EQ(c.value(0), 0); + BOOST_TEST_EQ(c.value(4), 4); + auto e = A(a, 2, 5, 1); + BOOST_TEST_EQ(e.size(), 3); + BOOST_TEST_EQ(e.value(0), 1); + BOOST_TEST_EQ(e.value(3), 4); + } + + // shrink and rebin with circular option + { + using A = axis::integer<int, axis::null_type, axis::option::circular_t>; + auto a = A(1, 5); + auto b = A(a, 0, 4, 1); + BOOST_TEST_EQ(a, b); + BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument); + BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument); + BOOST_TEST_THROWS(A(a, 0, 4, 2), std::invalid_argument); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_option_test.cpp b/src/boost/libs/histogram/test/axis_option_test.cpp new file mode 100644 index 000000000..4b7647486 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_option_test.cpp @@ -0,0 +1,56 @@ +// 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/core/lightweight_test.hpp> +#include <boost/histogram/axis/option.hpp> +#include <iostream> + +using namespace boost::histogram::axis; + +template <unsigned N, unsigned M> +bool operator==(option::bitset<N>, option::bitset<M>) { + return N == M; +} + +template <unsigned N> +std::ostream& operator<<(std::ostream& os, option::bitset<N>) { + os << "underflow " << static_cast<bool>(N & option::underflow) << " " + << "overflow " << static_cast<bool>(N & option::overflow) << " " + << "circular " << static_cast<bool>(N & option::circular) << " " + << "growth " << static_cast<bool>(N & option::growth); + return os; +} + +int main() { + using namespace option; + using uoflow = decltype(underflow | overflow); + constexpr auto uoflow_growth = uoflow{} | growth; + + BOOST_TEST_EQ(uoflow::value, underflow | overflow); + BOOST_TEST_EQ(underflow | overflow, overflow | underflow); + + BOOST_TEST(underflow.test(underflow)); + BOOST_TEST_NOT(underflow.test(overflow)); + BOOST_TEST_NOT(underflow.test(underflow | overflow)); + BOOST_TEST(uoflow::test(underflow)); + BOOST_TEST(uoflow::test(overflow)); + BOOST_TEST_NOT(uoflow::test(circular)); + BOOST_TEST_NOT(uoflow::test(growth)); + BOOST_TEST(uoflow_growth.test(underflow)); + BOOST_TEST(uoflow_growth.test(overflow)); + BOOST_TEST(uoflow_growth.test(growth)); + BOOST_TEST_NOT(uoflow_growth.test(circular)); + + BOOST_TEST_EQ(uoflow_growth & uoflow_growth, uoflow_growth); + BOOST_TEST_EQ(uoflow_growth & growth, growth); + BOOST_TEST_EQ(uoflow_growth & uoflow{}, uoflow::value); + + BOOST_TEST_EQ(uoflow_growth - growth, uoflow{}); + BOOST_TEST_EQ(uoflow_growth - uoflow{}, growth); + BOOST_TEST_EQ(uoflow_growth - underflow, growth | overflow); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_regular_fail0.cpp b/src/boost/libs/histogram/test/axis_regular_fail0.cpp new file mode 100644 index 000000000..579e13cad --- /dev/null +++ b/src/boost/libs/histogram/test/axis_regular_fail0.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 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/axis/regular.hpp> + +using namespace boost::histogram; + +int main() { + // regular axis requires a floating point value type + (void)axis::regular<int>(1, 2, 3); +} diff --git a/src/boost/libs/histogram/test/axis_regular_fail1.cpp b/src/boost/libs/histogram/test/axis_regular_fail1.cpp new file mode 100644 index 000000000..e5263a16b --- /dev/null +++ b/src/boost/libs/histogram/test/axis_regular_fail1.cpp @@ -0,0 +1,15 @@ +// Copyright 2019 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/axis/regular.hpp> + +using namespace boost::histogram; + +int main() { + // circular regular axis cannot be growing + (void)axis::regular<double, boost::use_default, boost::use_default, + decltype(axis::option::circular | axis::option::growth)>(1, 2, 3); +} diff --git a/src/boost/libs/histogram/test/axis_regular_test.cpp b/src/boost/libs/histogram/test/axis_regular_test.cpp new file mode 100644 index 000000000..8b7c8837f --- /dev/null +++ b/src/boost/libs/histogram/test/axis_regular_test.cpp @@ -0,0 +1,279 @@ +// Copyright 2015-2019 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.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <limits> +#include <sstream> +#include <type_traits> +#include "is_close.hpp" +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_axis.hpp" +#include "utility_str.hpp" + +int main() { + using namespace boost::histogram; + using def = use_default; + namespace tr = axis::transform; + + BOOST_TEST(std::is_nothrow_move_assignable<axis::regular<>>::value); + BOOST_TEST(std::is_nothrow_move_constructible<axis::regular<>>::value); + + // bad_ctors + { + BOOST_TEST_THROWS(axis::regular<>(1, 0, 0), std::invalid_argument); + BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument); + } + + // ctors and assignment + { + axis::regular<> a{4, -2, 2}; + axis::regular<> b; + BOOST_TEST_NE(a, b); + b = a; + 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); + } + + // input, output + { + axis::regular<> a{4, -2, 2, "foo"}; + BOOST_TEST_EQ(a.metadata(), "foo"); + BOOST_TEST_EQ(static_cast<const axis::regular<>&>(a).metadata(), "foo"); + a.metadata() = "bar"; + BOOST_TEST_EQ(static_cast<const axis::regular<>&>(a).metadata(), "bar"); + BOOST_TEST_EQ(a.value(0), -2); + BOOST_TEST_EQ(a.value(1), -1); + BOOST_TEST_EQ(a.value(2), 0); + BOOST_TEST_EQ(a.value(3), 1); + BOOST_TEST_EQ(a.value(4), 2); + BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(-1).upper(), -2); + BOOST_TEST_EQ(a.bin(a.size()).lower(), 2); + BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity()); + 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); + + BOOST_TEST_EQ(str(a), + "regular(4, -2, 2, metadata=\"bar\", options=underflow | overflow)"); + } + + // with inverted range + { + axis::regular<> a{2, 1, -2}; + BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(0).lower(), 1); + BOOST_TEST_EQ(a.bin(1).lower(), -0.5); + BOOST_TEST_EQ(a.bin(2).lower(), -2); + BOOST_TEST_EQ(a.bin(2).upper(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.index(2), -1); + BOOST_TEST_EQ(a.index(1.001), -1); + BOOST_TEST_EQ(a.index(1), 0); + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(-0.499), 0); + BOOST_TEST_EQ(a.index(-0.5), 1); + BOOST_TEST_EQ(a.index(-1), 1); + BOOST_TEST_EQ(a.index(-2), 2); + BOOST_TEST_EQ(a.index(-20), 2); + } + + // with log transform + { + auto a = axis::regular<double, tr::log>{2, 1e0, 1e2}; + BOOST_TEST_EQ(a.bin(-1).lower(), 0.0); + BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 10.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 100.0, 1e-9); + BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity()); + + BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(a.index(0), -1); + BOOST_TEST_EQ(a.index(1), 0); + BOOST_TEST_EQ(a.index(9), 0); + BOOST_TEST_EQ(a.index(10), 1); + BOOST_TEST_EQ(a.index(90), 1); + BOOST_TEST_EQ(a.index(100), 2); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2); + + BOOST_TEST_THROWS((axis::regular<double, tr::log>{2, -1, 0}), std::invalid_argument); + + BOOST_TEST_EQ(str(a), "regular_log(2, 1, 100, options=underflow | overflow)"); + } + + // with sqrt transform + { + axis::regular<double, tr::sqrt> a(2, 0, 4); + // this is weird, but -inf * -inf = inf, thus the lower bound + BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity()); + BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 4.0, 1e-9); + BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity()); + + BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(0.99), 0); + BOOST_TEST_EQ(a.index(1), 1); + BOOST_TEST_EQ(a.index(3.99), 1); + BOOST_TEST_EQ(a.index(4), 2); + BOOST_TEST_EQ(a.index(100), 2); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2); + + BOOST_TEST_EQ(str(a), "regular_sqrt(2, 0, 4, options=underflow | overflow)"); + } + + // with pow transform + { + axis::regular<double, tr::pow> a(tr::pow{0.5}, 2, 0, 4); + // this is weird, but -inf * -inf = inf, thus the lower bound + BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity()); + BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 4.0, 1e-9); + BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity()); + + BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(0.99), 0); + BOOST_TEST_EQ(a.index(1), 1); + BOOST_TEST_EQ(a.index(3.99), 1); + BOOST_TEST_EQ(a.index(4), 2); + BOOST_TEST_EQ(a.index(100), 2); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2); + + BOOST_TEST_EQ(str(a), + "regular_pow(2, 0, 4, options=underflow | overflow, power=0.5)"); + } + + // with step + { + axis::regular<> a(axis::step(0.5), 1, 3); + BOOST_TEST_EQ(a.size(), 4); + BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.value(0), 1); + BOOST_TEST_EQ(a.value(1), 1.5); + BOOST_TEST_EQ(a.value(2), 2); + BOOST_TEST_EQ(a.value(3), 2.5); + BOOST_TEST_EQ(a.value(4), 3); + BOOST_TEST_EQ(a.bin(4).upper(), std::numeric_limits<double>::infinity()); + + axis::regular<> b(axis::step(0.5), 1, 3.1); + BOOST_TEST_EQ(a, b); + } + + // with circular option + { + axis::circular<> a{4, 0, 1}; + BOOST_TEST_EQ(a.bin(-1).lower(), a.bin(a.size() - 1).lower() - 1); + BOOST_TEST_EQ(a.index(-1.0 * 3), 0); + BOOST_TEST_EQ(a.index(0.0), 0); + BOOST_TEST_EQ(a.index(0.25), 1); + BOOST_TEST_EQ(a.index(0.5), 2); + BOOST_TEST_EQ(a.index(0.75), 3); + BOOST_TEST_EQ(a.index(1.0), 0); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 4); + BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), 4); + BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 4); + } + + // with growth + { + using pii_t = std::pair<axis::index_type, axis::index_type>; + axis::regular<double, def, def, axis::option::growth_t> a{1, 0, 1}; + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(0), pii_t(0, 0)); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(1), pii_t(1, -1)); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(a.value(0), 0); + BOOST_TEST_EQ(a.value(2), 2); + BOOST_TEST_EQ(a.update(-1), pii_t(0, 1)); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a.value(0), -1); + BOOST_TEST_EQ(a.value(3), 2); + BOOST_TEST_EQ(a.update(-10), pii_t(0, 9)); + BOOST_TEST_EQ(a.size(), 12); + BOOST_TEST_EQ(a.value(0), -10); + BOOST_TEST_EQ(a.value(12), 2); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::infinity()), pii_t(a.size(), 0)); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::quiet_NaN()), pii_t(a.size(), 0)); + BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()), pii_t(-1, 0)); + } + + // iterators + { + test_axis_iterator(axis::regular<>(5, 0, 1), 0, 5); + test_axis_iterator(axis::regular<double, def, def, axis::option::none_t>(5, 0, 1), 0, + 5); + test_axis_iterator(axis::circular<>(5, 0, 1), 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.bin(0), "[0, 0.5)"); + } + + // null_type streamable + { + auto a = axis::regular<float, def, axis::null_type>(2, 0, 1); + BOOST_TEST_EQ(str(a), "regular(2, 0, 1, options=underflow | overflow)"); + } + + // shrink and rebin + { + using A = axis::regular<>; + auto a = A(5, 0, 5); + auto b = A(a, 1, 4, 1); + BOOST_TEST_EQ(b.size(), 3); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(3), 4); + auto c = A(a, 0, 4, 2); + BOOST_TEST_EQ(c.size(), 2); + BOOST_TEST_EQ(c.value(0), 0); + BOOST_TEST_EQ(c.value(2), 4); + auto e = A(a, 1, 5, 2); + BOOST_TEST_EQ(e.size(), 2); + BOOST_TEST_EQ(e.value(0), 1); + BOOST_TEST_EQ(e.value(2), 5); + } + + // shrink and rebin with circular option + { + using A = axis::circular<>; + auto a = A(4, 1, 5); + BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument); + BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument); + auto b = A(a, 0, 4, 2); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(2), 5); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_size.cpp b/src/boost/libs/histogram/test/axis_size.cpp new file mode 100644 index 000000000..54f551316 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_size.cpp @@ -0,0 +1,34 @@ +// 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/axis.hpp> +#include <iostream> + +#define SHOW_SIZE(x) std::cout << #x << " " << sizeof(x) << std::endl + +int main() { + using namespace boost::histogram; + + using regular = axis::regular<>; + using regular_float = axis::regular<float>; + using regular_pow = axis::regular<double, axis::transform::pow>; + using regular_no_metadata = axis::regular<double, axis::transform::id, axis::null_type>; + using circular = axis::circular<>; + using variable = axis::variable<>; + using integer = axis::integer<>; + using category = axis::category<>; + using variant = axis::variant<regular, circular, variable, integer, category>; + + SHOW_SIZE(regular); + SHOW_SIZE(regular_float); + SHOW_SIZE(regular_pow); + SHOW_SIZE(regular_no_metadata); + SHOW_SIZE(circular); + SHOW_SIZE(variable); + SHOW_SIZE(integer); + SHOW_SIZE(category); + SHOW_SIZE(variant); +} diff --git a/src/boost/libs/histogram/test/axis_traits_test.cpp b/src/boost/libs/histogram/test/axis_traits_test.cpp new file mode 100644 index 000000000..f102f7c14 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_traits_test.cpp @@ -0,0 +1,273 @@ +// 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/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/traits.hpp> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_axis.hpp" + +using namespace boost::histogram::axis; +using namespace std::literals; + +struct ValueTypeOverride { + index_type index(int); +}; + +namespace boost { +namespace histogram { +namespace detail { +template <> +struct value_type_deducer<ValueTypeOverride> { + using type = char; +}; +} // namespace detail +} // namespace histogram +} // namespace boost + +int main() { + // value_type + { + struct Foo { + void index(const long&); + }; + + BOOST_TEST_TRAIT_SAME(traits::value_type<Foo>, long); + BOOST_TEST_TRAIT_SAME(traits::value_type<ValueTypeOverride>, char); + BOOST_TEST_TRAIT_SAME(traits::value_type<integer<int>>, int); + BOOST_TEST_TRAIT_SAME(traits::value_type<category<int>>, int); + BOOST_TEST_TRAIT_SAME(traits::value_type<regular<double>>, double); + } + + // is_continuous + { + BOOST_TEST_TRAIT_TRUE((traits::is_continuous<regular<>>)); + BOOST_TEST_TRAIT_FALSE((traits::is_continuous<integer<int>>)); + BOOST_TEST_TRAIT_FALSE((traits::is_continuous<category<int>>)); + BOOST_TEST_TRAIT_TRUE((traits::is_continuous<integer<double>>)); + } + + // is_reducible + { + struct not_reducible {}; + struct reducible { + reducible(const reducible&, index_type, index_type, unsigned); + }; + + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<reducible>)); + BOOST_TEST_TRAIT_FALSE((traits::is_reducible<not_reducible>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<regular<>>)); + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<variable<>>)); + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<circular<>>)); + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<integer<>>)); + BOOST_TEST_TRAIT_TRUE((traits::is_reducible<category<>>)); + } + + // get_options, options() + { + using A = integer<>; + BOOST_TEST_EQ(traits::get_options<A>::test(option::growth), false); + auto expected = option::underflow | option::overflow; + auto a = A{}; + BOOST_TEST_EQ(traits::options(a), expected); + BOOST_TEST_EQ(traits::options(static_cast<A&>(a)), expected); + BOOST_TEST_EQ(traits::options(static_cast<const A&>(a)), expected); + BOOST_TEST_EQ(traits::options(std::move(a)), expected); + + using B = integer<int, null_type, option::growth_t>; + BOOST_TEST_EQ(traits::get_options<B>::test(option::growth), true); + BOOST_TEST_EQ(traits::options(B{}), option::growth); + + struct growing { + auto update(double) { return std::make_pair(0, 0); } + }; + using C = growing; + BOOST_TEST_EQ(traits::get_options<C>::test(option::growth), true); + auto c = C{}; + BOOST_TEST_EQ(traits::options(c), option::growth); + BOOST_TEST_EQ(traits::options(static_cast<C&>(c)), option::growth); + BOOST_TEST_EQ(traits::options(static_cast<const C&>(c)), option::growth); + BOOST_TEST_EQ(traits::options(std::move(c)), option::growth); + + struct notgrowing { + auto index(double) { return 0; } + }; + using D = notgrowing; + BOOST_TEST_EQ(traits::get_options<D>::test(option::growth), false); + auto d = D{}; + BOOST_TEST_EQ(traits::options(d), option::none); + BOOST_TEST_EQ(traits::options(static_cast<D&>(d)), option::none); + BOOST_TEST_EQ(traits::options(static_cast<const D&>(d)), option::none); + BOOST_TEST_EQ(traits::options(std::move(d)), option::none); + } + + // is_inclusive, inclusive() + { + struct empty {}; + struct with_opts_not_inclusive { + static constexpr unsigned options() { return option::underflow | option::overflow; } + static constexpr bool inclusive() { return false; } + }; + + BOOST_TEST_EQ(traits::inclusive(empty{}), false); + BOOST_TEST_EQ(traits::inclusive(regular<>{}), true); + + BOOST_TEST_TRAIT_FALSE((traits::is_inclusive<empty>)); + BOOST_TEST_TRAIT_FALSE((traits::is_inclusive<with_opts_not_inclusive>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_inclusive<regular<>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive< + regular<double, boost::use_default, boost::use_default, option::growth_t>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<regular<double, boost::use_default, boost::use_default, + option::circular_t>>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_inclusive<variable<>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<variable<double, boost::use_default, option::growth_t>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<variable<double, boost::use_default, option::circular_t>>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_inclusive<integer<int>>)); + BOOST_TEST_TRAIT_TRUE( + (traits::is_inclusive<integer<int, boost::use_default, option::growth_t>>)); + BOOST_TEST_TRAIT_TRUE( + (traits::is_inclusive<integer<int, boost::use_default, option::circular_t>>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_inclusive<integer<double>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<integer<double, boost::use_default, option::growth_t>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<integer<double, boost::use_default, option::circular_t>>)); + + BOOST_TEST_TRAIT_TRUE((traits::is_inclusive<category<int>>)); + BOOST_TEST_TRAIT_TRUE( + (traits::is_inclusive<category<int, boost::use_default, option::growth_t>>)); + BOOST_TEST_TRAIT_FALSE( + (traits::is_inclusive<category<int, boost::use_default, option::none_t>>)); + } + + // is_ordered, ordered() + { + struct ordered_1 { + constexpr static bool ordered() { return true; } + index_type index(ordered_1) { return true; } + }; + struct ordered_2 { + index_type index(int); + }; + struct not_ordered_1 { + constexpr static bool ordered() { return false; } + index_type index(int); + }; + struct not_ordered_2 { + index_type index(not_ordered_2); + }; + + BOOST_TEST_TRAIT_TRUE((traits::is_ordered<ordered_1>)); + BOOST_TEST_TRAIT_TRUE((traits::is_ordered<ordered_2>)); + BOOST_TEST_TRAIT_FALSE((traits::is_ordered<not_ordered_1>)); + BOOST_TEST_TRAIT_FALSE((traits::is_ordered<not_ordered_2>)); + + BOOST_TEST(traits::ordered(integer<>{})); + BOOST_TEST_NOT(traits::ordered(category<int>{})); + } + + // index, rank, value, width + { + auto a = integer<>(1, 3); + BOOST_TEST_EQ(traits::index(a, 1), 0); + BOOST_TEST_EQ(traits::rank(a), 1); + BOOST_TEST_EQ(traits::value(a, 0), 1); + BOOST_TEST_EQ(traits::width(a, 0), 0); + BOOST_TEST_EQ(traits::width(a, 0), 0); + + auto b = integer<double>(1, 3); + BOOST_TEST_EQ(traits::index(b, 1), 0); + BOOST_TEST_EQ(traits::rank(b), 1); + BOOST_TEST_EQ(traits::value(b, 0), 1); + BOOST_TEST_EQ(traits::width(b, 0), 1); + BOOST_TEST(traits::get_options<decltype(b)>::test(option::underflow)); + + auto c = category<std::string>{"red", "blue"}; + BOOST_TEST_EQ(traits::index(c, "blue"), 1); + BOOST_TEST_EQ(traits::rank(c), 1); + BOOST_TEST_EQ(traits::value(c, 0), "red"s); + BOOST_TEST_EQ(traits::width(c, 0), 0); + + struct D { + index_type index(const std::tuple<int, double>& args) const { + return static_cast<index_type>(std::get<0>(args) + std::get<1>(args)); + } + index_type size() const { return 5; } + } d; + BOOST_TEST_EQ(traits::index(d, std::make_tuple(1, 2.0)), 3.0); + BOOST_TEST_EQ(traits::rank(d), 2u); + + variant<D, integer<>> v; + v = a; + BOOST_TEST_EQ(traits::rank(v), 1u); + v = d; + BOOST_TEST_EQ(traits::rank(v), 2u); + + struct E : integer<> { + using integer::integer; // inherit ctors of base + // customization point: convert argument and call base class + auto index(const char* s) const { return integer::index(std::atoi(s)); } + }; + + BOOST_TEST_EQ(traits::index(E{0, 3}, "2"), 2); + } + + // update + { + auto a = integer<int, null_type, option::growth_t>(); + BOOST_TEST_EQ(traits::update(a, 0), (std::pair<index_type, index_type>(0, -1))); + BOOST_TEST_THROWS(traits::update(a, "foo"), std::invalid_argument); + + variant<integer<int, null_type, option::growth_t>, integer<>> v(a); + BOOST_TEST_EQ(traits::update(v, 0), (std::pair<index_type, index_type>(0, 0))); + } + + // metadata + { + struct None {}; + + struct Const { + const int& metadata() const { return m; }; + int m = 0; + }; + + struct Both { + const int& metadata() const { return m; }; + int& metadata() { return m; }; + int m = 0; + }; + + None none; + BOOST_TEST_TRAIT_SAME(decltype(traits::metadata(none)), null_type&); + BOOST_TEST_TRAIT_SAME(decltype(traits::metadata(static_cast<None&>(none))), + null_type&); + BOOST_TEST_TRAIT_SAME(decltype(traits::metadata(static_cast<const None&>(none))), + const null_type&); + + Const c; + BOOST_TEST_EQ(traits::metadata(c), 0); + BOOST_TEST_EQ(traits::metadata(static_cast<Const&>(c)), 0); + BOOST_TEST_EQ(traits::metadata(static_cast<const Const&>(c)), 0); + + Both b; + BOOST_TEST_EQ(traits::metadata(b), 0); + BOOST_TEST_EQ(traits::metadata(static_cast<Both&>(b)), 0); + BOOST_TEST_EQ(traits::metadata(static_cast<const Both&>(b)), 0); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_variable_fail0.cpp b/src/boost/libs/histogram/test/axis_variable_fail0.cpp new file mode 100644 index 000000000..b98930cb2 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variable_fail0.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 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/axis/variable.hpp> + +using namespace boost::histogram; + +int main() { + // variable axis requires a floating point value type + (void)axis::variable<int>({1, 2, 3}); +} diff --git a/src/boost/libs/histogram/test/axis_variable_fail1.cpp b/src/boost/libs/histogram/test/axis_variable_fail1.cpp new file mode 100644 index 000000000..2a9ea02f0 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variable_fail1.cpp @@ -0,0 +1,16 @@ +// Copyright 2019 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/axis/variable.hpp> + +using namespace boost::histogram; + +int main() { + // circular variable axis cannot be growing + (void)axis::variable<double, boost::use_default, + decltype(axis::option::circular | axis::option::growth)>( + {1, 2, 3}); +} diff --git a/src/boost/libs/histogram/test/axis_variable_test.cpp b/src/boost/libs/histogram/test/axis_variable_test.cpp new file mode 100644 index 000000000..ea90a5822 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variable_test.cpp @@ -0,0 +1,160 @@ +// 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.hpp> +#include <boost/histogram/axis/variable.hpp> +#include <limits> +#include <sstream> +#include <type_traits> +#include <vector> +#include "is_close.hpp" +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_axis.hpp" +#include "utility_str.hpp" + +using namespace boost::histogram; + +int main() { + BOOST_TEST(std::is_nothrow_move_assignable<axis::variable<>>::value); + BOOST_TEST(std::is_nothrow_move_constructible<axis::variable<>>::value); + + // bad_ctors + { + BOOST_TEST_THROWS(axis::variable<>(std::vector<double>{}), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0, 1.0}), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0, -1.0}), std::invalid_argument); + } + + // axis::variable + { + axis::variable<> a{{-1, 0, 1}, "foo"}; + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(a.metadata(), "foo"); + BOOST_TEST_EQ(static_cast<const axis::variable<>&>(a).metadata(), "foo"); + a.metadata() = "bar"; + BOOST_TEST_EQ(static_cast<const axis::variable<>&>(a).metadata(), "bar"); + BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.value(0), -1); + BOOST_TEST_EQ(a.value(0.5), -0.5); + BOOST_TEST_EQ(a.value(1), 0); + BOOST_TEST_EQ(a.value(1.5), 0.5); + BOOST_TEST_EQ(a.value(2), 1); + 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); + + BOOST_TEST_EQ(str(a), + "variable(-1, 0, 1, metadata=\"bar\", options=underflow | overflow)"); + + axis::variable<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + axis::variable<> c = std::move(b); + BOOST_TEST_EQ(c, 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); + } + + // axis::variable circular + { + axis::variable<double, axis::null_type, axis::option::circular_t> a{-1, 1, 2}; + BOOST_TEST_EQ(a.value(-2), -4); + BOOST_TEST_EQ(a.value(-1), -2); + BOOST_TEST_EQ(a.value(0), -1); + BOOST_TEST_EQ(a.value(1), 1); + BOOST_TEST_EQ(a.value(2), 2); + BOOST_TEST_EQ(a.value(3), 4); + BOOST_TEST_EQ(a.value(4), 5); + BOOST_TEST_EQ(a.index(-3), 0); // -3 + 3 = 0 + BOOST_TEST_EQ(a.index(-2), 1); // -2 + 3 = 1 + BOOST_TEST_EQ(a.index(-1), 0); + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(1), 1); + BOOST_TEST_EQ(a.index(2), 0); + BOOST_TEST_EQ(a.index(3), 0); // 3 - 3 = 0 + BOOST_TEST_EQ(a.index(4), 1); // 4 - 3 = 1 + } + + // axis::regular with growth + { + using pii_t = std::pair<axis::index_type, axis::index_type>; + axis::variable<double, axis::null_type, axis::option::growth_t> a{0, 1}; + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(0), pii_t(0, 0)); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(a.update(1.1), pii_t(1, -1)); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(a.value(0), 0); + BOOST_TEST_EQ(a.value(1), 1); + BOOST_TEST_EQ(a.value(2), 1.5); + BOOST_TEST_EQ(a.update(-0.1), pii_t(0, 1)); + BOOST_TEST_EQ(a.value(0), -0.5); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a.update(10), pii_t(3, -1)); + BOOST_TEST_EQ(a.size(), 4); + BOOST_TEST_IS_CLOSE(a.value(4), 10, 1e-9); + BOOST_TEST_EQ(a.update(-10), pii_t(0, 1)); + BOOST_TEST_EQ(a.size(), 5); + BOOST_TEST_IS_CLOSE(a.value(0), -10, 1e-9); + + BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()), pii_t(-1, 0)); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::infinity()), pii_t(a.size(), 0)); + BOOST_TEST_EQ(a.update(std::numeric_limits<double>::quiet_NaN()), pii_t(a.size(), 0)); + } + + // iterators + { + test_axis_iterator(axis::variable<>{1, 2, 3}, 0, 2); + test_axis_iterator( + axis::variable<double, axis::null_type, axis::option::circular_t>{1, 2, 3}, 0, 2); + } + + // shrink and rebin + { + using A = axis::variable<>; + auto a = A({0, 1, 2, 3, 4, 5}); + auto b = A(a, 1, 4, 1); + BOOST_TEST_EQ(b.size(), 3); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(3), 4); + auto c = A(a, 0, 4, 2); + BOOST_TEST_EQ(c.size(), 2); + BOOST_TEST_EQ(c.value(0), 0); + BOOST_TEST_EQ(c.value(2), 4); + auto e = A(a, 1, 5, 2); + BOOST_TEST_EQ(e.size(), 2); + BOOST_TEST_EQ(e.value(0), 1); + BOOST_TEST_EQ(e.value(2), 5); + } + + // shrink and rebin with circular option + { + using A = axis::variable<double, axis::null_type, axis::option::circular_t>; + auto a = A({1, 2, 3, 4, 5}); + BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument); + BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument); + auto b = A(a, 0, 4, 2); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(2), 5); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_variant_serialization_test.cpp b/src/boost/libs/histogram/test/axis_variant_serialization_test.cpp new file mode 100644 index 000000000..8514ddb38 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variant_serialization_test.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2019 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) + +// This test is inspired by the corresponding boost/beast test of detail_variant. + +#include <boost/assert.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/axis/variant.hpp> +#include <boost/histogram/serialization.hpp> +#include "throw_exception.hpp" +#include "utility_axis.hpp" +#include "utility_serialization.hpp" + +using namespace boost::histogram::axis; + +int main(int argc, char** argv) { + BOOST_ASSERT(argc == 2); + + const auto filename = join(argv[1], "axis_variant_serialization_test.xml"); + + using R = regular<>; + using I = integer<>; + + variant<I, R> a(I(0, 3)); + variant<I, R> b(R(1, 0, 1)); + print_xml(filename, b); + BOOST_TEST_NE(a, b); + load_xml(filename, a); + BOOST_TEST_EQ(a, b); + + variant<I> c; // load incompatible version + BOOST_TEST_THROWS(load_xml(filename, c), std::runtime_error); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/axis_variant_serialization_test.xml b/src/boost/libs/histogram/test/axis_variant_serialization_test.xml new file mode 100644 index 000000000..9504c5112 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variant_serialization_test.xml @@ -0,0 +1,24 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <variant class_id="1" tracking_level="0" version="0"> + <which>1</which> + <value class_id="2" tracking_level="0" version="0"> + <transform class_id="3" tracking_level="0" version="0"></transform> + <size>1</size> + <meta></meta> + <min>0.00000000000000000e+00</min> + <delta>1.00000000000000000e+00</delta> + </value> + </variant> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/axis_variant_test.cpp b/src/boost/libs/histogram/test/axis_variant_test.cpp new file mode 100644 index 000000000..016b592d8 --- /dev/null +++ b/src/boost/libs/histogram/test/axis_variant_test.cpp @@ -0,0 +1,250 @@ +// 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/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/axis/variant.hpp> +#include <boost/histogram/detail/type_name.hpp> +#include <string> +#include <type_traits> +#include <vector> +#include "throw_exception.hpp" +#include "utility_allocator.hpp" +#include "utility_axis.hpp" +#include "utility_str.hpp" + +int main() { + using namespace boost::histogram; + namespace tr = axis::transform; + + { + (void)axis::variant<>{}; + } + + { + using meta_type = std::vector<int>; + using variant_type = + axis::variant<axis::integer<double>, axis::category<std::string, meta_type>>; + auto a = variant_type{axis::integer<double>(0, 2, "foo")}; + BOOST_TEST_EQ(a.index(-10), -1); + BOOST_TEST_EQ(a.index(-1), -1); + BOOST_TEST_EQ(a.index(0), 0); + BOOST_TEST_EQ(a.index(0.5), 0); + BOOST_TEST_EQ(a.index(1), 1); + BOOST_TEST_EQ(a.index(2), 2); + BOOST_TEST_EQ(a.index(10), 2); + BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(-10).lower(), -std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.bin(a.size() + 10).upper(), std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.metadata(), "foo"); + a.metadata() = "bar"; + BOOST_TEST_EQ(static_cast<const variant_type&>(a).metadata(), "bar"); + BOOST_TEST_EQ(a.options(), axis::option::underflow | axis::option::overflow); + + a = axis::category<std::string, meta_type>({"A", "B"}, {1, 2, 3}); + BOOST_TEST_EQ(a.index("A"), 0); + BOOST_TEST_EQ(a.index("B"), 1); + BOOST_TEST_THROWS(a.metadata(), std::runtime_error); + BOOST_TEST_THROWS(static_cast<const variant_type&>(a).metadata(), std::runtime_error); + BOOST_TEST_EQ(a.options(), axis::option::overflow_t::value); + } + + // axis::variant with pointers + { + using A = axis::integer<>; + using B = axis::regular<>; + auto a = A(1, 5, "foo"); + auto b = B(3, 1, 5, "bar"); + axis::variant<A*, B*> r1(&a); + BOOST_TEST_EQ(r1, a); + BOOST_TEST_NE(r1, A(2, 4)); + BOOST_TEST_NE(r1, b); + BOOST_TEST_EQ(r1.size(), 4); + BOOST_TEST_EQ(r1.value(0), 1); + BOOST_TEST_EQ(r1.metadata(), a.metadata()); + BOOST_TEST_EQ(r1.options(), a.options()); + // change original through r1 + axis::get<A>(r1).metadata() = "bar"; + BOOST_TEST_EQ(a.metadata(), "bar"); + r1 = &b; + BOOST_TEST_EQ(r1, b); + + axis::variant<const A*, const B*> r2(static_cast<const B*>(&b)); + BOOST_TEST_EQ(r2, b); + BOOST_TEST_NE(r2, B(4, 1, 5)); + BOOST_TEST_NE(r2, a); + BOOST_TEST_EQ(r2.size(), 3); + BOOST_TEST_EQ(r2.value(0), 1); + BOOST_TEST_EQ(r2.metadata(), "bar"); + b.metadata() = "baz"; + BOOST_TEST_EQ(r2.metadata(), "baz"); + } + + // 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_NE(c, b); + c = std::move(b); + BOOST_TEST(c == r); + } + + // axis::variant streamable + { + auto test = [](auto&& a, const char* ref) { + using T = std::decay_t<decltype(a)>; + axis::variant<T> axis(std::move(a)); + BOOST_TEST_CSTR_EQ(str(axis).c_str(), ref); + }; + + test(axis::regular<>(2, -1, 1, "regular1"), + "regular(2, -1, 1, metadata=\"regular1\", options=underflow | overflow)"); + + struct user_defined {}; + const auto ref = "integer(-1, 1, metadata=" + detail::type_name<user_defined>() + + ", options=none)"; + test(axis::integer<int, user_defined, axis::option::none_t>(-1, 1), ref.c_str()); + } + + // bin_type operator<< + { + auto test = [](auto&& a, const char* ref) { + using T = std::decay_t<decltype(a)>; + axis::variant<T> axis(std::move(a)); + BOOST_TEST_CSTR_EQ(str(axis.bin(0)).c_str(), ref); + }; + + test(axis::regular<>(2, 1, 2), "[1, 1.5)"); + test(axis::category<>({1, 2}), "1"); + } + + // axis::variant operator== + { + enum { A, B, C }; + using variant = + axis::variant<axis::regular<>, axis::regular<double, axis::transform::pow>, + axis::category<>, axis::integer<>>; + std::vector<variant> axes; + axes.push_back(axis::regular<>{2, -1, 1}); + axes.push_back(axis::regular<double, tr::pow>(tr::pow(0.5), 2, 1, 4)); + 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 axis that has incompatible bin type + { + auto a = axis::variant<axis::category<std::string>>( + axis::category<std::string>({"A", "B", "C"})); + BOOST_TEST_THROWS(a.bin(0), std::runtime_error); + auto b = axis::variant<axis::category<int>>(axis::category<int>({2, 1, 3})); + BOOST_TEST_EQ(b.bin(0), 2); + BOOST_TEST_EQ(b.bin(0).lower(), + b.bin(0).upper()); // lower == upper for bin without interval + } + + // axis::variant support for user-defined axis types + { + struct minimal_axis { + int index(int x) const { return x % 2; } + int size() const { return 2; } + }; + + axis::variant<minimal_axis, axis::category<std::string>> axis; + BOOST_TEST_EQ(axis.index(0), 0); + BOOST_TEST_EQ(axis.index(9), 1); + BOOST_TEST_EQ(axis.size(), 2); + BOOST_TEST_EQ(axis.metadata(), axis::null_type{}); + BOOST_TEST_CSTR_EQ(str(axis).c_str(), "<unstreamable>"); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + + axis = axis::category<std::string>({"A", "B"}, "category"); + BOOST_TEST_EQ(axis.index("B"), 1); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + } + + // vector of axes with custom allocators + { + using M = std::vector<char, tracing_allocator<char>>; + using T1 = axis::regular<double, tr::id, M>; + using T2 = axis::integer<int, axis::null_type>; + using T3 = axis::category<long, axis::null_type, axis::option::overflow_t, + tracing_allocator<long>>; + using axis_type = axis::variant<T1, T2, T3>; // 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(3); + axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); + axes.emplace_back(T2(0, 4)); + axes.emplace_back(T3({1, 2, 3, 4, 5}, {}, a)); + } + // 3 axis::variant objects + BOOST_TEST_EQ(db.at<axis_type>().first, 0); + BOOST_TEST_EQ(db.at<axis_type>().second, 3); + + // label of T1 + BOOST_TEST_EQ(db.at<char>().first, 0); + BOOST_TEST_EQ(db.at<char>().second, 3); + + // T3 allocates storage for long array + BOOST_TEST_EQ(db.at<long>().first, 0); + BOOST_TEST_EQ(db.at<long>().second, 5); + } + + // testing pass-through versions of get + { + axis::regular<> a(10, 0, 1); + axis::integer<> b(0, 3); + const auto& ta = axis::get<axis::regular<>>(a); + BOOST_TEST_EQ(ta, a); + const auto* tb = axis::get_if<axis::integer<>>(&b); + BOOST_TEST_EQ(tb, &b); + const auto* tc = axis::get_if<axis::regular<>>(&b); + BOOST_TEST_EQ(tc, nullptr); + } + + // iterators + test_axis_iterator(axis::variant<axis::regular<>>(axis::regular<>(5, 0, 1)), 0, 5); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/boost_accumulators_support_test.cpp b/src/boost/libs/histogram/test/boost_accumulators_support_test.cpp new file mode 100644 index 000000000..f8f729240 --- /dev/null +++ b/src/boost/libs/histogram/test/boost_accumulators_support_test.cpp @@ -0,0 +1,45 @@ +// Copyright 2018-2019 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/accumulators/accumulators.hpp> +#include <boost/accumulators/statistics/mean.hpp> +#include <boost/accumulators/statistics/stats.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include "throw_exception.hpp" + +namespace ba = boost::accumulators; + +int main() { + using namespace boost::histogram; + + // mean + { + using mean = ba::accumulator_set<double, ba::stats<ba::tag::mean> >; + + auto h = make_histogram_with(dense_storage<mean>(), axis::integer<>(0, 2)); + h(0, sample(1)); + h(0, sample(2)); + h(0, sample(3)); + h(1, sample(2)); + h(1, sample(3)); + BOOST_TEST_EQ(ba::count(h[0]), 3); + BOOST_TEST_EQ(ba::mean(h[0]), 2); + BOOST_TEST_EQ(ba::count(h[1]), 2); + BOOST_TEST_EQ(ba::mean(h[1]), 2.5); + BOOST_TEST_EQ(ba::count(h[2]), 0); + + auto h2 = h; // copy ok + BOOST_TEST_EQ(ba::count(h2[0]), 3); + BOOST_TEST_EQ(ba::mean(h2[0]), 2); + BOOST_TEST_EQ(ba::count(h2[1]), 2); + BOOST_TEST_EQ(ba::mean(h2[1]), 2.5); + BOOST_TEST_EQ(ba::count(h2[2]), 0); + } + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/boost_range_support_test.cpp b/src/boost/libs/histogram/test/boost_range_support_test.cpp new file mode 100644 index 000000000..cf1bedc3b --- /dev/null +++ b/src/boost/libs/histogram/test/boost_range_support_test.cpp @@ -0,0 +1,29 @@ +// Copyright 2018-2019 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/integer.hpp> +#include "throw_exception.hpp" +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/range/adaptor/filtered.hpp> +#include <boost/range/numeric.hpp> + +using namespace boost::histogram; +using namespace boost::adaptors; + +int main() { + auto h = make_histogram(axis::integer<>(1, 4)); + h(1, weight(1)); + h(2, weight(2)); + h(3, weight(3)); + h(4, weight(4)); + + auto s1 = boost::accumulate(h | filtered([](double x) { return x > 2; }), 0.0); + BOOST_TEST_EQ(s1, 7); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/boost_units_support_test.cpp b/src/boost/libs/histogram/test/boost_units_support_test.cpp new file mode 100644 index 000000000..1a3df6531 --- /dev/null +++ b/src/boost/libs/histogram/test/boost_units_support_test.cpp @@ -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) + +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/regular.hpp> +#include "throw_exception.hpp" +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/indexed.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/units/quantity.hpp> +#include <boost/units/systems/si/length.hpp> +#include <limits> +#include "is_close.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::literals; +namespace tr = axis::transform; + +int main() { + using namespace boost::units; + using Q = quantity<si::length>; + + // axis::regular with quantity + { + axis::regular<Q> b(2, 0 * si::meter, 2 * si::meter); + BOOST_TEST_EQ(b.bin(-1).lower() / si::meter, + -std::numeric_limits<double>::infinity()); + BOOST_TEST_IS_CLOSE(b.bin(0).lower() / si::meter, 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b.bin(1).lower() / si::meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b.bin(2).lower() / si::meter, 2.0, 1e-9); + BOOST_TEST_EQ(b.bin(2).upper() / si::meter, std::numeric_limits<double>::infinity()); + + BOOST_TEST_EQ(b.index(-std::numeric_limits<double>::infinity() * si::meter), -1); + BOOST_TEST_EQ(b.index(-1 * si::meter), -1); // produces NaN in conversion + BOOST_TEST_EQ(b.index(0 * si::meter), 0); + BOOST_TEST_EQ(b.index(0.99 * si::meter), 0); + BOOST_TEST_EQ(b.index(1 * si::meter), 1); + BOOST_TEST_EQ(b.index(1.99 * si::meter), 1); + BOOST_TEST_EQ(b.index(2 * si::meter), 2); + BOOST_TEST_EQ(b.index(100 * si::meter), 2); + BOOST_TEST_EQ(b.index(std::numeric_limits<double>::infinity() * si::meter), 2); + } + + // axis::regular with quantity and transform + { + axis::regular<Q, tr::log> b(2, 1 * si::meter, 10 * si::meter); + BOOST_TEST_EQ(b.value(-1) / si::meter, 0); + BOOST_TEST_IS_CLOSE(b.value(0) / si::meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b.value(1) / si::meter, 3.1623, 1e-3); + BOOST_TEST_IS_CLOSE(b.value(2) / si::meter, 10.0, 1e-9); + BOOST_TEST_EQ(b.value(3) / si::meter, std::numeric_limits<double>::infinity()); + } + + // histogram with quantity axis + { + auto h = make_histogram(axis::regular<Q>(2, 0 * si::meter, 1 * si::meter), + axis::regular<>(2, 0, 1)); + h(0.1 * si::meter, 0.1); // fills bin (0, 0) + BOOST_TEST_EQ(h.at(0, 0), 1); + for (auto&& x : indexed(h)) { + BOOST_TEST_THROWS(x.density(), std::runtime_error); // cannot use density method + BOOST_TEST_EQ(x.index(0), 2.0 * x.bin(0_c).lower() / si::meter); + BOOST_TEST_EQ(x.index(1), 2.0 * x.bin(1_c).lower()); + } + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/check_build_system.py b/src/boost/libs/histogram/test/check_build_system.py new file mode 100755 index 000000000..39bc112ce --- /dev/null +++ b/src/boost/libs/histogram/test/check_build_system.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# Copyright Hans Dembinski 2019 +# 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 + +from __future__ import print_function +import sys +import glob +import os +import re + +ps = os.path.split +pj = os.path.join + +# assumes that check_build_system.py sits in <project_path>/tests +project_path = ps(ps(__file__)[0])[0] + +exit_code = 0 + +for dir in (pj(project_path, "test"), pj(project_path, "examples")): + cpp = set([os.path.basename(x) for x in glob.glob(dir + "/*.cpp")]) + + for build_file in ("Jamfile", "CMakeLists.txt"): + filename = os.path.join(dir, build_file) + if not os.path.exists(filename): + continue + run = set(re.findall("([a-zA-Z0-9_]+\.cpp)", open(filename).read())) + + diff = cpp - run + diff.discard("check_cmake_version.cpp") # ignore + diff.discard("check_build_system.py") # ignore + + if diff: + print( + "NOT TESTED in %s\n " % filename + + "\n ".join(["%s/%s" % (dir, x) for x in diff]) + ) + exit_code = 1 + +sys.exit(exit_code) diff --git a/src/boost/libs/histogram/test/check_odr_test.py b/src/boost/libs/histogram/test/check_odr_test.py new file mode 100644 index 000000000..d2a48a573 --- /dev/null +++ b/src/boost/libs/histogram/test/check_odr_test.py @@ -0,0 +1,66 @@ +# Copyright 2019 Hans Dembinski, Henry Schreiner +# +# 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) + +""" +This test makes sure that all boost.histogram headers are included in the ODR test carried out in odr_main_test.cpp. See that file for details on why this test needed. +""" + +import os +import sys +import re + +this_path = os.path.dirname(__file__) + +all_headers = set() +# Includes are either in this_path/../include for the standalone version or ... +include_path = os.path.join(this_path, "..", "include", "boost", "histogram") +if not os.path.exists(include_path): + # ... in this_path/../../.. for the bundled boost release + include_path = os.path.join(this_path, "..", "..", "..", "boost", "histogram") + assert os.path.exists(include_path) + +# this has to be rindex, because any leading path could also be called boost +root_include_path = include_path[: include_path.rindex("boost")] + +for root, dirs, files in os.walk(include_path): + for fn in files: + fn = os.path.join(root, fn) + fn = fn[len(root_include_path) :] + all_headers.add(fn) + + +def get_headers(filename): + with open(filename) as f: + for hdr in re.findall('^#include [<"]([^>"]+)[>"]', f.read(), re.MULTILINE): + if not hdr.startswith("boost/histogram"): + continue + yield hdr.replace("/", os.path.sep) # adapt the paths for Windows + + +included_headers = set() +unread_headers = set() +for hdr in get_headers(os.path.join(this_path, "odr_test.cpp")): + unread_headers.add(hdr) + +while unread_headers: + included_headers.update(unread_headers) + for hdr in tuple(unread_headers): # copy needed because unread_headers is modified + unread_headers.remove(hdr) + for hdr2 in get_headers(os.path.join(root_include_path, hdr)): + if hdr2 not in included_headers: + unread_headers.add(hdr2) + +diff = sorted(all_headers - set(included_headers)) + +if not diff: + sys.exit(0) + + +print("Header not included in odr_test.cpp:") +for fn in diff: + print(fn) + +sys.exit(1) diff --git a/src/boost/libs/histogram/test/deduction_guides_test.cpp b/src/boost/libs/histogram/test/deduction_guides_test.cpp new file mode 100644 index 000000000..2bd0f8bcb --- /dev/null +++ b/src/boost/libs/histogram/test/deduction_guides_test.cpp @@ -0,0 +1,145 @@ +// 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/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/accumulators/weighted_sum.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <tuple> +#include <type_traits> +#include <vector> +#include "std_ostream.hpp" +#include "throw_exception.hpp" + +using namespace boost::histogram; +namespace tr = axis::transform; + +// tests requires a C++17 compatible compiler + +#define TEST BOOST_TEST_TRAIT_SAME + +int main() { + using axis::null_type; + + { + using axis::regular; + BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0.0, 1.0)), + regular<double, tr::id, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0, 1)), regular<double, tr::id, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0.0f, 1.0f)), + regular<float, tr::id, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0, 1, 0)), regular<double, tr::id, int>); + BOOST_TEST_TRAIT_SAME(decltype(regular(1, 0.0f, 1.0f, "x")), + regular<float, tr::id, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(regular(tr::sqrt(), 1, 0, 1)), + regular<double, tr::sqrt, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(regular(tr::sqrt(), 1, 0.0f, 1.0f, "x")), + regular<float, tr::sqrt, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(regular(tr::sqrt(), 1, 0, 1, 0)), + regular<double, tr::sqrt, int>); + } + + { + using axis::integer; + BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2)), integer<int, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(integer(1l, 2l)), integer<int, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(integer(1.0, 2.0)), integer<double, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(integer(1.0f, 2.0f)), integer<float, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2, "foo")), integer<int, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(integer(1, 2, 0)), integer<int, int>); + } + + { + using axis::variable; + BOOST_TEST_TRAIT_SAME(decltype(variable{-1.0f, 1.0f}), variable<float, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(variable{-1, 0, 1, 2}), variable<double, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(variable{-1.0, 1.0}), variable<double, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 0, 1}, "foo")), + variable<double, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(variable({-1, 1}, 0)), variable<double, int>); + + BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<int>{{-1, 1}})), + variable<double, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<float>{{-1.0f, 1.0f}})), + variable<float, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<int>{{-1, 1}}, "foo")), + variable<double, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(variable(std::vector<int>{{-1, 1}}, 0)), + variable<double, int>); + } + + { + using axis::category; + BOOST_TEST_TRAIT_SAME(decltype(category{1, 2}), category<int, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(category{"x", "y"}), category<std::string, null_type>); + BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, "foo")), category<int, std::string>); + BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, 0)), category<int, int>); + } + + { + auto h = histogram(axis::regular(3, -1, 1), axis::integer(0, 4)); + BOOST_TEST_TRAIT_SAME(decltype(h), + histogram<std::tuple<axis::regular<double, tr::id, null_type>, + axis::integer<int, null_type>>>); + BOOST_TEST_EQ(h.axis(0), axis::regular(3, -1, 1)); + BOOST_TEST_EQ(h.axis(1), axis::integer(0, 4)); + } + + { + auto h = histogram(std::tuple(axis::regular(3, -1, 1), axis::integer(0, 4)), + weight_storage()); + BOOST_TEST_TRAIT_SAME(decltype(h), + histogram<std::tuple<axis::regular<double, tr::id, null_type>, + axis::integer<int, null_type>>, + weight_storage>); + BOOST_TEST_EQ(h.axis(0), axis::regular(3, -1, 1)); + BOOST_TEST_EQ(h.axis(1), axis::integer(0, 4)); + } + + { + auto a0 = axis::regular(5, 0, 5); + auto a1 = axis::regular(3, -1, 1); + auto axes = {a0, a1}; + auto h = histogram(axes); + BOOST_TEST_TRAIT_SAME( + decltype(h), histogram<std::vector<axis::regular<double, tr::id, null_type>>>); + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.axis(0), a0); + BOOST_TEST_EQ(h.axis(1), a1); + } + + { + auto a0 = axis::regular(5, 0, 5); + auto a1 = axis::regular(3, -1, 1); + auto axes = {a0, a1}; + auto h = histogram(axes, weight_storage()); + BOOST_TEST_TRAIT_SAME( + decltype(h), + histogram<std::vector<axis::regular<double, tr::id, null_type>>, weight_storage>); + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.axis(0), a0); + BOOST_TEST_EQ(h.axis(1), a1); + } + + { + auto a0 = axis::regular(5, 0, 5); + auto a1 = axis::regular(3, -1, 1); + auto axes = std::vector<decltype(a0)>{{a0, a1}}; + auto h = histogram(axes); + BOOST_TEST_TRAIT_SAME( + decltype(h), histogram<std::vector<axis::regular<double, tr::id, null_type>>>); + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.axis(0), a0); + BOOST_TEST_EQ(h.axis(1), a1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_accumulator_traits_test.cpp b/src/boost/libs/histogram/test/detail_accumulator_traits_test.cpp new file mode 100644 index 000000000..ea108ff0c --- /dev/null +++ b/src/boost/libs/histogram/test/detail_accumulator_traits_test.cpp @@ -0,0 +1,82 @@ +// Copyright 2019 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/detail/accumulator_traits.hpp> +#include <boost/histogram/weight.hpp> +#include <tuple> + +namespace dtl = boost::histogram::detail; + +int main() { + using boost::histogram::weight_type; + + struct A1 { + void operator()(); + }; + + BOOST_TEST_NOT(dtl::accumulator_traits<A1>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A1>::args, std::tuple<>); + + struct A2 { + void operator()(int, double); + }; + + BOOST_TEST_NOT(dtl::accumulator_traits<A2>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A2>::args, + std::tuple<int, double>); + + struct A3 { + void operator()(); + void operator()(weight_type<int>); + }; + + BOOST_TEST(dtl::accumulator_traits<A3>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A3>::args, std::tuple<>); + + struct A4 { + void operator()(int, double, char); + void operator()(weight_type<int>, int, double, char); + }; + + BOOST_TEST(dtl::accumulator_traits<A4>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A4>::args, + std::tuple<int, double, char>); + + struct A5 { + void operator()(const weight_type<int>, int); + }; + + BOOST_TEST(dtl::accumulator_traits<A5>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A5>::args, std::tuple<int>); + + struct A6 { + void operator()(const weight_type<int>&, const int); + }; + + BOOST_TEST(dtl::accumulator_traits<A6>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A6>::args, std::tuple<int>); + + struct A7 { + void operator()(weight_type<int>&&, int&&); + }; + + BOOST_TEST(dtl::accumulator_traits<A7>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<A7>::args, std::tuple<int&&>); + + struct B { + int operator+=(int); + }; + + BOOST_TEST(dtl::accumulator_traits<B>::weight_support); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits<B>::args, std::tuple<>); + + BOOST_TEST(dtl::accumulator_traits<int>::weight_support); + BOOST_TEST_TRAIT_SAME(dtl::accumulator_traits<int>::args, std::tuple<>); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_args_type_test.cpp b/src/boost/libs/histogram/test/detail_args_type_test.cpp new file mode 100644 index 000000000..d9e4b242a --- /dev/null +++ b/src/boost/libs/histogram/test/detail_args_type_test.cpp @@ -0,0 +1,31 @@ +// Copyright 2015-2019 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_trait.hpp> +#include <boost/histogram/detail/args_type.hpp> +#include <tuple> +#include "std_ostream.hpp" + +namespace dtl = boost::histogram::detail; + +struct Foo { + int f1(char); + int f2(long) const; + static int f3(char, int); + auto f4(char, int) { return 0; }; +}; + +int main() { + BOOST_TEST_TRAIT_SAME(dtl::args_type<decltype(&Foo::f1)>, std::tuple<char>); + BOOST_TEST_TRAIT_SAME(dtl::args_type<decltype(&Foo::f2)>, std::tuple<long>); + BOOST_TEST_TRAIT_SAME(dtl::args_type<decltype(&Foo::f3)>, std::tuple<char, int>); + BOOST_TEST_TRAIT_SAME(dtl::args_type<decltype(&Foo::f4)>, std::tuple<char, int>); + + BOOST_TEST_TRAIT_SAME(dtl::arg_type<decltype(&Foo::f3)>, char); + BOOST_TEST_TRAIT_SAME(dtl::arg_type<decltype(&Foo::f3), 1>, int); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_argument_traits_test.cpp b/src/boost/libs/histogram/test/detail_argument_traits_test.cpp new file mode 100644 index 000000000..23f740d08 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_argument_traits_test.cpp @@ -0,0 +1,61 @@ +// Copyright 2019 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/detail/argument_traits.hpp> +#include <boost/histogram/sample.hpp> +#include <boost/histogram/weight.hpp> + +namespace dtl = boost::histogram::detail; + +int main() { + using boost::histogram::sample; + using boost::histogram::weight; + + // is_weight + { + struct A {}; + using B = int; + BOOST_TEST_NOT(dtl::is_weight<A>::value); + BOOST_TEST_NOT(dtl::is_weight<B>::value); + BOOST_TEST_NOT(dtl::is_weight<decltype(sample(0))>::value); + BOOST_TEST(dtl::is_weight<decltype(weight(0))>::value); + } + + // is_sample + { + struct A {}; + using B = int; + BOOST_TEST_NOT(dtl::is_sample<A>::value); + BOOST_TEST_NOT(dtl::is_sample<B>::value); + BOOST_TEST_NOT(dtl::is_sample<decltype(weight(0))>::value); + BOOST_TEST(dtl::is_sample<decltype(sample(0))>::value); + BOOST_TEST(dtl::is_sample<decltype(sample(0, A{}))>::value); + } + + using T1 = dtl::argument_traits<int>; + using E1 = dtl::argument_traits_holder<1, 0, -1, -1, std::tuple<>>; + BOOST_TEST_TRAIT_SAME(T1, E1); + + using T2 = dtl::argument_traits<int, int>; + using E2 = dtl::argument_traits_holder<2, 0, -1, -1, std::tuple<>>; + BOOST_TEST_TRAIT_SAME(T2, E2); + + using T3 = dtl::argument_traits<decltype(weight(0)), int, int>; + using E3 = dtl::argument_traits_holder<2, 1, 0, -1, std::tuple<>>; + BOOST_TEST_TRAIT_SAME(T3, E3); + + using T4 = dtl::argument_traits<decltype(weight(0)), int, int, decltype(sample(0))>; + using E4 = dtl::argument_traits_holder<2, 1, 0, 3, std::tuple<int>>; + BOOST_TEST_TRAIT_SAME(T4, E4); + + using T5 = dtl::argument_traits<int, decltype(sample(0, 0.0))>; + using E5 = dtl::argument_traits_holder<1, 0, -1, 1, std::tuple<int, double>>; + BOOST_TEST_TRAIT_SAME(T5, E5); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_array_wrapper_serialization_test.cpp b/src/boost/libs/histogram/test/detail_array_wrapper_serialization_test.cpp new file mode 100644 index 000000000..edf3a97fa --- /dev/null +++ b/src/boost/libs/histogram/test/detail_array_wrapper_serialization_test.cpp @@ -0,0 +1,71 @@ +// Copyright 2019 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/archive/binary_iarchive.hpp> +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/detail/array_wrapper.hpp> +#include <sstream> +#include <vector> +#include "std_ostream.hpp" +#include "throw_exception.hpp" + +namespace dtl = boost::histogram::detail; +namespace ba = boost::archive; + +template <class T> +struct dummy_array_wrapper { + T* ptr; + std::size_t size; + template <class Archive> + void serialize(Archive& ar, unsigned /* version */) { + for (auto&& x : dtl::make_span(ptr, size)) ar& x; + } +}; + +template <class OArchive, class IArchive> +void run_tests() { + std::vector<int> v = {{1, 2, 3}}; + + std::stringstream os1; + { + OArchive oa(os1); + auto w = dtl::make_array_wrapper(v.data(), v.size()); + oa << w; + } + + std::ostringstream os2; + { + OArchive oa(os2); + auto w = dummy_array_wrapper<int>{v.data(), v.size()}; + oa << w; + } + + BOOST_TEST_EQ(os1.str(), os2.str()); + + std::vector<int> v2(3, 0); + { + IArchive ia(os1); + auto w = dtl::make_array_wrapper(v2.data(), v2.size()); + ia >> w; + } + + BOOST_TEST_EQ(v, v2); +} + +int main() { + BOOST_TEST(dtl::has_array_optimization<ba::binary_oarchive>::value); + BOOST_TEST(dtl::has_array_optimization<ba::binary_iarchive>::value); + BOOST_TEST_NOT(dtl::has_array_optimization<ba::text_oarchive>::value); + BOOST_TEST_NOT(dtl::has_array_optimization<ba::text_iarchive>::value); + + run_tests<ba::binary_oarchive, ba::binary_iarchive>(); + run_tests<ba::text_oarchive, ba::text_iarchive>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_axes_test.cpp b/src/boost/libs/histogram/test/detail_axes_test.cpp new file mode 100644 index 000000000..c16c762c7 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_axes_test.cpp @@ -0,0 +1,177 @@ +// 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 <array> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/detail/axes.hpp> +#include <limits> +#include <tuple> +#include <utility> +#include <vector> +#include "std_ostream.hpp" +#include "throw_exception.hpp" + +using namespace boost::histogram; + +int main() { + // dynamic axis_get with tuples + { + auto a1 = axis::integer<>(0, 1); + auto a2 = axis::integer<>(1, 2); + auto tup = std::make_tuple(a1, a2); + using E1 = axis::variant<axis::integer<>*>; + BOOST_TEST_TRAIT_SAME(decltype(detail::axis_get(tup, 0)), E1); + BOOST_TEST_EQ(detail::axis_get(tup, 0), a1); + BOOST_TEST_EQ(detail::axis_get(tup, 1), a2); + BOOST_TEST_NE(detail::axis_get(tup, 0), a2); + } + + // sequence equality + { + using R = axis::regular<>; + using I = axis::integer<>; + using V = axis::variable<>; + auto r = R(2, -1, 1); + auto i = I(-1, 1); + auto v = V{-1, 0, 1}; + + std::vector<axis::variant<R, I, V>> v1 = {r, i}; + std::vector<axis::variant<R, I>> v2 = {r, i}; + std::vector<axis::variant<R, I>> v3 = {i, r}; + std::vector<axis::variant<I, R>> v4 = {r, i}; + std::vector<axis::variant<R, I>> v5 = {r, r}; + std::vector<R> v6 = {r, r}; + + BOOST_TEST(detail::axes_equal(v1, v2)); + BOOST_TEST(detail::axes_equal(v1, v4)); + BOOST_TEST(detail::axes_equal(v5, v6)); + BOOST_TEST_NOT(detail::axes_equal(v1, v3)); + BOOST_TEST_NOT(detail::axes_equal(v2, v3)); + BOOST_TEST_NOT(detail::axes_equal(v3, v4)); + BOOST_TEST_NOT(detail::axes_equal(v1, v5)); + + auto t1 = std::make_tuple(r, i); + auto t2 = std::make_tuple(i, r); + auto t3 = std::make_tuple(v, i); + auto t4 = std::make_tuple(r, r); + + BOOST_TEST(detail::axes_equal(t1, v1)); + BOOST_TEST(detail::axes_equal(t1, v2)); + BOOST_TEST(detail::axes_equal(t1, v4)); + BOOST_TEST(detail::axes_equal(v1, t1)); + BOOST_TEST(detail::axes_equal(v2, t1)); + BOOST_TEST(detail::axes_equal(v4, t1)); + BOOST_TEST(detail::axes_equal(t2, v3)); + BOOST_TEST(detail::axes_equal(v3, t2)); + BOOST_TEST(detail::axes_equal(t4, v5)); + BOOST_TEST(detail::axes_equal(t4, v6)); + BOOST_TEST_NOT(detail::axes_equal(t1, t2)); + BOOST_TEST_NOT(detail::axes_equal(t2, t3)); + BOOST_TEST_NOT(detail::axes_equal(t1, v3)); + BOOST_TEST_NOT(detail::axes_equal(t1, v3)); + BOOST_TEST_NOT(detail::axes_equal(t3, v1)); + BOOST_TEST_NOT(detail::axes_equal(t3, v2)); + BOOST_TEST_NOT(detail::axes_equal(t3, v3)); + BOOST_TEST_NOT(detail::axes_equal(t3, v4)); + } + + // sequence assign + { + using R = axis::regular<>; + using I = axis::integer<>; + using V = axis::variable<>; + auto r = R(2, -1, 1); + auto i = I(-1, 1); + auto v = V{-1, 0, 1}; + + std::vector<axis::variant<R, V, I>> v1 = {r, i}; + std::vector<axis::variant<I, R>> v2; + std::vector<R> v3 = {r, r}; + + BOOST_TEST_NOT(detail::axes_equal(v2, v1)); + detail::axes_assign(v2, v1); + BOOST_TEST(detail::axes_equal(v2, v1)); + detail::axes_assign(v2, v3); + BOOST_TEST(detail::axes_equal(v2, v3)); + + auto t1 = std::make_tuple(r); + detail::axes_assign(v3, t1); + BOOST_TEST(detail::axes_equal(v3, t1)); + + auto t2 = std::make_tuple(r, i); + detail::axes_assign(v2, t2); + BOOST_TEST(detail::axes_equal(v2, t2)); + + auto t3 = std::make_tuple(R{3, -1, 1}, i); + BOOST_TEST_NOT(detail::axes_equal(t2, t3)); + detail::axes_assign(t2, t3); + BOOST_TEST(detail::axes_equal(t2, t3)); + } + + // axes_rank + { + std::tuple<int, int> a; + std::vector<int> b(3); + std::array<int, 4> c; + const std::tuple<int> d; + BOOST_TEST_EQ(detail::axes_rank(a), 2); + BOOST_TEST_EQ(detail::axes_rank(b), 3); + BOOST_TEST_EQ(detail::axes_rank(c), 4); + BOOST_TEST_EQ(detail::axes_rank(d), 1); + } + + // bincount overflow + { + auto v = std::vector<axis::integer<>>( + 100, axis::integer<>(0, (std::numeric_limits<int>::max)() - 2)); + BOOST_TEST_THROWS(detail::bincount(v), std::overflow_error); + } + + // has_growing_axis + { + struct growing { + auto update(int) { return std::make_pair(0, 0); } + }; + using T = growing; + using I = axis::integer<>; + + using A = std::tuple<I, T>; + using B = std::vector<T>; + using C = std::vector<axis::variant<I, T>>; + using D = std::tuple<I>; + using E = std::vector<I>; + using F = std::vector<axis::variant<I>>; + + BOOST_TEST_TRAIT_TRUE((detail::has_growing_axis<A>)); + BOOST_TEST_TRAIT_TRUE((detail::has_growing_axis<B>)); + BOOST_TEST_TRAIT_TRUE((detail::has_growing_axis<C>)); + BOOST_TEST_TRAIT_FALSE((detail::has_growing_axis<D>)); + BOOST_TEST_TRAIT_FALSE((detail::has_growing_axis<E>)); + BOOST_TEST_TRAIT_FALSE((detail::has_growing_axis<F>)); + } + + // value_types + { + using R = axis::regular<float>; + using I = axis::integer<int>; + using CI = axis::category<int>; + using CS = axis::category<std::string>; + using A = std::vector<axis::variant<R, I, CS>>; + using B = std::vector<axis::variant<CS, I, CI, R>>; + using C = std::tuple<I, R, CS>; + using D = std::tuple<CS, I, CI, R>; + using Expected = boost::mp11::mp_list<int, float, std::string>; + BOOST_TEST_TRAIT_SAME(detail::value_types<A>, Expected); + BOOST_TEST_TRAIT_SAME(detail::value_types<B>, Expected); + BOOST_TEST_TRAIT_SAME(detail::value_types<C>, Expected); + BOOST_TEST_TRAIT_SAME(detail::value_types<D>, Expected); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_convert_integer_test.cpp b/src/boost/libs/histogram/test/detail_convert_integer_test.cpp new file mode 100644 index 000000000..10e5f1054 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_convert_integer_test.cpp @@ -0,0 +1,22 @@ +// 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/core/lightweight_test_trait.hpp> +#include <boost/histogram/detail/convert_integer.hpp> +#include "std_ostream.hpp" + +using namespace boost::histogram::detail; + +int main() { + + BOOST_TEST_TRAIT_SAME(convert_integer<char, float>, float); + BOOST_TEST_TRAIT_SAME(convert_integer<int, float>, float); + BOOST_TEST_TRAIT_SAME(convert_integer<double, float>, double); + BOOST_TEST_TRAIT_SAME(convert_integer<float, double>, float); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_detect_test.cpp b/src/boost/libs/histogram/test/detail_detect_test.cpp new file mode 100644 index 000000000..1304ef447 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_detect_test.cpp @@ -0,0 +1,299 @@ +// 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 <array> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.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/detect.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <deque> +#include <initializer_list> +#include <map> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_allocator.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::detail; + +int main() { + // has_method_value* + { + struct A {}; + struct B { + A value(int) const { return {}; } + }; + struct C { + char value(int) const { return 0; } + }; + + BOOST_TEST_TRAIT_FALSE((has_method_value<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_value<B>)); + BOOST_TEST_TRAIT_TRUE((has_method_value<C>)); + } + + // has_method_options + { + struct A {}; + struct B { + void options() {} + }; + + BOOST_TEST_TRAIT_FALSE((has_method_options<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_options<B>)); + } + + // 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>)); + } + + // has_method_update + { + struct A {}; + struct B { + void update(int) {} + }; + using C = axis::integer<int, axis::null_type, use_default>; + + BOOST_TEST_TRAIT_FALSE((has_method_update<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_update<B>)); + BOOST_TEST_TRAIT_TRUE((has_method_update<C>)); + } + + // has_method_resize + { + struct A {}; + using B = std::vector<int>; + using C = std::map<int, int>; + + BOOST_TEST_TRAIT_FALSE((has_method_resize<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_resize<B>)); + BOOST_TEST_TRAIT_FALSE((has_method_resize<C>)); + } + + // has_method_size + { + struct A {}; + using B = std::vector<int>; + using C = std::map<int, int>; + + BOOST_TEST_TRAIT_FALSE((has_method_size<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_size<B>)); + BOOST_TEST_TRAIT_TRUE((has_method_size<C>)); + } + + // has_method_clear + { + struct A {}; + using B = std::vector<int>; + using C = std::map<int, int>; + using D = std::array<int, 10>; + + BOOST_TEST_TRAIT_FALSE((has_method_clear<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_clear<B>)); + BOOST_TEST_TRAIT_TRUE((has_method_clear<C>)); + BOOST_TEST_TRAIT_FALSE((has_method_clear<D>)); + } + + // has_allocator + { + struct A {}; + using B = std::vector<int>; + using C = std::map<int, int>; + using D = std::array<int, 10>; + + BOOST_TEST_TRAIT_FALSE((has_method_clear<A>)); + BOOST_TEST_TRAIT_TRUE((has_method_clear<B>)); + BOOST_TEST_TRAIT_TRUE((has_method_clear<C>)); + BOOST_TEST_TRAIT_FALSE((has_method_clear<D>)); + } + + // is_storage + { + struct A {}; + using B = std::vector<int>; + using C = unlimited_storage<>; + + BOOST_TEST_TRAIT_FALSE((is_storage<A>)); + BOOST_TEST_TRAIT_FALSE((is_storage<B>)); + BOOST_TEST_TRAIT_TRUE((is_storage<C>)); + } + + // is_indexable + { + struct A {}; + using B = std::vector<int>; + using C = std::map<int, int>; + using D = std::map<A, int>; + + BOOST_TEST_TRAIT_FALSE((is_indexable<A>)); + BOOST_TEST_TRAIT_TRUE((is_indexable<B>)); + BOOST_TEST_TRAIT_TRUE((is_indexable<C>)); + BOOST_TEST_TRAIT_FALSE((is_indexable<D>)); + } + + // is_transform + { + struct A {}; + struct B { + double forward(A); + A inverse(double); + }; + + BOOST_TEST_TRAIT_FALSE((is_transform<A, double>)); + BOOST_TEST_TRAIT_TRUE((is_transform<B, A>)); + BOOST_TEST_TRAIT_TRUE((is_transform<axis::transform::id, double>)); + } + + // is_vector_like + { + struct A {}; + using B = std::vector<int>; + using C = std::array<int, 10>; + using D = std::map<unsigned, int>; + using E = std::deque<int>; + BOOST_TEST_TRAIT_FALSE((is_vector_like<A>)); + BOOST_TEST_TRAIT_TRUE((is_vector_like<B>)); + BOOST_TEST_TRAIT_FALSE((is_vector_like<C>)); + BOOST_TEST_TRAIT_FALSE((is_vector_like<D>)); + BOOST_TEST_TRAIT_TRUE((is_vector_like<E>)); + } + + // is_array_like + { + struct A {}; + using B = std::vector<int>; + using C = std::array<int, 10>; + using D = std::map<unsigned, int>; + BOOST_TEST_TRAIT_FALSE((is_array_like<A>)); + BOOST_TEST_TRAIT_FALSE((is_array_like<B>)); + BOOST_TEST_TRAIT_TRUE((is_array_like<C>)); + BOOST_TEST_TRAIT_FALSE((is_array_like<D>)); + } + + // is_map_like + { + struct A {}; + using B = std::vector<int>; + using C = std::array<int, 10>; + using D = std::map<unsigned, int>; + using E = std::unordered_map<unsigned, int>; + BOOST_TEST_TRAIT_FALSE((is_map_like<A>)); + BOOST_TEST_TRAIT_FALSE((is_map_like<B>)); + BOOST_TEST_TRAIT_FALSE((is_map_like<C>)); + BOOST_TEST_TRAIT_TRUE((is_map_like<D>)); + BOOST_TEST_TRAIT_TRUE((is_map_like<E>)); + } + + // is_axis + { + struct A {}; + struct B { + int index(double); + int size() const; + }; + struct C { + int index(double); + }; + struct D { + int size(); + }; + using E = axis::variant<axis::regular<>>; + + 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>)); + BOOST_TEST_TRAIT_FALSE((is_axis<E>)); + } + + // 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<axis::variant<>>)); + BOOST_TEST_TRAIT_TRUE((is_axis_variant<axis::variant<axis::regular<>>>)); + } + + // is_sequence_of_axis + { + using A = std::vector<axis::regular<>>; + using B = std::vector<axis::variant<axis::regular<>>>; + using C = std::vector<int>; + auto v = std::vector<axis::variant<axis::regular<>, axis::integer<>>>(); + BOOST_TEST_TRAIT_TRUE((is_sequence_of_any_axis<A>)); + BOOST_TEST_TRAIT_TRUE((is_sequence_of_axis<A>)); + BOOST_TEST_TRAIT_FALSE((is_sequence_of_axis_variant<A>)); + BOOST_TEST_TRAIT_TRUE((is_sequence_of_any_axis<B>)); + BOOST_TEST_TRAIT_TRUE((is_sequence_of_axis_variant<B>)); + BOOST_TEST_TRAIT_FALSE((is_sequence_of_axis<B>)); + BOOST_TEST_TRAIT_FALSE((is_sequence_of_any_axis<C>)); + BOOST_TEST_TRAIT_TRUE((is_sequence_of_any_axis<decltype(v)>)); + } + + // has_operator_equal + { + struct A {}; + struct B { + bool operator==(const B&) const { return true; } + }; + + BOOST_TEST_TRAIT_FALSE((has_operator_equal<A, A>)); + BOOST_TEST_TRAIT_FALSE((has_operator_equal<B, A>)); + BOOST_TEST_TRAIT_TRUE((has_operator_equal<B, B>)); + BOOST_TEST_TRAIT_TRUE((has_operator_equal<const B&, const B&>)); + } + + // has_operator_radd + { + struct A {}; + struct B { + B& operator+=(const B&) { return *this; } + }; + + BOOST_TEST_TRAIT_FALSE((has_operator_radd<A, A>)); + BOOST_TEST_TRAIT_FALSE((has_operator_radd<B, A>)); + BOOST_TEST_TRAIT_TRUE((has_operator_radd<B, B>)); + BOOST_TEST_TRAIT_TRUE((has_operator_radd<B&, const B&>)); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_iterator_adaptor_test.cpp b/src/boost/libs/histogram/test/detail_iterator_adaptor_test.cpp new file mode 100644 index 000000000..c7a3303db --- /dev/null +++ b/src/boost/libs/histogram/test/detail_iterator_adaptor_test.cpp @@ -0,0 +1,191 @@ +// (C) Copyright Thomas Witt 2003. +// (C) Copyright Hans Dembinski 2019. + +// 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) + +// See http://www.boost.org for most recent version including documentation. + +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/detail/detect.hpp> +#include <boost/histogram/detail/iterator_adaptor.hpp> +#include "std_ostream.hpp" +#include "utility_iterator.hpp" + +#include <deque> +#include <set> +#include <vector> + +using namespace boost::histogram; +using boost::histogram::detail::iterator_adaptor; + +typedef std::deque<int> storage; +typedef std::deque<int*> pointer_deque; +typedef std::set<storage::iterator> iterator_set; + +template <class T> +struct foo; + +void blah(int) {} + +struct my_gen { + typedef int result_type; + my_gen() : n(0) {} + int operator()() { return ++n; } + int n; +}; + +template <class V> +struct ptr_iterator : iterator_adaptor<ptr_iterator<V>, V*> { +private: + typedef iterator_adaptor<ptr_iterator<V>, V*> super_t; + +public: + using base_type = typename super_t::base_type; + + ptr_iterator() {} + ptr_iterator(V* d) : super_t(d) {} + + template <class V2, class = detail::requires_convertible<V2*, V*>> + ptr_iterator(const ptr_iterator<V2>& x) : super_t(x.base()) {} + + V& operator*() const { return *(this->base()); } +}; + +template <class Iter> +struct constant_iterator + : iterator_adaptor<constant_iterator<Iter>, Iter, + typename std::iterator_traits<Iter>::value_type const&> { + typedef iterator_adaptor<constant_iterator<Iter>, Iter, + typename std::iterator_traits<Iter>::value_type const&> + base_t; + + constant_iterator() {} + constant_iterator(Iter it) : base_t(it) {} + + typename std::iterator_traits<Iter>::value_type const& operator*() const { + this->base().operator*(); + } +}; + +struct mutable_it : iterator_adaptor<mutable_it, int*> { + typedef iterator_adaptor<mutable_it, int*> super_t; + + mutable_it(); + explicit mutable_it(int* p) : super_t(p) {} + + bool equal(mutable_it const& rhs) const { return this->base() == rhs.base(); } +}; + +struct constant_it : iterator_adaptor<constant_it, int const*> { + typedef iterator_adaptor<constant_it, int const*> super_t; + + constant_it(); + explicit constant_it(int* p) : super_t(p) {} + constant_it(mutable_it const& x) : super_t(x.base()) {} + + bool equal(constant_it const& rhs) const { return this->base() == rhs.base(); } +}; + +template <class T> +class static_object { +public: + static T& get() { + static char d[sizeof(T)]; + return *reinterpret_cast<T*>(d); + } +}; + +int main() { + dummyT array[] = {dummyT(0), dummyT(1), dummyT(2), dummyT(3), dummyT(4), dummyT(5)}; + const int N = sizeof(array) / sizeof(dummyT); + + // Test the iterator_adaptor + { + ptr_iterator<dummyT> i(array); + using reference = typename std::iterator_traits<ptr_iterator<dummyT>>::reference; + using pointer = typename std::iterator_traits<ptr_iterator<dummyT>>::pointer; + BOOST_TEST_TRAIT_SAME(reference, dummyT&); + BOOST_TEST_TRAIT_SAME(pointer, dummyT*); + + random_access_iterator_test(i, N, array); + + ptr_iterator<const dummyT> j(array); + random_access_iterator_test(j, N, array); + const_nonconst_iterator_test(i, ++j); + } + + // Test the iterator_traits + { + // Test computation of defaults + typedef ptr_iterator<int> Iter1; + // don't use std::iterator_traits here to avoid VC++ problems + BOOST_TEST_TRAIT_SAME(Iter1::value_type, int); + BOOST_TEST_TRAIT_SAME(Iter1::reference, int&); + BOOST_TEST_TRAIT_SAME(Iter1::pointer, int*); + BOOST_TEST_TRAIT_SAME(Iter1::difference_type, std::ptrdiff_t); + } + + { + // Test computation of default when the Value is const + typedef ptr_iterator<int const> Iter1; + BOOST_TEST_TRAIT_SAME(Iter1::value_type, int); + BOOST_TEST_TRAIT_SAME(Iter1::reference, const int&); + BOOST_TEST_TRAIT_SAME(Iter1::pointer, int const*); + } + + { + // Test constant iterator idiom + typedef ptr_iterator<int> BaseIter; + typedef constant_iterator<BaseIter> Iter; + + BOOST_TEST_TRAIT_SAME(Iter::value_type, int); + BOOST_TEST_TRAIT_SAME(Iter::reference, int const&); + BOOST_TEST_TRAIT_SAME(Iter::pointer, int const*); + } + + // Test the iterator_adaptor + { + ptr_iterator<dummyT> i(array); + random_access_iterator_test(i, N, array); + + ptr_iterator<const dummyT> j(array); + random_access_iterator_test(j, N, array); + const_nonconst_iterator_test(i, ++j); + } + + // check that base_type is correct + { + // Test constant iterator idiom + typedef ptr_iterator<int> BaseIter; + + BOOST_TEST_TRAIT_SAME(BaseIter::base_type, int*); + BOOST_TEST_TRAIT_SAME(constant_iterator<BaseIter>::base_type, BaseIter); + } + + { + int data[] = {49, 77}; + + mutable_it i(data); + constant_it j(data + 1); + BOOST_TEST(i < j); + BOOST_TEST(j > i); + BOOST_TEST(i <= j); + BOOST_TEST(j >= i); + BOOST_TEST(j - i == 1); + BOOST_TEST(i - j == -1); + + constant_it k = i; + + BOOST_TEST(!(i < k)); + BOOST_TEST(!(k > i)); + BOOST_TEST(i <= k); + BOOST_TEST(k >= i); + BOOST_TEST(k - i == 0); + BOOST_TEST(i - k == 0); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_large_int_test.cpp b/src/boost/libs/histogram/test/detail_large_int_test.cpp new file mode 100644 index 000000000..7e50fcef9 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_large_int_test.cpp @@ -0,0 +1,206 @@ +// Copyright 2018-2019 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/detail/large_int.hpp> +#include <cstdint> +#include <iosfwd> +#include <limits> +#include "std_ostream.hpp" + +namespace boost { +namespace histogram { +namespace detail { +template <class Allocator> +std::ostream& operator<<(std::ostream& os, const large_int<Allocator>& x) { + os << "large_int" << x.data; + return os; +} +} // namespace detail +} // namespace histogram +} // namespace boost + +using namespace boost::histogram; + +using large_int = detail::large_int<std::allocator<std::uint64_t>>; + +template <class... Ts> +auto make_large_int(Ts... ts) { + large_int r; + r.data = {static_cast<uint64_t>(ts)...}; + return r; +} + +int main() { + // low-level tools + { + uint8_t c = 0; + BOOST_TEST_EQ(detail::safe_increment(c), true); + BOOST_TEST_EQ(c, 1); + c = 255; + BOOST_TEST_EQ(detail::safe_increment(c), false); + BOOST_TEST_EQ(c, 255); + c = 0; + BOOST_TEST_EQ(detail::safe_radd(c, 255u), true); + BOOST_TEST_EQ(c, 255); + c = 1; + BOOST_TEST_EQ(detail::safe_radd(c, 255u), false); + BOOST_TEST_EQ(c, 1); + c = 255; + BOOST_TEST_EQ(detail::safe_radd(c, 1u), false); + BOOST_TEST_EQ(c, 255); + } + + const auto vmax = (std::numeric_limits<std::uint64_t>::max)(); + + // ctors, assign + { + large_int a(42); + large_int b(a); + BOOST_TEST_EQ(b.data.front(), 42); + large_int c(std::move(b)); + BOOST_TEST_EQ(c.data.front(), 42); + large_int d, e; + d = a; + BOOST_TEST_EQ(d.data.front(), 42); + e = std::move(c); + BOOST_TEST_EQ(e.data.front(), 42); + } + + // comparison + { + BOOST_TEST_EQ(large_int(), 0u); + BOOST_TEST_EQ(large_int(1u), 1u); + BOOST_TEST_EQ(large_int(1u), 1.0); + BOOST_TEST_EQ(large_int(1u), large_int(1u)); + BOOST_TEST_NE(large_int(1u), 2u); + BOOST_TEST_NE(large_int(1u), 2.0); + BOOST_TEST_NE(large_int(1u), 2.0f); + BOOST_TEST_NE(large_int(1u), large_int(2u)); + BOOST_TEST_LT(large_int(1u), 2u); + BOOST_TEST_LT(large_int(1u), 2.0); + BOOST_TEST_LT(large_int(1u), 2.0f); + BOOST_TEST_LT(large_int(1u), large_int(2u)); + BOOST_TEST_LE(large_int(1u), 2u); + BOOST_TEST_LE(large_int(1u), 2.0); + BOOST_TEST_LE(large_int(1u), 2.0f); + BOOST_TEST_LE(large_int(1u), large_int(2u)); + BOOST_TEST_LE(large_int(1u), 1u); + BOOST_TEST_GT(large_int(1u), 0u); + BOOST_TEST_GT(large_int(1u), 0.0); + BOOST_TEST_GT(large_int(1u), 0.0f); + BOOST_TEST_GT(large_int(1u), large_int(0u)); + BOOST_TEST_GE(large_int(1u), 0u); + BOOST_TEST_GE(large_int(1u), 0.0); + BOOST_TEST_GE(large_int(1u), 0.0f); + BOOST_TEST_GE(large_int(1u), 1u); + BOOST_TEST_GE(large_int(1u), large_int(0u)); + BOOST_TEST_NOT(large_int(1u) < large_int(1u)); + BOOST_TEST_NOT(large_int(1u) > large_int(1u)); + + BOOST_TEST_GT(1, large_int()); + BOOST_TEST_LT(-1, large_int()); + BOOST_TEST_GE(1, large_int()); + BOOST_TEST_LE(-1, large_int()); + BOOST_TEST_NE(1, large_int()); + + constexpr auto nan = std::numeric_limits<double>::quiet_NaN(); + BOOST_TEST_NOT(large_int(1u) < nan); + BOOST_TEST_NOT(large_int(1u) > nan); + BOOST_TEST_NOT(large_int(1u) == nan); + BOOST_TEST(large_int(1u) != nan); // same behavior as int compared to nan + BOOST_TEST_NOT(large_int(1u) <= nan); + BOOST_TEST_NOT(large_int(1u) >= nan); + + BOOST_TEST_NOT(nan < large_int(1u)); + BOOST_TEST_NOT(nan > large_int(1u)); + BOOST_TEST_NOT(nan == large_int(1u)); + BOOST_TEST(nan != large_int(1u)); // same behavior as int compared to nan + BOOST_TEST_NOT(nan <= large_int(1u)); + BOOST_TEST_NOT(nan >= large_int(1u)); + } + + // increment + { + auto a = large_int(); + ++a; + BOOST_TEST_EQ(a.data.size(), 1); + BOOST_TEST_EQ(a.data[0], 1); + ++a; + BOOST_TEST_EQ(a.data[0], 2); + a = vmax; + BOOST_TEST_EQ(a, vmax); + BOOST_TEST_EQ(a, static_cast<double>(vmax)); + ++a; + BOOST_TEST_EQ(a, make_large_int(0, 1)); + ++a; + BOOST_TEST_EQ(a, make_large_int(1, 1)); + a += a; + BOOST_TEST_EQ(a, make_large_int(2, 2)); + BOOST_TEST_EQ(a, 2 * static_cast<double>(vmax) + 2); + + // carry once A + a.data[0] = vmax; + a.data[1] = 1; + ++a; + BOOST_TEST_EQ(a, make_large_int(0, 2)); + // carry once B + a.data[0] = vmax; + a.data[1] = 1; + a += 1; + BOOST_TEST_EQ(a, make_large_int(0, 2)); + // carry once C + a.data[0] = vmax; + a.data[1] = 1; + a += make_large_int(1, 1); + BOOST_TEST_EQ(a, make_large_int(0, 3)); + + a.data[0] = vmax - 1; + a.data[1] = vmax; + ++a; + BOOST_TEST_EQ(a, make_large_int(vmax, vmax)); + + // carry two times A + ++a; + BOOST_TEST_EQ(a, make_large_int(0, 0, 1)); + // carry two times B + a = make_large_int(vmax, vmax); + a += 1; + BOOST_TEST_EQ(a, make_large_int(0, 0, 1)); + // carry two times C + a = make_large_int(vmax, vmax); + a += large_int(1); + BOOST_TEST_EQ(a, make_large_int(0, 0, 1)); + + // carry and enlarge + a = make_large_int(vmax, vmax); + a += a; + BOOST_TEST_EQ(a, make_large_int(vmax - 1, vmax, 1)); + + // add smaller to larger + a = make_large_int(1, 1, 1); + a += make_large_int(1, 1); + BOOST_TEST_EQ(a, make_large_int(2, 2, 1)); + + // add larger to smaller + a = make_large_int(1, 1); + a += make_large_int(1, 1, 1); + BOOST_TEST_EQ(a, make_large_int(2, 2, 1)); + + a = large_int(1); + auto b = 1.0; + BOOST_TEST_EQ(a, b); + for (unsigned i = 0; i < 80; ++i) { + b += b; + BOOST_TEST_NE(a, b); + a += a; + BOOST_TEST_EQ(a, b); + } + BOOST_TEST_GT(a.data.size(), 1u); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_limits_test.cpp b/src/boost/libs/histogram/test/detail_limits_test.cpp new file mode 100644 index 000000000..dc0d0417a --- /dev/null +++ b/src/boost/libs/histogram/test/detail_limits_test.cpp @@ -0,0 +1,24 @@ +// 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/detail/limits.hpp> +#include <limits> + +using namespace boost::histogram::detail; + +int main() { + + BOOST_TEST_EQ(lowest<int>(), (std::numeric_limits<int>::min)()); + BOOST_TEST_EQ(lowest<float>(), -std::numeric_limits<float>::infinity()); + BOOST_TEST_EQ(lowest<double>(), -std::numeric_limits<double>::infinity()); + + BOOST_TEST_EQ(highest<int>(), (std::numeric_limits<int>::max)()); + BOOST_TEST_EQ(highest<float>(), std::numeric_limits<float>::infinity()); + BOOST_TEST_EQ(highest<double>(), std::numeric_limits<double>::infinity()); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_make_default_test.cpp b/src/boost/libs/histogram/test/detail_make_default_test.cpp new file mode 100644 index 000000000..3488cf281 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_make_default_test.cpp @@ -0,0 +1,57 @@ +// 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/detail/make_default.hpp> +#include <new> +#include <vector> + +using namespace boost::histogram::detail; + +template <class T> +struct allocator_with_state { + using value_type = T; + + allocator_with_state(int s = 0) : state(s) {} + + template <class U> + allocator_with_state(const allocator_with_state<U>& o) : state(o.state) {} + + value_type* allocate(std::size_t n) { + return static_cast<value_type*>(::operator new(n * sizeof(T))); + } + void deallocate(value_type* ptr, std::size_t) { + ::operator delete(static_cast<void*>(ptr)); + } + + template <class U> + bool operator==(const allocator_with_state<U>&) const { + return true; + } + + template <class U> + bool operator!=(const allocator_with_state<U>&) const { + return false; + } + + int state; +}; + +int main() { + + using V = std::vector<int, allocator_with_state<int>>; + V a(3, 0, allocator_with_state<int>{42}); + V b = make_default(a); + V c; + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(b.size(), 0); + BOOST_TEST_EQ(c.size(), 0); + BOOST_TEST_EQ(a.get_allocator().state, 42); + BOOST_TEST_EQ(b.get_allocator().state, 42); + BOOST_TEST_EQ(c.get_allocator().state, 0); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_misc_test.cpp b/src/boost/libs/histogram/test/detail_misc_test.cpp new file mode 100644 index 000000000..abebe783d --- /dev/null +++ b/src/boost/libs/histogram/test/detail_misc_test.cpp @@ -0,0 +1,88 @@ +// Copyright 2015-2019 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/accumulators/weighted_sum.hpp> +#include <boost/histogram/detail/common_type.hpp> +#include <boost/histogram/detail/counting_streambuf.hpp> +#include <boost/histogram/detail/non_member_container_access.hpp> +#include <boost/histogram/fwd.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <ostream> +#include "std_ostream.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::literals; +namespace dtl = boost::histogram::detail; + +int main() { + // literals + { + BOOST_TEST_TRAIT_SAME(std::integral_constant<unsigned, 0>, decltype(0_c)); + BOOST_TEST_TRAIT_SAME(std::integral_constant<unsigned, 3>, decltype(3_c)); + BOOST_TEST_EQ(decltype(10_c)::value, 10); + BOOST_TEST_EQ(decltype(213_c)::value, 213); + } + + // common_storage + { + BOOST_TEST_TRAIT_SAME(dtl::common_storage<unlimited_storage<>, unlimited_storage<>>, + unlimited_storage<>); + BOOST_TEST_TRAIT_SAME( + dtl::common_storage<dense_storage<double>, dense_storage<double>>, + dense_storage<double>); + BOOST_TEST_TRAIT_SAME(dtl::common_storage<dense_storage<int>, dense_storage<double>>, + dense_storage<double>); + BOOST_TEST_TRAIT_SAME(dtl::common_storage<dense_storage<double>, dense_storage<int>>, + dense_storage<double>); + BOOST_TEST_TRAIT_SAME(dtl::common_storage<dense_storage<double>, unlimited_storage<>>, + dense_storage<double>); + BOOST_TEST_TRAIT_SAME(dtl::common_storage<dense_storage<int>, unlimited_storage<>>, + unlimited_storage<>); + BOOST_TEST_TRAIT_SAME(dtl::common_storage<dense_storage<double>, weight_storage>, + weight_storage); + } + + // size & data + { + char a[4] = {1, 2, 3, 4}; + BOOST_TEST_EQ(dtl::size(a), 4u); + BOOST_TEST_EQ(dtl::data(a), a); + auto b = {1, 2}; + BOOST_TEST_EQ(dtl::size(b), 2u); + BOOST_TEST_EQ(dtl::data(b), b.begin()); + struct C { + unsigned size() const { return 3; } + int* data() { return buf; } + const int* data() const { return buf; } + int buf[1]; + } c; + BOOST_TEST_EQ(dtl::size(c), 3u); + BOOST_TEST_EQ(dtl::data(c), c.buf); + BOOST_TEST_EQ(dtl::data(static_cast<const C&>(c)), c.buf); + struct { + int size() const { return 5; } + } d; + BOOST_TEST_EQ(dtl::size(d), 5u); + } + + // counting_streambuf + { + dtl::counting_streambuf<char> cbuf; + std::ostream os(&cbuf); + os.put('x'); + BOOST_TEST_EQ(cbuf.count, 1); + os << 12; + BOOST_TEST_EQ(cbuf.count, 3); + os << "123"; + BOOST_TEST_EQ(cbuf.count, 6); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_operators_test.cpp b/src/boost/libs/histogram/test/detail_operators_test.cpp new file mode 100644 index 000000000..9fee2b338 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_operators_test.cpp @@ -0,0 +1,143 @@ +// Copyright 2019 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/detail/operators.hpp> +#include <limits> +#include <ostream> + +using namespace boost::histogram::detail; + +struct TotallyOrdered : totally_ordered<TotallyOrdered, TotallyOrdered, void> { + TotallyOrdered(int i) : x(i) {} + + bool operator<(const TotallyOrdered& o) const noexcept { return x < o.x; } + bool operator==(const TotallyOrdered& o) const noexcept { return x == o.x; } + bool operator<(const int& o) const noexcept { return x < o; } + bool operator>(const int& o) const noexcept { return x > o; } + bool operator==(const int& o) const noexcept { return x == o; } + + int x; +}; + +std::ostream& operator<<(std::ostream& os, const TotallyOrdered& t) { + os << t.x; + return os; +} + +struct PartiallyOrdered : partially_ordered<PartiallyOrdered, PartiallyOrdered, void> { + PartiallyOrdered(double i) : x(i) {} + + bool operator<(const PartiallyOrdered& o) const noexcept { return x < o.x; } + bool operator==(const PartiallyOrdered& o) const noexcept { return x == o.x; } + bool operator<(const double& o) const noexcept { return x < o; } + bool operator>(const double& o) const noexcept { return x > o; } + bool operator==(const double& o) const noexcept { return x == o; } + + double x; +}; + +std::ostream& operator<<(std::ostream& os, const PartiallyOrdered& t) { + os << t.x; + return os; +} + +template <class T, class U> +void test_nan(std::false_type) {} + +template <class T, class U> +void test_nan(std::true_type) { + const U nan = std::numeric_limits<U>::quiet_NaN(); + + // IEEE 754: Any comparison with nan is false, except != which is true. + // But some of the following tests fail on MSVC: + // BOOST_TEST_NOT(nan < nan); // true, should be false + // BOOST_TEST_NOT(nan > nan); + // BOOST_TEST_NOT(nan == nan); + // BOOST_TEST_NOT(nan <= nan); // true, should be false + // BOOST_TEST_NOT(nan >= nan); + // BOOST_TEST(nan != nan); + // Probably related: + // https://developercommunity.visualstudio.com/content/problem/445462/nan-nan-is-constant-folded-to-true-but-should-prob.html + // The tests below don't fail probably because constant-folding doesn't happen. + + BOOST_TEST_NOT(T(1.0) < T(nan)); + BOOST_TEST_NOT(T(1.0) > T(nan)); + BOOST_TEST_NOT(T(1.0) == T(nan)); + BOOST_TEST_NOT(T(1.0) <= T(nan)); + BOOST_TEST_NOT(T(1.0) >= T(nan)); + BOOST_TEST(T(1.0) != T(nan)); + + BOOST_TEST_NOT(T(nan) < 1.0); + BOOST_TEST_NOT(T(nan) > 1.0); + BOOST_TEST_NOT(T(nan) == 1.0); + BOOST_TEST_NOT(T(nan) <= 1.0); + BOOST_TEST_NOT(T(nan) >= 1.0); + BOOST_TEST(T(nan) != 1.0); + + BOOST_TEST_NOT(1.0 < T(nan)); + BOOST_TEST_NOT(1.0 > T(nan)); + BOOST_TEST_NOT(1.0 == T(nan)); + BOOST_TEST_NOT(1.0 <= T(nan)); + BOOST_TEST_NOT(1.0 >= T(nan)); + BOOST_TEST(1.0 != T(nan)); + + BOOST_TEST_NOT(T(nan) < T(nan)); + BOOST_TEST_NOT(T(nan) > T(nan)); + BOOST_TEST_NOT(T(nan) == T(nan)); + BOOST_TEST_NOT(T(nan) <= T(nan)); + BOOST_TEST_NOT(T(nan) >= T(nan)); + BOOST_TEST(T(nan) != T(nan)); + + BOOST_TEST_NOT(T(nan) < nan); + BOOST_TEST_NOT(T(nan) > nan); + BOOST_TEST_NOT(T(nan) == nan); + BOOST_TEST_NOT(T(nan) <= nan); + BOOST_TEST_NOT(T(nan) >= nan); + BOOST_TEST(T(nan) != nan); + + BOOST_TEST_NOT(nan < T(nan)); + BOOST_TEST_NOT(nan > T(nan)); + BOOST_TEST_NOT(nan == T(nan)); + BOOST_TEST_NOT(nan <= T(nan)); + BOOST_TEST_NOT(nan >= T(nan)); + BOOST_TEST(nan != T(nan)); +} + +template <class T, class U> +void test() { + T x{1}; + U e{1}, l{0}, u{2}; + BOOST_TEST_EQ(x, e); + BOOST_TEST_NE(x, l); + BOOST_TEST_LT(x, u); + BOOST_TEST_GT(x, l); + BOOST_TEST_LE(x, e); + BOOST_TEST_LE(x, u); + BOOST_TEST_GE(x, e); + BOOST_TEST_GE(x, l); + + BOOST_TEST_EQ(e, x); + BOOST_TEST_NE(l, x); + BOOST_TEST_LT(l, x); + BOOST_TEST_GT(u, x); + BOOST_TEST_LE(e, x); + BOOST_TEST_LE(l, x); + BOOST_TEST_GE(e, x); + BOOST_TEST_GE(u, x); + + test_nan<T, U>(std::is_floating_point<U>{}); +} + +int main() { + test<TotallyOrdered, TotallyOrdered>(); + test<TotallyOrdered, int>(); + test<PartiallyOrdered, PartiallyOrdered>(); + test<PartiallyOrdered, double>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_relaxed_equal_test.cpp b/src/boost/libs/histogram/test/detail_relaxed_equal_test.cpp new file mode 100644 index 000000000..bba643e85 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_relaxed_equal_test.cpp @@ -0,0 +1,27 @@ +// 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/core/lightweight_test_trait.hpp> +#include <boost/histogram/detail/relaxed_equal.hpp> +#include "std_ostream.hpp" + +using namespace boost::histogram::detail; + +int main() { + struct A {}; + A a, b; + + struct B { + bool operator==(const B&) const { return false; } + }; + B c, d; + + BOOST_TEST(relaxed_equal(a, b)); + BOOST_TEST_NOT(relaxed_equal(c, d)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_replace_type_test.cpp b/src/boost/libs/histogram/test/detail_replace_type_test.cpp new file mode 100644 index 000000000..6318e11f7 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_replace_type_test.cpp @@ -0,0 +1,22 @@ +// 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/core/lightweight_test_trait.hpp> +#include <boost/histogram/detail/replace_type.hpp> +#include "std_ostream.hpp" + +using namespace boost::histogram::detail; + +int main() { + + BOOST_TEST_TRAIT_SAME(replace_type<long, char, int>, long); + BOOST_TEST_TRAIT_SAME(replace_type<char, char, int>, int); + BOOST_TEST_TRAIT_SAME(replace_default<boost::use_default, char>, char); + BOOST_TEST_TRAIT_SAME(replace_default<int, char>, int); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_safe_comparison_test.cpp b/src/boost/libs/histogram/test/detail_safe_comparison_test.cpp new file mode 100644 index 000000000..57f0f4d75 --- /dev/null +++ b/src/boost/libs/histogram/test/detail_safe_comparison_test.cpp @@ -0,0 +1,60 @@ +// Copyright 2018-2019 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/detail/safe_comparison.hpp> + +using namespace boost::histogram::detail; + +int main() { + auto eq = safe_equal{}; + BOOST_TEST(eq(-1, -1)); + BOOST_TEST(eq(1, 1u)); + BOOST_TEST(eq(1u, 1)); + BOOST_TEST(eq(1u, 1u)); + BOOST_TEST(eq(1.0, 1)); + BOOST_TEST(eq(1, 1.0)); + BOOST_TEST(eq(1.0, 1u)); + BOOST_TEST(eq(1u, 1.0)); + BOOST_TEST_NOT(eq(-1, static_cast<unsigned>(-1))); + BOOST_TEST_NOT(eq(static_cast<unsigned>(-1), -1)); + + auto lt = safe_less{}; + BOOST_TEST(lt(1u, 2u)); + BOOST_TEST(lt(-1, 1u)); + BOOST_TEST(lt(1u, 2)); + BOOST_TEST(lt(-2, -1)); + BOOST_TEST(lt(-2.0, -1)); + BOOST_TEST(lt(1, 2.0)); + BOOST_TEST(lt(-1.0, 1u)); + BOOST_TEST(lt(1u, 2.0)); + BOOST_TEST(lt(1.0, 2.0)); + BOOST_TEST_NOT(lt(1u, 1)); + BOOST_TEST_NOT(lt(1, 1u)); + BOOST_TEST_NOT(lt(1.0, 1)); + BOOST_TEST_NOT(lt(1, 1.0)); + BOOST_TEST_NOT(lt(1.0, 1u)); + BOOST_TEST_NOT(lt(1u, 1.0)); + BOOST_TEST_NOT(lt(1.0, 1.0)); + + auto gt = safe_greater{}; + BOOST_TEST(gt(2u, 1u)); + BOOST_TEST(gt(1u, -1)); + BOOST_TEST(gt(2, 1u)); + BOOST_TEST(gt(-1, -2)); + BOOST_TEST(gt(-1, -2.0)); + BOOST_TEST(gt(2.0, 1)); + BOOST_TEST(gt(1u, -1.0)); + BOOST_TEST(gt(2.0, 1u)); + BOOST_TEST_NOT(gt(1u, 1)); + BOOST_TEST_NOT(gt(1, 1u)); + BOOST_TEST_NOT(gt(1.0, 1)); + BOOST_TEST_NOT(gt(1, 1.0)); + BOOST_TEST_NOT(gt(1.0, 1u)); + BOOST_TEST_NOT(gt(1u, 1.0)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_static_if_test.cpp b/src/boost/libs/histogram/test/detail_static_if_test.cpp new file mode 100644 index 000000000..d25d7ed7a --- /dev/null +++ b/src/boost/libs/histogram/test/detail_static_if_test.cpp @@ -0,0 +1,33 @@ +// 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/detail/static_if.hpp> +#include "throw_exception.hpp" + +using namespace boost::histogram::detail; + +int main() { + using T = std::true_type; + using F = std::false_type; + + // check that branch not taken does not have to compile + BOOST_TEST_EQ(static_if<T>([](auto) { return 1; }, [] {}, 0), 1); + BOOST_TEST_EQ(static_if<F>([] {}, [](auto) { return 1; }, 0), 1); + + BOOST_TEST_EQ(static_if_c<true>([](auto) { return 1; }, [] {}, 0), 1); + BOOST_TEST_EQ(static_if_c<false>([] {}, [](auto) { return 1; }, 0), 1); + + // check that noexcept is correctly propagated + auto may_throw = [](auto x) { return x; }; + auto no_throw = [](auto x) noexcept { return x; }; + + // make this work with -fno-exceptions + BOOST_TEST_EQ(noexcept(static_if<F>(no_throw, may_throw, 0)), noexcept(may_throw(0))); + BOOST_TEST(noexcept(static_if<T>(no_throw, may_throw, 0))); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/detail_tuple_slice_test.cpp b/src/boost/libs/histogram/test/detail_tuple_slice_test.cpp new file mode 100644 index 000000000..465e0f83a --- /dev/null +++ b/src/boost/libs/histogram/test/detail_tuple_slice_test.cpp @@ -0,0 +1,21 @@ +// 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/detail/tuple_slice.hpp> +#include <tuple> +#include "std_ostream.hpp" + +using namespace boost::histogram::detail; + +int main() { + + auto a = std::make_tuple(1, 2, 3, 4); + const auto b = tuple_slice<1, 2>(a); + BOOST_TEST_EQ(b, std::make_tuple(2, 3)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/dummy_storage.hpp b/src/boost/libs/histogram/test/dummy_storage.hpp new file mode 100644 index 000000000..69e28cf69 --- /dev/null +++ b/src/boost/libs/histogram/test/dummy_storage.hpp @@ -0,0 +1,58 @@ +// Copyright 2020 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_TEST_DUMMY_STORAGE_HPP +#define BOOST_HISTOGRAM_TEST_DUMMY_STORAGE_HPP + +#include <algorithm> +#include <array> +#include <boost/histogram/detail/detect.hpp> +#include <ostream> +#include <type_traits> + +template <class ValueType, bool Scaleable> +struct dummy_storage : std::array<ValueType, 10> { + using base_t = std::array<ValueType, 10>; + + static constexpr bool has_threading_support = false; + static constexpr bool scaleable = + Scaleable && boost::histogram::detail::has_operator_rmul<ValueType, double>::value; + + std::size_t size_ = 0; + + std::size_t size() const { return size_; } + + void reset(std::size_t n) { + assert(n < this->max_size()); + size_ = n; + } + + auto end() { return this->begin() + size(); } + auto end() const { return this->begin() + size(); } + + bool operator==(const dummy_storage& o) const { + return std::equal(this->begin(), end(), o.begin(), o.end()); + } + + template <class S = dummy_storage> + std::enable_if_t<S::scaleable, dummy_storage&> operator*=(double) { + // do nothing, so it works with unscalable value types for testing purposes + return *this; + } +}; + +struct unscaleable { + int value = 0; + void operator++() { ++value; } + bool operator==(const int& o) const { return value == o; } +}; + +inline std::ostream& operator<<(std::ostream& os, const unscaleable& x) { + os << x.value; + return os; +} + +#endif diff --git a/src/boost/libs/histogram/test/histogram_custom_axis_test.cpp b/src/boost/libs/histogram/test/histogram_custom_axis_test.cpp new file mode 100644 index 000000000..d31b33753 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_custom_axis_test.cpp @@ -0,0 +1,132 @@ +// Copyright 2018-2019 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/accumulators/mean.hpp> +#include <boost/histogram/accumulators/weighted_mean.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <sstream> +#include <stdexcept> +#include <tuple> +#include <utility> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +struct modified_axis : public axis::integer<> { + using integer::integer; // inherit ctors of base + // customization point: convert argument and call base class + auto index(const char* s) const { return integer::index(std::atoi(s)); } +}; + +struct minimal { + auto index(int x) const { return static_cast<axis::index_type>(x % 2); } + auto size() const { return axis::index_type{2}; } +}; + +struct axis2d { + auto index(const std::tuple<double, double>& x) const { + return axis::index_type{std::get<0>(x) == 1 && std::get<1>(x) == 2}; + } + auto size() const { return axis::index_type{2}; } +}; + +class axis2d_growing { +public: + auto index(std::tuple<double, double> xy) const { + const auto x = std::get<0>(xy); + const auto y = std::get<1>(xy); + const auto r = std::sqrt(x * x + y * y); + return std::min(static_cast<axis::index_type>(r), size()); + } + + auto update(std::tuple<double, double> xy) { + const auto x = std::get<0>(xy); + const auto y = std::get<1>(xy); + const auto r = std::sqrt(x * x + y * y); + const auto n = static_cast<int>(r); + const auto old = size_; + if (n >= size_) size_ = n + 1; + return std::make_pair(n, old - size_); + } + + axis::index_type size() const { return size_; } + +private: + axis::index_type size_ = 0; +}; + +template <class Tag> +void run_tests() { + // one 2d axis + { + auto h = make(Tag(), axis2d()); + h(1, 2); // ok, forwards 2d tuple to axis + h(std::make_tuple(1, 2)); // also ok, forwards 2d tuple to axis + BOOST_TEST_THROWS(h(1), std::invalid_argument); + BOOST_TEST_THROWS(h(1, 2, 3), std::invalid_argument); + BOOST_TEST_EQ(h.at(0), 0); // ok, bin access is still 1d + BOOST_TEST_EQ(h[std::make_tuple(1)], 2); + // also works with weights + h(1, 2, weight(2)); + h(std::make_tuple(weight(3), 1, 2)); + BOOST_TEST_EQ(h.at(0), 0); + BOOST_TEST_EQ(h.at(1), 7); + + auto h2 = make_s(Tag(), profile_storage(), axis2d()); + h2(1, 2, sample(2)); + BOOST_TEST_EQ(h2[1].count(), 1); + BOOST_TEST_EQ(h2[1].value(), 2); + + auto h3 = make_s(Tag(), weighted_profile_storage(), axis2d()); + h3(1, 2, weight(3), sample(2)); + BOOST_TEST_EQ(h3[1].sum_of_weights(), 3); + BOOST_TEST_EQ(h3[1].value(), 2); + } + + // several axes, one 2d + { + auto h = make(Tag(), modified_axis(0, 3), minimal(), axis2d()); + h("0", 1, std::make_tuple(1.0, 2.0)); + h("1", 2, std::make_tuple(2.0, 1.0)); + + BOOST_TEST_EQ(h.rank(), 3); + BOOST_TEST_EQ(h.at(0, 0, 0), 0); + BOOST_TEST_EQ(h.at(0, 1, 1), 1); + BOOST_TEST_EQ(h.at(1, 0, 0), 1); + } + + // growing axis + { + auto h = make_s(Tag{}, std::vector<int>{}, axis2d_growing{}); + BOOST_TEST_EQ(h.size(), 0); + h(0, 0); + BOOST_TEST_EQ(h.size(), 1); + h(1, 0); + h(0, 1); + BOOST_TEST_EQ(h.size(), 2); + h(10, 0); + BOOST_TEST_EQ(h.size(), 11); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 2); + BOOST_TEST_EQ(h[10], 1); + BOOST_TEST_THROWS(h(0), std::invalid_argument); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_dynamic_test.cpp b/src/boost/libs/histogram/test/histogram_dynamic_test.cpp new file mode 100644 index 000000000..8200f94a2 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_dynamic_test.cpp @@ -0,0 +1,123 @@ +// 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 <algorithm> +#include <boost/core/ignore_unused.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <cstdlib> +#include <limits> +#include <numeric> +#include <sstream> +#include <tuple> +#include <utility> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +int main() { + // special stuff that only works with dynamic_tag + + // init with vector of axis and vector of axis::variant + { + using R = axis::regular<>; + using I = axis::integer<>; + using V = axis::variable<>; + + auto v = std::vector<axis::variant<R, I, V>>(); + v.emplace_back(R{4, -1, 1}); + v.emplace_back(I{1, 7}); + v.emplace_back(V{1, 2, 3}); + auto h = make_histogram(v.begin(), v.end()); + BOOST_TEST_EQ(h.rank(), 3); + BOOST_TEST_EQ(h.axis(0), v[0]); + BOOST_TEST_EQ(h.axis(1), v[1]); + BOOST_TEST_EQ(h.axis(2), v[2]); + + auto h2 = make_histogram_with(std::vector<int>(), v); + BOOST_TEST_EQ(h2.rank(), 3); + BOOST_TEST_EQ(h2.axis(0), v[0]); + BOOST_TEST_EQ(h2.axis(1), v[1]); + BOOST_TEST_EQ(h2.axis(2), v[2]); + + auto v2 = std::vector<R>(); + v2.emplace_back(10, 0, 1); + v2.emplace_back(20, 0, 2); + auto h3 = make_histogram(v2); + BOOST_TEST_EQ(h3.axis(0), v2[0]); + BOOST_TEST_EQ(h3.axis(1), v2[1]); + } + + // too many axes + { + using I = axis::integer<int, axis::null_type, axis::option::none_t>; + + // test edge case + auto av = std::vector<I>(BOOST_HISTOGRAM_DETAIL_AXES_LIMIT, I(0, 1)); + auto h = make_histogram(av); + auto inputs = std::vector<std::vector<int>>(BOOST_HISTOGRAM_DETAIL_AXES_LIMIT, + std::vector<int>(1, 0)); + h.fill(inputs); // should not crash + + auto bad = std::vector<I>(BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1, I(0, 1)); + boost::ignore_unused(bad); + BOOST_TEST_THROWS((void)make_histogram(bad), std::invalid_argument); + } + + // bad fill + { + auto a = axis::integer<>(0, 1); + auto b = make(dynamic_tag(), a); + BOOST_TEST_THROWS(b(0, 0), std::invalid_argument); + auto c = make(dynamic_tag(), a, a); + BOOST_TEST_THROWS(c(0), std::invalid_argument); + auto d = make(dynamic_tag(), a); + BOOST_TEST_THROWS(d(std::string()), std::invalid_argument); + + struct axis2d { + auto index(const std::tuple<double, double>& x) const { + return axis::index_type{std::get<0>(x) == 1 && std::get<1>(x) == 2}; + } + auto size() const { return axis::index_type{2}; } + } e; + + auto f = make(dynamic_tag(), a, e); + BOOST_TEST_THROWS(f(0, 0, 0), std::invalid_argument); + BOOST_TEST_THROWS(f(0, std::make_tuple(0, 0), 1), std::invalid_argument); + } + + // bad at + { + auto h1 = make(dynamic_tag(), axis::integer<>(0, 2)); + h1(1); + BOOST_TEST_THROWS(h1.at(0, 0), std::invalid_argument); + BOOST_TEST_THROWS(h1.at(std::make_tuple(0, 0)), std::invalid_argument); + } + + // incompatible axis variant methods + { + auto c = make(dynamic_tag(), axis::category<std::string>({"A", "B"})); + BOOST_TEST_THROWS(c.axis().value(0), std::runtime_error); + } + + { + auto h = make_histogram(std::vector<axis::integer<>>(1, axis::integer<>(0, 3))); + h(0); + h(1); + h(2); + BOOST_TEST_EQ(h.at(0), 1); + BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(h.at(2), 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_fail0.cpp b/src/boost/libs/histogram/test/histogram_fail0.cpp new file mode 100644 index 000000000..f4ca4a4b8 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fail0.cpp @@ -0,0 +1,20 @@ +// Copyright 2019 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/axis/integer.hpp> +#include <boost/histogram/make_histogram.hpp> + +int main() { + using namespace boost::histogram; + + auto h = make_histogram(axis::integer<>(0, 5)); + + // invalid sample argument + h(0, sample(1)); + + auto values = {0, 1}; + h.fill(values, sample(values)); // invalid sample argument +} diff --git a/src/boost/libs/histogram/test/histogram_fail1.cpp b/src/boost/libs/histogram/test/histogram_fail1.cpp new file mode 100644 index 000000000..5f175259b --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fail1.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 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/axis/integer.hpp> +#include <boost/histogram/make_histogram.hpp> + +int main() { + using namespace boost::histogram; + + struct accumulator { + void operator()() {} + }; + + auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5)); + + // invalid weight argument + h(0, weight(1)); + + auto values = {0, 1}; + h.fill(values, weight(1)); +} diff --git a/src/boost/libs/histogram/test/histogram_fail2.cpp b/src/boost/libs/histogram/test/histogram_fail2.cpp new file mode 100644 index 000000000..29ffac1ef --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fail2.cpp @@ -0,0 +1,25 @@ +// Copyright 2019 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/axis/integer.hpp> +#include <boost/histogram/make_profile.hpp> + +int main() { + using namespace boost::histogram; + + struct accumulator { + void operator()(double) {} + void operator()(weight_type<double>, double) {} + }; + + auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5)); + + // accumulator requires sample + h(0, weight(1)); + + auto values = {1, 2}; + h.fill(values, weight(1)); +} diff --git a/src/boost/libs/histogram/test/histogram_fail3.cpp b/src/boost/libs/histogram/test/histogram_fail3.cpp new file mode 100644 index 000000000..9ff53415d --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fail3.cpp @@ -0,0 +1,23 @@ +// Copyright 2019 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/axis/integer.hpp> +#include <boost/histogram/make_histogram.hpp> + +int main() { + struct accumulator { + void operator()(double) {} + }; + + using namespace boost::histogram; + auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5)); + + // wrong number of sample arguments + h(0, sample(1, 2)); + + auto values = {0, 1}; + h.fill(values, sample(values, values)); +} diff --git a/src/boost/libs/histogram/test/histogram_fail4.cpp b/src/boost/libs/histogram/test/histogram_fail4.cpp new file mode 100644 index 000000000..22686daa8 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fail4.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 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/axis/integer.hpp> +#include <boost/histogram/make_histogram.hpp> + +int main() { + using namespace boost::histogram; + + struct accumulator { + void operator()(std::string) {} + }; + + auto h = make_histogram_with(dense_storage<accumulator>(), axis::integer<>(0, 5)); + + // invalid weight argument + h(0, sample(1)); + + auto values = {1, 2}; + h.fill(values, sample(values)); +} diff --git a/src/boost/libs/histogram/test/histogram_fill_test.cpp b/src/boost/libs/histogram/test/histogram_fill_test.cpp new file mode 100644 index 000000000..b91ff74b8 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_fill_test.cpp @@ -0,0 +1,361 @@ +// Copyright 2019 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 <array> +#include <boost/config.hpp> +#include <boost/core/ignore_unused.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators.hpp> +#include <boost/histogram/accumulators/ostream.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis/category.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/variant2/variant.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <random> +#include <sstream> +#include <stdexcept> +#include <tuple> +#include <utility> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::algorithm; +using namespace boost::histogram::literals; // to get _c suffix +using boost::variant2::variant; + +constexpr auto ndata = 1 << 16; // should be larger than index buffer in fill_n + +using in = axis::integer<int, axis::null_type>; +using in0 = axis::integer<int, axis::null_type, axis::option::none_t>; +using ing = axis::integer<double, axis::null_type, + decltype(axis::option::growth | axis::option::underflow | + axis::option::overflow)>; +using cs = axis::category<std::string, axis::null_type>; +using csg = axis::category<std::string, axis::null_type, axis::option::growth_t>; + +struct axis2d { + auto size() const { return axis::index_type{2}; } + + auto index(const std::tuple<double, double>& xy) const { + const auto x = std::get<0>(xy); + const auto y = std::get<1>(xy); + const auto r = std::sqrt(x * x + y * y); + return std::min(static_cast<axis::index_type>(r), size()); + } + + friend std::ostream& operator<<(std::ostream& os, const axis2d&) { + os << "axis2d()"; + return os; + } +}; + +template <class Tag> +void run_tests(const std::vector<int>& x, const std::vector<int>& y, + const std::vector<double>& w) { + + // 1D simple A + { + auto h = make(Tag(), in{1, 3}); + auto h2 = h; + for (auto&& xi : x) h(xi); + // uses 1D specialization + h2.fill(x); + BOOST_TEST_EQ(h, h2); + } + + // 1D simple B + { + auto h = make(Tag(), in{1, 3}); + auto h2 = h; + for (auto&& xi : x) h(xi); + // uses generic form + const auto vx = {x}; + h2.fill(vx); + BOOST_TEST_EQ(h, h2); + } + + // 1D simple C + { + auto h = make(Tag(), in{1, 3}); + auto h2 = h; + h(1); + for (auto&& xi : x) h(xi); + // uses variant + variant<int, std::vector<int>, std::string> v[1]; + v[0] = 1; + h2.fill(v); + v[0] = x; + h2.fill(v); + BOOST_TEST_EQ(h, h2); + } + + // 1D bad arguments + { + auto h = make(Tag(), in{1, 3}); + + int bad1[2][4]; + boost::ignore_unused(bad1); + BOOST_TEST_THROWS(h.fill(bad1), std::invalid_argument); + + std::vector<std::array<int, 4>> bad2; + boost::ignore_unused(bad2); + BOOST_TEST_THROWS(h.fill(bad2), std::invalid_argument); + } + + // 1D with category axis + { + auto h = make(Tag(), cs{"A", "B"}); + auto h2 = h; + + const auto s = {"A", "B", "C"}; + for (auto&& si : s) h(si); + h2.fill(s); + + variant<int, std::string, std::vector<std::string>> v[1]; + h("B"); + v[0] = "B"; + h2.fill(v); + + v[0] = std::vector<std::string>(s.begin(), s.end()); + for (auto&& si : s) h(si); + h2.fill(v); + + BOOST_TEST_EQ(h, h2); + } + + // 1D weight + { + auto h = make(Tag(), in{1, 3}); + auto h2 = h; + + for (auto&& xi : x) h(weight(2), xi); + h2.fill(weight(2), x); + + for (unsigned i = 0; i < ndata; ++i) h(weight(w[i]), x[i]); + h2.fill(weight(w), x); + + BOOST_TEST_EQ(h, h2); + + auto w2 = {1}; + boost::ignore_unused(w2); + BOOST_TEST_THROWS(h2.fill(x, weight(w2)), std::invalid_argument); + } + + // 2D simple + { + auto h = make(Tag(), in{1, 3}, in0{1, 5}); + auto h2 = h; + + for (int i = 0; i < ndata; ++i) h(x[i], y[i]); + const auto xy = {x, y}; + h2.fill(xy); + + BOOST_TEST_EQ(h, h2); + + // wrong rank + BOOST_TEST_THROWS(h.fill(x), std::invalid_argument); + + // not rectangular + std::array<std::vector<int>, 2> bad = {{std::vector<int>(1), std::vector<int>(2)}}; + boost::ignore_unused(bad); + BOOST_TEST_THROWS(h2.fill(bad), std::invalid_argument); + } + + // 2D variant and weight + { + auto h = make(Tag(), in{1, 3}, in0{1, 5}); + + using V = variant<int, std::vector<int>, std::string>; + V xy[2]; + + { + xy[0] = 3; + xy[1] = y; + auto h1 = h; + auto h2 = h; + for (auto&& vi : y) h1(3, vi); + h2.fill(xy); + BOOST_TEST_EQ(h1, h2); + } + + { + xy[0] = x; + xy[1] = 3; + auto h1 = h; + auto h2 = h; + for (auto&& vi : x) h1(vi, 3); + h2.fill(xy); + BOOST_TEST_EQ(h1, h2); + } + + { + xy[0] = 3; + xy[1] = y; + auto h1 = h; + auto h2 = h; + for (auto&& vi : y) h1(3, vi, weight(2)); + h2.fill(xy, weight(2)); + BOOST_TEST_EQ(h1, h2); + } + + { + xy[0] = 3; + xy[1] = y; + auto h1 = h; + auto h2 = h; + for (unsigned i = 0; i < ndata; ++i) h1(3, y[i], weight(w[i])); + h2.fill(xy, weight(w)); + BOOST_TEST_EQ(sum(h1), sum(h2)); + BOOST_TEST_EQ(h1, h2); + } + } + + // 1D growing + { + auto h = make(Tag(), ing()); + auto h2 = h; + for (const auto& xi : x) h(xi); + h2.fill(x); + BOOST_TEST_EQ(h, h2); + } + + // 2D growing A + { + auto h = make(Tag(), in(1, 3), ing()); + auto h2 = h; + for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]); + const auto xy = {x, y}; + h2.fill(xy); + BOOST_TEST_EQ(h, h2); + } + + // 2D growing B + { + auto h = make(Tag(), ing(), ing()); + auto h2 = h; + for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]); + const auto xy = {x, y}; + h2.fill(xy); + BOOST_TEST_EQ(h, h2); + } + + // 2D growing with weights A + { + auto h = make(Tag(), in(1, 3), ing()); + auto h2 = h; + for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(w[i])); + const auto xy = {x, y}; + h2.fill(xy, weight(w)); + BOOST_TEST_EQ(h, h2); + } + + // 2D growing with weights B + { + auto h = make(Tag(), ing(), ing()); + auto h2 = h; + for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(2)); + const auto xy = {x, y}; + h2.fill(xy, weight(2)); + BOOST_TEST_EQ(h, h2); + } + + // 2D growing and variant + { + auto h = make(Tag(), csg{}, in{1, 2}); + auto h2 = h; + + h("foo", 1); + h("foo", 2); + + using V = variant<std::string, std::vector<std::string>, int, std::vector<int>>; + const auto xy = {V("foo"), V(std::vector<int>{1, 2})}; + h2.fill(xy); + + BOOST_TEST_EQ(h, h2); + + const auto bad = {V(std::vector<std::string>(1, "foo")), V(std::vector<int>{1, 2})}; + boost::ignore_unused(bad); + BOOST_TEST_THROWS(h.fill(bad), std::invalid_argument); + } + + // 1D profile with samples + { + auto h = make_s(Tag(), profile_storage(), in(1, 3)); + auto h2 = h; + + for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i])); + for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(w[i])); + for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(2), weight(w[i])); + for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(2)); + + h2.fill(x, sample(w)); + h2.fill(x, sample(w), weight(w)); + h2.fill(x, sample(2), weight(w)); + h2.fill(x, sample(w), weight(2)); + + BOOST_TEST_EQ(h, h2); + } + + // 2D weighted profile with samples and weights + { + auto h = make_s(Tag(), weighted_profile_storage(), in(1, 3), in0(1, 3)); + auto h2 = h; + + for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(w[i])); + for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(2), weight(w[i])); + for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(2)); + + using V = variant<int, std::vector<int>>; + std::array<V, 2> xy; + xy[0] = x; + xy[1] = 3; + h2.fill(xy, sample(w), weight(w)); + h2.fill(xy, sample(2), weight(w)); + h2.fill(xy, sample(w), weight(2)); + + BOOST_TEST_EQ(h, h2); + } + + // axis2d + { + auto h = make(Tag(), axis2d{}); + auto h2 = h; + + std::vector<std::tuple<double, double>> xy; + xy.reserve(ndata); + for (unsigned i = 0; i < ndata; ++i) xy.emplace_back(x[i], y[i]); + + for (auto&& xyi : xy) h(xyi); + h2.fill(xy); + + BOOST_TEST_EQ(h, h2); + } +} + +int main() { + std::mt19937 gen(1); + std::normal_distribution<> id(0, 2); + std::vector<int> x(ndata), y(ndata); + auto generator = [&] { return static_cast<int>(id(gen)); }; + std::generate(x.begin(), x.end(), generator); + std::generate(y.begin(), y.end(), generator); + std::vector<double> w(ndata); + // must be all positive + std::generate(w.begin(), w.end(), [&] { return 0.5 + std::abs(id(gen)); }); + + run_tests<static_tag>(x, y, w); + run_tests<dynamic_tag>(x, y, w); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_growing_test.cpp b/src/boost/libs/histogram/test/histogram_growing_test.cpp new file mode 100644 index 000000000..2ab9a9c9a --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_growing_test.cpp @@ -0,0 +1,206 @@ +// 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/algorithm/sum.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <string> +#include <utility> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +using def = use_default; + +using regular = axis::regular<double, def, def, axis::option::growth_t>; + +using integer = axis::integer<double, def, + decltype(axis::option::underflow | axis::option::overflow | + axis::option::growth)>; + +using category = axis::category<std::string, def, axis::option::growth_t>; + +class custom_2d_axis { +public: + auto index(std::tuple<double, double> xy) const { + const auto x = std::get<0>(xy); + const auto y = std::get<1>(xy); + const auto r = std::sqrt(x * x + y * y); + return (std::min)(static_cast<axis::index_type>(r), size()); + } + + auto update(std::tuple<double, double> xy) { + const auto x = std::get<0>(xy); + const auto y = std::get<1>(xy); + const auto r = std::sqrt(x * x + y * y); + const auto n = static_cast<int>(r); + const auto old = size_; + if (n >= size_) size_ = n + 1; + return std::make_pair(n, old - size_); + } + + axis::index_type size() const { return size_; } + +private: + axis::index_type size_ = 0; +}; + +template <typename Tag> +void run_tests() { + { + auto h = make(Tag(), regular(2, 0, 1)); + const auto& a = h.axis(); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(h.size(), 2); + // [0.0, 0.5, 1.0] + h(0.1); + h(0.9); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(h.size(), 2); + h(-std::numeric_limits<double>::infinity()); + h(std::numeric_limits<double>::quiet_NaN()); + h(std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(h.size(), 2); + h(-0.3); + // [-0.5, 0.0, 0.5, 1.0] + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(h.size(), 3); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 1); + BOOST_TEST_EQ(h[2], 1); + h(1.9); + // [-0.5, 0.0, 0.5, 1.0, 1.5, 2.0] + BOOST_TEST_EQ(a.size(), 5); + BOOST_TEST_EQ(h.size(), 5); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 1); + BOOST_TEST_EQ(h[2], 1); + BOOST_TEST_EQ(h[3], 0); + BOOST_TEST_EQ(h[4], 1); + } + + { + auto h = make_s(Tag(), std::vector<int>(), integer()); + const auto& a = h.axis(); + h(-std::numeric_limits<double>::infinity()); + h(std::numeric_limits<double>::quiet_NaN()); + h(std::numeric_limits<double>::infinity()); + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(h.size(), 2); + BOOST_TEST_EQ(h[-1], 1); + BOOST_TEST_EQ(h[0], 2); + h(0); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(h.size(), 3); + BOOST_TEST_EQ(h[-1], 1); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 2); + h(2); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(h.size(), 5); + BOOST_TEST_EQ(h[-1], 1); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 0); + BOOST_TEST_EQ(h[2], 1); + BOOST_TEST_EQ(h[3], 2); + h(-2); + BOOST_TEST_EQ(a.size(), 5); + BOOST_TEST_EQ(h.size(), 7); + // BOOST_TEST_EQ(h[-1], 1) + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 0); + BOOST_TEST_EQ(h[2], 1); + BOOST_TEST_EQ(h[3], 0); + BOOST_TEST_EQ(h[4], 1); + BOOST_TEST_EQ(h[5], 2); + } + + { + auto h = make_s(Tag(), std::vector<int>(), integer(), category()); + const auto& a = h.axis(0); + const auto& b = h.axis(1); + BOOST_TEST_EQ(a.size(), 0); + BOOST_TEST_EQ(b.size(), 0); + BOOST_TEST_EQ(h.size(), 0); + h(0, "x"); + h(-std::numeric_limits<double>::infinity(), "x"); + h(std::numeric_limits<double>::infinity(), "x"); + h(std::numeric_limits<double>::quiet_NaN(), "x"); + BOOST_TEST_EQ(a.size(), 1); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(h.size(), 3); + h(2, "x"); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(h.size(), 5); + h(1, "y"); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(h.size(), 10); + BOOST_TEST_EQ(h.at(-1, 0), 1); + BOOST_TEST_EQ(h.at(-1, 1), 0); + BOOST_TEST_EQ(h.at(3, 0), 2); + BOOST_TEST_EQ(h.at(3, 1), 0); + BOOST_TEST_EQ(h.at(a.index(0), b.index("x")), 1); + BOOST_TEST_EQ(h.at(a.index(1), b.index("x")), 0); + BOOST_TEST_EQ(h.at(a.index(2), b.index("x")), 1); + BOOST_TEST_EQ(h.at(a.index(0), b.index("y")), 0); + BOOST_TEST_EQ(h.at(a.index(1), b.index("y")), 1); + BOOST_TEST_EQ(h.at(a.index(2), b.index("y")), 0); + + BOOST_TEST_THROWS(h(0, "x", 42), std::invalid_argument); + } + + { + auto h = make_s(Tag{}, std::vector<int>{}, custom_2d_axis{}); + BOOST_TEST_EQ(h.size(), 0); + h(0, 0); + BOOST_TEST_EQ(h.size(), 1); + h(1, 0); + h(0, 1); + BOOST_TEST_EQ(h.size(), 2); + h(10, 0); + BOOST_TEST_EQ(h.size(), 11); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 2); + BOOST_TEST_EQ(h[10], 1); + BOOST_TEST_THROWS(h(0), std::invalid_argument); + } + + // mix of a growing and a non-growing axis + { + using reg_nogrow = axis::regular<>; + auto h = make(Tag(), reg_nogrow{2, 0.0, 1.0}, regular{2, 0.0, 1.0}); + BOOST_TEST_EQ(h.size(), 4 * 2); + h(0.0, 0.0); + BOOST_TEST_EQ(h.size(), 4 * 2); + BOOST_TEST_EQ(h.at(0, 0), 1); + h(-1.0, -0.1); + BOOST_TEST_EQ(h.size(), 4 * 3); + BOOST_TEST_EQ(h.at(-1, 0), 1); + h(2.0, 1.1); + BOOST_TEST_EQ(h.size(), 4 * 4); + // axis 0: [0.0, 0.5, 1.0] + under/overflow + // axis 1: [-0.5, 0.0, 0.5, 1.0, 1.5] + BOOST_TEST_EQ(h.at(-1, 0), 1); + BOOST_TEST_EQ(h.at(0, 1), 1); + BOOST_TEST_EQ(h.at(2, 3), 1); + BOOST_TEST_EQ(algorithm::sum(h), 3); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_mixed_test.cpp b/src/boost/libs/histogram/test/histogram_mixed_test.cpp new file mode 100644 index 000000000..a24d7fb4f --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_mixed_test.cpp @@ -0,0 +1,83 @@ +// 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/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +template <typename T1, typename T2> +void run_tests() { + // compare + { + auto a = make(T1{}, axis::regular<>{3, 0, 3}, axis::integer<>(0, 2)); + auto b = make_s(T2{}, std::vector<unsigned>(), axis::regular<>{3, 0, 3}, + axis::integer<>(0, 2)); + BOOST_TEST_EQ(a, b); + auto b2 = make(T2{}, axis::integer<>{0, 3}, axis::integer<>(0, 2)); + BOOST_TEST_NE(a, b2); + auto b3 = make(T2{}, axis::regular<>(3, 0, 4), axis::integer<>(0, 2)); + BOOST_TEST_NE(a, b3); + } + + // operators + { + auto a = make(T1{}, axis::integer<int, use_default, axis::option::none_t>{0, 2}); + auto b = make(T2{}, axis::integer<int, use_default, axis::option::none_t>{0, 2}); + BOOST_TEST_EQ(a, b); + a(0); // 1 0 + b(1); // 0 1 + a += b; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 1); + a *= b; + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 1); + a -= b; + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 0); + a[0] = 2; + a[1] = 4; + b[0] = 2; + b[1] = 2; + a /= b; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 2); + + BOOST_TEST_THROWS(a += make(T2{}, axis::integer<>{0, 3}), std::invalid_argument); + BOOST_TEST_THROWS(a -= make(T2{}, axis::integer<>{0, 3}), std::invalid_argument); + BOOST_TEST_THROWS(a *= make(T2{}, axis::integer<>{0, 3}), std::invalid_argument); + BOOST_TEST_THROWS(a /= make(T2{}, axis::integer<>{0, 3}), std::invalid_argument); + } + + // copy_assign + { + auto a = make(T1{}, axis::regular<>{3, 0, 3}, axis::integer<>{0, 2}); + auto b = make_s(T2{}, std::vector<double>(), axis::regular<>{3, 0, 3}, + axis::integer<>{0, 2}); + a(1, 1); + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + } +} + +int main() { + run_tests<static_tag, dynamic_tag>(); + run_tests<dynamic_tag, static_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_operators_test.cpp b/src/boost/libs/histogram/test/histogram_operators_test.cpp new file mode 100644 index 000000000..52a99627b --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_operators_test.cpp @@ -0,0 +1,209 @@ +// 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/is_same.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/detail/detect.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/throw_exception.hpp> +#include <string> +#include <vector> +#include "dummy_storage.hpp" +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +template <typename Tag> +void run_tests() { + // arithmetic operators + { + auto a = make(Tag(), axis::integer<int, use_default, axis::option::none_t>(0, 2)); + auto b = a; + a(0); + b(1); + auto c = a + b; + BOOST_TEST_EQ(c.at(0), 1); + BOOST_TEST_EQ(c.at(1), 1); + c += b; + BOOST_TEST_EQ(c.at(0), 1); + BOOST_TEST_EQ(c.at(1), 2); + auto d = a + b + c; + BOOST_TEST_TRAIT_SAME(decltype(d), decltype(a)); + BOOST_TEST_EQ(d.at(0), 2); + BOOST_TEST_EQ(d.at(1), 3); + + auto d2 = d - a - b - c; + BOOST_TEST_TRAIT_SAME(decltype(d2), decltype(a)); + BOOST_TEST_EQ(d2.at(0), 0); + BOOST_TEST_EQ(d2.at(1), 0); + d2 -= a; + BOOST_TEST_EQ(d2.at(0), -1); + BOOST_TEST_EQ(d2.at(1), 0); + + auto d3 = d; + d3 *= d; + BOOST_TEST_EQ(d3.at(0), 4); + BOOST_TEST_EQ(d3.at(1), 9); + auto d4 = d3 * (1 * d); // converted return type + BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(d4), decltype(d3)>)); + BOOST_TEST_EQ(d4.at(0), 8); + BOOST_TEST_EQ(d4.at(1), 27); + d4 /= d; + BOOST_TEST_EQ(d4.at(0), 4); + BOOST_TEST_EQ(d4.at(1), 9); + auto d5 = d4 / d; + BOOST_TEST_EQ(d5.at(0), 2); + BOOST_TEST_EQ(d5.at(1), 3); + + auto e = 3 * a; // converted return type + auto f = b * 2; // converted return type + BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(e), decltype(a)>)); + BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(f), decltype(a)>)); + 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 = 1.0 * a; + r += b; + r += e; + 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), 1); + BOOST_TEST_EQ(r.at(1), 0.25); + BOOST_TEST_EQ(r, s); + } + + // arithmetic operators with mixed storage: unlimited vs. vector<unsigned> + { + auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2); + auto a = make(Tag(), ia); + a(0, weight(2)); + a(1, weight(2)); + auto b = a; + auto c = make_s(Tag(), std::vector<int>(), ia); + c(0, weight(2)); + c(1, weight(2)); + auto a2 = a; + a2 += c; + BOOST_TEST_EQ(a2, (a + b)); + auto a3 = a; + a3 *= c; + BOOST_TEST_EQ(a3, (a * b)); + auto a4 = a; + a4 -= c; + BOOST_TEST_EQ(a4, (a - b)); + auto a5 = a; + a5 /= c; + BOOST_TEST_EQ(a5, (a / b)); + } + + // arithmetic operators with mixed storage: vector<unsigned char> vs. vector<unsigned> + { + auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2); + auto a = make_s(Tag(), std::vector<unsigned long>{}, ia); + auto c = make_s(Tag(), std::vector<unsigned>(), ia); + a(0, weight(2u)); + a(1, weight(2u)); + auto b = a; + c(0, weight(2u)); + c(1, weight(2u)); + auto a2 = a; + a2 += c; + BOOST_TEST_EQ(a2, (a + b)); + auto a3 = a; + a3 *= c; + BOOST_TEST_EQ(a3, (a * b)); + auto a4 = a; + a4 -= c; + BOOST_TEST_EQ(a4, (a - b)); + auto a5 = a; + a5 /= c; + BOOST_TEST_EQ(a5, (a / b)); + } + + // add operators with weighted storage + { + auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2); + auto a = make_s(Tag(), std::vector<accumulators::weighted_sum<>>(), ia); + auto b = make_s(Tag(), std::vector<accumulators::weighted_sum<>>(), ia); + + a(0); + BOOST_TEST_EQ(a.at(0).variance(), 1); + b(weight(3), 1); + BOOST_TEST_EQ(b.at(1).variance(), 9); + auto c = a; + c += b; + BOOST_TEST_EQ(c.at(0).value(), 1); + BOOST_TEST_EQ(c.at(0).variance(), 1); + BOOST_TEST_EQ(c.at(1).value(), 3); + BOOST_TEST_EQ(c.at(1).variance(), 9); + auto d = a; + d += b; + BOOST_TEST_EQ(d.at(0).value(), 1); + BOOST_TEST_EQ(d.at(0).variance(), 1); + BOOST_TEST_EQ(d.at(1).value(), 3); + BOOST_TEST_EQ(d.at(1).variance(), 9); + + // add unweighted histogram + auto e = make_s(Tag(), std::vector<int>(), ia); + std::fill(e.begin(), e.end(), 2); + + d += e; + BOOST_TEST_EQ(d.at(0).value(), 3); + BOOST_TEST_EQ(d.at(0).variance(), 3); + BOOST_TEST_EQ(d.at(1).value(), 5); + BOOST_TEST_EQ(d.at(1).variance(), 11); + } + + // bad operations + { + auto a = make(Tag(), axis::integer<>(0, 2)); + auto b = make(Tag(), axis::integer<>(0, 3)); + BOOST_TEST_THROWS(a += b, std::invalid_argument); + BOOST_TEST_THROWS(a -= b, std::invalid_argument); + BOOST_TEST_THROWS(a *= b, std::invalid_argument); + BOOST_TEST_THROWS(a /= b, std::invalid_argument); + } + + // scaling + { + auto b = make_s(Tag{}, dummy_storage<double, true>{}, axis::integer<>(0, 1)); + b(0); + BOOST_TEST_EQ(b[0], 1); + b *= 2; // intentionally does not do anything + BOOST_TEST_EQ(b[0], 1); + + auto c = make_s(Tag{}, dummy_storage<double, false>{}, axis::integer<>(0, 1)); + c(0); + BOOST_TEST_EQ(c[0], 1); + c *= 2; // this calls *= on each element + BOOST_TEST_EQ(c[0], 2); + + using h1_t = decltype( + make_s(Tag{}, dummy_storage<unscaleable, false>{}, axis::integer<>(0, 1))); + BOOST_TEST_NOT((detail::has_operator_rmul<h1_t, double>::value)); + + using h2_t = decltype( + make_s(Tag{}, dummy_storage<unscaleable, true>{}, axis::integer<>(0, 1))); + BOOST_TEST_NOT((detail::has_operator_rmul<h2_t, double>::value)); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_ostream_test.cpp b/src/boost/libs/histogram/test/histogram_ostream_test.cpp new file mode 100644 index 000000000..5047ba192 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_ostream_test.cpp @@ -0,0 +1,249 @@ +// Copyright 2019 Przemyslaw Bartosik +// Copyright 2019 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/accumulators/mean.hpp> +#include <boost/histogram/accumulators/ostream.hpp> +#include <boost/histogram/axis/category.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/option.hpp> +#include <boost/histogram/axis/regular.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <limits> +#include <sstream> +#include <string> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +template <class Histogram> +auto str(const Histogram& h, const unsigned width = 0) { + std::ostringstream os; + // BEGIN and END make nicer error messages + os << "BEGIN\n" << std::setw(width) << h << "END"; + return os.str(); +} + +template <class Tag> +void run_tests() { + using R = axis::regular<>; + using R2 = + axis::regular<double, boost::use_default, axis::null_type, axis::option::none_t>; + using R3 = axis::regular<double, axis::transform::log>; + using C = axis::category<std::string>; + using I = axis::integer<>; + + // regular + { + auto h = make(Tag(), R(3, -0.5, 1.0)); + h.at(0) = 1; + h.at(1) = 10; + h.at(2) = 5; + + const auto expected = + "BEGIN\n" + "histogram(regular(3, -0.5, 1, options=underflow | overflow))\n" + " +------------------------------------------------------------+\n" + "[-inf, -0.5) 0 | |\n" + "[-0.5, 0) 1 |====== |\n" + "[ 0, 0.5) 10 |=========================================================== |\n" + "[ 0.5, 1) 5 |============================== |\n" + "[ 1, inf) 0 | |\n" + " +------------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // regular, narrow + { + auto h = make(Tag(), R2(3, -0.5, 1.0)); + h.at(0) = 1; + h.at(1) = 10; + h.at(2) = 2; + + const auto expected = "BEGIN\n" + "histogram(regular(3, -0.5, 1, options=none))\n" + " +-----------------------+\n" + "[-0.5, 0) 1 |== |\n" + "[ 0, 0.5) 10 |====================== |\n" + "[ 0.5, 1) 2 |==== |\n" + " +-----------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h, 40).c_str()); + + // too narrow + BOOST_TEST_CSTR_EQ("BEGIN\n" + "histogram(regular(3, -0.5, 1, options=none))END", + str(h, 10).c_str()); + } + + // regular2 + { + auto h = make(Tag(), R2(3, -0.5, 1.0)); + h.at(0) = 1; + h.at(1) = -5; + h.at(2) = 2; + + const auto expected = + "BEGIN\n" + "histogram(regular(3, -0.5, 1, options=none))\n" + " +-------------------------------------------------------------+\n" + "[-0.5, 0) 1 | ========= |\n" + "[ 0, 0.5) -5 |=========================================== |\n" + "[ 0.5, 1) 2 | ================= |\n" + " +-------------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // regular with log + { + auto h = make(Tag(), R3(6, 1e-3, 1e3, "foo")); + + const auto expected = + "BEGIN\n" + "histogram(regular_log(6, 0.001, 1000, metadata=\"foo\", options=underflow | " + "overflow))\n" + " +-----------------------------------------------------------+\n" + "[ 0, 0.001) 0 | |\n" + "[0.001, 0.01) 0 | |\n" + "[ 0.01, 0.1) 0 | |\n" + "[ 0.1, 1) 0 | |\n" + "[ 1, 10) 0 | |\n" + "[ 10, 100) 0 | |\n" + "[ 100, 1000) 0 | |\n" + "[ 1000, inf) 0 | |\n" + " +-----------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // integer + { + auto h = make(Tag(), I(0, 1)); + h.at(0) = -10; + h.at(1) = 5; + + const auto expected = + "BEGIN\n" + "histogram(integer(0, 1, options=underflow | overflow))\n" + " +---------------------------------------------------------------------+\n" + "-1 0 | |\n" + " 0 -10 |============================================= |\n" + " 1 5 | ======================= |\n" + " +---------------------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // catorgy<string> + { + auto h = make(Tag(), C({"a", "bb", "ccc", "dddd"})); + h.at(0) = 1.23; + h.at(1) = 1; + h.at(2) = 1.2345789e-3; + h.at(3) = 1.2345789e-12; + h.at(4) = std::numeric_limits<double>::quiet_NaN(); + + const auto expected = + "BEGIN\n" + "histogram(category(\"a\", \"bb\", \"ccc\", \"dddd\", options=overflow))\n" + " +------------------------------------------------------------+\n" + " a 1.23 |=========================================================== |\n" + " bb 1 |================================================ |\n" + " ccc 0.001235 | |\n" + " dddd 1.235e-12 | |\n" + "other nan | |\n" + " +------------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // histogram with axis that has no value method + { + struct minimal_axis { + int index(int x) const { return x % 2; } + int size() const { return 2; } + }; + + auto h = make(Tag(), minimal_axis{}); + h.at(0) = 3; + h.at(1) = 4; + + const auto expected = + "BEGIN\n" + "histogram(<unstreamable>)\n" + " +------------------------------------------------------------------------+\n" + "0 3 |===================================================== |\n" + "1 4 |======================================================================= |\n" + " +------------------------------------------------------------------------+\n" + "END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // fallback for 2D + { + auto h = make(Tag(), R(1, -1, 1), R(2, -4, 7)); + h.at(-1, 0) = 1000; + h.at(-1, -1) = 123; + h.at(1, 0) = 1.23456789; + h.at(-1, 2) = std::numeric_limits<double>::quiet_NaN(); + + const auto expected = + "BEGIN\n" + "histogram(\n" + " regular(1, -1, 1, options=underflow | overflow)\n" + " regular(2, -4, 7, options=underflow | overflow)\n" + " (-1 -1): 123 ( 0 -1): 0 ( 1 -1): 0 (-1 0): 1000 \n" + " ( 0 0): 0 ( 1 0): 1.235 (-1 1): 0 ( 0 1): 0 \n" + " ( 1 1): 0 (-1 2): nan ( 0 2): 0 ( 1 2): 0 \n" + ")END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + // fallback for profile + { + auto h = make_s(Tag(), profile_storage(), R(1, -1, 1)); + h.at(0) = accumulators::mean<>(10, 100, 1000); + + const auto expected = "BEGIN\n" + "histogram(\n" + " regular(1, -1, 1, options=underflow | overflow)\n" + " (-1): mean(0, 0, -0) ( 0): mean(10, 100, 1000)\n" + " ( 1): mean(0, 0, -0) \n" + ")END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + { + // cannot make empty static histogram + auto h = histogram<std::vector<axis::regular<>>>(); + + const auto expected = "BEGIN\n" + "histogram()END"; + + BOOST_TEST_CSTR_EQ(expected, str(h).c_str()); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_serialization_test.cpp b/src/boost/libs/histogram/test/histogram_serialization_test.cpp new file mode 100644 index 000000000..29f6e5ae2 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_serialization_test.cpp @@ -0,0 +1,50 @@ +// 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/assert.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/serialization.hpp> +#include <cmath> +#include <string> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" +#include "utility_serialization.hpp" + +using namespace boost::histogram; + +template <typename Tag> +void run_tests(const std::string& filename) { + // histogram_serialization + namespace tr = axis::transform; + using def = use_default; + using axis::option::none_t; + auto a = + make(Tag(), axis::regular<double, def, def, none_t>(1, -1, 1, "reg"), + axis::circular<float, def, none_t>(1, 0.0, 1.0, "cir"), + axis::regular<double, tr::log, def, none_t>(1, 1, std::exp(2), "reg-log"), + axis::regular<double, tr::pow, std::vector<int>, axis::option::overflow_t>( + tr::pow(0.5), 1, 1, 100, {1, 2, 3}), + axis::variable<double, def, none_t>({1.5, 2.5}, "var"), + axis::category<int, def, none_t>{3, 1}, + axis::integer<int, axis::null_type, none_t>(1, 2)); + a(0.5, 0.2, 2, 20, 2.2, 1, 1); + print_xml(filename, a); + + auto b = decltype(a)(); + BOOST_TEST_NE(a, b); + load_xml(filename, b); + BOOST_TEST_EQ(a, b); +} + +int main(int argc, char** argv) { + BOOST_ASSERT(argc == 2); + run_tests<static_tag>(join(argv[1], "histogram_serialization_test_static.xml")); + run_tests<dynamic_tag>(join(argv[1], "histogram_serialization_test_dynamic.xml")); + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_serialization_test_dynamic.xml b/src/boost/libs/histogram/test/histogram_serialization_test_dynamic.xml new file mode 100644 index 000000000..3411c7586 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_serialization_test_dynamic.xml @@ -0,0 +1,122 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <axes class_id="1" tracking_level="0" version="0"> + <count>7</count> + <item_version>0</item_version> + <item class_id="2" tracking_level="0" version="0"> + <variant class_id="3" tracking_level="1" version="0" object_id="_0"> + <which>0</which> + <value class_id="4" tracking_level="0" version="0"> + <transform class_id="5" tracking_level="0" version="0"></transform> + <size>1</size> + <meta>reg</meta> + <min>-1.00000000000000000e+00</min> + <delta>2.00000000000000000e+00</delta> + </value> + </variant> + </item> + <item> + <variant object_id="_1"> + <which>1</which> + <value class_id="6" tracking_level="0" version="0"> + <transform></transform> + <size>1</size> + <meta>cir</meta> + <min>0.000000000e+00</min> + <delta>1.000000000e+00</delta> + </value> + </variant> + </item> + <item> + <variant object_id="_2"> + <which>2</which> + <value class_id="7" tracking_level="0" version="0"> + <transform class_id="8" tracking_level="0" version="0"></transform> + <size>1</size> + <meta>reg-log</meta> + <min>0.00000000000000000e+00</min> + <delta>2.00000000000000000e+00</delta> + </value> + </variant> + </item> + <item> + <variant object_id="_3"> + <which>3</which> + <value class_id="9" tracking_level="0" version="0"> + <transform class_id="10" tracking_level="0" version="0"> + <power>5.00000000000000000e-01</power> + </transform> + <size>1</size> + <meta> + <count>3</count> + <item_version>0</item_version> + <item>1</item> + <item>2</item> + <item>3</item> + </meta> + <min>1.00000000000000000e+00</min> + <delta>9.00000000000000000e+00</delta> + </value> + </variant> + </item> + <item> + <variant object_id="_4"> + <which>4</which> + <value class_id="12" tracking_level="0" version="0"> + <seq> + <count>2</count> + <item_version>0</item_version> + <item>1.50000000000000000e+00</item> + <item>2.50000000000000000e+00</item> + </seq> + <meta>var</meta> + </value> + </variant> + </item> + <item> + <variant object_id="_5"> + <which>5</which> + <value class_id="14" tracking_level="0" version="0"> + <seq> + <count>2</count> + <item_version>0</item_version> + <item>3</item> + <item>1</item> + </seq> + <meta></meta> + </value> + </variant> + </item> + <item> + <variant object_id="_6"> + <which>6</which> + <value class_id="15" tracking_level="0" version="0"> + <size>1</size> + <meta class_id="16" tracking_level="0" version="0"></meta> + <min>1</min> + </value> + </variant> + </item> + </axes> + <storage class_id="17" tracking_level="0" version="0"> + <type>0</type> + <size>4</size> + <buffer> + <item>0</item> + <item>0</item> + <item>1</item> + <item>0</item> + </buffer> + </storage> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/histogram_serialization_test_static.xml b/src/boost/libs/histogram/test/histogram_serialization_test_static.xml new file mode 100644 index 000000000..956997802 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_serialization_test_static.xml @@ -0,0 +1,85 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <axes class_id="1" tracking_level="0" version="0"> + <item class_id="2" tracking_level="0" version="0"> + <transform class_id="3" tracking_level="0" version="0"></transform> + <size>1</size> + <meta>reg</meta> + <min>-1.00000000000000000e+00</min> + <delta>2.00000000000000000e+00</delta> + </item> + <item class_id="4" tracking_level="0" version="0"> + <transform></transform> + <size>1</size> + <meta>cir</meta> + <min>0.000000000e+00</min> + <delta>1.000000000e+00</delta> + </item> + <item class_id="5" tracking_level="0" version="0"> + <transform class_id="6" tracking_level="0" version="0"></transform> + <size>1</size> + <meta>reg-log</meta> + <min>0.00000000000000000e+00</min> + <delta>2.00000000000000000e+00</delta> + </item> + <item class_id="7" tracking_level="0" version="0"> + <transform class_id="8" tracking_level="0" version="0"> + <power>5.00000000000000000e-01</power> + </transform> + <size>1</size> + <meta> + <count>3</count> + <item_version>0</item_version> + <item>1</item> + <item>2</item> + <item>3</item> + </meta> + <min>1.00000000000000000e+00</min> + <delta>9.00000000000000000e+00</delta> + </item> + <item class_id="10" tracking_level="0" version="0"> + <seq> + <count>2</count> + <item_version>0</item_version> + <item>1.50000000000000000e+00</item> + <item>2.50000000000000000e+00</item> + </seq> + <meta>var</meta> + </item> + <item class_id="12" tracking_level="0" version="0"> + <seq> + <count>2</count> + <item_version>0</item_version> + <item>3</item> + <item>1</item> + </seq> + <meta></meta> + </item> + <item class_id="13" tracking_level="0" version="0"> + <size>1</size> + <meta class_id="14" tracking_level="0" version="0"></meta> + <min>1</min> + </item> + </axes> + <storage class_id="15" tracking_level="0" version="0"> + <type>0</type> + <size>4</size> + <buffer> + <item>0</item> + <item>0</item> + <item>1</item> + <item>0</item> + </buffer> + </storage> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/histogram_test.cpp b/src/boost/libs/histogram/test/histogram_test.cpp new file mode 100644 index 000000000..81ac07b3e --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_test.cpp @@ -0,0 +1,509 @@ +// 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/ignore_unused.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators.hpp> +#include <boost/histogram/accumulators/ostream.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <sstream> +#include <stdexcept> +#include <tuple> +#include <utility> +#include <vector> +#include "is_close.hpp" +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_allocator.hpp" +#include "utility_axis.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::literals; // to get _c suffix + +template <class A, class S> +void pass_histogram(boost::histogram::histogram<A, S>& h) { + BOOST_TEST_EQ(h.at(0), 0); + BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(h.at(2), 0); + BOOST_TEST_EQ(h.axis(0_c), axis::integer<>(0, 3)); +} + +template <class Tag> +void run_tests() { + // init_1 + { + auto h = make(Tag(), axis::regular<>{3, -1, 1}); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.size(), 5); + BOOST_TEST_EQ(h.axis(0_c).size(), 3); + BOOST_TEST_EQ(h.axis().size(), 3); + auto h2 = make_s(Tag(), std::vector<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}, + axis::circular<>{2, 0, 360}, 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(), std::vector<unsigned>(), axis::regular<>{3, -1, 1}, + axis::integer<>{-1, 2}, axis::circular<>{2, 0, 360}, + axis::variable<>{-1, 0, 1}, axis::category<>{{3, 1, 2}}); + BOOST_TEST_EQ(h2, h); + } + + // copy_ctor + { + auto h = make(Tag(), axis::integer<>{0, 2}, axis::integer<>{0, 3}); + h(0, 0); + auto h2 = decltype(h)(h); + BOOST_TEST_EQ(h2, h); + auto h3 = + histogram<std::tuple<axis::integer<>, axis::integer<>>, dense_storage<double>>(h); + BOOST_TEST_EQ(h3, h); + } + + // copy_assign + { + auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 2)); + h(0, 0); + auto h2 = decltype(h)(); + BOOST_TEST_NE(h, h2); + h2 = h; + BOOST_TEST_EQ(h, h2); + auto h3 = + histogram<std::tuple<axis::integer<>, axis::integer<>>, dense_storage<double>>(); + h3 = h; + BOOST_TEST_EQ(h, h3); + } + + // move + { + auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 2)); + h(0, 0); + const auto href = h; + decltype(h) h2(std::move(h)); + BOOST_TEST_EQ(algorithm::sum(h), 0); + BOOST_TEST_EQ(h.size(), 0); + BOOST_TEST_EQ(h2, href); + decltype(h) h3; + h3 = std::move(h2); + BOOST_TEST_EQ(algorithm::sum(h2), 0); + BOOST_TEST_EQ(h2.size(), 0); + BOOST_TEST_EQ(h3, href); + } + + // axis methods + { + auto a = make(Tag(), axis::integer<double>(1, 2, "foo")); + BOOST_TEST_EQ(a.axis().size(), 1); + BOOST_TEST_EQ(a.axis().bin(0).lower(), 1); + BOOST_TEST_EQ(a.axis().bin(0).upper(), 2); + BOOST_TEST_EQ(a.axis().metadata(), "foo"); + unsafe_access::axis(a, 0).metadata() = "bar"; + BOOST_TEST_EQ(a.axis().metadata(), "bar"); + + auto b = make(Tag(), axis::integer<double>(1, 2, "foo"), axis::integer<>(1, 3)); + + // check static access + BOOST_TEST_EQ(b.axis(0_c).size(), 1); + BOOST_TEST_EQ(b.axis(0_c).bin(0).lower(), 1); + BOOST_TEST_EQ(b.axis(0_c).bin(0).upper(), 2); + BOOST_TEST_EQ(b.axis(1_c).size(), 2); + BOOST_TEST_EQ(b.axis(1_c).bin(0), 1); + BOOST_TEST_EQ(b.axis(1_c).bin(1), 2); + unsafe_access::axis(b, 1_c).metadata() = "bar"; + BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo"); + BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar"); + + // check dynamic access + BOOST_TEST_EQ(b.axis(0).size(), 1); + BOOST_TEST_EQ(b.axis(0).bin(0).lower(), 1); + BOOST_TEST_EQ(b.axis(0).bin(0).upper(), 2); + BOOST_TEST_EQ(b.axis(1).size(), 2); + BOOST_TEST_EQ(b.axis(1).bin(0), 1); + BOOST_TEST_EQ(b.axis(1).bin(1), 2); + BOOST_TEST_EQ(b.axis(0).metadata(), "foo"); + BOOST_TEST_EQ(b.axis(1).metadata(), "bar"); + unsafe_access::axis(b, 0).metadata() = "baz"; + BOOST_TEST_EQ(b.axis(0).metadata(), "baz"); + + auto c = make(Tag(), axis::category<>({1, 2})); + BOOST_TEST_EQ(c.axis().size(), 2); + unsafe_access::axis(c, 0).metadata() = "foo"; + BOOST_TEST_EQ(c.axis().metadata(), "foo"); + // need to cast here for this to work with Tag == dynamic_tag, too + const auto& ca = axis::get<axis::category<>>(c.axis()); + BOOST_TEST_EQ(ca.bin(0), 1); + const auto& ca2 = axis::get<axis::category<>>(c.axis(0)); + BOOST_TEST_EQ(&ca2, &ca); + } + + // equal_compare + { + auto a = make(Tag(), axis::integer<>(0, 2)); + auto b = make(Tag(), axis::integer<>(0, 2), axis::integer<>(0, 3)); + BOOST_TEST(a != b); + BOOST_TEST(b != a); + auto c = make(Tag(), axis::integer<>(0, 2)); + BOOST_TEST(b != c); + BOOST_TEST(c != b); + BOOST_TEST(a == c); + BOOST_TEST(c == a); + auto d = make(Tag(), axis::regular<>(2, 0, 1)); + BOOST_TEST(c != d); + BOOST_TEST(d != c); + c(0); + BOOST_TEST(a != c); + BOOST_TEST(c != a); + a(0); + BOOST_TEST(a == c); + BOOST_TEST(c == a); + a(0); + BOOST_TEST(a != c); + BOOST_TEST(c != a); + } + + // 1D + { + auto h = make(Tag(), axis::integer<int, axis::null_type>{0, 2}); + h(0); + auto i = h(0); + BOOST_TEST(i == h.begin() + 1); // +1 because of underflow + i = h(-1); + BOOST_TEST(i == h.begin()); // underflow + i = h(10); + BOOST_TEST(i == h.end() - 1); // overflow + + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); + BOOST_TEST_EQ(algorithm::sum(h), 4); + + BOOST_TEST_EQ(h.at(-1), 1); + BOOST_TEST_EQ(h.at(0), 2); + BOOST_TEST_EQ(h.at(1), 0); + BOOST_TEST_EQ(h.at(2), 1); + } + + // 1D no *flow + { + auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2)); + h(0); + auto i = h(-0); + BOOST_TEST(i == h.begin()); + i = h(-1); + BOOST_TEST(i == h.end()); + i = h(10); + BOOST_TEST(i == h.end()); + + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); + BOOST_TEST_EQ(algorithm::sum(h), 2); + + BOOST_TEST_EQ(h.at(0), 2); + BOOST_TEST_EQ(h.at(1), 0); + } + + // 1D category axis + { + auto h = make(Tag(), axis::category<>({1, 2})); + h(1); + h(2); + h(4); + h(5); + + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); + BOOST_TEST_EQ(algorithm::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 + } + + // 1D weight + { + auto h = make_s(Tag(), weight_storage(), axis::integer<>(0, 2)); + h(-1); + h(0); + h(weight(0.5), 0); + h(1); + h(weight(2), 2); + + BOOST_TEST_EQ(algorithm::sum(h).value(), 5.5); + BOOST_TEST_EQ(algorithm::sum(h).variance(), 7.25); + + BOOST_TEST_EQ(h[-1].value(), 1); + BOOST_TEST_EQ(h[-1].variance(), 1); + BOOST_TEST_EQ(h[0].value(), 1.5); + BOOST_TEST_EQ(h[0].variance(), 1.25); + BOOST_TEST_EQ(h[1].value(), 1); + BOOST_TEST_EQ(h[1].variance(), 1); + BOOST_TEST_EQ(h[2].value(), 2); + BOOST_TEST_EQ(h[2].variance(), 4); + } + + // 1D profile + { + auto h = make_s(Tag(), profile_storage(), axis::integer<>(0, 2)); + + h(0, sample(1)); + h(0, sample(2)); + h(0, sample(3)); + h(sample(4), 1); + h(sample(5), 1); + h(sample(6), 1); + + BOOST_TEST_EQ(h[0].count(), 3); + BOOST_TEST_EQ(h[0].value(), 2); + BOOST_TEST_EQ(h[0].variance(), 1); + BOOST_TEST_EQ(h[1].count(), 3); + BOOST_TEST_EQ(h[1].value(), 5); + BOOST_TEST_EQ(h[1].variance(), 1); + } + + // 1D weighted profile + { + auto h = make_s(Tag(), weighted_profile_storage(), axis::integer<>(0, 2)); + + h(0, sample(1)); + h(sample(1), 0); + + h(0, weight(2), sample(3)); + h(0, sample(5), weight(2)); + + h(weight(2), 1, sample(1)); + h(sample(2), 1, weight(2)); + + h(weight(2), sample(3), 1); + h(sample(4), weight(2), 1); + + BOOST_TEST_EQ(h[0].sum_of_weights(), 6); + BOOST_TEST_EQ(h[0].value(), 3); + BOOST_TEST_EQ(h[1].sum_of_weights(), 8); + BOOST_TEST_EQ(h[1].value(), 2.5); + } + + // 2D + { + auto h = make(Tag(), axis::integer<>(-1, 1), + axis::integer<int, axis::null_type, axis::option::none_t>(-1, 2)); + h(-1, -1); + h(-1, 0); + h(-1, -10); + h(-10, 0); + + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.axis(0_c).size(), 2); + BOOST_TEST_EQ(h.axis(1_c).size(), 3); + BOOST_TEST_EQ(algorithm::sum(h), 3); + + BOOST_TEST_EQ(h.at(-1, 0), 0); + BOOST_TEST_EQ(h.at(-1, 1), 1); + BOOST_TEST_EQ(h.at(-1, 2), 0); + + BOOST_TEST_EQ(h.at(0, 0), 1); + BOOST_TEST_EQ(h.at(0, 1), 1); + BOOST_TEST_EQ(h.at(0, 2), 0); + + BOOST_TEST_EQ(h.at(1, 0), 0); + BOOST_TEST_EQ(h.at(1, 1), 0); + BOOST_TEST_EQ(h.at(1, 2), 0); + + BOOST_TEST_EQ(h.at(2, 0), 0); + BOOST_TEST_EQ(h.at(2, 1), 0); + BOOST_TEST_EQ(h.at(2, 2), 0); + } + + // 2D weight + { + auto h = make_s(Tag(), weight_storage(), axis::integer<>(-1, 1), + axis::integer<int, axis::null_type, axis::option::none_t>(-1, 2)); + h(-1, 0); // -> 0, 1 + h(weight(10), -1, -1); // -> 0, 0 + h(weight(5), -1, -10); // is ignored + h(weight(7), -10, 0); // -> -1, 1 + + BOOST_TEST_EQ(algorithm::sum(h).value(), 18); + BOOST_TEST_EQ(algorithm::sum(h).variance(), 150); + + BOOST_TEST_EQ(h.at(-1, 0).value(), 0); + BOOST_TEST_EQ(h.at(-1, 1).value(), 7); + BOOST_TEST_EQ(h.at(-1, 2).value(), 0); + + BOOST_TEST_EQ(h.at(0, 0).value(), 10); + BOOST_TEST_EQ(h.at(0, 1).value(), 1); + BOOST_TEST_EQ(h.at(0, 2).value(), 0); + + BOOST_TEST_EQ(h.at(1, 0).value(), 0); + BOOST_TEST_EQ(h.at(1, 1).value(), 0); + BOOST_TEST_EQ(h.at(1, 2).value(), 0); + + BOOST_TEST_EQ(h.at(2, 0).value(), 0); + BOOST_TEST_EQ(h.at(2, 1).value(), 0); + BOOST_TEST_EQ(h.at(2, 2).value(), 0); + + BOOST_TEST_EQ(h.at(-1, 0).variance(), 0); + BOOST_TEST_EQ(h.at(-1, 1).variance(), 49); + BOOST_TEST_EQ(h.at(-1, 2).variance(), 0); + + BOOST_TEST_EQ(h.at(0, 0).variance(), 100); + BOOST_TEST_EQ(h.at(0, 1).variance(), 1); + BOOST_TEST_EQ(h.at(0, 2).variance(), 0); + + BOOST_TEST_EQ(h.at(1, 0).variance(), 0); + BOOST_TEST_EQ(h.at(1, 1).variance(), 0); + BOOST_TEST_EQ(h.at(1, 2).variance(), 0); + + BOOST_TEST_EQ(h.at(2, 0).variance(), 0); + BOOST_TEST_EQ(h.at(2, 1).variance(), 0); + BOOST_TEST_EQ(h.at(2, 2).variance(), 0); + } + + // 3D weight + { + auto h = make_s(Tag(), weight_storage(), 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(i, j, k, weight(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) { + 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)); + } + } + } + } + + // 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))); + BOOST_TEST_EQ(h.at(0), 1); + BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(h.at(2), 1); + BOOST_TEST_EQ(algorithm::sum(h), 3); + + auto a = std::vector<double>(); + // walks over all bins, including underflow and overflow + std::partial_sum(h.begin(), h.end(), std::back_inserter(a)); + BOOST_TEST_EQ(a.size(), 5); + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[1], 1); + BOOST_TEST_EQ(a[2], 2); + BOOST_TEST_EQ(a[3], 3); + BOOST_TEST_EQ(a[4], 3); + } + + // histogram reset + { + auto h = make(Tag(), axis::integer<int, axis::null_type, axis::option::none_t>(0, 2)); + h(0); + h(1); + BOOST_TEST_EQ(h.at(0), 1); + BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(algorithm::sum(h), 2); + h.reset(); + BOOST_TEST_EQ(h.at(0), 0); + BOOST_TEST_EQ(h.at(1), 0); + BOOST_TEST_EQ(algorithm::sum(h), 0); + } + + // using containers for input and output + { + auto h = make(Tag(), axis::integer<>(0, 2), axis::integer<double>(2, 4)); + // tuple in + h(std::make_tuple(0, 2.0)); + h(std::make_tuple(1, 3.0)); + + auto i00 = std::make_tuple(0, 0); + auto i11 = std::make_tuple(1, 1); + + // tuple out + BOOST_TEST_EQ(h.at(i00), 1); + BOOST_TEST_EQ(h[i00], 1); + BOOST_TEST_EQ(h[i11], 1); + + // iterable out + int j11[] = {1, 1}; + BOOST_TEST_EQ(h.at(j11), 1); + BOOST_TEST_EQ(h[j11], 1); + int j111[] = {1, 1, 1}; + boost::ignore_unused(j111); + BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument); + int j13[] = {1, 3}; + boost::ignore_unused(j13); + BOOST_TEST_THROWS((void)h.at(j13), std::out_of_range); + + // tuple with weight + h(std::make_tuple(weight(2), 0, 2.0)); + h(std::make_tuple(1, 3.0, weight(2))); + + BOOST_TEST_EQ(h.at(i00), 3); + BOOST_TEST_EQ(h[i00], 3); + + // test special case of 1-dimensional histogram, which should unpack + // 1-dimensional tuple normally, but forward larger tuples to the axis + auto h1 = make(Tag(), axis::integer<>(0, 2)); + h1(std::make_tuple(0)); // as if one had passed 0 directly + BOOST_TEST_EQ(h1.at(std::make_tuple(0)), 1); // as if one had passed 0 directly + } + + // bad bin access + { + auto h = make(Tag(), axis::integer<>(0, 1), axis::integer<>(0, 1)); + BOOST_TEST_THROWS(h.at(0, 2), std::out_of_range); + BOOST_TEST_THROWS(h.at(std::make_tuple(2, 0)), std::out_of_range); + } + + // pass histogram to function + { + auto h = make(Tag(), axis::integer<>(0, 3)); + h(1); + pass_histogram(h); + } + + // allocator support + { + tracing_allocator_db db; + { + tracing_allocator<char> a(db); + auto h = make_s(Tag(), std::vector<int, tracing_allocator<int>>(a), + axis::integer<>(0, 1000)); + h(0); + } + + // int allocation for std::vector + BOOST_TEST_EQ(db.at<int>().first, 0); + BOOST_TEST_EQ(db.at<int>().second, 1002); + + if (Tag()) { // axis::variant allocation, only for dynamic histogram + using T = axis::variant<axis::integer<>>; + BOOST_TEST_EQ(db.at<T>().first, 0); + // may be zero if vector uses small-vector-optimisation + BOOST_TEST_LE(db.at<T>().second, 1); + } + } +} + +int main() { + run_tests<static_tag>(); + run_tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/histogram_threaded_test.cpp b/src/boost/libs/histogram/test/histogram_threaded_test.cpp new file mode 100644 index 000000000..1db41ea91 --- /dev/null +++ b/src/boost/libs/histogram/test/histogram_threaded_test.cpp @@ -0,0 +1,74 @@ +// Copyright 2019 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/accumulators/thread_safe.hpp> +#include <boost/histogram/algorithm/sum.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <iostream> +#include <random> +#include <thread> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; + +constexpr auto n_fill = 80000; +static_assert(n_fill % 4 == 0, "must be multiple of 4"); + +template <class Tag, class A1, class A2, class X, class Y> +void fill_test(const A1& a1, const A2& a2, const X& x, const Y& y) { + auto h1 = make_s(Tag{}, dense_storage<int>(), a1, a2); + auto xy = {x, y}; + h1.fill(xy); + + auto h2 = make_s(Tag{}, dense_storage<accumulators::thread_safe<int>>(), a1, a2); + auto run = [&h2, &x, &y](int k) { + constexpr auto shift = n_fill / 4; + auto xit = x.cbegin() + k * shift; + auto yit = y.cbegin() + k * shift; + for (unsigned i = 0; i < shift; ++i) h2(*xit++, *yit++); + }; + + std::thread t1([&] { run(0); }); + std::thread t2([&] { run(1); }); + std::thread t3([&] { run(2); }); + std::thread t4([&] { run(3); }); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + + BOOST_TEST_EQ(algorithm::sum(h1), n_fill); + BOOST_TEST_EQ(algorithm::sum(h2), n_fill); + BOOST_TEST_EQ(h1, h2); +} + +template <class T> +void tests() { + std::mt19937 gen(1); + std::uniform_int_distribution<> id(-5, 5); + std::vector<int> vi(n_fill), vj(n_fill); + std::generate(vi.begin(), vi.end(), [&] { return id(gen); }); + std::generate(vj.begin(), vj.end(), [&] { return id(gen); }); + + using i = axis::integer<>; + using ig = axis::integer<int, use_default, axis::option::growth_t>; + fill_test<T>(i{0, 1}, i{0, 1}, vi, vj); + fill_test<T>(ig{0, 1}, i{0, 1}, vi, vj); + fill_test<T>(i{0, 1}, ig{0, 1}, vi, vj); + fill_test<T>(ig{0, 1}, ig{0, 1}, vi, vj); +} + +int main() { + tests<static_tag>(); + tests<dynamic_tag>(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/indexed_test.cpp b/src/boost/libs/histogram/test/indexed_test.cpp new file mode 100644 index 000000000..0b09ad2fe --- /dev/null +++ b/src/boost/libs/histogram/test/indexed_test.cpp @@ -0,0 +1,170 @@ +// 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 <algorithm> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/axis/integer.hpp> +#include <boost/histogram/axis/ostream.hpp> +#include <boost/histogram/axis/variable.hpp> +#include <boost/histogram/histogram.hpp> +#include <boost/histogram/indexed.hpp> +#include <boost/histogram/literals.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/mp11/algorithm.hpp> +#include <boost/mp11/list.hpp> +#include <iterator> +#include <ostream> +#include <type_traits> +#include <vector> +#include "throw_exception.hpp" +#include "utility_histogram.hpp" + +using namespace boost::histogram; +using namespace boost::histogram::literals; +using namespace boost::mp11; + +template <class IsDynamic, class Coverage> +void run_1d_tests(mp_list<IsDynamic, Coverage>) { + auto h = make(IsDynamic(), axis::integer<>(0, 3)); + h(-1, weight(1)); + h(0, weight(2)); + h(1, weight(3)); + h(2, weight(4)); + h(3, weight(5)); + + auto ind = indexed(h, Coverage()); + auto it = ind.begin(); + BOOST_TEST_EQ(it->indices().size(), 1); + BOOST_TEST_EQ(it->indices()[0], Coverage() == coverage::all ? -1 : 0); + + if (Coverage() == coverage::all) { + BOOST_TEST_EQ(it->index(0), -1); + BOOST_TEST_EQ(**it, 1); + BOOST_TEST_EQ(it->bin(0), h.axis().bin(-1)); + ++it; + } + BOOST_TEST_EQ(it->index(0), 0); + BOOST_TEST_EQ(**it, 2); + BOOST_TEST_EQ(it->bin(0), h.axis().bin(0)); + ++it; + BOOST_TEST_EQ(it->index(0), 1); + BOOST_TEST_EQ(**it, 3); + BOOST_TEST_EQ(it->bin(0), h.axis().bin(1)); + ++it; + // check post-increment + auto prev = it++; + BOOST_TEST_EQ(prev->index(0), 2); + BOOST_TEST_EQ(**prev, 4); + BOOST_TEST_EQ(prev->bin(0), h.axis().bin(2)); + if (Coverage() == coverage::all) { + BOOST_TEST_EQ(it->index(0), 3); + BOOST_TEST_EQ(**it, 5); + BOOST_TEST_EQ(it->bin(0), h.axis().bin(3)); + ++it; + } + BOOST_TEST(it == ind.end()); + + for (auto&& x : indexed(h, Coverage())) *x = 0; + + for (auto&& x : indexed(static_cast<const decltype(h)&>(h), Coverage())) + BOOST_TEST_EQ(*x, 0); +} + +template <class IsDynamic, class Coverage> +void run_3d_tests(mp_list<IsDynamic, Coverage>) { + auto h = make_s(IsDynamic(), std::vector<int>(), axis::integer<>(0, 2), + axis::integer<int, axis::null_type, axis::option::none_t>(0, 3), + axis::integer<int, axis::null_type, axis::option::overflow_t>(0, 4)); + + for (int i = -1; i < 3; ++i) + for (int j = -1; j < 4; ++j) + for (int k = -1; k < 5; ++k) h(i, j, k, weight(i * 100 + j * 10 + k)); + + auto ind = indexed(h, Coverage()); + auto it = ind.begin(); + BOOST_TEST_EQ(it->indices().size(), 3); + + const int d = Coverage() == coverage::all; + + // imitate iteration order of indexed loop + for (int k = 0; k < 4 + d; ++k) + for (int j = 0; j < 3; ++j) + for (int i = -d; i < 2 + d; ++i) { + BOOST_TEST_EQ(it->index(0), i); + BOOST_TEST_EQ(it->index(1), j); + BOOST_TEST_EQ(it->index(2), k); + BOOST_TEST_EQ(it->bin(0_c), h.axis(0_c).bin(i)); + BOOST_TEST_EQ(it->bin(1_c), h.axis(1_c).bin(j)); + BOOST_TEST_EQ(it->bin(2_c), h.axis(2_c).bin(k)); + BOOST_TEST_EQ(**it, i * 100 + j * 10 + k); + ++it; + } + BOOST_TEST(it == ind.end()); +} + +template <class IsDynamic, class Coverage> +void run_density_tests(mp_list<IsDynamic, Coverage>) { + auto ax = axis::variable<>({0.0, 0.1, 0.3, 0.6}); + auto ay = axis::integer<int>(0, 2); + auto az = ax; + auto h = make_s(IsDynamic(), std::vector<int>(), ax, ay, az); + + // fill uniformly + for (auto&& x : h) x = 1; + + for (auto&& x : indexed(h, Coverage())) { + BOOST_TEST_EQ(x.density(), *x / (x.bin(0).width() * x.bin(2).width())); + } +} + +template <class IsDynamic, class Coverage> +void run_stdlib_tests(mp_list<IsDynamic, Coverage>) { + auto ax = axis::regular<>(3, 0, 1); + auto ay = axis::integer<>(0, 2); + auto h = make_s(IsDynamic(), std::array<int, 20>(), ax, ay); + + struct generator { + int i = 0; + int operator()() { return ++i; } + }; + + auto ind = indexed(h, Coverage()); + std::generate(ind.begin(), ind.end(), generator{}); + + { + int i = 0; + for (auto&& x : ind) BOOST_TEST_EQ(*x, ++i); + } + + { + auto it = std::min_element(ind.begin(), ind.end()); + BOOST_TEST(it == ind.begin()); + BOOST_TEST(it != ind.end()); + } + + { + auto it = std::max_element(ind.begin(), ind.end()); + // get last before end() + auto it2 = ind.begin(); + auto it3 = it2; + while (it2 != ind.end()) it3 = it2++; + BOOST_TEST(it == it3); + BOOST_TEST(it != ind.begin()); + } +} + +int main() { + mp_for_each<mp_product<mp_list, mp_list<mp_false, mp_true>, + mp_list<std::integral_constant<coverage, coverage::inner>, + std::integral_constant<coverage, coverage::all>>>>( + [](auto&& x) { + run_1d_tests(x); + run_3d_tests(x); + run_density_tests(x); + run_stdlib_tests(x); + }); + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/is_close.hpp b/src/boost/libs/histogram/test/is_close.hpp new file mode 100644 index 000000000..7fe310575 --- /dev/null +++ b/src/boost/libs/histogram/test/is_close.hpp @@ -0,0 +1,15 @@ +// 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_TEST_IS_CLOSE_HPP +#define BOOST_HISTOGRAM_TEST_IS_CLOSE_HPP + +#include <boost/core/lightweight_test.hpp> +#include <cmath> + +#define BOOST_TEST_IS_CLOSE(a, b, eps) BOOST_TEST(std::abs(a - b) < eps) + +#endif diff --git a/src/boost/libs/histogram/test/make_histogram_fail0.cpp b/src/boost/libs/histogram/test/make_histogram_fail0.cpp new file mode 100644 index 000000000..1c508a49e --- /dev/null +++ b/src/boost/libs/histogram/test/make_histogram_fail0.cpp @@ -0,0 +1,14 @@ +// Copyright 2019 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/make_histogram.hpp> + +using namespace boost::histogram; + +int main() { + // argument is not an axis + (void)make_histogram(std::vector<int>{}); +} diff --git a/src/boost/libs/histogram/test/make_histogram_fail1.cpp b/src/boost/libs/histogram/test/make_histogram_fail1.cpp new file mode 100644 index 000000000..8768ca693 --- /dev/null +++ b/src/boost/libs/histogram/test/make_histogram_fail1.cpp @@ -0,0 +1,16 @@ +// Copyright 2019 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/axis/regular.hpp> +#include <boost/histogram/make_histogram.hpp> +#include <vector> + +using namespace boost::histogram; + +int main() { + // first and second arguments switched + (void)make_histogram_with(axis::regular<>(3, 0, 1), std::vector<int>{}); +} diff --git a/src/boost/libs/histogram/test/odr_main_test.cpp b/src/boost/libs/histogram/test/odr_main_test.cpp new file mode 100644 index 000000000..efb7eb9d1 --- /dev/null +++ b/src/boost/libs/histogram/test/odr_main_test.cpp @@ -0,0 +1,21 @@ +// Copyright 2019 Henry Schreiner, 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) + +/* + For a header-only library, it is important to not accidentally violate the + One-Definition-Rule (ODR), which causes linker errors. The ODR is violated + when a non-templated function declared in a header is not inlined, and that + header is then included in several translation units which are then linked + together. + + We carry out this test by including all headers in two separate translation + units which are then linked together. There is an additional test called + check_odr_test.py which checks that "odr_test.cpp" includes all headers. +*/ + +#include "odr_test.cpp" + +int main() { return 0; } diff --git a/src/boost/libs/histogram/test/odr_test.cpp b/src/boost/libs/histogram/test/odr_test.cpp new file mode 100644 index 000000000..3fa08ead2 --- /dev/null +++ b/src/boost/libs/histogram/test/odr_test.cpp @@ -0,0 +1,10 @@ +// Copyright 2019 Henry Schreiner, 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 all Boost.Histogram header here; see odr_main_test.cpp for details +#include <boost/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <boost/histogram/serialization.hpp> diff --git a/src/boost/libs/histogram/test/std_ostream.hpp b/src/boost/libs/histogram/test/std_ostream.hpp new file mode 100644 index 000000000..8a181d98d --- /dev/null +++ b/src/boost/libs/histogram/test/std_ostream.hpp @@ -0,0 +1,40 @@ +// Copyright 2018-2019 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_TEST_STD_OSTREAM_HPP +#define BOOST_HISTOGRAM_TEST_STD_OSTREAM_HPP + +#include <boost/mp11/tuple.hpp> +#include <ostream> +#include <utility> +#include <vector> + +namespace std { +// never add to std, we only do it here to get ADL working :( +template <class T> +ostream& operator<<(ostream& os, const vector<T>& v) { + os << "[ "; + for (const auto& x : v) os << x << " "; + os << "]"; + return os; +} + +template <class... Ts> +ostream& operator<<(ostream& os, const std::tuple<Ts...>& t) { + os << "[ "; + ::boost::mp11::tuple_for_each(t, [&os](const auto& x) { os << x << " "; }); + os << "]"; + return os; +} + +template <class T, class U> +ostream& operator<<(ostream& os, const std::pair<T, U>& t) { + os << "[ " << t.first << " " << t.second << " ]"; + return os; +} +} // namespace std + +#endif diff --git a/src/boost/libs/histogram/test/storage_adaptor_serialization_test.cpp b/src/boost/libs/histogram/test/storage_adaptor_serialization_test.cpp new file mode 100644 index 000000000..892449bbb --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_serialization_test.cpp @@ -0,0 +1,47 @@ +// 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 <array> +#include <boost/assert.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators/thread_safe.hpp> +#include <boost/histogram/serialization.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <map> +#include <vector> +#include "throw_exception.hpp" +#include "utility_serialization.hpp" + +using namespace boost::histogram; + +template <typename T> +void test_serialization(const std::string& filename) { + auto a = storage_adaptor<T>(); + a.reset(3); + a[1] += 1; + a[2] += 2; + print_xml(filename, a); + + auto b = storage_adaptor<T>(); + BOOST_TEST_NOT(a == b); + load_xml(filename, b); + BOOST_TEST(a == b); +} + +int main(int argc, char** argv) { + BOOST_ASSERT(argc == 2); + + test_serialization<std::vector<int>>( + join(argv[1], "storage_adaptor_serialization_test_vector_int.xml")); + test_serialization<std::array<unsigned, 10>>( + join(argv[1], "storage_adaptor_serialization_test_array_unsigned.xml")); + test_serialization<std::map<std::size_t, double>>( + join(argv[1], "storage_adaptor_serialization_test_map_double.xml")); + test_serialization<std::vector<accumulators::thread_safe<int>>>( + join(argv[1], "storage_adaptor_serialization_test_vector_thread_safe_int.xml")); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/storage_adaptor_serialization_test_array_unsigned.xml b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_array_unsigned.xml new file mode 100644 index 000000000..180d322bb --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_array_unsigned.xml @@ -0,0 +1,23 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <impl class_id="1" tracking_level="0" version="0"> + <size>3</size> + <array> + <item>0</item> + <item>1</item> + <item>2</item> + </array> + </impl> +</item> +</boost_serialization> + diff --git a/src/boost/libs/histogram/test/storage_adaptor_serialization_test_map_double.xml b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_map_double.xml new file mode 100644 index 000000000..ddf960f37 --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_map_double.xml @@ -0,0 +1,30 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <impl class_id="1" tracking_level="0" version="0"> + <size>3</size> + <map class_id="2" tracking_level="0" version="0"> + <count>2</count> + <item_version>0</item_version> + <item class_id="3" tracking_level="0" version="0"> + <first>1</first> + <second>1.00000000000000000e+00</second> + </item> + <item> + <first>2</first> + <second>2.00000000000000000e+00</second> + </item> + </map> + </impl> +</item> +</boost_serialization> + diff --git a/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_int.xml b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_int.xml new file mode 100644 index 000000000..c1e6de0c0 --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_int.xml @@ -0,0 +1,24 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <impl class_id="1" tracking_level="0" version="0"> + <vector> + <count>3</count> + <item_version>0</item_version> + <item>0</item> + <item>1</item> + <item>2</item> + </vector> + </impl> +</item> +</boost_serialization> + diff --git a/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_thread_safe_int.xml b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_thread_safe_int.xml new file mode 100644 index 000000000..50a18365e --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_serialization_test_vector_thread_safe_int.xml @@ -0,0 +1,29 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <impl class_id="1" tracking_level="0" version="0"> + <vector class_id="2" tracking_level="0" version="0"> + <count>3</count> + <item_version>0</item_version> + <item class_id="3" tracking_level="0" version="0"> + <value>0</value> + </item> + <item> + <value>1</value> + </item> + <item> + <value>2</value> + </item> + </vector> + </impl> +</item> +</boost_serialization> diff --git a/src/boost/libs/histogram/test/storage_adaptor_test.cpp b/src/boost/libs/histogram/test/storage_adaptor_test.cpp new file mode 100644 index 000000000..6ccdebd50 --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_test.cpp @@ -0,0 +1,292 @@ +// 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 <array> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/accumulators/weighted_mean.hpp> +#include <boost/histogram/accumulators/weighted_sum.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <boost/histogram/weight.hpp> +#include <cmath> +#include <deque> +#include <limits> +#include <map> +#include <sstream> +#include <vector> +#include "is_close.hpp" +#include "throw_exception.hpp" +#include "utility_allocator.hpp" + +using namespace boost::histogram; +using namespace std::literals; + +template <class T> +auto str(const T& t) { + std::ostringstream os; + os << t; + return os.str(); +} + +template <typename T> +void tests() { + using Storage = storage_adaptor<T>; + // ctor, copy, move + { + Storage a; + a.reset(2); + Storage b(a); + Storage c; + c = a; + BOOST_TEST_EQ(std::distance(a.begin(), a.end()), 2); + BOOST_TEST_EQ(a.size(), 2); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(c.size(), 2); + + Storage d(std::move(a)); + BOOST_TEST_EQ(d.size(), 2); + Storage e; + e = std::move(d); + BOOST_TEST_EQ(e.size(), 2); + + const auto t = T(); + storage_adaptor<T> g(t); // tests converting ctor + BOOST_TEST_EQ(g.size(), 0); + const auto u = std::vector<typename Storage::value_type>(3, 1); + Storage h(u); // tests converting ctor + BOOST_TEST_EQ(h.size(), 3); + BOOST_TEST_EQ(h[0], 1); + BOOST_TEST_EQ(h[1], 1); + BOOST_TEST_EQ(h[2], 1); + } + + // increment, add, sub, set, reset, compare + { + Storage a; + a.reset(1); + ++a[0]; + const auto save = a[0]++; + BOOST_TEST_EQ(save, 1); + BOOST_TEST_EQ(a[0], 2); + a.reset(2); + BOOST_TEST_EQ(a.size(), 2); + ++a[0]; + a[0] += 2; + a[1] += 5; + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 5); + a[0] -= 2; + a[1] -= 5; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 0); + a[1] = 9; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 9); + BOOST_TEST_LT(a[0], 2); + BOOST_TEST_LT(0, a[1]); + BOOST_TEST_GT(a[1], 4); + BOOST_TEST_GT(3, a[0]); + a[1] = a[0]; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 1); + a.reset(0); + BOOST_TEST_EQ(a.size(), 0); + } + + // copy + { + Storage a; + a.reset(1); + ++a[0]; + Storage b; + b.reset(2); + BOOST_TEST(!(a == b)); + b = a; + BOOST_TEST(a == b); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(b[0], 1); + + Storage c(a); + BOOST_TEST(a == c); + BOOST_TEST_EQ(c.size(), 1); + BOOST_TEST_EQ(c[0], 1); + } + + // move + { + Storage a; + a.reset(1); + ++a[0]; + Storage b; + BOOST_TEST(!(a == b)); + b = std::move(a); + BOOST_TEST_EQ(b.size(), 1); + BOOST_TEST_EQ(b[0], 1); + Storage c(std::move(b)); + BOOST_TEST_EQ(c.size(), 1); + BOOST_TEST_EQ(c[0], 1); + } + + { + Storage a; + a.reset(1); + a[0] += 2; + BOOST_TEST_EQ(str(a[0]), "2"s); + } +} + +template <typename A, typename B> +void mixed_tests() { + // comparison + { + A a, b; + a.reset(1); + b.reset(1); + B c, d; + c.reset(1); + d.reset(2); + ++a[0]; + ++b[0]; + c[0] += 2; + d[0] = 3; + d[1] = 5; + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(b[0], 1); + BOOST_TEST_EQ(c[0], 2); + BOOST_TEST_EQ(d[0], 3); + BOOST_TEST_EQ(d[1], 5); + BOOST_TEST(a == a); + BOOST_TEST(a == b); + BOOST_TEST(!(a == c)); + BOOST_TEST(!(a == d)); + } + + // ctor, copy, move, assign + { + A a; + a.reset(2); + ++a[1]; + B b(a); + B c; + c = a; + BOOST_TEST_EQ(c[0], 0); + BOOST_TEST_EQ(c[1], 1); + c = A(); + BOOST_TEST_EQ(c.size(), 0); + B d(std::move(a)); + B e; + e = std::move(d); + BOOST_TEST_EQ(e[0], 0); + BOOST_TEST_EQ(e[1], 1); + } +} + +int main() { + tests<std::vector<int>>(); + tests<std::array<int, 100>>(); + tests<std::deque<int>>(); + tests<std::map<std::size_t, int>>(); + tests<std::unordered_map<std::size_t, int>>(); + + mixed_tests<storage_adaptor<std::vector<int>>, + storage_adaptor<std::array<double, 100>>>(); + mixed_tests<unlimited_storage<>, storage_adaptor<std::vector<double>>>(); + mixed_tests<storage_adaptor<std::vector<int>>, unlimited_storage<>>(); + mixed_tests<storage_adaptor<std::vector<int>>, + storage_adaptor<std::map<std::size_t, int>>>(); + + // special case for division of map-based storage_adaptor + { + auto a = storage_adaptor<std::map<std::size_t, double>>(); + a.reset(2); + a[0] /= 2; + BOOST_TEST_EQ(a[0], 0); + a[0] = 2; + a[0] /= 2; + BOOST_TEST_EQ(a[0], 1); + a[1] /= std::numeric_limits<double>::quiet_NaN(); + BOOST_TEST(std::isnan(static_cast<double>(a[1]))); + } + + // with accumulators::weighted_sum + { + auto a = storage_adaptor<std::vector<accumulators::weighted_sum<double>>>(); + a.reset(1); + ++a[0]; + a[0] += 1; + a[0] += 2; + a[0] += accumulators::weighted_sum<double>(1, 2); + BOOST_TEST_EQ(a[0].value(), 5); + BOOST_TEST_EQ(a[0].variance(), 6); + a[0] *= 2; + BOOST_TEST_EQ(a[0].value(), 10); + BOOST_TEST_EQ(a[0].variance(), 24); + } + + // with accumulators::weighted_mean + { + auto a = storage_adaptor<std::vector<accumulators::weighted_mean<double>>>(); + a.reset(1); + a[0](/* sample */ 1); + a[0](weight(2), /* sample */ 2); + a[0] += accumulators::weighted_mean<>(1, 0, 0, 0); + BOOST_TEST_EQ(a[0].sum_of_weights(), 4); + BOOST_TEST_IS_CLOSE(a[0].value(), 1.25, 1e-3); + BOOST_TEST_IS_CLOSE(a[0].variance(), 0.242, 1e-3); + } + + // exceeding array capacity + { + auto a = storage_adaptor<std::array<int, 10>>(); + a.reset(10); // should not throw + BOOST_TEST_THROWS(a.reset(11), std::length_error); + auto b = storage_adaptor<std::vector<int>>(); + b.reset(11); + BOOST_TEST_THROWS(a = b, std::length_error); + } + + // test sparsity of map backend + { + tracing_allocator_db db; + tracing_allocator<char> alloc(db); + using map_t = std::map<std::size_t, double, std::less<std::size_t>, + tracing_allocator<std::pair<const std::size_t, double>>>; + using A = storage_adaptor<map_t>; + auto a = A(alloc); + // MSVC implementation allocates some structures for debugging + const auto baseline = db.second; + a.reset(10); + BOOST_TEST_EQ(db.first, baseline); // nothing allocated yet + // queries do not allocate + BOOST_TEST_EQ(a[0], 0); + BOOST_TEST_EQ(a[9], 0); + BOOST_TEST_EQ(db.first, baseline); + ++a[5]; // causes one allocation + const auto node = db.first - baseline; + BOOST_TEST_EQ(a[5], 1); + a[4] += 2; // causes one allocation + BOOST_TEST_EQ(a[4], 2); + BOOST_TEST_EQ(db.first, baseline + 2 * node); + a[3] -= 2; // causes one allocation + BOOST_TEST_EQ(a[3], -2); + BOOST_TEST_EQ(db.first, baseline + 3 * node); + a[2] *= 2; // no allocation + BOOST_TEST_EQ(db.first, baseline + 3 * node); + a[2] /= 2; // no allocation + BOOST_TEST_EQ(db.first, baseline + 3 * node); + a[4] = 0; // causes one deallocation + BOOST_TEST_EQ(db.first, baseline + 2 * node); + + auto b = storage_adaptor<std::vector<int>>(); + b.reset(5); + ++b[2]; + a = b; + // only one new allocation for non-zero value + BOOST_TEST_EQ(db.first, baseline + node); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/storage_adaptor_threaded_test.cpp b/src/boost/libs/histogram/test/storage_adaptor_threaded_test.cpp new file mode 100644 index 000000000..0ba475345 --- /dev/null +++ b/src/boost/libs/histogram/test/storage_adaptor_threaded_test.cpp @@ -0,0 +1,58 @@ +// Copyright 2019 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/accumulators/ostream.hpp> +#include <boost/histogram/accumulators/thread_safe.hpp> +#include "throw_exception.hpp" +#include <boost/histogram/storage_adaptor.hpp> + +#include <array> +#include <deque> +#include <map> +#include <thread> +#include <unordered_map> +#include <vector> + +using namespace boost::histogram; + +constexpr auto n_fill = 1000000; + +template <class T> +void tests() { + { + storage_adaptor<T> s; + s.reset(1); + + auto fill = [&s]() { + for (unsigned i = 0; i < n_fill; ++i) { + ++s[0]; + s[0] += 1; + } + }; + + std::thread t1(fill); + std::thread t2(fill); + std::thread t3(fill); + std::thread t4(fill); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + + BOOST_TEST_EQ(s[0], 4 * 2 * n_fill); + } +} + +int main() { + using ts_int = accumulators::thread_safe<int>; + tests<std::vector<ts_int>>(); + tests<std::array<ts_int, 100>>(); + tests<std::deque<ts_int>>(); + // stdlib maps are not thread-safe and not supported + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/throw_exception.hpp b/src/boost/libs/histogram/test/throw_exception.hpp new file mode 100644 index 000000000..bf5304f43 --- /dev/null +++ b/src/boost/libs/histogram/test/throw_exception.hpp @@ -0,0 +1,25 @@ +// Copyright 2019 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) + +#ifdef BOOST_NO_EXCEPTIONS + +#include <boost/assert/source_location.hpp> +#include <cstdlib> // std::abort +#include <exception> +#include <iostream> + +namespace boost { + +// dummy implementation for user-defined function from boost/throw_exception.hpp +inline void throw_exception(std::exception const& e, boost::source_location const& l) { + std::cerr << l.file_name() << ":" << l.line() << ":" << l.column() << ": exception in '" + << l.function_name() << " \"" << e.what() << "\"" << std::endl; + std::abort(); +} + +} // namespace boost + +#endif diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test.cpp b/src/boost/libs/histogram/test/unlimited_storage_serialization_test.cpp new file mode 100644 index 000000000..302bef3c6 --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test.cpp @@ -0,0 +1,57 @@ +// 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/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/assert.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/serialization.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <memory> +#include <sstream> +#include "throw_exception.hpp" +#include "utility_serialization.hpp" + +using unlimited_storage_type = boost::histogram::unlimited_storage<>; + +using namespace boost::histogram; + +template <typename T> +unlimited_storage_type prepare(std::size_t n, const T x) { + std::unique_ptr<T[]> v(new T[n]); + std::fill(v.get(), v.get() + n, static_cast<T>(0)); + v.get()[0] = x; + return unlimited_storage_type(n, v.get()); +} + +template <typename T> +unlimited_storage_type prepare(std::size_t n) { + return unlimited_storage_type(n, static_cast<T*>(nullptr)); +} + +template <typename T> +void run_test(const std::string& filename) { + const auto a = prepare(1, T(1)); + print_xml(filename, a); + unlimited_storage_type b; + BOOST_TEST(!(a == b)); + load_xml(filename, b); + BOOST_TEST(a == b); +} + +int main(int argc, char** argv) { + BOOST_ASSERT(argc == 2); + + run_test<uint8_t>(join(argv[1], "unlimited_storage_serialization_test_u8.xml")); + run_test<uint16_t>(join(argv[1], "unlimited_storage_serialization_test_u16.xml")); + run_test<uint32_t>(join(argv[1], "unlimited_storage_serialization_test_u32.xml")); + run_test<uint64_t>(join(argv[1], "unlimited_storage_serialization_test_u64.xml")); + run_test<unlimited_storage_type::large_int>( + join(argv[1], "unlimited_storage_serialization_test_large_int.xml")); + run_test<double>(join(argv[1], "unlimited_storage_serialization_test_double.xml")); + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_double.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_double.xml new file mode 100644 index 000000000..8532503ed --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_double.xml @@ -0,0 +1,19 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>5</type> + <size>1</size> + <buffer> + <item>1.00000000000000000e+00</item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_large_int.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_large_int.xml new file mode 100644 index 000000000..e77ec4370 --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_large_int.xml @@ -0,0 +1,25 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>4</type> + <size>1</size> + <buffer> + <item class_id="1" tracking_level="0" version="0"> + <data> + <count>1</count> + <item_version>0</item_version> + <item>1</item> + </data> + </item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u16.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u16.xml new file mode 100644 index 000000000..c29f8600f --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u16.xml @@ -0,0 +1,19 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>1</type> + <size>1</size> + <buffer> + <item>1</item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u32.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u32.xml new file mode 100644 index 000000000..709ad01ab --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u32.xml @@ -0,0 +1,19 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>2</type> + <size>1</size> + <buffer> + <item>1</item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u64.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u64.xml new file mode 100644 index 000000000..a6fed6ff5 --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u64.xml @@ -0,0 +1,19 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>3</type> + <size>1</size> + <buffer> + <item>1</item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u8.xml b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u8.xml new file mode 100644 index 000000000..80dd69b78 --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_serialization_test_u8.xml @@ -0,0 +1,19 @@ +<!-- + Copyright 2018-2019 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) +--> + +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<!DOCTYPE boost_serialization> +<boost_serialization signature="serialization::archive" version="17"> +<item class_id="0" tracking_level="0" version="0"> + <type>0</type> + <size>1</size> + <buffer> + <item>1</item> + </buffer> +</item> +</boost_serialization>
\ No newline at end of file diff --git a/src/boost/libs/histogram/test/unlimited_storage_test.cpp b/src/boost/libs/histogram/test/unlimited_storage_test.cpp new file mode 100644 index 000000000..1da2601e3 --- /dev/null +++ b/src/boost/libs/histogram/test/unlimited_storage_test.cpp @@ -0,0 +1,501 @@ +// 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 <algorithm> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/lightweight_test_trait.hpp> +#include <boost/histogram/detail/detect.hpp> +#include <boost/histogram/storage_adaptor.hpp> +#include <boost/histogram/unlimited_storage.hpp> +#include <boost/histogram/unsafe_access.hpp> +#include <boost/mp11.hpp> +#include <iosfwd> +#include <limits> +#include <memory> +#include <numeric> +#include <vector> +#include "std_ostream.hpp" +#include "throw_exception.hpp" +#include "utility_allocator.hpp" + +namespace boost { +namespace histogram { +namespace detail { +template <class Allocator> +std::ostream& operator<<(std::ostream& os, const large_int<Allocator>& x) { + os << "large_int"; + os << x.data; + return os; +} +} // namespace detail +} // namespace histogram +} // namespace boost + +using namespace boost::histogram; + +using unlimited_storage_type = unlimited_storage<>; +template <typename T> +using vector_storage = storage_adaptor<std::vector<T>>; +using large_int = unlimited_storage_type::large_int; + +template <typename T = std::uint8_t> +unlimited_storage_type prepare(std::size_t n, T x = T{}) { + std::unique_ptr<T[]> v(new T[n]); + std::fill(v.get(), v.get() + n, static_cast<T>(0)); + v.get()[0] = x; + return unlimited_storage_type(n, v.get()); +} + +template <class T> +auto limits_max() { + return (std::numeric_limits<T>::max)(); +} + +template <> +inline auto limits_max<large_int>() { + return large_int(limits_max<uint64_t>()); +} + +template <typename T> +void copy() { + const auto b = prepare<T>(1); + auto a(b); + BOOST_TEST(a == b); + ++a[0]; + BOOST_TEST(!(a == b)); + a = b; + BOOST_TEST(a == b); + ++a[0]; + BOOST_TEST(!(a == b)); + a = prepare<T>(2); + BOOST_TEST(!(a == b)); + a = b; + BOOST_TEST(a == b); +} + +template <typename T> +void equal_1() { + auto a = prepare(1); + auto b = prepare(1, T(0)); + BOOST_TEST_EQ(a[0], 0.0); + BOOST_TEST(a == b); + ++b[0]; + BOOST_TEST(!(a == b)); +} + +template <typename T, typename U> +void equal_2() { + auto a = prepare<T>(1); + vector_storage<U> b; + b.reset(1); + BOOST_TEST(a == b); + ++b[0]; + BOOST_TEST(!(a == b)); +} + +template <typename T> +void increase_and_grow() { + auto tmax = limits_max<T>(); + auto s = prepare(2, tmax); + auto n = s; + auto n2 = s; + + ++n[0]; + + auto x = prepare(2); + ++x[0]; + n2[0] += x[0]; + + auto v = static_cast<double>(tmax); + ++v; + 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 <typename T> +void convert_foreign_storage() { + + { + vector_storage<T> s; + s.reset(1); + ++s[0]; + BOOST_TEST_EQ(s[0], 1); + + // test converting copy ctor + unlimited_storage_type u(s); + using buffer_t = std::decay_t<decltype(unsafe_access::unlimited_storage_buffer(u))>; + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(u).type, + buffer_t::template type_index<T>()); + BOOST_TEST(u == s); + BOOST_TEST_EQ(u.size(), 1u); + BOOST_TEST_EQ(u[0], 1.0); + ++s[0]; + BOOST_TEST_NOT(u == s); + } + + vector_storage<uint8_t> s; + s.reset(1); + ++s[0]; + + // test assign and equal + auto a = prepare<T>(1); + a = s; + BOOST_TEST_EQ(a[0], 1.0); + BOOST_TEST(a == s); + ++a[0]; + BOOST_TEST_NOT(a == s); + + // test radd + auto c = prepare<T>(1); + c[0] += s[0]; + BOOST_TEST_EQ(c[0], 1); + BOOST_TEST(c == s); + c[0] += s[0]; + BOOST_TEST_EQ(c[0], 2); + BOOST_TEST_NOT(c == s); + + // test assign from float + vector_storage<float> t; + t.reset(1); + t[0] = 1.5; + auto d = prepare<T>(1); + d = t; + BOOST_TEST(d == t); + BOOST_TEST(d[0] == 1.5); + + // test "copy" ctor from float + unlimited_storage_type f(t); + BOOST_TEST_EQ(f[0], 1.5); + BOOST_TEST(f == t); + + // test radd from float + auto g = prepare<T>(1); + g[0] += t[0]; + BOOST_TEST_EQ(g[0], 1.5); + BOOST_TEST(g == t); + + vector_storage<int8_t> u; + u.reset(1); + u[0] = -10; + auto h = prepare<T>(1); + BOOST_TEST_NOT(h == u); + h = u; + BOOST_TEST(h == u); + BOOST_TEST_EQ(h[0], -10); + h[0] -= u[0]; + BOOST_TEST_EQ(h[0], 0); +} + +struct adder { + template <class LHS, class RHS> + void operator()(boost::mp11::mp_list<LHS, RHS>) { + using buffer_type = + std::remove_reference_t<decltype(unsafe_access::unlimited_storage_buffer( + std::declval<unlimited_storage_type&>()))>; + constexpr auto iLHS = buffer_type::template type_index<LHS>(); + constexpr auto iRHS = buffer_type::template type_index<RHS>(); + { + auto a = prepare<LHS>(1); + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, iLHS); + a[0] += static_cast<RHS>(2); + // LHS is never downgraded, only upgraded to RHS. + // If RHS is normal integer, LHS doesn't change. + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, + iRHS < 4 ? iLHS : (std::max)(iLHS, iRHS)); + BOOST_TEST_EQ(a[0], 2); + } + { + auto a = prepare<LHS>(1); + a[0] += 2; + BOOST_TEST_EQ(a[0], 2); + // subtracting converts to double + a[0] -= 2; + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, 5); + BOOST_TEST_EQ(a[0], 0); + } + { + auto a = prepare<LHS>(1); + auto b = prepare<RHS>(1, static_cast<RHS>(2u)); + // LHS is never downgraded, only upgraded to RHS. + // If RHS is normal integer, LHS doesn't change. + a[0] += b[0]; + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, + iRHS < 4 ? iLHS : (std::max)(iLHS, iRHS)); + BOOST_TEST_EQ(a[0], 2); + a[0] -= b[0]; + BOOST_TEST_EQ(a[0], 0); + a[0] -= b[0]; + BOOST_TEST_EQ(a[0], -2); + } + { + auto a = prepare<LHS>(1); + auto b = limits_max<RHS>(); + // LHS is never downgraded, only upgraded to RHS. + // If RHS is normal integer, LHS doesn't change. + a[0] += b; + // BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, + // iRHS < 4 ? iLHS : std::max(iLHS, iRHS)); + BOOST_TEST_EQ(a[0], limits_max<RHS>()); + a[0] += prepare<RHS>(1, b)[0]; + // BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(a).type, + // iRHS < 4 ? iLHS + 1 : std::max(iLHS, iRHS)); + BOOST_TEST_EQ(a[0], 2 * double(limits_max<RHS>())); + } + } +}; + +int main() { + // empty state + { + unlimited_storage_type a; + BOOST_TEST_EQ(a.size(), 0); + } + + // copy + { + copy<uint8_t>(); + copy<uint16_t>(); + copy<uint32_t>(); + copy<uint64_t>(); + copy<large_int>(); + copy<double>(); + } + + // equal_operator + { + equal_1<uint8_t>(); + equal_1<uint16_t>(); + equal_1<uint32_t>(); + equal_1<uint64_t>(); + equal_1<large_int>(); + equal_1<double>(); + + equal_2<uint8_t, unsigned>(); + equal_2<uint16_t, unsigned>(); + equal_2<uint32_t, unsigned>(); + equal_2<uint64_t, unsigned>(); + equal_2<large_int, unsigned>(); + equal_2<double, unsigned>(); + + equal_2<large_int, double>(); + + auto a = prepare<double>(1); + auto b = prepare<large_int>(1); + BOOST_TEST(a == b); + ++a[0]; + BOOST_TEST_NOT(a == b); + } + + // increase_and_grow + { + increase_and_grow<uint8_t>(); + increase_and_grow<uint16_t>(); + increase_and_grow<uint32_t>(); + increase_and_grow<uint64_t>(); + + // only increase for large_int + auto a = prepare<large_int>(2, static_cast<large_int>(1)); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 0); + ++a[0]; + BOOST_TEST_EQ(a[0], 2); + BOOST_TEST_EQ(a[1], 0); + } + + // add + { + using namespace boost::mp11; + using L = mp_list<uint8_t, uint16_t, uint64_t, large_int, double>; + mp_for_each<mp_product<mp_list, L, L>>(adder()); + } + + // add_and_grow + { + auto a = prepare(1); + a[0] += a[0]; + BOOST_TEST_EQ(a[0], 0); + ++a[0]; + double x = 1; + auto b = prepare(1); + ++b[0]; + BOOST_TEST_EQ(b[0], x); + for (unsigned i = 0; i < 80; ++i) { + x += x; + a[0] += a[0]; + b[0] += b[0]; + BOOST_TEST_EQ(a[0], x); + BOOST_TEST_EQ(b[0], x); + auto c = prepare(1); + c[0] += a[0]; + BOOST_TEST_EQ(c[0], x); + c[0] += 0; + BOOST_TEST_EQ(c[0], x); + auto d = prepare(1); + d[0] += x; + BOOST_TEST_EQ(d[0], x); + } + } + + // multiply + { + auto a = prepare(2); + ++a[0]; + a *= 3; + BOOST_TEST_EQ(a[0], 3); + BOOST_TEST_EQ(a[1], 0); + a[1] += 2; + a *= 3; + BOOST_TEST_EQ(a[0], 9); + BOOST_TEST_EQ(a[1], 6); + } + + // convert_foreign_storage + { + convert_foreign_storage<uint8_t>(); + convert_foreign_storage<uint16_t>(); + convert_foreign_storage<uint32_t>(); + convert_foreign_storage<uint64_t>(); + convert_foreign_storage<large_int>(); + convert_foreign_storage<double>(); + } + + // reference + { + auto a = prepare(1); + auto b = prepare<uint32_t>(1); + BOOST_TEST_EQ(a[0], b[0]); + BOOST_TEST_GE(a[0], b[0]); + BOOST_TEST_LE(a[0], b[0]); + a[0] = 1; + BOOST_TEST_NE(a[0], b[0]); + BOOST_TEST_LT(b[0], a[0]); + BOOST_TEST_GT(a[0], b[0]); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_GE(a[0], 1); + BOOST_TEST_LE(a[0], 1); + BOOST_TEST_NE(a[0], 2); + BOOST_TEST_GT(2, a[0]); + BOOST_TEST_LT(0, a[0]); + BOOST_TEST_GE(1, a[0]); + BOOST_TEST_GE(2, a[0]); + BOOST_TEST_LE(0, a[0]); + BOOST_TEST_LE(1, a[0]); + BOOST_TEST_EQ(1, a[0]); + BOOST_TEST_NE(2, a[0]); + + ++b[0]; + BOOST_TEST_EQ(a[0], b[0]); + b[0] += 2; + a[0] = b[0]; + BOOST_TEST_EQ(a[0], 3); + a[0] -= 10; + BOOST_TEST_EQ(a[0], -7); + auto c = prepare(2); + c[0] = c[1] = 1; + BOOST_TEST_EQ(c[0], 1); + BOOST_TEST_EQ(c[1], 1); + + auto d = prepare(2); + d[1] = unlimited_storage_type::large_int{2}; + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(d).type, 4); + d[0] = -2; + BOOST_TEST_EQ(unsafe_access::unlimited_storage_buffer(d).type, 5); + BOOST_TEST_EQ(d[0], -2); + BOOST_TEST_EQ(d[1], 2); + + BOOST_TEST_TRAIT_TRUE((detail::has_operator_preincrement<decltype(d[0])>)); + } + + // iterators + { + using iterator = typename unlimited_storage_type::iterator; + using value_type = typename std::iterator_traits<iterator>::value_type; + using reference = typename std::iterator_traits<iterator>::reference; + + BOOST_TEST_TRAIT_SAME(value_type, double); + BOOST_TEST_TRAIT_FALSE((std::is_same<reference, double&>)); + + auto a = prepare(2); + for (auto&& x : a) BOOST_TEST_EQ(x, 0); + + std::vector<double> b(2, 1); + std::copy(b.begin(), b.end(), a.begin()); + + const auto& aconst = a; + BOOST_TEST(std::equal(aconst.begin(), aconst.end(), b.begin(), b.end())); + + unlimited_storage_type::iterator it1 = a.begin(); + BOOST_TEST_EQ(*it1, 1); + *it1 = 3; + BOOST_TEST_EQ(*it1, 3); + unlimited_storage_type::const_iterator it2 = a.begin(); + BOOST_TEST_EQ(*it2, 3); + unlimited_storage_type::const_iterator it3 = aconst.begin(); + BOOST_TEST_EQ(*it3, 3); + + std::copy(b.begin(), b.end(), a.begin()); + std::partial_sum(a.begin(), a.end(), a.begin()); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 2); + } + + // memory exhaustion + { + using S = unlimited_storage<tracing_allocator<char>>; + using alloc_t = typename S::allocator_type; + { + // check that large_int allocates in ctor + tracing_allocator_db db; + typename S::large_int li{1, alloc_t{db}}; + BOOST_TEST_GT(db.first, 0); + } + + tracing_allocator_db db; + // db.tracing = true; // uncomment this to monitor allocator activity + S s(alloc_t{db}); + s.reset(10); // should work + BOOST_TEST_EQ(db.at<uint8_t>().first, 10); + +#ifndef BOOST_NO_EXCEPTIONS + db.failure_countdown = 0; + BOOST_TEST_THROWS(s.reset(5), std::bad_alloc); + // storage must be still in valid state + BOOST_TEST_EQ(s.size(), 0); + auto& buffer = unsafe_access::unlimited_storage_buffer(s); + BOOST_TEST_EQ(buffer.ptr, nullptr); + BOOST_TEST_EQ(buffer.type, 0); + // all allocated memory should have returned + BOOST_TEST_EQ(db.first, 0); + + // test failure in buffer.make<large_int>(n, iter), AT::construct + s.reset(3); + s[1] = (std::numeric_limits<std::uint64_t>::max)(); + db.failure_countdown = 2; + const auto old_ptr = buffer.ptr; + BOOST_TEST_THROWS(++s[1], std::bad_alloc); + + // storage remains in previous state + BOOST_TEST_EQ(buffer.size, 3); + BOOST_TEST_EQ(buffer.ptr, old_ptr); + BOOST_TEST_EQ(buffer.type, 3); + + // test buffer.make<large_int>(n), AT::construct, called by serialization code + db.failure_countdown = 1; + BOOST_TEST_THROWS(buffer.make<typename S::large_int>(2), std::bad_alloc); + + // storage still in valid state + BOOST_TEST_EQ(s.size(), 0); + BOOST_TEST_EQ(buffer.ptr, nullptr); + BOOST_TEST_EQ(buffer.type, 0); + // all memory returned + BOOST_TEST_EQ(db.first, 0); +#endif + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/test/utility_allocator.hpp b/src/boost/libs/histogram/test/utility_allocator.hpp new file mode 100644 index 000000000..60745d0e0 --- /dev/null +++ b/src/boost/libs/histogram/test/utility_allocator.hpp @@ -0,0 +1,131 @@ +// 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 <algorithm> +#include <boost/config.hpp> +#include <boost/core/lightweight_test.hpp> +#include <boost/core/typeinfo.hpp> +#include <boost/histogram/detail/type_name.hpp> +#include <boost/throw_exception.hpp> +#include <iostream> +#include <unordered_map> +#include <utility> + +struct tracing_allocator_db : std::pair<int, int> { + template <class T> + auto& at() { + return map_[&BOOST_CORE_TYPEID(T)]; + } + + void clear() { + map_.clear(); + this->first = 0; + this->second = 0; + } + + int failure_countdown = -1; + bool tracing = false; + + template <class... Ts> + void log(Ts&&... ts) { + if (!tracing) return; + log_impl(std::forward<Ts>(ts)...); + std::cerr << std::endl; + } + + std::size_t size() const { return map_.size(); } + +private: + using map_t = std::unordered_map<const boost::core::typeinfo*, std::pair<int, int>>; + map_t map_; + + BOOST_ATTRIBUTE_UNUSED inline void log_impl() {} + + template <class T, class... Ts> + void log_impl(T&& t, Ts&&... ts) { + std::cerr << t; + log_impl(std::forward<Ts>(ts)...); + } +}; + +template <class T> +struct tracing_allocator { + using value_type = T; + + tracing_allocator_db* db = nullptr; + + tracing_allocator() noexcept = default; + tracing_allocator(const tracing_allocator&) noexcept = default; + tracing_allocator(tracing_allocator&&) noexcept = default; + + tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {} + template <class U> + tracing_allocator(const tracing_allocator<U>& a) noexcept : db(a.db) {} + template <class U> + tracing_allocator& operator=(const tracing_allocator<U>& a) noexcept { + db = a.db; + return *this; + } + ~tracing_allocator() noexcept {} + + T* allocate(std::size_t n) { + if (db) { + if (db->failure_countdown >= 0) { + const auto count = db->failure_countdown--; + db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>(), + " [failure in ", count, "]"); + if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{}); + } else + db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>()); + auto& p = db->at<T>(); + p.first += static_cast<int>(n); + p.second += static_cast<int>(n); + db->first += static_cast<int>(n * sizeof(T)); + db->second += static_cast<int>(n * sizeof(T)); + } + return static_cast<T*>(::operator new(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t n) { + if (db) { + db->at<T>().first -= static_cast<int>(n); + db->first -= static_cast<int>(n * sizeof(T)); + db->log("allocator -", n, " ", boost::histogram::detail::type_name<T>()); + } + ::operator delete((void*)p); + } + + template <class... Ts> + void construct(T* p, Ts&&... ts) { + if (db) { + if (db->failure_countdown >= 0) { + const auto count = db->failure_countdown--; + db->log("allocator construct ", boost::histogram::detail::type_name<T>(), + "[ failure in ", count, "]"); + if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{}); + } else + db->log("allocator construct ", boost::histogram::detail::type_name<T>()); + } + ::new (static_cast<void*>(p)) T(std::forward<Ts>(ts)...); + } + + void destroy(T* p) { + if (db) db->log("allocator destroy ", boost::histogram::detail::type_name<T>()); + p->~T(); + } +}; + +template <class T, class U> +constexpr bool operator==(const tracing_allocator<T>&, + const tracing_allocator<U>&) noexcept { + return true; +} + +template <class T, class U> +constexpr bool operator!=(const tracing_allocator<T>& t, + const tracing_allocator<U>& u) noexcept { + return !operator==(t, u); +} diff --git a/src/boost/libs/histogram/test/utility_axis.hpp b/src/boost/libs/histogram/test/utility_axis.hpp new file mode 100644 index 000000000..5d40b7fa2 --- /dev/null +++ b/src/boost/libs/histogram/test/utility_axis.hpp @@ -0,0 +1,37 @@ +// Copyright 2018-2019 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_TEST_UTILITY_AXIS_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_AXIS_HPP + +#include <boost/core/lightweight_test.hpp> +#include <boost/histogram/fwd.hpp> + +namespace boost { +namespace histogram { + +template <class Axis> +void test_axis_iterator(const Axis& a, axis::index_type begin, axis::index_type end) { + for (auto bin : a) { + BOOST_TEST_EQ(bin, a.bin(begin)); + ++begin; + } + BOOST_TEST_EQ(begin, end); + auto rit = a.rbegin(); + for (; rit != a.rend(); ++rit) { + --begin; + BOOST_TEST_EQ(*rit, a.bin(begin)); + } +} + +namespace axis { +bool operator==(const null_type&, const null_type&) { return true; } +} // namespace axis + +} // namespace histogram +} // namespace boost + +#endif diff --git a/src/boost/libs/histogram/test/utility_histogram.hpp b/src/boost/libs/histogram/test/utility_histogram.hpp new file mode 100644 index 000000000..16839fc44 --- /dev/null +++ b/src/boost/libs/histogram/test/utility_histogram.hpp @@ -0,0 +1,60 @@ +// 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_TEST_UTILITY_HISTOGRAM_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_HISTOGRAM_HPP + +#include <boost/histogram/axis/category.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/make_histogram.hpp> +#include <boost/mp11/algorithm.hpp> +#include <type_traits> +#include <vector> + +namespace boost { +namespace histogram { + +template <class... Ts> +auto make_axis_vector(const Ts&... ts) { + // make sure the variant is never trivial (contains only one type) + using R = axis::regular<double, boost::use_default, axis::null_type>; + using I = axis::integer<int, axis::null_type, axis::option::none_t>; + using V = axis::variable<double, axis::null_type>; + using C = axis::category<int, axis::null_type>; + using Var = boost::mp11::mp_unique<axis::variant<Ts..., R, I, V, C>>; + return std::vector<Var>({Var(ts)...}); +} + +using static_tag = std::false_type; +using dynamic_tag = std::true_type; + +template <class... Axes> +auto make(static_tag, const Axes&... axes) { + return make_histogram(axes...); +} + +template <class S, class... Axes> +auto make_s(static_tag, S&& s, const Axes&... axes) { + return make_histogram_with(s, axes...); +} + +template <class... Axes> +auto make(dynamic_tag, const Axes&... axes) { + return make_histogram(make_axis_vector(axes...)); +} + +template <class S, class... Axes> +auto make_s(dynamic_tag, S&& s, const Axes&... axes) { + return make_histogram_with(s, make_axis_vector(axes...)); +} + +} // namespace histogram +} // namespace boost + +#endif diff --git a/src/boost/libs/histogram/test/utility_iterator.hpp b/src/boost/libs/histogram/test/utility_iterator.hpp new file mode 100644 index 000000000..eb99a92ce --- /dev/null +++ b/src/boost/libs/histogram/test/utility_iterator.hpp @@ -0,0 +1,229 @@ +// Copyright 2003 David Abrahams and Jeremy Siek +// Copyright 2019 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_TEST_ITERATOR_TESTS_HPP +#define BOOST_HISTOGRAM_TEST_ITERATOR_TESTS_HPP + +// This file contains adapted code from +// - boost::core::iterator; boost/pending/iterator_tests.hpp +// - boost::core::conversion; boost/implicit_cast.hpp + +#include <boost/core/ignore_unused.hpp> +#include <boost/core/lightweight_test.hpp> +#include <iterator> +#include <type_traits> + +namespace boost { +namespace histogram { + +namespace detail { +template <class T> +struct icast_identity { + typedef T type; +}; +} // namespace detail + +template <class T> +inline T implicit_cast(typename detail::icast_identity<T>::type x) { + return x; +} + +// use this for the value type +struct dummyT { + dummyT() {} + dummyT(char) {} + dummyT(int x) : m_x(x) {} + int foo() const { return m_x; } + bool operator==(const dummyT& d) const { return m_x == d.m_x; } + int m_x; +}; + +// Tests whether type Iterator satisfies the requirements for a +// TrivialIterator. +// Preconditions: i != j, *i == val +template <class Iterator, class T> +void trivial_iterator_test(const Iterator i, const Iterator j, T val) { + Iterator k; + BOOST_TEST(i == i); + BOOST_TEST(j == j); + BOOST_TEST(i != j); + typename std::iterator_traits<Iterator>::value_type v = *i; + BOOST_TEST(v == val); + ignore_unused(v); + BOOST_TEST(v == i->foo()); + k = i; + BOOST_TEST(k == k); + BOOST_TEST(k == i); + BOOST_TEST(k != j); + BOOST_TEST(*k == val); + ignore_unused(k); +} + +// Preconditions: i != j +template <class Iterator, class T> +void mutable_trivial_iterator_test(const Iterator i, const Iterator j, T val) { + *i = val; + trivial_iterator_test(i, j, val); +} + +// Preconditions: *i == v1, *++i == v2 +template <class Iterator, class T> +void input_iterator_test(Iterator i, T v1, T v2) { + Iterator i1(i); + + BOOST_TEST(i == i1); + BOOST_TEST(!(i != i1)); + + // I can see no generic way to create an input iterator + // that is in the domain of== of i and != i. + // The following works for istream_iterator but is not + // guaranteed to work for arbitrary input iterators. + // + // Iterator i2; + // + // BOOST_TEST(i != i2); + // BOOST_TEST(!(i == i2)); + + BOOST_TEST(*i1 == v1); + BOOST_TEST(*i == v1); + + // we cannot test for equivalence of (void)++i & (void)i++ + // as i is only guaranteed to be single pass. + BOOST_TEST(*i++ == v1); + ignore_unused(i1); + + i1 = i; + + BOOST_TEST(i == i1); + BOOST_TEST(!(i != i1)); + + BOOST_TEST(*i1 == v2); + BOOST_TEST(*i == v2); + ignore_unused(i1); + + // i is dereferencable, so it must be incrementable. + ++i; + + // how to test for operator-> ? +} + +template <class Iterator, class T> +void forward_iterator_test(Iterator i, T v1, T v2) { + input_iterator_test(i, v1, v2); + + Iterator i1 = i, i2 = i; + + BOOST_TEST(i == i1++); + BOOST_TEST(i != ++i2); + + trivial_iterator_test(i, i1, v1); + trivial_iterator_test(i, i2, v1); + + ++i; + BOOST_TEST(i == i1); + BOOST_TEST(i == i2); + ++i1; + ++i2; + + trivial_iterator_test(i, i1, v2); + trivial_iterator_test(i, i2, v2); + + typedef typename std::iterator_traits<Iterator>::reference reference; + typedef typename std::iterator_traits<Iterator>::value_type value_type; + BOOST_TEST(std::is_reference<reference>::value); + BOOST_TEST((std::is_same<reference, value_type&>::value || + std::is_same<reference, const value_type&>::value)); +} + +// Preconditions: *i == v1, *++i == v2 +template <class Iterator, class T> +void bidirectional_iterator_test(Iterator i, T v1, T v2) { + forward_iterator_test(i, v1, v2); + ++i; + + Iterator i1 = i, i2 = i; + + BOOST_TEST(i == i1--); + BOOST_TEST(i != --i2); + + trivial_iterator_test(i, i1, v2); + trivial_iterator_test(i, i2, v2); + + --i; + BOOST_TEST(i == i1); + BOOST_TEST(i == i2); + ++i1; + ++i2; + + trivial_iterator_test(i, i1, v1); + trivial_iterator_test(i, i2, v1); +} + +// mutable_bidirectional_iterator_test + +template <class U> +struct undefined; + +// Preconditions: [i,i+N) is a valid range +template <class Iterator, class TrueVals> +void random_access_iterator_test(Iterator i, int N, TrueVals vals) { + bidirectional_iterator_test(i, vals[0], vals[1]); + const Iterator j = i; + int c; + + typedef typename std::iterator_traits<Iterator>::value_type value_type; + ignore_unused<value_type>(); + + for (c = 0; c < N - 1; ++c) { + BOOST_TEST(i == j + c); + BOOST_TEST(*i == vals[c]); + BOOST_TEST(*i == implicit_cast<value_type>(j[c])); + BOOST_TEST(*i == *(j + c)); + BOOST_TEST(*i == *(c + j)); + ++i; + BOOST_TEST(i > j); + BOOST_TEST(i >= j); + BOOST_TEST(j <= i); + BOOST_TEST(j < i); + } + + Iterator k = j + N - 1; + for (c = 0; c < N - 1; ++c) { + BOOST_TEST(i == k - c); + BOOST_TEST(*i == vals[N - 1 - c]); + BOOST_TEST(*i == implicit_cast<value_type>(j[N - 1 - c])); + Iterator q = k - c; + ignore_unused(q); + BOOST_TEST(*i == *q); + BOOST_TEST(i > j); + BOOST_TEST(i >= j); + BOOST_TEST(j <= i); + BOOST_TEST(j < i); + --i; + } +} + +// Precondition: i != j +template <class Iterator, class ConstIterator> +void const_nonconst_iterator_test(Iterator i, ConstIterator j) { + BOOST_TEST(i != j); + BOOST_TEST(j != i); + + ConstIterator k(i); + BOOST_TEST(k == i); + BOOST_TEST(i == k); + + k = i; + BOOST_TEST(k == i); + BOOST_TEST(i == k); + ignore_unused(k); +} + +} // namespace histogram +} // namespace boost + +#endif // BOOST_HISTOGRAM_TEST_ITERATOR_TESTS_HPP diff --git a/src/boost/libs/histogram/test/utility_serialization.hpp b/src/boost/libs/histogram/test/utility_serialization.hpp new file mode 100644 index 000000000..735add6a8 --- /dev/null +++ b/src/boost/libs/histogram/test/utility_serialization.hpp @@ -0,0 +1,54 @@ +// Copyright 2019 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_TEST_UTILITY_SERIALIZATION_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_SERIALIZATION_HPP + +#include <boost/archive/xml_iarchive.hpp> +#include <boost/archive/xml_oarchive.hpp> +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/core/nvp.hpp> +#include <fstream> +#include <iostream> +#include <string> + +std::string join(const char* a, const char* b) { + std::string filename = a; + filename += +#ifdef BOOST_WINDOWS + "\\"; +#else + "/"; +#endif + filename += b; + return filename; +} + +template <class T> +void load_xml(const std::string& filename, T& t) { + std::ifstream ifs(filename); + BOOST_ASSERT(ifs.is_open()); + // manually skip XML comments at the beginning of the stream, because of + // https://github.com/boostorg/serialization/issues/169 + char line[128]; + do { + ifs.getline(line, 128); + BOOST_ASSERT(std::strlen(line) < 127); + } while (!ifs.fail() && !ifs.eof() && std::strstr(line, "-->") == nullptr); + boost::archive::xml_iarchive ia(ifs); + ia >> boost::make_nvp("item", t); +} + +template <class T> +void print_xml(const std::string& filename, const T& t) { + std::cout << filename << "\n"; + boost::archive::xml_oarchive oa(std::cout); + oa << boost::make_nvp("item", t); + std::cout << std::flush; +} + +#endif diff --git a/src/boost/libs/histogram/test/utility_str.hpp b/src/boost/libs/histogram/test/utility_str.hpp new file mode 100644 index 000000000..41edcdee2 --- /dev/null +++ b/src/boost/libs/histogram/test/utility_str.hpp @@ -0,0 +1,26 @@ +// Copyright 2019 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_TEST_UTILITY_STR_HPP +#define BOOST_HISTOGRAM_TEST_UTILITY_STR_HPP + +#include <sstream> + +template <class T> +auto str(const T& t, int w = 0, bool left = true) { + std::ostringstream os; + auto saved = os.width(); + os.width(w); + if (left) + os << std::left; + else + os << std::right; + os << t; + os.width(saved); + return os.str(); +} + +#endif diff --git a/src/boost/libs/histogram/test/utility_test.cpp b/src/boost/libs/histogram/test/utility_test.cpp new file mode 100644 index 000000000..f2e7fec3b --- /dev/null +++ b/src/boost/libs/histogram/test/utility_test.cpp @@ -0,0 +1,53 @@ +// 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/core/lightweight_test.hpp> +#include "throw_exception.hpp" +#include <sstream> +#include <tuple> +#include <vector> +#include "utility_allocator.hpp" +#include "std_ostream.hpp" + +using namespace boost::histogram; + +int main() { + // vector streaming + { + std::ostringstream os; + std::vector<int> v = {1, 3, 2}; + os << v; + BOOST_TEST_EQ(os.str(), std::string("[ 1 3 2 ]")); + } + + // tuple streaming + { + std::ostringstream os; + auto v = std::make_tuple(1, 2.5, "hi"); + os << v; + BOOST_TEST_EQ(os.str(), std::string("[ 1 2.5 hi ]")); + } + + // tracing_allocator + { + tracing_allocator_db db; + tracing_allocator<char> a(db); + auto p1 = a.allocate(2); + a.deallocate(p1, 2); + tracing_allocator<int> b(a); + auto p2 = b.allocate(3); + b.deallocate(p2, 3); + BOOST_TEST_EQ(db.size(), 2); + BOOST_TEST_EQ(db.at<char>().first, 0); + BOOST_TEST_EQ(db.at<char>().second, 2); + BOOST_TEST_EQ(db.at<int>().first, 0); + BOOST_TEST_EQ(db.at<int>().second, 3); + BOOST_TEST_EQ(db.first, 0); + BOOST_TEST_EQ(db.second, 2 + 3 * sizeof(int)); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/histogram/tools/add_boilerplate.py b/src/boost/libs/histogram/tools/add_boilerplate.py new file mode 100755 index 000000000..3ba81c4de --- /dev/null +++ b/src/boost/libs/histogram/tools/add_boilerplate.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# Copyright Hans Dembinski 2019 +# 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 + +import sys +from os.path import abspath, join +import re +import datetime + +project_dir = "/".join(abspath(__file__).split("/")[:-2]) + +filename = abspath(sys.argv[1]) + +copyright = """// Copyright 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) + +""".format(datetime.datetime.today().year) + +if filename.endswith(".hpp"): + with open(filename) as f: + content = f.read() + if not content.startswith("// Copyright"): + content = copyright + content + + sub = filename[len(project_dir) + 1:] + if sub.startswith("include/boost/"): + sub = sub[len("include/boost/"):] + if sub.startswith("test/"): + sub = "histogram/" + sub + guard_name = "BOOST_" + sub.replace(".", "_").replace("/", "_").upper() + + if guard_name not in content: + lines = content.split("\n") + for end, line in enumerate(lines): + if line.startswith("//"): + continue + break + for start in range(end, len(lines)): + if lines[start] != "": + break + lines = lines[:end] + ["", "#ifndef " + guard_name, "#define " + guard_name, ""] + lines[start:] + while lines[-1] == "": + lines.pop() + lines += ["", "#endif // " + guard_name, ""] + content = "\n".join(lines) + + with open(filename, "w") as f: + f.write(content) diff --git a/src/boost/libs/histogram/tools/blacklist.supp b/src/boost/libs/histogram/tools/blacklist.supp new file mode 100644 index 000000000..7df5aa569 --- /dev/null +++ b/src/boost/libs/histogram/tools/blacklist.supp @@ -0,0 +1,14 @@ +# boost/serialization/singleton.hpp:181:13: runtime error: reference binding to null pointer of type X +src:../../boost/serialization/singleton.hpp + +# boost/archive/detail/interface_oarchive.hpp:47:16: runtime error: downcast of address X which does not point to an object of type Y +src:../../boost/archive/detail/interface_oarchive.hpp + +# boost/archive/detail/interface_iarchive.hpp:46:16: runtime error: downcast of address X which does not point to an object of type Y +src:../../boost/archive/detail/interface_iarchive.hpp + +# boost/archive/basic_binary_oprimitive.hpp:73:16: runtime error: downcast of address X which does not point to an object of type Y +src:../../boost/archive/basic_binary_oprimitive.hpp + +# boost/archive/basic_binary_iprimitive.hpp:77:16: runtime error: downcast of address X which does not point to an object of type Y +src:../../boost/archive/basic_binary_iprimitive.hpp diff --git a/src/boost/libs/histogram/tools/cov.sh b/src/boost/libs/histogram/tools/cov.sh new file mode 100755 index 000000000..f9fd8cece --- /dev/null +++ b/src/boost/libs/histogram/tools/cov.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# must be executed in project root folder + +# Copyright Hans Dembinski 2018-2019 +# 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 + +if [ -z $GCOV ]; then + # gcov-9, gcov-7, gcov-6 do not work + for i in 8 5; do + if test $(which gcov-$i); then + GCOV=gcov-$i + break; + fi; + done +fi + +LCOV_VERSION="1.14" +LCOV_DIR="tools/lcov-${LCOV_VERSION}" + +if [ ! -e $LCOV_DIR ]; then + cd tools + curl -L https://github.com/linux-test-project/lcov/releases/download/v${LCOV_VERSION}/lcov-${LCOV_VERSION}.tar.gz | tar zxf - + cd .. +fi + +# --rc lcov_branch_coverage=1 doesn't work on travis +# LCOV="${LCOV_DIR}/bin/lcov --gcov-tool=${GCOV} --rc lcov_branch_coverage=1" +LCOV="${LCOV_DIR}/bin/lcov --gcov-tool=${GCOV}" + +# collect raw data +$LCOV --base-directory `pwd` \ + --directory `pwd`/../../bin.v2/libs/histogram/test \ + --capture --output-file coverage.info + +# remove uninteresting entries +$LCOV --extract coverage.info "*/boost/histogram/*" --output-file coverage.info + +if [ $CI ] || [ $1 ]; then + # upload if on CI or when token is passed as argument + which cpp-coveralls || echo "Error: you need to install cpp-coveralls" + if [ $1 ]; then + cpp-coveralls -l coverage.info -r ../.. -n -t $1 + else + cpp-coveralls -l coverage.info -r ../.. -n + fi +else + # otherwise generate html report + $LCOV_DIR/bin/genhtml coverage.info --demangle-cpp -o coverage-report +fi diff --git a/src/boost/libs/histogram/tools/crlf_to_lf.sh b/src/boost/libs/histogram/tools/crlf_to_lf.sh new file mode 100755 index 000000000..62dee6701 --- /dev/null +++ b/src/boost/libs/histogram/tools/crlf_to_lf.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Copyright Hans Dembinski 2019 +# 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 + +# Find files with CRLF: `find <path> -not -type d -exec file "{}" ";" | grep CRLF` + +perl -pi -e 's/\r\n/\n/g' $1 diff --git a/src/boost/libs/histogram/tools/llvm-gcov.sh b/src/boost/libs/histogram/tools/llvm-gcov.sh new file mode 100755 index 000000000..3b1fff465 --- /dev/null +++ b/src/boost/libs/histogram/tools/llvm-gcov.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Copyright Hans Dembinski 2018-2019 +# 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 + +exec llvm-cov gcov "$@" diff --git a/src/boost/libs/histogram/tools/tidy.py b/src/boost/libs/histogram/tools/tidy.py new file mode 100755 index 000000000..7da34ecdc --- /dev/null +++ b/src/boost/libs/histogram/tools/tidy.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +# Copyright 2019 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 + +import subprocess as subp +from pathlib import Path +from multiprocessing.pool import ThreadPool + +clang_tidy_cmd = None +for version in range(15, 5, -1): + clang_tidy_cmd = f"clang-tidy-{version}" + if subp.run(("which", clang_tidy_cmd), stdout=subp.DEVNULL).returncode == 0: + break + +project_dir = Path(__file__).resolve().parents[1] +assert project_dir.exists() +boost_dir = project_dir.parents[1] + +filenames = (project_dir / "include").rglob("*.hpp") + + +def run_tidy(filename): + n = len(project_dir.parts) + 2 + cmd = f"{clang_tidy_cmd} {filename} -- -I{boost_dir}" + return ( + cmd, + subp.run(cmd.split(), stdout=subp.PIPE, stderr=subp.STDOUT).stdout.decode( + "utf-8" + ), + ) + + +pool = ThreadPool() +for cmd, report in pool.map(run_tidy, filenames): + if report: + print(cmd) + print(report) |