Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-11-02 08:54:37

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Utilities/Axis.hpp"
0013 #include "Acts/Utilities/AxisDefinitions.hpp"
0014 
0015 #include <cstddef>
0016 #include <vector>
0017 
0018 using namespace Acts;
0019 
0020 namespace ActsTests {
0021 
0022 BOOST_AUTO_TEST_SUITE(UtilitiesSuite)
0023 
0024 BOOST_AUTO_TEST_CASE(equidistant_axis) {
0025   Axis a(0.0, 10.0, 10u);
0026 
0027   // general binning properties
0028   BOOST_CHECK_EQUAL(a.getNBins(), 10u);
0029   BOOST_CHECK_EQUAL(a.getMax(), 10.);
0030   BOOST_CHECK_EQUAL(a.getMin(), 0.);
0031   BOOST_CHECK_EQUAL(a.getBinWidth(), 1.);
0032 
0033   // bin index calculation
0034   BOOST_CHECK_EQUAL(a.getBin(-0.3), 0u);
0035   BOOST_CHECK_EQUAL(a.getBin(-0.), 1u);
0036   BOOST_CHECK_EQUAL(a.getBin(0.), 1u);
0037   BOOST_CHECK_EQUAL(a.getBin(0.7), 1u);
0038   BOOST_CHECK_EQUAL(a.getBin(1), 2u);
0039   BOOST_CHECK_EQUAL(a.getBin(1.2), 2u);
0040   BOOST_CHECK_EQUAL(a.getBin(2.), 3u);
0041   BOOST_CHECK_EQUAL(a.getBin(2.7), 3u);
0042   BOOST_CHECK_EQUAL(a.getBin(3.), 4u);
0043   BOOST_CHECK_EQUAL(a.getBin(3.6), 4u);
0044   BOOST_CHECK_EQUAL(a.getBin(4.), 5u);
0045   BOOST_CHECK_EQUAL(a.getBin(4.98), 5u);
0046   BOOST_CHECK_EQUAL(a.getBin(5.), 6u);
0047   BOOST_CHECK_EQUAL(a.getBin(5.12), 6u);
0048   BOOST_CHECK_EQUAL(a.getBin(6.), 7u);
0049   BOOST_CHECK_EQUAL(a.getBin(6.00001), 7u);
0050   BOOST_CHECK_EQUAL(a.getBin(7.), 8u);
0051   BOOST_CHECK_EQUAL(a.getBin(7.5), 8u);
0052   BOOST_CHECK_EQUAL(a.getBin(8.), 9u);
0053   BOOST_CHECK_EQUAL(a.getBin(8.1), 9u);
0054   BOOST_CHECK_EQUAL(a.getBin(9.), 10u);
0055   BOOST_CHECK_EQUAL(a.getBin(9.999), 10u);
0056   BOOST_CHECK_EQUAL(a.getBin(10.), 11u);
0057   BOOST_CHECK_EQUAL(a.getBin(100.3), 11u);
0058 
0059   // lower bin boundaries
0060   BOOST_CHECK_EQUAL(a.getBinLowerBound(1), 0.);
0061   BOOST_CHECK_EQUAL(a.getBinLowerBound(2), 1.);
0062   BOOST_CHECK_EQUAL(a.getBinLowerBound(3), 2.);
0063   BOOST_CHECK_EQUAL(a.getBinLowerBound(4), 3.);
0064   BOOST_CHECK_EQUAL(a.getBinLowerBound(5), 4.);
0065   BOOST_CHECK_EQUAL(a.getBinLowerBound(6), 5.);
0066   BOOST_CHECK_EQUAL(a.getBinLowerBound(7), 6.);
0067   BOOST_CHECK_EQUAL(a.getBinLowerBound(8), 7.);
0068   BOOST_CHECK_EQUAL(a.getBinLowerBound(9), 8.);
0069   BOOST_CHECK_EQUAL(a.getBinLowerBound(10), 9.);
0070 
0071   // upper bin boundaries
0072   BOOST_CHECK_EQUAL(a.getBinUpperBound(1), 1.);
0073   BOOST_CHECK_EQUAL(a.getBinUpperBound(2), 2.);
0074   BOOST_CHECK_EQUAL(a.getBinUpperBound(3), 3.);
0075   BOOST_CHECK_EQUAL(a.getBinUpperBound(4), 4.);
0076   BOOST_CHECK_EQUAL(a.getBinUpperBound(5), 5.);
0077   BOOST_CHECK_EQUAL(a.getBinUpperBound(6), 6.);
0078   BOOST_CHECK_EQUAL(a.getBinUpperBound(7), 7.);
0079   BOOST_CHECK_EQUAL(a.getBinUpperBound(8), 8.);
0080   BOOST_CHECK_EQUAL(a.getBinUpperBound(9), 9.);
0081   BOOST_CHECK_EQUAL(a.getBinUpperBound(10), 10.);
0082 
0083   // bin centers
0084   BOOST_CHECK_EQUAL(a.getBinCenter(1), 0.5);
0085   BOOST_CHECK_EQUAL(a.getBinCenter(2), 1.5);
0086   BOOST_CHECK_EQUAL(a.getBinCenter(3), 2.5);
0087   BOOST_CHECK_EQUAL(a.getBinCenter(4), 3.5);
0088   BOOST_CHECK_EQUAL(a.getBinCenter(5), 4.5);
0089   BOOST_CHECK_EQUAL(a.getBinCenter(6), 5.5);
0090   BOOST_CHECK_EQUAL(a.getBinCenter(7), 6.5);
0091   BOOST_CHECK_EQUAL(a.getBinCenter(8), 7.5);
0092   BOOST_CHECK_EQUAL(a.getBinCenter(9), 8.5);
0093   BOOST_CHECK_EQUAL(a.getBinCenter(10), 9.5);
0094 
0095   // inside check
0096   BOOST_CHECK(!a.isInside(-0.2));
0097   BOOST_CHECK(a.isInside(0.));
0098   BOOST_CHECK(a.isInside(3.));
0099   BOOST_CHECK(!a.isInside(10.));
0100   BOOST_CHECK(!a.isInside(12.));
0101 }
0102 
0103 BOOST_AUTO_TEST_CASE(variable_axis) {
0104   Axis a({0, 0.5, 3, 4.5, 6});
0105 
0106   // general binning properties
0107   BOOST_CHECK_EQUAL(a.getNBins(), 4u);
0108   BOOST_CHECK_EQUAL(a.getMax(), 6.);
0109   BOOST_CHECK_EQUAL(a.getMin(), 0.);
0110 
0111   // bin index calculation
0112   BOOST_CHECK_EQUAL(a.getBin(-0.3), 0u);
0113   BOOST_CHECK_EQUAL(a.getBin(-0.), 1u);
0114   BOOST_CHECK_EQUAL(a.getBin(0.), 1u);
0115   BOOST_CHECK_EQUAL(a.getBin(0.3), 1u);
0116   BOOST_CHECK_EQUAL(a.getBin(0.5), 2u);
0117   BOOST_CHECK_EQUAL(a.getBin(1.2), 2u);
0118   BOOST_CHECK_EQUAL(a.getBin(2.7), 2u);
0119   BOOST_CHECK_EQUAL(a.getBin(3.), 3u);
0120   BOOST_CHECK_EQUAL(a.getBin(4.49999), 3u);
0121   BOOST_CHECK_EQUAL(a.getBin(4.5), 4u);
0122   BOOST_CHECK_EQUAL(a.getBin(5.12), 4u);
0123   BOOST_CHECK_EQUAL(a.getBin(6.), 5u);
0124   BOOST_CHECK_EQUAL(a.getBin(6.00001), 5u);
0125   BOOST_CHECK_EQUAL(a.getBin(7.5), 5u);
0126 
0127   // lower bin boundaries
0128   BOOST_CHECK_EQUAL(a.getBinLowerBound(1), 0.);
0129   BOOST_CHECK_EQUAL(a.getBinLowerBound(2), 0.5);
0130   BOOST_CHECK_EQUAL(a.getBinLowerBound(3), 3.);
0131   BOOST_CHECK_EQUAL(a.getBinLowerBound(4), 4.5);
0132 
0133   // upper bin boundaries
0134   BOOST_CHECK_EQUAL(a.getBinUpperBound(1), 0.5);
0135   BOOST_CHECK_EQUAL(a.getBinUpperBound(2), 3.);
0136   BOOST_CHECK_EQUAL(a.getBinUpperBound(3), 4.5);
0137   BOOST_CHECK_EQUAL(a.getBinUpperBound(4), 6.);
0138 
0139   // bin centers
0140   BOOST_CHECK_EQUAL(a.getBinCenter(1), 0.25);
0141   BOOST_CHECK_EQUAL(a.getBinCenter(2), 1.75);
0142   BOOST_CHECK_EQUAL(a.getBinCenter(3), 3.75);
0143   BOOST_CHECK_EQUAL(a.getBinCenter(4), 5.25);
0144 
0145   // inside check
0146   BOOST_CHECK(!a.isInside(-0.2));
0147   BOOST_CHECK(a.isInside(0.));
0148   BOOST_CHECK(a.isInside(3.));
0149   BOOST_CHECK(!a.isInside(6.));
0150   BOOST_CHECK(!a.isInside(12.));
0151 }
0152 
0153 BOOST_AUTO_TEST_CASE(open_axis) {
0154   Axis<AxisType::Equidistant, AxisBoundaryType::Bound> a(0, 10, 10);
0155 
0156   // normal inside
0157   BOOST_CHECK_EQUAL(a.getBin(0.5), 1u);
0158   BOOST_CHECK_EQUAL(a.getBin(9.5), 10u);
0159 
0160   // out of bounds, but is open
0161   // -> should clamp
0162   BOOST_CHECK_EQUAL(a.getBin(-0.5), 1u);
0163   BOOST_CHECK_EQUAL(a.getBin(10.5), 10u);
0164 
0165   Axis<AxisType::Variable, AxisBoundaryType::Bound> b(
0166       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
0167 
0168   // normal inside
0169   BOOST_CHECK_EQUAL(b.getBin(0.5), 1u);
0170   BOOST_CHECK_EQUAL(b.getBin(9.5), 10u);
0171 
0172   // out of bounds, but is open
0173   // -> should clamp
0174   BOOST_CHECK_EQUAL(b.getBin(-0.5), 1u);
0175   BOOST_CHECK_EQUAL(b.getBin(10.5), 10u);
0176 }
0177 
0178 BOOST_AUTO_TEST_CASE(closed_axis) {
0179   Axis<AxisType::Equidistant, AxisBoundaryType::Closed> a(0, 10, 10);
0180 
0181   // normal inside
0182   BOOST_CHECK_EQUAL(a.getBin(0.5), 1u);
0183   BOOST_CHECK_EQUAL(a.getBin(9.5), 10u);
0184 
0185   // out of bounds, but is closed
0186   // -> should wrap to opposite side bin
0187   BOOST_CHECK_EQUAL(a.getBin(-0.5), 10u);
0188   BOOST_CHECK_EQUAL(a.getBin(10.5), 1u);
0189 
0190   Axis<AxisType::Variable, AxisBoundaryType::Closed> b(
0191       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
0192 
0193   // normal inside
0194   BOOST_CHECK_EQUAL(b.getBin(0.5), 1u);
0195   BOOST_CHECK_EQUAL(b.getBin(9.5), 10u);
0196 
0197   // out of bounds, but is closed
0198   // -> should wrap to opposite side bin
0199   BOOST_CHECK_EQUAL(b.getBin(-0.5), 10u);
0200   BOOST_CHECK_EQUAL(b.getBin(10.5), 1u);
0201 }
0202 
0203 BOOST_AUTO_TEST_CASE(neighborhood) {
0204   using bins_t = std::vector<std::size_t>;
0205   Axis<AxisType::Equidistant, AxisBoundaryType::Open> a1(0.0, 1.0, 10u);
0206 
0207   BOOST_CHECK(a1.neighborHoodIndices(0, 1).collect() == bins_t({0, 1}));
0208   BOOST_CHECK(a1.neighborHoodIndices(1, 1).collect() == bins_t({0, 1, 2}));
0209   BOOST_CHECK(a1.neighborHoodIndices(11, 1).collect() == bins_t({10, 11}));
0210   BOOST_CHECK(a1.neighborHoodIndices(10, 1).collect() == bins_t({9, 10, 11}));
0211   BOOST_CHECK(a1.neighborHoodIndices(5, 1).collect() == bins_t({4, 5, 6}));
0212   BOOST_CHECK(a1.neighborHoodIndices(5, {-1, 0}).collect() == bins_t({4, 5}));
0213   BOOST_CHECK(a1.neighborHoodIndices(5, {0, 1}).collect() == bins_t({5, 6}));
0214 
0215   BOOST_CHECK(a1.neighborHoodIndices(0, 2).collect() == bins_t({0, 1, 2}));
0216   BOOST_CHECK(a1.neighborHoodIndices(1, 2).collect() == bins_t({0, 1, 2, 3}));
0217   BOOST_CHECK(a1.neighborHoodIndices(11, 2).collect() == bins_t({9, 10, 11}));
0218   BOOST_CHECK(a1.neighborHoodIndices(10, 2).collect() ==
0219               bins_t({8, 9, 10, 11}));
0220   BOOST_CHECK(a1.neighborHoodIndices(5, 2).collect() ==
0221               bins_t({3, 4, 5, 6, 7}));
0222 
0223   Axis<AxisType::Variable, AxisBoundaryType::Open> a2(
0224       {0.0, 2.0, 4.0, 9.0, 10.0});
0225   BOOST_CHECK(a2.neighborHoodIndices(0, 1).collect() == bins_t({0, 1}));
0226   BOOST_CHECK(a2.neighborHoodIndices(1, 1).collect() == bins_t({0, 1, 2}));
0227   BOOST_CHECK(a2.neighborHoodIndices(5, 1).collect() == bins_t({4, 5}));
0228   BOOST_CHECK(a2.neighborHoodIndices(4, 1).collect() == bins_t({3, 4, 5}));
0229   BOOST_CHECK(a2.neighborHoodIndices(4, {-1, 0}).collect() == bins_t({3, 4}));
0230   BOOST_CHECK(a2.neighborHoodIndices(2, 1).collect() == bins_t({1, 2, 3}));
0231   BOOST_CHECK(a2.neighborHoodIndices(2, {0, 1}).collect() == bins_t({2, 3}));
0232 
0233   BOOST_CHECK(a2.neighborHoodIndices(0, 2).collect() == bins_t({0, 1, 2}));
0234   BOOST_CHECK(a2.neighborHoodIndices(1, 2).collect() == bins_t({0, 1, 2, 3}));
0235   BOOST_CHECK(a2.neighborHoodIndices(5, 2).collect() == bins_t({3, 4, 5}));
0236   BOOST_CHECK(a2.neighborHoodIndices(4, 2).collect() == bins_t({2, 3, 4, 5}));
0237   BOOST_CHECK(a2.neighborHoodIndices(3, 2).collect() ==
0238               bins_t({1, 2, 3, 4, 5}));
0239 
0240   Axis<AxisType::Equidistant, AxisBoundaryType::Bound> a3(0.0, 1.0, 10u);
0241 
0242   BOOST_CHECK(a3.neighborHoodIndices(0, 1).collect() == bins_t({}));
0243   BOOST_CHECK(a3.neighborHoodIndices(1, 1).collect() == bins_t({1, 2}));
0244   BOOST_CHECK(a3.neighborHoodIndices(11, 1).collect() == bins_t({}));
0245   BOOST_CHECK(a3.neighborHoodIndices(10, 1).collect() == bins_t({9, 10}));
0246   BOOST_CHECK(a3.neighborHoodIndices(10, {0, 1}).collect() == bins_t({10}));
0247   BOOST_CHECK(a3.neighborHoodIndices(5, 1).collect() == bins_t({4, 5, 6}));
0248   BOOST_CHECK(a3.neighborHoodIndices(5, {-1, 0}).collect() == bins_t({4, 5}));
0249   BOOST_CHECK(a3.neighborHoodIndices(5, {0, 1}).collect() == bins_t({5, 6}));
0250 
0251   BOOST_CHECK(a3.neighborHoodIndices(0, 2).collect() == bins_t({}));
0252   BOOST_CHECK(a3.neighborHoodIndices(1, 2).collect() == bins_t({1, 2, 3}));
0253   BOOST_CHECK(a3.neighborHoodIndices(11, 2).collect() == bins_t({}));
0254   BOOST_CHECK(a3.neighborHoodIndices(10, 2).collect() == bins_t({8, 9, 10}));
0255   BOOST_CHECK(a3.neighborHoodIndices(5, 2).collect() ==
0256               bins_t({3, 4, 5, 6, 7}));
0257 
0258   Axis<AxisType::Equidistant, AxisBoundaryType::Closed> a4(0.0, 1.0, 10u);
0259 
0260   BOOST_CHECK(a4.neighborHoodIndices(0, 1).collect() == bins_t({}));
0261   BOOST_CHECK(a4.neighborHoodIndices(1, 1).collect() == bins_t({10, 1, 2}));
0262   BOOST_CHECK(a4.neighborHoodIndices(11, 1).collect() == bins_t({}));
0263   BOOST_CHECK(a4.neighborHoodIndices(10, 1).collect() == bins_t({9, 10, 1}));
0264   BOOST_CHECK(a4.neighborHoodIndices(10, {0, 1}).collect() == bins_t({10, 1}));
0265   BOOST_CHECK(a4.neighborHoodIndices(5, 1).collect() == bins_t({4, 5, 6}));
0266   BOOST_CHECK(a4.neighborHoodIndices(5, {-1, 0}).collect() == bins_t({4, 5}));
0267   BOOST_CHECK(a4.neighborHoodIndices(5, {0, 1}).collect() == bins_t({5, 6}));
0268 
0269   BOOST_CHECK(a4.neighborHoodIndices(0, 2).collect() == bins_t({}));
0270   BOOST_CHECK(a4.neighborHoodIndices(1, 2).collect() ==
0271               bins_t({9, 10, 1, 2, 3}));
0272   BOOST_CHECK(a4.neighborHoodIndices(11, 2).collect() == bins_t({}));
0273   BOOST_CHECK(a4.neighborHoodIndices(10, 2).collect() ==
0274               bins_t({8, 9, 10, 1, 2}));
0275   BOOST_CHECK(a4.neighborHoodIndices(5, 2).collect() ==
0276               bins_t({3, 4, 5, 6, 7}));
0277   BOOST_CHECK(a4.neighborHoodIndices(3, 2).collect() ==
0278               bins_t({1, 2, 3, 4, 5}));
0279 
0280   Axis<AxisType::Variable, AxisBoundaryType::Bound> a5(
0281       {0.0, 2.0, 4.0, 9.0, 9.5, 10.0});
0282   BOOST_CHECK(a5.neighborHoodIndices(0, 1).collect() == bins_t({}));
0283   BOOST_CHECK(a5.neighborHoodIndices(1, 1).collect() == bins_t({1, 2}));
0284   BOOST_CHECK(a5.neighborHoodIndices(6, 1).collect() == bins_t({}));
0285   BOOST_CHECK(a5.neighborHoodIndices(5, 1).collect() == bins_t({4, 5}));
0286   BOOST_CHECK(a5.neighborHoodIndices(5, {0, 1}).collect() == bins_t({5}));
0287   BOOST_CHECK(a5.neighborHoodIndices(2, 1).collect() == bins_t({1, 2, 3}));
0288   BOOST_CHECK(a5.neighborHoodIndices(2, {-1, 0}).collect() == bins_t({1, 2}));
0289   BOOST_CHECK(a5.neighborHoodIndices(2, {0, 1}).collect() == bins_t({2, 3}));
0290 
0291   BOOST_CHECK(a5.neighborHoodIndices(0, 2).collect() == bins_t({}));
0292   BOOST_CHECK(a5.neighborHoodIndices(1, 2).collect() == bins_t({1, 2, 3}));
0293   BOOST_CHECK(a5.neighborHoodIndices(6, 2).collect() == bins_t({}));
0294   BOOST_CHECK(a5.neighborHoodIndices(5, 2).collect() == bins_t({3, 4, 5}));
0295   BOOST_CHECK(a5.neighborHoodIndices(3, 2).collect() ==
0296               bins_t({1, 2, 3, 4, 5}));
0297 
0298   Axis<AxisType::Variable, AxisBoundaryType::Closed> a6(
0299       {0.0, 2.0, 4.0, 9.0, 9.5, 10.0});
0300   BOOST_CHECK(a6.neighborHoodIndices(0, 1).collect() == bins_t({}));
0301   BOOST_CHECK(a6.neighborHoodIndices(1, 1).collect() == bins_t({5, 1, 2}));
0302   BOOST_CHECK(a6.neighborHoodIndices(6, 1).collect() == bins_t({}));
0303   BOOST_CHECK(a6.neighborHoodIndices(5, 1).collect() == bins_t({4, 5, 1}));
0304   BOOST_CHECK(a6.neighborHoodIndices(5, {0, 1}).collect() == bins_t({5, 1}));
0305   BOOST_CHECK(a6.neighborHoodIndices(2, 1).collect() == bins_t({1, 2, 3}));
0306   BOOST_CHECK(a6.neighborHoodIndices(2, {-1, 0}).collect() == bins_t({1, 2}));
0307   BOOST_CHECK(a6.neighborHoodIndices(2, {0, 1}).collect() == bins_t({2, 3}));
0308 
0309   BOOST_CHECK(a6.neighborHoodIndices(0, 2).collect() == bins_t({}));
0310   BOOST_CHECK(a6.neighborHoodIndices(1, 2).collect() ==
0311               bins_t({4, 5, 1, 2, 3}));
0312   BOOST_CHECK(a6.neighborHoodIndices(6, 2).collect() == bins_t({}));
0313   BOOST_CHECK(a6.neighborHoodIndices(5, 2).collect() ==
0314               bins_t({3, 4, 5, 1, 2}));
0315   BOOST_CHECK(a6.neighborHoodIndices(3, 2).collect() ==
0316               bins_t({1, 2, 3, 4, 5}));
0317   BOOST_CHECK(a6.neighborHoodIndices(3, {0, 2}).collect() == bins_t({3, 4, 5}));
0318 
0319   BOOST_CHECK(a6.neighborHoodIndices(1, 3).collect() ==
0320               bins_t({1, 2, 3, 4, 5}));
0321   BOOST_CHECK(a6.neighborHoodIndices(5, 3).collect() ==
0322               bins_t({1, 2, 3, 4, 5}));
0323 }
0324 
0325 BOOST_AUTO_TEST_CASE(wrapBin) {
0326   Axis<AxisType::Equidistant, AxisBoundaryType::Open> a1(0.0, 1.0, 10u);
0327   BOOST_CHECK_EQUAL(a1.wrapBin(0), 0u);
0328   BOOST_CHECK_EQUAL(a1.wrapBin(1), 1u);
0329   BOOST_CHECK_EQUAL(a1.wrapBin(-1), 0u);
0330   BOOST_CHECK_EQUAL(a1.wrapBin(10), 10u);
0331   BOOST_CHECK_EQUAL(a1.wrapBin(11), 11u);
0332   BOOST_CHECK_EQUAL(a1.wrapBin(12), 11u);
0333 
0334   Axis<AxisType::Equidistant, AxisBoundaryType::Bound> a2(0.0, 1.0, 10u);
0335   BOOST_CHECK_EQUAL(a2.wrapBin(0), 1u);
0336   BOOST_CHECK_EQUAL(a2.wrapBin(1), 1u);
0337   BOOST_CHECK_EQUAL(a2.wrapBin(-1), 1u);
0338   BOOST_CHECK_EQUAL(a2.wrapBin(10), 10u);
0339   BOOST_CHECK_EQUAL(a2.wrapBin(11), 10u);
0340   BOOST_CHECK_EQUAL(a2.wrapBin(12), 10u);
0341 
0342   Axis<AxisType::Equidistant, AxisBoundaryType::Closed> a3(0.0, 1.0, 10u);
0343   BOOST_CHECK_EQUAL(a3.wrapBin(0), 10u);
0344   BOOST_CHECK_EQUAL(a3.wrapBin(1), 1u);
0345   BOOST_CHECK_EQUAL(a3.wrapBin(-1), 9u);
0346   BOOST_CHECK_EQUAL(a3.wrapBin(10), 10u);
0347   BOOST_CHECK_EQUAL(a3.wrapBin(11), 1u);
0348   BOOST_CHECK_EQUAL(a3.wrapBin(12), 2u);
0349 
0350   Axis<AxisType::Variable, AxisBoundaryType::Open> a4(
0351       {0.0, 2.0, 4.0, 9.0, 10.0});
0352   BOOST_CHECK_EQUAL(a4.wrapBin(0), 0u);
0353   BOOST_CHECK_EQUAL(a4.wrapBin(1), 1u);
0354   BOOST_CHECK_EQUAL(a4.wrapBin(-1), 0u);
0355   BOOST_CHECK_EQUAL(a4.wrapBin(4), 4u);
0356   BOOST_CHECK_EQUAL(a4.wrapBin(5), 5u);
0357   BOOST_CHECK_EQUAL(a4.wrapBin(6), 5u);
0358 
0359   Axis<AxisType::Variable, AxisBoundaryType::Bound> a5(
0360       {0.0, 2.0, 4.0, 9.0, 9.5, 10.0});
0361   BOOST_CHECK_EQUAL(a5.wrapBin(0), 1u);
0362   BOOST_CHECK_EQUAL(a5.wrapBin(1), 1u);
0363   BOOST_CHECK_EQUAL(a5.wrapBin(-1), 1u);
0364   BOOST_CHECK_EQUAL(a5.wrapBin(4), 4u);
0365   BOOST_CHECK_EQUAL(a5.wrapBin(5), 5u);
0366   BOOST_CHECK_EQUAL(a5.wrapBin(6), 5u);
0367 
0368   Axis<AxisType::Variable, AxisBoundaryType::Closed> a6(
0369       {0.0, 2.0, 4.0, 9.0, 9.5, 10.0});
0370   BOOST_CHECK_EQUAL(a6.wrapBin(0), 5u);
0371   BOOST_CHECK_EQUAL(a6.wrapBin(1), 1u);
0372   BOOST_CHECK_EQUAL(a6.wrapBin(-1), 4u);
0373   BOOST_CHECK_EQUAL(a6.wrapBin(4), 4u);
0374   BOOST_CHECK_EQUAL(a6.wrapBin(5), 5u);
0375   BOOST_CHECK_EQUAL(a6.wrapBin(6), 1u);
0376   BOOST_CHECK_EQUAL(a6.wrapBin(7), 2u);
0377 }
0378 
0379 BOOST_AUTO_TEST_CASE(AxisTypeDeduction) {
0380   auto eqOpen = Axis{0.0, 10., 10};
0381   static_assert(
0382       std::is_same_v<decltype(eqOpen),
0383                      Axis<AxisType::Equidistant, AxisBoundaryType::Open>>);
0384   auto eqBound = Axis{AxisBound, 0.0, 10., 10};
0385   static_assert(
0386       std::is_same_v<decltype(eqBound),
0387                      Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>);
0388   auto eqClosed = Axis{AxisClosed, 0.0, 10., 10};
0389   static_assert(
0390       std::is_same_v<decltype(eqClosed),
0391                      Axis<AxisType::Equidistant, AxisBoundaryType::Closed>>);
0392 
0393   auto varOpen = Axis{{0, 1, 2., 3, 4}};
0394   static_assert(
0395       std::is_same_v<decltype(varOpen),
0396                      Axis<AxisType::Variable, AxisBoundaryType::Open>>);
0397   auto varBound = Axis{AxisBound, {0, 1, 2., 3, 4}};
0398   static_assert(
0399       std::is_same_v<decltype(varBound),
0400                      Axis<AxisType::Variable, AxisBoundaryType::Bound>>);
0401   auto varClosed = Axis{AxisClosed, {0, 1, 2., 3, 4}};
0402   static_assert(
0403       std::is_same_v<decltype(varClosed),
0404                      Axis<AxisType::Variable, AxisBoundaryType::Closed>>);
0405 }
0406 
0407 BOOST_AUTO_TEST_CASE(AxisVisit) {
0408   using enum AxisBoundaryType;
0409   using enum AxisType;
0410 
0411   auto eqOpen = Axis{0.0, 10., 10};
0412   eqOpen.visit([](const auto& axis) {
0413     BOOST_CHECK((
0414         std::is_same_v<std::decay_t<decltype(axis)>, Axis<Equidistant, Open>>));
0415   });
0416 
0417   auto eqBound = Axis{AxisBound, 0.0, 10., 10};
0418   eqBound.visit([](const auto& axis) {
0419     BOOST_CHECK((std::is_same_v<std::decay_t<decltype(axis)>,
0420                                 Axis<Equidistant, Bound>>));
0421   });
0422 
0423   auto eqClosed = Axis{AxisClosed, 0.0, 10., 10};
0424   eqClosed.visit([](const auto& axis) {
0425     BOOST_CHECK((std::is_same_v<std::decay_t<decltype(axis)>,
0426                                 Axis<Equidistant, Closed>>));
0427   });
0428 
0429   auto varOpen = Axis{{0, 1, 2., 3, 4}};
0430   varOpen.visit([](const auto& axis) {
0431     BOOST_CHECK(
0432         (std::is_same_v<std::decay_t<decltype(axis)>, Axis<Variable, Open>>));
0433   });
0434 
0435   auto varBound = Axis{AxisBound, {0, 1, 2., 3, 4}};
0436   varBound.visit([](const auto& axis) {
0437     BOOST_CHECK(
0438         (std::is_same_v<std::decay_t<decltype(axis)>, Axis<Variable, Bound>>));
0439   });
0440 
0441   auto varClosed = Axis{AxisClosed, {0, 1, 2., 3, 4}};
0442   varClosed.visit([](const auto& axis) {
0443     BOOST_CHECK(
0444         (std::is_same_v<std::decay_t<decltype(axis)>, Axis<Variable, Closed>>));
0445   });
0446 
0447   std::vector<double> edges =
0448       varClosed.visit([](const auto& axis) { return axis.getBinEdges(); });
0449   BOOST_CHECK_EQUAL(edges.size(), varClosed.getBinEdges().size());
0450 
0451   // Test return values from visit method with type-dependent values
0452   int typeValue = eqOpen.visit([](const auto& axis) {
0453     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0454                                  Axis<Equidistant, Open>>) {
0455       return 1;
0456     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0457                                         Axis<Equidistant, Bound>>) {
0458       return 2;
0459     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0460                                         Axis<Equidistant, Closed>>) {
0461       return 3;
0462     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0463                                         Axis<Variable, Open>>) {
0464       return 4;
0465     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0466                                         Axis<Variable, Bound>>) {
0467       return 5;
0468     } else {
0469       return 6;  // Variable, Closed
0470     }
0471   });
0472   BOOST_CHECK_EQUAL(typeValue, 1);  // Should be Equidistant, Open
0473 
0474   typeValue = eqBound.visit([](const auto& axis) {
0475     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0476                                  Axis<Equidistant, Open>>) {
0477       return 1;
0478     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0479                                         Axis<Equidistant, Bound>>) {
0480       return 2;
0481     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0482                                         Axis<Equidistant, Closed>>) {
0483       return 3;
0484     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0485                                         Axis<Variable, Open>>) {
0486       return 4;
0487     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0488                                         Axis<Variable, Bound>>) {
0489       return 5;
0490     } else {
0491       return 6;  // Variable, Closed
0492     }
0493   });
0494   BOOST_CHECK_EQUAL(typeValue, 2);  // Should be Equidistant, Bound
0495 
0496   typeValue = eqClosed.visit([](const auto& axis) {
0497     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0498                                  Axis<Equidistant, Open>>) {
0499       return 1;
0500     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0501                                         Axis<Equidistant, Bound>>) {
0502       return 2;
0503     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0504                                         Axis<Equidistant, Closed>>) {
0505       return 3;
0506     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0507                                         Axis<Variable, Open>>) {
0508       return 4;
0509     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0510                                         Axis<Variable, Bound>>) {
0511       return 5;
0512     } else {
0513       return 6;  // Variable, Closed
0514     }
0515   });
0516   BOOST_CHECK_EQUAL(typeValue, 3);  // Should be Equidistant, Closed
0517 
0518   typeValue = varOpen.visit([](const auto& axis) {
0519     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0520                                  Axis<Equidistant, Open>>) {
0521       return 1;
0522     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0523                                         Axis<Equidistant, Bound>>) {
0524       return 2;
0525     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0526                                         Axis<Equidistant, Closed>>) {
0527       return 3;
0528     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0529                                         Axis<Variable, Open>>) {
0530       return 4;
0531     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0532                                         Axis<Variable, Bound>>) {
0533       return 5;
0534     } else {
0535       return 6;  // Variable, Closed
0536     }
0537   });
0538   BOOST_CHECK_EQUAL(typeValue, 4);  // Should be Variable, Open
0539 
0540   typeValue = varBound.visit([](const auto& axis) {
0541     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0542                                  Axis<Equidistant, Open>>) {
0543       return 1;
0544     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0545                                         Axis<Equidistant, Bound>>) {
0546       return 2;
0547     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0548                                         Axis<Equidistant, Closed>>) {
0549       return 3;
0550     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0551                                         Axis<Variable, Open>>) {
0552       return 4;
0553     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0554                                         Axis<Variable, Bound>>) {
0555       return 5;
0556     } else {
0557       return 6;  // Variable, Closed
0558     }
0559   });
0560   BOOST_CHECK_EQUAL(typeValue, 5);  // Should be Variable, Bound
0561 
0562   typeValue = varClosed.visit([](const auto& axis) {
0563     if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0564                                  Axis<Equidistant, Open>>) {
0565       return 1;
0566     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0567                                         Axis<Equidistant, Bound>>) {
0568       return 2;
0569     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0570                                         Axis<Equidistant, Closed>>) {
0571       return 3;
0572     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0573                                         Axis<Variable, Open>>) {
0574       return 4;
0575     } else if constexpr (std::is_same_v<std::decay_t<decltype(axis)>,
0576                                         Axis<Variable, Bound>>) {
0577       return 5;
0578     } else {
0579       return 6;  // Variable, Closed
0580     }
0581   });
0582   BOOST_CHECK_EQUAL(typeValue, 6);  // Should be Variable, Closed
0583 
0584   // Test return value using axis properties
0585   double minValue =
0586       eqOpen.visit([](const auto& axis) { return axis.getMin(); });
0587   BOOST_CHECK_EQUAL(minValue, 0.0);
0588 
0589   double maxValue =
0590       eqBound.visit([](const auto& axis) { return axis.getMax(); });
0591   BOOST_CHECK_EQUAL(maxValue, 10.0);
0592 
0593   std::size_t nBins =
0594       varClosed.visit([](const auto& axis) { return axis.getNBins(); });
0595   BOOST_CHECK_EQUAL(nBins, 4u);
0596 }
0597 
0598 BOOST_AUTO_TEST_CASE(IAxis_Factories) {
0599   using enum AxisType;
0600   using enum AxisBoundaryType;
0601 
0602   // Equidistan: Bound, Open, Closed
0603   auto eb = IAxis::createEquidistant(Bound, 0.0, 10., 10);
0604   BOOST_CHECK_EQUAL(eb->getType(), Equidistant);
0605   BOOST_CHECK_EQUAL(eb->getBoundaryType(), Bound);
0606 
0607   auto eo = IAxis::createEquidistant(Open, 0.0, 10., 10);
0608   BOOST_CHECK_EQUAL(eo->getType(), Equidistant);
0609   BOOST_CHECK_EQUAL(eo->getBoundaryType(), Open);
0610 
0611   auto ec = IAxis::createEquidistant(Closed, 0.0, 10., 10);
0612   BOOST_CHECK_EQUAL(ec->getType(), Equidistant);
0613   BOOST_CHECK_EQUAL(ec->getBoundaryType(), Closed);
0614 
0615   // Variable: Bound, Open, Closed
0616   auto vb = IAxis::createVariable(Bound, {0, 1, 2., 3, 4});
0617   BOOST_CHECK_EQUAL(vb->getType(), Variable);
0618   BOOST_CHECK_EQUAL(vb->getBoundaryType(), Bound);
0619 
0620   auto vo = IAxis::createVariable(Open, {0, 1, 2., 3, 4});
0621   BOOST_CHECK_EQUAL(vo->getType(), Variable);
0622   BOOST_CHECK_EQUAL(vo->getBoundaryType(), Open);
0623 
0624   auto vc = IAxis::createVariable(Closed, {0, 1, 2., 3, 4});
0625   BOOST_CHECK_EQUAL(vc->getType(), Variable);
0626   BOOST_CHECK_EQUAL(vc->getBoundaryType(), Closed);
0627 
0628   // Invalid constructors
0629   // min > max
0630   BOOST_CHECK_THROW(IAxis::createEquidistant(Bound, 10., 0., 3.),
0631                     std::invalid_argument);
0632   // nBins = 0
0633   BOOST_CHECK_THROW(IAxis::createEquidistant(Bound, 0., 10., 0.),
0634                     std::invalid_argument);
0635   // #edges < 2
0636   BOOST_CHECK_THROW(IAxis::createVariable(Bound, std::vector<double>{2.}),
0637                     std::invalid_argument);
0638   // edges not ordered
0639   BOOST_CHECK_THROW(
0640       IAxis::createVariable(Bound, std::vector<double>{2., 1.5, 1.}),
0641       std::invalid_argument);
0642 
0643   // Test memory management
0644   auto axis = IAxis::createEquidistant(Bound, 0.0, 10., 10);
0645   BOOST_CHECK_NO_THROW(axis.reset());
0646 }
0647 
0648 BOOST_AUTO_TEST_CASE(Output) {
0649   std::stringstream ss;
0650 
0651   Axis a{AxisBound, 0.0, 10., 10};
0652   Axis b{AxisBound, {0.0, 10., 11}};
0653 
0654   ss << a;
0655 
0656   BOOST_CHECK_EQUAL(ss.str(), "Axis<Equidistant, Bound>(0, 10, 10)");
0657 
0658   ss.str("");
0659 
0660   const IAxis& ia = a;
0661 
0662   ss << ia;
0663 
0664   BOOST_CHECK_EQUAL(ss.str(), "Axis<Equidistant, Bound>(0, 10, 10)");
0665 
0666   ss.str("");
0667 
0668   ss << b;
0669 
0670   BOOST_CHECK_EQUAL(ss.str(), "Axis<Variable, Bound>(0, 10, 11)");
0671 }
0672 
0673 BOOST_AUTO_TEST_CASE(Equality) {
0674   Axis a{AxisBound, 0.0, 10., 10};
0675   Axis b{AxisClosed, 0.0, 10., 10};
0676 
0677   BOOST_CHECK_EQUAL(a, a);
0678   BOOST_CHECK_NE(a, b);
0679 
0680   const IAxis& ia = a;
0681   const IAxis& ib = b;
0682 
0683   BOOST_CHECK_EQUAL(ia, ia);
0684   BOOST_CHECK_NE(ia, ib);
0685   BOOST_CHECK_NE(ia, b);
0686   BOOST_CHECK_NE(a, ib);
0687   BOOST_CHECK_EQUAL(a, ia);
0688   BOOST_CHECK_EQUAL(b, ib);
0689 }
0690 
0691 BOOST_AUTO_TEST_SUITE_END()
0692 
0693 }  // namespace ActsTests