Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-29 08:20:05

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