diff --git a/doc/Jamfile b/doc/Jamfile index 17f73a81..7a47f834 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -23,7 +23,7 @@ doxygen reference MACRO_EXPANSION=YES JAVADOC_AUTOBRIEF=YES USE_MATHJAX=NO - SEARCH_INCLUDES=NO + SEARCH_INCLUDES=YES "EXCLUDE=\"../include/boost/histogram/fwd.hpp\" \\ \"../include/boost/histogram/ostream.hpp\" \\ \"../include/boost/histogram/axis/ostream.hpp\" \\ diff --git a/doc/concepts.qbk b/doc/concepts.qbk index dc08f57a..d024b994 100644 --- a/doc/concepts.qbk +++ b/doc/concepts.qbk @@ -1,6 +1,6 @@ [section:concepts Concepts] -Users can extend the library with new axis and storage types. +Users can extend the library with new axis, storage, and accumulator types. [section:axis AxisType] @@ -8,7 +8,6 @@ An `AxisType` converts input values into bin indices. An `AxisType` is required to: -* derive publically from [classref boost::histogram::axis::labeled_base] and [classref boost::histogram::axis::iterator_mixin] * be default/copy/move constructable * be copy/move assignable * be equal comparable @@ -53,8 +52,6 @@ A `StorageType` is required to: * optionally, it can have the following method to support weighted increments: * `template void add(std::size_t index, const boost::histogram::detail::weight_type& w)` -[classref boost::histogram::array_storage] is a simple storage type which may serve as a template for creating a new storage type. - [endsect] [endsect] diff --git a/doc/getting_started.qbk b/doc/getting_started.qbk index 2201c0bc..f872b5d3 100644 --- a/doc/getting_started.qbk +++ b/doc/getting_started.qbk @@ -11,7 +11,7 @@ When the axis types for the histogram are known at compile-time, the library gen [import ../examples/getting_started_listing_01.cpp] [getting_started_listing_01] -We passed the [classref boost::histogram::axis::regular regular] axis type directly to the [funcref boost::histogram::make_histogram make_histogram] function. The library then generates a specialized histogram type with just one regular axis from a generic template. +We passed the [classref boost::histogram::axis::regular regular] axis type directly to the [funcref boost::histogram::make_histogram() make_histogram] function. The library then generates a specialized histogram type with just one regular axis from a generic template. * Pro: Many user errors are already caught at compile-time, not at run-time. * Con: You get template errors if you make a mistake, which may be hard to read. We try to give you useful error messages, but still. diff --git a/doc/guide.qbk b/doc/guide.qbk index 87c0c07d..db7acffc 100644 --- a/doc/guide.qbk +++ b/doc/guide.qbk @@ -1,51 +1,124 @@ [section:guide User guide] -This guide covers the basic and more advanced usage of the library. It is designed to make simple things simple, yet complex things possible. For a quick start, you don't need to read the complete user guide; have a look at the [link histogram.getting_started Getting started] section. +Boost.Histogram is designed to make simple things simple, yet complex things possible. Correspondingly, this guides covers the basic usage first, and the advanced usage in later sections. For an alternative quick start guide, have a look at the [link histogram.getting_started Getting started] section. -[section Create a histogram] +[section Make a histogram] -[section Static or dynamic histogram] +A histogram consists of a [link histogram.concepts.storage storage] and a sequence of [link histogram.concepts.axis axis] objects. The storage represents a grid of cells of counters. The axis objects maps input values to indices, which are used to look up the cell. You don't normally have to worry about the storage, since the library provides a very good default. There are many interesting axis types to choose from, but for now let us stick to the most common axis, the [classref boost::histogram::axis::regular regular] axis. It represents equidistant intervals on the real line. -The histogram host class can store axis objects in a static or dynamic container, see the [link histogram.rationale.structure.histogram_host rationale] for details. Use the convenient factory function [funcref boost::histogram::make_histogram make_histogram] to make the histograms. It will generate the matching histogram type for each input. - -Here is an example on how to use it. You pass one or several axis instances, which define the layout of the histogram. +Use the convenient factory function [funcref boost::histogram::make_histogram make_histogram] to make the histograms. In the following example, a histogram with a single axis is created. [import ../examples/guide_make_static_histogram.cpp] [guide_make_static_histogram] An axis object defines how input values are mapped to bins, which means that it defines the number of bins along that axis and a mapping function from input values to bins. If you provide one axis, the histogram is one-dimensional. If you provide two, it is two-dimensional, and so on. -You get the best performance if the axis types and their number per histogram are known at compile-time. But sometimes you only know the axis configurations at run-time, perhaps because it depends on run-time user input. No problem, you can also create a sequence of axes at run-time and pass them to the factory. +In the example above, the compiler knows the axis type at compile-time, because the type is explicitly declared in the source code. You get the best performance if the axis types and their number per histogram are known at compile-time. Sometimes you only know the axis configuration at run-time, for example, because it depends on run-time user input. No problem, you can also create a sequence of axes at run-time and pass them to the factory. Here is an example. [import ../examples/guide_make_dynamic_histogram.cpp] [guide_make_dynamic_histogram] -Strictly speaking, the axis configuration was again fixed at compile-time in this example, but you could have filled the vector at run-time, based on run-time user input. +Strictly speaking, the axis configuration was again known at compile-time in this example, but you could have filled the vector at run-time, based on run-time user input. When the axis types are known at compile-time, the histogram stores them in a `std::tuple`. If they are only known at run-time, it uses a `std::vector`. In almost all ways, the two versions of the histogram act identically, except that the compile-time version is faster. The [link histogram.overview.rationale.structure.host rationale] has more details on this point. -The factory function [funcref boost::histogram::make_histogram make_histogram] creates histograms with the [classref boost::histogram::default_storage] type, which provides safe counting and is fast and memory efficient. If you want to create a histogram with another storage type, use [funcref boost::histogram::make_histogram_with make_histogram_with]. To learn more about other storage types and how to create your own, have a look at the section [link histogram.guide.expert Advanced Usage]. +The factory function [funcref boost::histogram::make_histogram make_histogram] uses the default storage type, which provides safe counting and is fast and memory efficient. If you want to create a histogram with another storage type, use [funcref boost::histogram::make_histogram_with make_histogram_with]. To learn more about other storage types and how to create your own, have a look at the section [link histogram.guide.expert Advanced Usage]. -[note Memory for bin counters is allocated lazily, if the [classref boost::histogram::default_storage default_storage] is used. Allocation is delayed until the first input values are passed to the histogram. Therefore, memory allocation exceptions are not thrown when the histogram is created, but possibly later. This gives you a chance to check how much memory the histogram will allocate and possibly give a warning if that amount is excessively large. Use the method `histogram::size()` to see how many bins your axis layout requires. That many bytes will be allocated at the first fill. The allocated amount of memory may grow further later to a multiple of that.] +[section Choose an axis] -[endsect] +The library provides a number of useful axis types. -[section Axis configuration] +[variablelist + [ + [ + [classref boost::histogram::axis::regular] + ] + [ + Axis over an interval on the real line with bins of equal width. Value-to-index conversion is O(1) and very fast. The axis does not allocate memory dynamically. The axis is very flexible thanks to transforms (see below). This should be your default. + ] + ] + [ + [ + [classref boost::histogram::axis::variable] + ] + [ + Axis over an interval on the real line with bins of variable width. Value-to-index conversion is O(log(N)). The axis allocates memory dynamically to store the bin edges. + ] + ] + [ + [ + [classref boost::histogram::axis::integer] + ] + [ + Axis over an integer sequence [i, i+1, i+2, ...]. Can also handle real input values, then it represents bins with a fixed bin width of 1. Value-to-index conversion is O(1) and even faster than for the [classref boost::histogram::axis::regular regular axis]. Does not allocate memory dynamically. + ] + ] + [ + [ + [classref boost::histogram::axis::category] + ] + [ + Axis over a set of unique values, which may be of an arbitrary equal-comparable type. Value-to-index conversion is O(N), but faster than [classref boost::histogram::axis::variable variable axis] for N < 10, the typical use case. The axis allocates memory dynamically to store the values. + ] + ] +] -The library comes with a number of axis classes (you can write your own, too, see [link histogram.guide.expert Advanced usage]). The [classref boost::histogram::axis::regular regular axis] should be your default choice, because it is simple and efficient. It splits an interval over a continuous variable into `N` bins of equal width. If you want bins over a range of integers, the [classref boost::histogram::axis::integer integer axis] is faster. If you have data which wraps around, like angles, use a [classref boost::histogram::axis::circular circular axis]. If your bins vary in width, use a [classref boost::histogram::axis::variable variable axis]. If you want to bin categorical values, like `red`, `blue`, `green`, you can use a [classref boost::histogram::axis::category category axis]. +Check the class descriptions for more information about each axis type. You can write your own axis types, too, see [link histogram.guide.expert Advanced usage]. -[note All axes which define bins in terms of intervals always use semi-open intervals by convention. The last value is never included. For example, the axis `axis::integer<>(0, 3)` has three bins with intervals `[0, 1), [1, 2), [2, 3)`. To remember this, think of iterator ranges from `begin` to `end`, where `end` is also not included.] +Here is an example which shows the basic use case for each axis type. -Check the class descriptions for more information about each axis type. All axes are templated on the data type, which means that you can use a [classref boost::histogram::axis::regular regular axis] with floats or doubles, an [classref boost::histogram::axis::integer integer axis] with any kind of integer, and so on. Reasonable defaults are provided, so usually you just pass an empty bracket to the class, like in most examples in this guide. +[import ../examples/guide_axis_basic_demo.cpp] +[guide_axis_basic_demo] -The [classref boost::histogram::axis::regular regular axis] also accepts a second template parameter, a class that implements a bijective transform between the data space and the space where the bins are equi-distant. A [classref boost::histogram::axis::transform::log log transform] is useful for data that is strictly positive and spans over many orders of magnitude (masses of stellar objects are an example). Several other transforms are provided. Users can define their own transforms and use them with the axis. +[note All builtin axes which define bins over intervals on the real line use semi-open intervals by convention. The last value is never included. For example, the axis `axis::regular<>(3, 1.0, 2.5)` has three bins with intervals `[1.0, 1.5), [1.5, 2.0), [2.0, 2.5)`. As a mnemonic, think of iterator ranges from `begin` to `end`, where `end` is also not included.] -In addition to the required parameters for an axis, you can assign an optional label to any axis, which helps to remember what the axis is about. Example: you have census data and you want to investigate how yearly income correlates with age, you could do: +[section Use metadata] + +You can assign an optional label to any axis, which helps to remember what the axis is about. Example: you have census data and you want to investigate how yearly income correlates with age, you could do: [import ../examples/guide_axis_with_labels.cpp] [guide_axis_with_labels] -Without the labels it would be difficult to remember which axis was covering which quantity, because they look the same otherwise. Labels are the only axis property that can be changed later. Axis objects with different labels do not compare equal with `operator==`. +Without the metadata it would be difficult to keep track which axis was covering which quantity. Metadata is the only axis property that can be modified after construction by the user. Axis objects with different metadata do not compare equal. -By default, additional under- and overflow bins are added automatically for each axis where that makes sense. If you create an axis with 20 bins, the histogram will actually have 22 bins along that axis. The two extra bins are generally very good to have, as explained in [link histogram.rationale.uoflow the rationale]. If you are certain that the input cannot exceed the axis range, you can disable the extra bins to save memory. This is done by passing the enum value [enumref boost::histogram::axis::uoflow_type uoflow_type::off] to the axis constructor: +[endsect] + +[section Axis template arguments] + +All axes are templated. All have reasonable defaults so you can use empty brackets. If your compiler supports C++17, you can drop the brackets altogether. The value and metadata types are then deduced from the constructor call. + +[variablelist + [ + [value_type] + [ + The value type is the argument of the `index()` method. An argument passed to the axis must be implicitly convertible to the value type. + ] + ] + [ + [metadata_type] + [ + The axis uses an instance the metadata type is used to store the metadata. By default, this is a `std::string`, but it can by any copyable type. + ] + ] + [ + [options] + [ + Compile-time options for the axis, see [enumref boost::histogram::axis::option]. + ] + ] + [ + [allocator_type (only [classref boost::histogram::axis::variable variable] and [classref boost::histogram::axis::category category] axes)] + [ + Allocator that is used to request memory dynamically to store values. + ] + ] + [ + [transform_type (only [classref boost::histogram::axis::regular regular] axis)] + [ + A class that implements a bijective transform between the data space and the space where the bins are equi-distant. A [classref boost::histogram::axis::transform::log log transform] is useful for data that is strictly positive and spans over many orders of magnitude (masses of stellar objects are an example). Several other transforms are provided. Users can define their own transforms and use them with the axis. + ] + ] +] + +By default, additional under- and overflow bins are added automatically for each axis where that makes sense. If you create an axis with 20 bins, the histogram will actually have 22 bins along that axis. The extra bins are very useful, as explained in the [link histogram.overview.rationale.uoflow rationale]. If you are certain that the input cannot exceed the axis range, you can disable the extra bins to save memory. This is done by using [enumref boost::histogram::axis::option option::none] as the template argument. Example: [import ../examples/guide_axis_with_uoflow_off.cpp] [guide_axis_with_uoflow_off] @@ -58,6 +131,8 @@ We use an [classref boost::histogram::axis::integer integer axis] here, because [endsect] +[endsect] + [section Fill histogram] After you created a histogram, you want to insert tuples of possibly multi-dimensional and heterogenous values. This is done with the flexible `histogram(...)` call, which you typically do in a loop. Some extra parameters can be passed to the method as shown in the next example. @@ -69,7 +144,7 @@ After you created a histogram, you want to insert tuples of possibly multi-dimen `histogram(weight(x), ...)` does the same as the first call, but increments the bin counter by the value `x`. The type of `x` is not restricted, usually it is a real number. The `weight(x)` helper class must be first argument. You can freely mix calls with and without a `weight`. Calls without a `weight` act like the weight is `1`. -Why weighted increments are sometimes useful, especially in a scientific context, is explained [link histogram.rationale.weights in the rationale]. If you don't see the point, you can just ignore this type of call. This feature does not affect the performance of the histogram if you don't use it. +Why weighted increments are sometimes useful, especially in a scientific context, is explained [link histogram.overview.rationale.weights in the rationale]. If you don't see the point, you can just ignore this type of call. This feature does not affect the performance of the histogram if you don't use it. [note The first call to a weighted fill internally switches the default storage from integral counters to another type, which holds two real numbers per bin, one for the sum of weights (the weighted count), and another for the sum of weights squared (the variance of the weighted count). This is not necessary for unweighted fills, because the two sums are identical is all weights are `1`. The default storage automatically optimizes this case by using only one integral number per bin as long as no weights are encountered.] @@ -107,7 +182,7 @@ Two histograms compare equal, if... Adding histograms is useful, if you want to parallelize the filling of a histogram over several threads or processes. Fill independent copies of the histogram in worker threads, and then add them all up in the main thread. -Multiplying by a number is useful to re-weight histograms before adding them, for those who need to work with weights. Multiplying by a factor `x` has a different effect on value and variance of each bin counter. The value is multiplied by `x`, but the variance is multiplied by `x*x`. This follows from the properties of the variance, as explained in [link histogram.rationale.variance the rationale]. +Multiplying by a number is useful to re-weight histograms before adding them, for those who need to work with weights. Multiplying by a factor `x` has a different effect on value and variance of each bin counter. The value is multiplied by `x`, but the variance is multiplied by `x*x`. This follows from the properties of the variance, as explained in [link histogram.overview.rationale.variance the rationale]. [warning Because of special behavior of the variance, adding a histogram to itself is not identical to multiplying the original histogram by two, as far as the variance is concerned.] diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 25cbb714..00c343a2 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -1,6 +1,6 @@ -[section Rationale] +[section:rationale Rationale] -[section Guidelines] +[section:guidelines Guidelines] This library was written based on a decade of experience collected in working with big data, more precisely in the field of particle physics and astroparticle physics. The design is guided by advice from people like Bjarne Stroustrup, Scott Meyers, Herb Sutter, and Andrei Alexandrescu, and Chandler Carruth. I also like the [@https://www.python.org/dev/peps/pep-0020 Zen of Python] (also applies to other languages). I also borrowed ideas from the [@https://eigen.tuxfamily.org/ Eigen library]. @@ -20,13 +20,13 @@ Design goals of the library: The library consists of three orthogonal components: -* [link histogram.rationale.structure.histogram_host histogram host class]: The histogram host class defines the public user interface and holds axis objects (one for each dimension) and a storage object. The user can chose whether axis objects are stored in a static tuple, a vector, or a vector of variants. +* [link histogram.overview.rationale.structure.host histogram host class]: The histogram host class defines the public user interface and holds axis objects (one for each dimension) and a storage object. The user can chose whether axis objects are stored in a static tuple, a vector, or a vector of variants. -* [link histogram.rationale.structure.axis_types axis types]: Defines how input values are mapped to bins. Several axis types are provided which implement different specializations. Users can make their own axis types following the axis concept and use them with the library. A variant type is provided, which can hold one of several concrete axis types. +* [link histogram.overview.rationale.structure.axis axis types]: Defines how input values are mapped to bins. Several axis types are provided which implement different specializations. Users can make their own axis types following the axis concept and use them with the library. A variant type is provided, which can hold one of several concrete axis types. -* [link histogram.rationale.structure.storage_types storage types]: Manages a collection of bin counters. The requirements for a storage differ from those of an STL container, it needs to follow the storage concept. Two implementations are provided. +* [link histogram.overview.rationale.structure.storage storage types]: Manages a collection of bin counters. The requirements for a storage differ from those of an STL container, it needs to follow the storage concept. Two implementations are provided. -[section:histogram_host Histogram host class] +[section:host Histogram host class] Histograms store axis objects and a storage object. A one-dimensional histogram has one axis, a multi-dimensional histogram has several. When you pass an input tuple, say (v1, v2, v3), then the first axis will map v1 onto index i1, the second axis v2 onto i2, and so on, to generate the index tuple (i1, i2, i3). The histogram host class then converts these indices into a linear global index that is used to address bin counter in the storage object. @@ -34,7 +34,7 @@ Histograms store axis objects and a storage object. A one-dimensional histogram To understand the need for multi-dimensional histograms, think of point coordinates. If all points that you consider lie on a line, you need only one value to describe the point. If all points lie in a plane, you need two values to describe the position. Three values are needed for a point in space. A histogram puts a discrete grid over the line, the plane or the space, and counts how many points lie in each cell of the grid. To approximate a point distribution on a line, a 1d-histogram is sufficient. To do the same in 3d-space, one needs a 3d-histogram. ] -This library supports different axis types, so that the user can customize how the mapping is done exactly, see [link histogram.rationale.structure.axis_types axis types]. Users can furthermore chose between several ways of storing axis types in the histogram. +This library supports different axis types, so that the user can customize how the mapping is done exactly, see [link histogram.overview.rationale.structure.axis axis types]. Users can furthermore chose between several ways of storing axis types in the histogram. When the number and types of the axes are known at compile-time, the histogram host class stores axis types in a `std::tuple`. We call this a /static histogram/. To access a particular axis, one should use a compile-time number as index (a run-time index also works with some limitations). A static histogram is extremely fast (see [link histogram.benchmarks benchmark]), because there is no overhead and the compiler can inline code, unroll loops, and more. Also nice: many user errors are can be caught at compile-time rather than run-time. @@ -50,7 +50,7 @@ The design decision to store axis types in the variant-like type `boost::histogr [endsect] -[section:axis_types Axis types] +[section:axis Axis types] An axis defines an injective mapping of (a range of) input values to a bin. The logic is encapsulated in an axis type. Users can create their own axis classes and use them with the library, by providing a class compatible with the [link histogram.concepts.axis axis concept]. The library comes with four builtin types, which implement different specializations. @@ -68,7 +68,7 @@ Each builtin axis type has a few compile-time options, which change its behavior [endsect] -[section:storage_types Storage types] +[section:storage Storage types] A storage type stores the actual cell values. It uses a one-dimensional index for cell lookup, computed by the histogram host from the indices generated from all its axes. The storage needs to know nothing about axes. Users can integrate their own storage classes with the library, by providing a class compatible with the [link histogram.concepts.storage storage concept]. Standard containers are automatically adapted by the library to storage types. @@ -99,7 +99,7 @@ The no-overflow-guarantee only applies when the histogram is not using weighted The best part: this approach is even faster for a histogram with sufficient size despite the run-time overheads of handling the counter type dynamically. The benchmarks show that the gains from better cache usage outweigh the run-time overheads of dynamic dispatching to the right bin counter type and the occasional allocation costs. Doubling the size of the bin counters each time helps, too, because the allocations happen only O(logN) times for N increments. [note -In a sense, [classref boost::histogram::unlimited_storage] is the opposite of a `std::vector`, which keeps the size of the stored type constant, but grows to hold a larger number of elements. Here, the number of elements remains the same, but the storage grows to hold a uniform collection of larger and larger elements. +In a sense, [classref boost::histogram::unlimited_storage] is the complement of a `std::vector`, which keeps the size of the stored type constant, but grows to hold a larger number of elements. Here, the number of elements remains the same, but the storage grows to hold a uniform collection of larger and larger elements. ] [endsect] @@ -134,7 +134,7 @@ The presence of the extra bins does not interfere with normal indexing. On an ax The standard library returns a container size as an unsigned integer, because a container size cannot be negative. The `size()` method of the histogram class follows this rule, but the `size()` methods of axis types return a signed integral type. Why? -As explained in the [link histogram.rationale.uoflow section about under- and overflow], a histogram axis may have an optional underflow bin, which is addressed by the index `-1`. It follows that the index type must be signed integer for all axis types. +As explained in the [link histogram.overview.rationale.uoflow section about under- and overflow], a histogram axis may have an optional underflow bin, which is addressed by the index `-1`. It follows that the index type must be signed integer for all axis types. The `size()` method of any axis returns the same signed integer type. The size of an axis cannot be negative, but this choice has two advantages. Firstly, the value returned by `size()` itself is guaranteed to be a valid index, which is good since it may address the overflow bin. Secondly, comparisons between an index and the value returned by `size()` are frequent. If `size()` returned an unsigned integral type, compilers would produce a warning for each comparisons, and rightly so. [@https://www.youtube.com/watch?v=wvtFGa6XJDU Something awful happens] on most machines when you compare `-1` with an unsigned integer, `-1 < 1u == false`, which causes a serious bug in the following innocent-looking loop: ``` @@ -183,7 +183,7 @@ Now, if the number returned by the `variance()` method is just the same as the n [section:weights Support of weighted fills] -A histogram sorts input values into bins and increments a bin counter if an input value falls into the range covered by that bin. The [classref boost::histogram::unlimited_storage standard storage] uses integer types to store these counts, see the [link histogram.rationale.structure.storage_types storage section] how integer overflow is avoided. However, sometimes histograms need to be filled with values that have a weight ['w] attached to them. In this case, the corresponding bin counter is not increased by one, but by the weight value ['w]. +A histogram sorts input values into bins and increments a bin counter if an input value falls into the range covered by that bin. The [classref boost::histogram::unlimited_storage standard storage] uses integer types to store these counts, see the [link histogram.overview.rationale.structure.storage storage section] how integer overflow is avoided. However, sometimes histograms need to be filled with values that have a weight ['w] attached to them. In this case, the corresponding bin counter is not increased by one, but by the weight value ['w]. [note There are several use-cases for weighted increments. The main use in particle physics is to adapt simulated data of an experiment to real data. Simulations are needed to determine various corrections and efficiencies, but a simulated experiment is almost never a perfect replica of the real experiment. In addition, simulations are expensive to do. So, when deviations in a simulated distribution of a variable are found, one typically does not rerun the simulations, but assigns weights to match the simulated distribution to the real one. ] diff --git a/examples/guide_axis_basic_demo.cpp b/examples/guide_axis_basic_demo.cpp new file mode 100644 index 00000000..e69de29b diff --git a/include/boost/histogram/algorithm/reduce.hpp b/include/boost/histogram/algorithm/reduce.hpp index 4ab7384b..25f98500 100644 --- a/include/boost/histogram/algorithm/reduce.hpp +++ b/include/boost/histogram/algorithm/reduce.hpp @@ -79,24 +79,38 @@ inline auto rebin(unsigned iaxis, unsigned merge) { std::numeric_limits::quiet_NaN(), merge}; } -/// Convenience overload for single axis. -/// @copydoc shrink_and_rebin(unsigned, double, double, unsigned) +/** + Convenience overload for single axis. + + @param lower lowest bound that should be kept. + @param upper highest bound that should be kept. If upper is inside bin interval, the + whole interval is removed. + @param merge how many adjacent bins to merge into one. +*/ inline auto shrink_and_rebin(double lower, double upper, unsigned merge) { return shrink_and_rebin(0, lower, upper, merge); } -/// Convenience overload for single axis. -/// @copydoc shrink(unsigned, double, double) +/** + Convenience overload for single axis. + + @param lower lowest bound that should be kept. + @param upper highest bound that should be kept. If upper is inside bin interval, the + whole interval is removed. +*/ inline auto shrink(double lower, double upper) { return shrink(0, lower, upper); } -/// Convenience overload for single axis. -/// @copydoc rebin(unsigned, unsigned) +/** + Convenience overload for single axis. + + @param merge how many adjacent bins to merge into one. +*/ inline auto rebin(unsigned merge) { return rebin(0, merge); } /** Shrink and/or rebin axes of a histogram. - Returns the modified copy. + Returns the reduced copy of the histogram. @param hist original histogram. @param options iterable sequence of reduce_options, generated by shrink_and_rebin(), diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp index faf3acdc..caa28151 100644 --- a/include/boost/histogram/axis/category.hpp +++ b/include/boost/histogram/axis/category.hpp @@ -41,15 +41,19 @@ public: namespace axis { -/** Maps at a set of unique values to bin indices. - * - * The axis maps a set of values to bins, following the order of arguments in the - * constructor. There is an optional overflow bin for this axis, which counts values that - * are not part of the set. Binning has a O(N) complexity, but with a very small factor. - * For small N (the typical use case) it beats other kinds of lookup. - * - * Value types must be equal-comparable. - */ +/** + Maps at a set of unique values to bin indices. + + The axis maps a set of values to bins, following the order of arguments in the + constructor. The optional overflow bin for this axis counts input values that + are not part of the set. Binning has O(N) complexity, but with a very small factor. + For small N (the typical use case) it beats other kinds of lookup. + + @tparam Value input value type, must be equal-comparable. + @tparam MetaData type to store meta data. + @tparam Options whether axis has an overflow bin or is growing. + @tparam Allocator allocator to use for dynamic memory management. +*/ template class category : public iterator_mixin>, diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp index 5be85249..96b6ca82 100644 --- a/include/boost/histogram/axis/integer.hpp +++ b/include/boost/histogram/axis/integer.hpp @@ -57,10 +57,14 @@ public: namespace axis { -/** Axis for an interval of integer values with unit steps. - * - * Binning is a O(1) operation. This axis operates - * faster than a regular axis. +/** + Axis for an interval of integer values with unit steps. + + Binning is a O(1) operation. This axis bins faster than a regular axis. + + @tparam Value input value type. Must be integer or floating point. + @tparam MetaData type to store meta data. + @tparam Options whether axis has an under- and/or overflow bin, is circular, or growing. */ template class integer : public iterator_mixin>, diff --git a/include/boost/histogram/axis/option.hpp b/include/boost/histogram/axis/option.hpp new file mode 100644 index 00000000..d0cfdb37 --- /dev/null +++ b/include/boost/histogram/axis/option.hpp @@ -0,0 +1,61 @@ +// 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) + +#ifndef BOOST_HISTOGRAM_AXIS_OPTION_HPP +#define BOOST_HISTOGRAM_AXIS_OPTION_HPP + +namespace boost { +namespace histogram { +namespace axis { + +/** + Options for builtin axis types. + + Options should be combined with `operator|`. +*/ +enum class option { + none = 0, ///< all options are off. + underflow = 0b1, ///< axis has underflow bin. + overflow = 0b10, ///< axis has overflow bin. + circular = 0b100, ///< axis is circular, mutually exclusive with underflow. + growth = 0b1000, ///< axis can grow, mutually exclusive with circular. + use_default = static_cast(underflow) | static_cast(overflow), +}; + +/// Invert options. +constexpr inline option operator~(option a) { + return static_cast