Allow some empty axes

This commit is contained in:
Hans Dembinski 2019-11-18 23:20:34 +01:00 committed by GitHub
parent ab4913d3f0
commit d0c1ef5b27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 21 deletions

View File

@ -70,8 +70,12 @@ public:
*/ */
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {}) category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: metadata_base<MetaData>(std::move(meta)), vec_(begin, end, alloc) { : metadata_base<MetaData>(std::move(meta)), vec_(alloc) {
if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); if (std::distance(begin, end) < 0)
BOOST_THROW_EXCEPTION(
std::invalid_argument("end must be reachable by incrementing begin"));
vec_.reserve(std::distance(begin, end));
while (begin != end) vec_.emplace_back(*begin++);
} }
/** Construct axis from iterable sequence of unique values. /** Construct axis from iterable sequence of unique values.

View File

@ -77,7 +77,8 @@ public:
: metadata_base<MetaData>(std::move(meta)) : metadata_base<MetaData>(std::move(meta))
, size_(static_cast<index_type>(stop - start)) , size_(static_cast<index_type>(stop - start))
, min_(start) { , min_(start) {
if (stop <= start) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); if (!(stop >= start))
BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
} }
/// Constructor used by algorithm::reduce to shrink and rebin. /// Constructor used by algorithm::reduce to shrink and rebin.

View File

@ -78,7 +78,7 @@ public:
template <class It, class = detail::requires_iterator<It>> template <class It, class = detail::requires_iterator<It>>
variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {}) variable(It begin, It end, metadata_type meta = {}, allocator_type alloc = {})
: metadata_base<MetaData>(std::move(meta)), vec_(std::move(alloc)) { : metadata_base<MetaData>(std::move(meta)), vec_(std::move(alloc)) {
if (std::distance(begin, end) <= 1) if (std::distance(begin, end) < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required")); BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
vec_.reserve(std::distance(begin, end)); vec_.reserve(std::distance(begin, end));

View File

@ -4,6 +4,7 @@
// (See accompanying file LICENSE_1_0.txt // (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/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.hpp>
#include <boost/core/lightweight_test_trait.hpp> #include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/axis/category.hpp> #include <boost/histogram/axis/category.hpp>
@ -26,10 +27,11 @@ int main() {
BOOST_TEST(std::is_nothrow_move_assignable<axis::category<int>>::value); BOOST_TEST(std::is_nothrow_move_assignable<axis::category<int>>::value);
BOOST_TEST(std::is_nothrow_move_assignable<axis::category<std::string>>::value); BOOST_TEST(std::is_nothrow_move_assignable<axis::category<std::string>>::value);
// bad_ctors // bad ctor
{ {
auto empty = std::vector<int>(0); int x[2];
BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); 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 // value should return copy for arithmetic types and const reference otherwise
@ -50,6 +52,17 @@ int main() {
BOOST_TEST_TRAIT_SAME(decltype(std::declval<axis::category<int>>().value(0)), 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 // axis::category
{ {
std::string A("A"), B("B"), C("C"), other; std::string A("A"), B("B"), C("C"), other;

View File

@ -22,10 +22,7 @@ int main() {
BOOST_TEST(std::is_nothrow_move_constructible<axis::integer<>>::value); BOOST_TEST(std::is_nothrow_move_constructible<axis::integer<>>::value);
// bad_ctor // bad_ctor
{ { BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); }
BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument);
BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument);
}
// axis::integer with double type // axis::integer with double type
{ {
@ -60,6 +57,34 @@ int main() {
BOOST_TEST_EQ(d, a); 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 with int type
{ {
axis::integer<int> a{-1, 2}; axis::integer<int> a{-1, 2};

View File

@ -5,6 +5,7 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt) // or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <algorithm> #include <algorithm>
#include <boost/core/ignore_unused.hpp>
#include <boost/core/lightweight_test.hpp> #include <boost/core/lightweight_test.hpp>
#include <boost/histogram/axis.hpp> #include <boost/histogram/axis.hpp>
#include <boost/histogram/axis/ostream.hpp> #include <boost/histogram/axis/ostream.hpp>
@ -67,10 +68,9 @@ int main() {
std::vector<int>(1, 0)); std::vector<int>(1, 0));
h.fill(inputs); // should not crash h.fill(inputs); // should not crash
#ifndef BOOST_NO_EXCEPTIONS
auto bad = std::vector<I>(BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1, I(0, 1)); 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); BOOST_TEST_THROWS((void)make_histogram(bad), std::invalid_argument);
#endif
} }
// bad fill // bad fill

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/core/lightweight_test.hpp> #include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators.hpp> #include <boost/histogram/accumulators.hpp>
#include <boost/histogram/accumulators/ostream.hpp> #include <boost/histogram/accumulators/ostream.hpp>
@ -74,12 +75,12 @@ void run_tests(const std::vector<int>& x, const std::vector<int>& y,
BOOST_TEST_EQ(h, h2); BOOST_TEST_EQ(h, h2);
BOOST_TEST_EQ(h, h3); BOOST_TEST_EQ(h, h3);
#ifndef BOOST_NO_EXCEPTIONS
int bad1[2][4]; int bad1[2][4];
boost::ignore_unused(bad1);
std::vector<std::array<int, 4>> bad2; std::vector<std::array<int, 4>> bad2;
boost::ignore_unused(bad2);
BOOST_TEST_THROWS(h.fill(bad1), std::invalid_argument); BOOST_TEST_THROWS(h.fill(bad1), std::invalid_argument);
BOOST_TEST_THROWS(h.fill(bad2), std::invalid_argument); BOOST_TEST_THROWS(h.fill(bad2), std::invalid_argument);
#endif
} }
// 2D simple // 2D simple
@ -93,14 +94,13 @@ void run_tests(const std::vector<int>& x, const std::vector<int>& y,
BOOST_TEST_EQ(h, h2); BOOST_TEST_EQ(h, h2);
#ifndef BOOST_NO_EXCEPTIONS
// wrong rank // wrong rank
BOOST_TEST_THROWS(h.fill(x), std::invalid_argument); BOOST_TEST_THROWS(h.fill(x), std::invalid_argument);
// not rectangular // not rectangular
std::array<std::vector<int>, 2> bad = {{std::vector<int>(2), std::vector<int>(3)}}; std::array<std::vector<int>, 2> bad = {{std::vector<int>(2), std::vector<int>(3)}};
boost::ignore_unused(bad);
BOOST_TEST_THROWS(h2.fill(bad), std::invalid_argument); BOOST_TEST_THROWS(h2.fill(bad), std::invalid_argument);
#endif
} }
// 1D variant and weight // 1D variant and weight
@ -130,11 +130,10 @@ void run_tests(const std::vector<int>& x, const std::vector<int>& y,
BOOST_TEST_EQ(h1, h2); BOOST_TEST_EQ(h1, h2);
#ifndef BOOST_NO_EXCEPTIONS
auto w2 = w; auto w2 = w;
w2.resize(ndata - 1); w2.resize(ndata - 1);
boost::ignore_unused(w2);
BOOST_TEST_THROWS(h2.fill(v, weight(w2)), std::invalid_argument); BOOST_TEST_THROWS(h2.fill(v, weight(w2)), std::invalid_argument);
#endif
} }
// 2D variant and weight // 2D variant and weight

View File

@ -4,6 +4,7 @@
// (See accompanying file LICENSE_1_0.txt // (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/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.hpp>
#include <boost/histogram/accumulators.hpp> #include <boost/histogram/accumulators.hpp>
#include <boost/histogram/accumulators/ostream.hpp> #include <boost/histogram/accumulators/ostream.hpp>
@ -441,12 +442,12 @@ void run_tests() {
int j11[] = {1, 1}; int j11[] = {1, 1};
BOOST_TEST_EQ(h.at(j11), 1); BOOST_TEST_EQ(h.at(j11), 1);
BOOST_TEST_EQ(h[j11], 1); BOOST_TEST_EQ(h[j11], 1);
#ifndef BOOST_NO_EXCEPTIONS
int j111[] = {1, 1, 1}; int j111[] = {1, 1, 1};
boost::ignore_unused(j111);
BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument); BOOST_TEST_THROWS((void)h.at(j111), std::invalid_argument);
int j13[] = {1, 3}; int j13[] = {1, 3};
boost::ignore_unused(j13);
BOOST_TEST_THROWS((void)h.at(j13), std::out_of_range); BOOST_TEST_THROWS((void)h.at(j13), std::out_of_range);
#endif
// tuple with weight // tuple with weight
h(std::make_tuple(weight(2), 0, 2.0)); h(std::make_tuple(weight(2), 0, 2.0));