diff --git a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp index 521e62e9009..9affac3af92 100644 --- a/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp +++ b/Core/include/Acts/Seeding/CandidatesForMiddleSp.hpp @@ -8,10 +8,13 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" + #include #include #include #include +#include #include namespace Acts { @@ -41,8 +44,23 @@ struct TripletCandidate { TripletCandidate& operator=(const TripletCandidate&) = default; /// @brief Move operations - TripletCandidate(TripletCandidate&&) = default; - TripletCandidate& operator=(TripletCandidate&&) = default; + TripletCandidate(TripletCandidate&& other) noexcept + : bottom(std::exchange(other.bottom, nullptr)), + middle(std::exchange(other.middle, nullptr)), + top(std::exchange(other.top, nullptr)), + weight(other.weight), + zOrigin(other.zOrigin), + isQuality(other.isQuality) {} + + TripletCandidate& operator=(TripletCandidate&& other) noexcept { + bottom = std::exchange(other.bottom, nullptr); + middle = std::exchange(other.middle, nullptr); + top = std::exchange(other.top, nullptr); + weight = other.weight; + zOrigin = other.zOrigin; + isQuality = other.isQuality; + return *this; + } external_space_point_t* bottom{nullptr}; external_space_point_t* middle{nullptr}; @@ -60,19 +78,21 @@ struct TripletCandidate { /// @tparam external_space_point_t The external spacepoint type. template +concept SatisfyCandidateConcept = requires(external_space_point_t spacePoint) { + { spacePoint.x() } -> std::convertible_to; + { spacePoint.y() } -> std::convertible_to; + { spacePoint.z() } -> std::convertible_to; +}; + +template class CandidatesForMiddleSp { public: using value_type = TripletCandidate; - /// @brief constructor - CandidatesForMiddleSp() = default; - /// @brief Destructor - ~CandidatesForMiddleSp() = default; - /// @brief Setting maximum number of candidates to keep - /// @param n_low Maximum number of candidates in the low-quality collection - /// @param n_high Maximum number of candidates in the high-quality collection - void setMaxElements(std::size_t n_low, std::size_t n_high); + /// @param nLow Maximum number of candidates in the low-quality collection + /// @param nHigh Maximum number of candidates in the high-quality collection + void setMaxElements(std::size_t nLow, std::size_t nHigh); /// @brief Retrieve the triplet candidates, the resulting vector is already sorted, /// elements with higher quality first @@ -120,7 +140,7 @@ class CandidatesForMiddleSp { /// selection criteria /// @param indices The collection into which the candidate should be stored /// @param n The current number of stored elements in the container - /// @param n_max The maximum number of elements that can be stored in the container + /// @param nMax The maximum number of elements that can be stored in the container /// @param SpB The bottom space point /// @param SpM The middle space point /// @param SpT The top space point @@ -129,25 +149,25 @@ class CandidatesForMiddleSp { /// @param isQuality Whether the triplet candidate is high or low quality /// @returns whether the triplet candidate has been added or not to the collection bool push(std::vector& indices, std::size_t& n, - const std::size_t n_max, external_space_point_t& SpB, + const std::size_t nMax, external_space_point_t& SpB, external_space_point_t& SpM, external_space_point_t& SpT, float weight, float zOrigin, bool isQuality); /// @brief Check if an element exists in the collection. The element to be checked /// is supposed to be in the n position of the collection. /// @param n Index of the requested element - /// @param max_size Number of elements currently stored in the collection + /// @param maxSize Number of elements currently stored in the collection /// @returns Whether the element exists - bool exists(const std::size_t n, const std::size_t max_size) const; + bool exists(const std::size_t n, const std::size_t maxSize) const; /// @brief Pop an element from a collection. The removal of the element from the collection /// does not imply its destruction. In fact, the number of stored elements is /// simply diminished by 1. The popped element is technically still available /// at the end of the collection. /// @param indices The collection - /// @param current_size The current number of element stored in the collection. The function will + /// @param currentSize The current number of element stored in the collection. The function will /// diminish this value by 1 - void pop(std::vector& indices, std::size_t& current_size); + void pop(std::vector& indices, std::size_t& currentSize); /// @brief Return the weight for a candidate /// @param indices The collection in which the element is stored @@ -168,29 +188,29 @@ class CandidatesForMiddleSp { /// is in the correct position on the tree /// @param indices The collection /// @param n The index of the element to place in the correct position - /// @param actual_size The current number of elements stored in the collection + /// @param actualSize The current number of elements stored in the collection void bubbledw(std::vector& indices, std::size_t n, - std::size_t actual_size); + std::size_t actualSize); /// @brief Adding a new triplet candidate to the collection. The function is called after the candidate has satisfied /// all the selection criteria /// @param indices The collection /// @param n Current number of stored elements in the collection - /// @param n_max The maximum number of elements that can be stored in the collection + /// @param nMax The maximum number of elements that can be stored in the collection /// @param element The element that must be added to the collection void addToCollection(std::vector& indices, std::size_t& n, - const std::size_t n_max, value_type&& element); + const std::size_t nMax, value_type&& element); private: // sizes - // m_max_size_* is the maximum size of the indices collections. These values + // m_maxSize* is the maximum size of the indices collections. These values // are set by the user once - std::size_t m_max_size_high{0}; - std::size_t m_max_size_low{0}; - // m_n_* is the current size of the indices collections [0, m_max_size_*). + std::size_t m_maxSizeHigh{0}; + std::size_t m_maxSizeLow{0}; + // m_n_* is the current size of the indices collections [0, m_maxSize*). // These values are set internally by the class - std::size_t m_n_high{0}; - std::size_t m_n_low{0}; + std::size_t m_nHigh{0}; + std::size_t m_nLow{0}; // storage contains the collection of the candidates std::vector m_storage{}; @@ -205,9 +225,9 @@ class CandidatesForMiddleSp { // and std::priority_queue were tried and were found to be slower. // list of indexes of candidates with high quality in the storage - std::vector m_indices_high{}; + std::vector m_indicesHigh{}; // list of indexes of candidates with low quality in the storage - std::vector m_indices_low{}; + std::vector m_indicesLow{}; }; } // namespace Acts diff --git a/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp b/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp index 509f0855fa6..7fbaa006a59 100644 --- a/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp +++ b/Core/include/Acts/Seeding/CandidatesForMiddleSp.ipp @@ -8,117 +8,116 @@ namespace Acts { -template +template inline void CandidatesForMiddleSp::setMaxElements( - std::size_t n_low, std::size_t n_high) { - m_max_size_high = n_high; - m_max_size_low = n_low; + std::size_t nLow, std::size_t nHigh) { + m_maxSizeHigh = nHigh; + m_maxSizeLow = nLow; // protection against default numbers // it may cause std::bad_alloc if we don't protect - if (n_high == std::numeric_limits::max() || - n_low == std::numeric_limits::max()) { + if (nHigh == std::numeric_limits::max() || + nLow == std::numeric_limits::max()) { return; } // Reserve enough memory for all collections - m_storage.reserve(n_low + n_high); - m_indices_high.reserve(n_high); - m_indices_low.reserve(n_low); + m_storage.reserve(nLow + nHigh); + m_indicesHigh.reserve(nHigh); + m_indicesLow.reserve(nLow); } -template +template inline void CandidatesForMiddleSp::pop( - std::vector& indices, std::size_t& current_size) { + std::vector& indices, std::size_t& currentSize) { // Remove the candidate with the lowest weight in the collection // By construction, this element is always the first element in the // collection. // The removal works this way: the first element of the collection // is overwritten with the last element of the collection. - // The number of stored elements (current_size) is lowered by 1 + // The number of stored elements (currentSize) is lowered by 1 // The new first element is moved down in the tree to its correct position - std::swap(indices[0], indices[current_size - 1]); - bubbledw(indices, 0, --current_size); + std::swap(indices[0], indices[currentSize - 1]); + bubbledw(indices, 0, --currentSize); } -template +template inline bool CandidatesForMiddleSp::exists( - const std::size_t n, const std::size_t max_size) const { + const std::size_t n, const std::size_t maxSize) const { // If the element exists, its index is lower than the current number // of stored elements - return n < max_size; + return n < maxSize; } -template +template inline float CandidatesForMiddleSp::weight( const std::vector& indices, std::size_t n) const { // Get the weight of the n-th element return m_storage[indices[n]].weight; } -template +template inline void CandidatesForMiddleSp::clear() { // do not clear max size, this is set only once // reset to 0 the number of stored elements - m_n_high = 0; - m_n_low = 0; + m_nHigh = 0; + m_nLow = 0; // Reset all values to default m_storage.clear(); - m_indices_high.clear(); - m_indices_low.clear(); + m_indicesHigh.clear(); + m_indicesLow.clear(); } -template +template bool CandidatesForMiddleSp::push( external_space_point_t& SpB, external_space_point_t& SpM, external_space_point_t& SpT, float weight, float zOrigin, bool isQuality) { // Decide in which collection this candidate may be added to according to the // isQuality boolean if (isQuality) { - return push(m_indices_high, m_n_high, m_max_size_high, SpB, SpM, SpT, - weight, zOrigin, isQuality); + return push(m_indicesHigh, m_nHigh, m_maxSizeHigh, SpB, SpM, SpT, weight, + zOrigin, isQuality); } - return push(m_indices_low, m_n_low, m_max_size_low, SpB, SpM, SpT, weight, + return push(m_indicesLow, m_nLow, m_maxSizeLow, SpB, SpM, SpT, weight, zOrigin, isQuality); } -template +template bool CandidatesForMiddleSp::push( - std::vector& indices, std::size_t& n, const std::size_t n_max, + std::vector& indices, std::size_t& n, const std::size_t nMax, external_space_point_t& SpB, external_space_point_t& SpM, external_space_point_t& SpT, float weight, float zOrigin, bool isQuality) { // If we do not want to store candidates, returns - if (n_max == 0) { + if (nMax == 0) { return false; } // if there is still space, add anything - if (n < n_max) { - addToCollection(indices, n, n_max, + if (n < nMax) { + addToCollection(indices, n, nMax, value_type(SpB, SpM, SpT, weight, zOrigin, isQuality)); return true; } // if no space, replace one if quality is enough // compare to element with lowest weight - const auto& lowest_weight = this->weight(indices, 0); - if (weight <= lowest_weight) { + if (float lowestWeight = this->weight(indices, 0); weight <= lowestWeight) { return false; } // remove element with lower weight and add this one pop(indices, n); - addToCollection(indices, n, n_max, + addToCollection(indices, n, nMax, value_type(SpB, SpM, SpT, weight, zOrigin, isQuality)); return true; } -template +template void CandidatesForMiddleSp::addToCollection( - std::vector& indices, std::size_t& n, const std::size_t n_max, + std::vector& indices, std::size_t& n, const std::size_t nMax, value_type&& element) { // adds elements to the end of the collection - if (indices.size() == n_max) { + if (indices.size() == nMax) { m_storage[indices[n]] = std::move(element); } else { m_storage.push_back(std::move(element)); @@ -128,16 +127,16 @@ void CandidatesForMiddleSp::addToCollection( bubbleup(indices, n++); } -template +template void CandidatesForMiddleSp::bubbledw( - std::vector& indices, std::size_t n, std::size_t actual_size) { - while (n < actual_size) { + std::vector& indices, std::size_t n, std::size_t actualSize) { + while (n < actualSize) { // The collection of indexes are sorted as min heap trees // left child : 2 * n + 1 // right child: 2 * n + 2 float current = weight(indices, n); - std::size_t left_child = 2 * n + 1; - std::size_t right_child = 2 * n + 2; + std::size_t leftChild = 2 * n + 1; + std::size_t rightChild = 2 * n + 2; // We have to move the current node down the tree to its correct position. // This is done by comparing its weight with the weights of its two @@ -150,7 +149,7 @@ void CandidatesForMiddleSp::bubbledw( // if there is no left child, that also means no right child is present. // We do nothing - if (!exists(left_child, actual_size)) { + if (!exists(leftChild, actualSize)) { break; } @@ -158,96 +157,95 @@ void CandidatesForMiddleSp::bubbledw( // have to check. We take the lowest weight of the children. By default this // is the weight of the left child, and we then check for the right child - float weight_left_child = weight(indices, left_child); + float weightLeftChild = weight(indices, leftChild); - std::size_t selected_child = left_child; - float selected_weight = weight_left_child; + std::size_t selectedChild = leftChild; + float selectedWeight = weightLeftChild; // Check which child has the lower weight - if (exists(right_child, actual_size)) { - float weight_right_child = weight(indices, right_child); - if (weight_right_child <= weight_left_child) { - selected_child = right_child; - selected_weight = weight_right_child; + if (exists(rightChild, actualSize)) { + float weightRightChild = weight(indices, rightChild); + if (weightRightChild <= weightLeftChild) { + selectedChild = rightChild; + selectedWeight = weightRightChild; } } // At this point we have the minimum weight of the children // We can compare this to the current weight // If weight of the children is higher we stop - if (selected_weight >= current) { + if (selectedWeight >= current) { break; } // swap and repeat the process - std::swap(indices[n], indices[selected_child]); - n = selected_child; + std::swap(indices[n], indices[selectedChild]); + n = selectedChild; } // while loop } -template +template void CandidatesForMiddleSp::bubbleup( std::vector& indices, std::size_t n) { while (n != 0) { // The collection of indexes are sorted as min heap trees // parent: (n - 1) / 2; // this works because it is an integer operation - std::size_t parent_idx = (n - 1) / 2; - - float weight_current = weight(indices, n); - float weight_parent = weight(indices, parent_idx); + std::size_t parentIdx = (n - 1) / 2; + float weightCurrent = weight(indices, n); // If weight of the parent is lower than this one, we stop - if (weight_parent <= weight_current) { + if (float weightParent = weight(indices, parentIdx); + weightParent <= weightCurrent) { break; } // swap and repeat the process - std::swap(indices[n], indices[parent_idx]); - n = parent_idx; + std::swap(indices[n], indices[parentIdx]); + n = parentIdx; } } -template +template std::vector::value_type> CandidatesForMiddleSp::storage() { // this will retrieve the entire storage // the resulting vector is already sorted from high to low quality - std::vector output(m_n_high + m_n_low); - std::size_t out_idx = output.size() - 1; + std::vector output(m_nHigh + m_nLow); + std::size_t outIdx = output.size() - 1; - // rely on the fact that m_indices_* are both min heap trees + // rely on the fact that m_indices* are both min heap trees // Sorting comes naturally by popping elements one by one and // placing this element at the end of the output vector - while (m_n_high != 0 || m_n_low != 0) { + while (m_nHigh != 0 || m_nLow != 0) { // no entries in collection high, we attach the entire low collection - if (m_n_high == 0) { - std::size_t idx = m_n_low; + if (m_nHigh == 0) { + std::size_t idx = m_nLow; for (std::size_t i(0); i < idx; i++) { - output[out_idx--] = std::move(m_storage[m_indices_low[0]]); - pop(m_indices_low, m_n_low); + output[outIdx--] = std::move(m_storage[m_indicesLow[0]]); + pop(m_indicesLow, m_nLow); } break; } // no entries in collection low, we attach the entire high collection - if (m_n_low == 0) { - std::size_t idx = m_n_high; + if (m_nLow == 0) { + std::size_t idx = m_nHigh; for (std::size_t i(0); i < idx; i++) { - output[out_idx--] = std::move(m_storage[m_indices_high[0]]); - pop(m_indices_high, m_n_high); + output[outIdx--] = std::move(m_storage[m_indicesHigh[0]]); + pop(m_indicesHigh, m_nHigh); } break; } // Both have entries, get the minimum - if (descendingByQuality(m_storage[m_indices_low[0]], - m_storage[m_indices_high[0]])) { - output[out_idx--] = std::move(m_storage[m_indices_high[0]]); - pop(m_indices_high, m_n_high); + if (descendingByQuality(m_storage[m_indicesLow[0]], + m_storage[m_indicesHigh[0]])) { + output[outIdx--] = std::move(m_storage[m_indicesHigh[0]]); + pop(m_indicesHigh, m_nHigh); } else { - output[out_idx--] = std::move(m_storage[m_indices_low[0]]); - pop(m_indices_low, m_n_low); + output[outIdx--] = std::move(m_storage[m_indicesLow[0]]); + pop(m_indicesLow, m_nLow); } } // while loop @@ -256,7 +254,7 @@ CandidatesForMiddleSp::storage() { return output; } -template +template bool CandidatesForMiddleSp::descendingByQuality( const value_type& i1, const value_type& i2) { if (i1.weight != i2.weight) { @@ -266,48 +264,44 @@ bool CandidatesForMiddleSp::descendingByQuality( // This is for the case when the weights from different seeds // are same. This makes cpu & cuda results same - const auto& bottom_l1 = i1.bottom; - const auto& middle_l1 = i1.middle; - const auto& top_l1 = i1.top; + const auto& bottomL1 = i1.bottom; + const auto& middleL1 = i1.middle; + const auto& topL1 = i1.top; - const auto& bottom_l2 = i2.bottom; - const auto& middle_l2 = i2.middle; - const auto& top_l2 = i2.top; + const auto& bottomL2 = i2.bottom; + const auto& middleL2 = i2.middle; + const auto& topL2 = i2.top; float seed1_sum = 0.; float seed2_sum = 0.; - seed1_sum += - bottom_l1->y() * bottom_l1->y() + bottom_l1->z() * bottom_l1->z(); - seed1_sum += - middle_l1->y() * middle_l1->y() + middle_l1->z() * middle_l1->z(); - seed1_sum += top_l1->y() * top_l1->y() + top_l1->z() * top_l1->z(); + seed1_sum += bottomL1->y() * bottomL1->y() + bottomL1->z() * bottomL1->z(); + seed1_sum += middleL1->y() * middleL1->y() + middleL1->z() * middleL1->z(); + seed1_sum += topL1->y() * topL1->y() + topL1->z() * topL1->z(); - seed2_sum += - bottom_l2->y() * bottom_l2->y() + bottom_l2->z() * bottom_l2->z(); - seed2_sum += - middle_l2->y() * middle_l2->y() + middle_l2->z() * middle_l2->z(); - seed2_sum += top_l2->y() * top_l2->y() + top_l2->z() * top_l2->z(); + seed2_sum += bottomL2->y() * bottomL2->y() + bottomL2->z() * bottomL2->z(); + seed2_sum += middleL2->y() * middleL2->y() + middleL2->z() * middleL2->z(); + seed2_sum += topL2->y() * topL2->y() + topL2->z() * topL2->z(); return seed1_sum > seed2_sum; } -template +template bool CandidatesForMiddleSp::ascendingByQuality( const value_type& i1, const value_type& i2) { return !descendingByQuality(i1, i2); } -template +template std::size_t CandidatesForMiddleSp::nLowQualityCandidates() const { - return m_n_low; + return m_nLow; } -template +template std::size_t CandidatesForMiddleSp::nHighQualityCandidates() const { - return m_n_high; + return m_nHigh; } } // namespace Acts diff --git a/Tests/UnitTests/Core/Seeding/CMakeLists.txt b/Tests/UnitTests/Core/Seeding/CMakeLists.txt index c3187405826..5f59cf614ba 100644 --- a/Tests/UnitTests/Core/Seeding/CMakeLists.txt +++ b/Tests/UnitTests/Core/Seeding/CMakeLists.txt @@ -5,3 +5,4 @@ add_unittest(EstimateTrackParamsFromSeed EstimateTrackParamsFromSeedTest.cpp) add_unittest(BinnedGroupTest BinnedGroupTest.cpp) add_unittest(HoughTransformTest HoughTransformTest.cpp) add_unittest(UtilityFunctions UtilityFunctionsTests.cpp) +add_unittest(CandidatesForMiddleSp CandidatesForMiddleSpTests.cpp) diff --git a/Tests/UnitTests/Core/Seeding/CandidatesForMiddleSpTests.cpp b/Tests/UnitTests/Core/Seeding/CandidatesForMiddleSpTests.cpp new file mode 100644 index 00000000000..3d8b672cb45 --- /dev/null +++ b/Tests/UnitTests/Core/Seeding/CandidatesForMiddleSpTests.cpp @@ -0,0 +1,177 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Seeding/CandidatesForMiddleSp.hpp" + +#include +#include + +#include "SpacePoint.hpp" + +namespace Acts::Test { + +BOOST_AUTO_TEST_CASE(TripletCandidateObject) { + using UnitTestSpacePoint = ::SpacePoint; + std::vector spacePoints(3); + + // Default Constructor + Acts::TripletCandidate defaultCandidate; + BOOST_CHECK_EQUAL(defaultCandidate.bottom, nullptr); + BOOST_CHECK_EQUAL(defaultCandidate.middle, nullptr); + BOOST_CHECK_EQUAL(defaultCandidate.top, nullptr); + BOOST_CHECK_EQUAL(defaultCandidate.weight, 0.); + BOOST_CHECK_EQUAL(defaultCandidate.zOrigin, 0.); + BOOST_CHECK_EQUAL(defaultCandidate.isQuality, false); + + // Constructor + Acts::TripletCandidate constructedCandidate( + spacePoints[0], spacePoints[1], spacePoints[2], 2.4f, 1.1f, true); + BOOST_CHECK_EQUAL(constructedCandidate.bottom, &spacePoints[0]); + BOOST_CHECK_EQUAL(constructedCandidate.middle, &spacePoints[1]); + BOOST_CHECK_EQUAL(constructedCandidate.top, &spacePoints[2]); + BOOST_CHECK_EQUAL(constructedCandidate.weight, 2.4f); + BOOST_CHECK_EQUAL(constructedCandidate.zOrigin, 1.1f); + BOOST_CHECK_EQUAL(constructedCandidate.isQuality, true); + + // Copy Constructor + Acts::TripletCandidate copiedConstructedCandidate( + constructedCandidate); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.bottom, &spacePoints[0]); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.middle, &spacePoints[1]); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.top, &spacePoints[2]); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.weight, 2.4f); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.zOrigin, 1.1f); + BOOST_CHECK_EQUAL(copiedConstructedCandidate.isQuality, true); + + // Copy Assign + Acts::TripletCandidate copiedAssignCandidate = + constructedCandidate; + BOOST_CHECK_EQUAL(copiedAssignCandidate.bottom, &spacePoints[0]); + BOOST_CHECK_EQUAL(copiedAssignCandidate.middle, &spacePoints[1]); + BOOST_CHECK_EQUAL(copiedAssignCandidate.top, &spacePoints[2]); + BOOST_CHECK_EQUAL(copiedAssignCandidate.weight, 2.4f); + BOOST_CHECK_EQUAL(copiedAssignCandidate.zOrigin, 1.1f); + BOOST_CHECK_EQUAL(copiedAssignCandidate.isQuality, true); + + // Move Constructor + Acts::TripletCandidate movedConstructedCandidate( + std::move(constructedCandidate)); + BOOST_CHECK_EQUAL(movedConstructedCandidate.bottom, &spacePoints[0]); + BOOST_CHECK_EQUAL(movedConstructedCandidate.middle, &spacePoints[1]); + BOOST_CHECK_EQUAL(movedConstructedCandidate.top, &spacePoints[2]); + BOOST_CHECK_EQUAL(movedConstructedCandidate.weight, 2.4f); + BOOST_CHECK_EQUAL(movedConstructedCandidate.zOrigin, 1.1f); + BOOST_CHECK_EQUAL(movedConstructedCandidate.isQuality, true); + BOOST_CHECK_EQUAL(constructedCandidate.bottom, nullptr); + BOOST_CHECK_EQUAL(constructedCandidate.middle, nullptr); + BOOST_CHECK_EQUAL(constructedCandidate.top, nullptr); + + // Move Assign + Acts::TripletCandidate movedAssignCandidate = + std::move(copiedAssignCandidate); + BOOST_CHECK_EQUAL(movedAssignCandidate.bottom, &spacePoints[0]); + BOOST_CHECK_EQUAL(movedAssignCandidate.middle, &spacePoints[1]); + BOOST_CHECK_EQUAL(movedAssignCandidate.top, &spacePoints[2]); + BOOST_CHECK_EQUAL(movedAssignCandidate.weight, 2.4f); + BOOST_CHECK_EQUAL(movedAssignCandidate.zOrigin, 1.1f); + BOOST_CHECK_EQUAL(movedAssignCandidate.isQuality, true); + BOOST_CHECK_EQUAL(copiedAssignCandidate.bottom, nullptr); + BOOST_CHECK_EQUAL(copiedAssignCandidate.middle, nullptr); + BOOST_CHECK_EQUAL(copiedAssignCandidate.top, nullptr); +} + +BOOST_AUTO_TEST_CASE(CandidatesForMiddleSpObject) { + using UnitTestSpacePoint = ::SpacePoint; + using value_t = + typename Acts::CandidatesForMiddleSp::value_type; + UnitTestSpacePoint spacePoint; + + Acts::CandidatesForMiddleSp container; + container.setMaxElements(std::numeric_limits::max(), + std::numeric_limits::max()); + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 0); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + for (int i(0); i < 20; ++i) { + container.push(spacePoint, spacePoint, spacePoint, 1, 2.1, false); + } + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 20); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + container.clear(); + + container.setMaxElements(5, 3); + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 0); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + + std::vector emptyStorage = container.storage(); + BOOST_CHECK_EQUAL(emptyStorage.size(), 0); + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 0); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + + // push low quality + for (int i(0); i < 2; ++i) { + container.push(spacePoint, spacePoint, spacePoint, i, 2.1, false); + } + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 2); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + + for (int i(0); i < 7; ++i) { + container.push(spacePoint, spacePoint, spacePoint, 2.01, 2.15, false); + } + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 5); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + + // push high quality + for (int i(0); i < 5; ++i) { + container.push(spacePoint, spacePoint, spacePoint, 0.5f + i, 2.1, true); + } + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 5); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 3); + + std::vector storagedValues = container.storage(); + // check size array + BOOST_CHECK_EQUAL(storagedValues.size(), 5 + 3); + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 0); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); + + // check elements are sorted + for (std::size_t i(0); i < storagedValues.size() - 1; ++i) { + BOOST_CHECK(storagedValues[i].weight >= storagedValues[i + 1].weight); + } + + std::sort( + storagedValues.begin(), storagedValues.end(), + Acts::CandidatesForMiddleSp::ascendingByQuality); + // check values are sorted properly + for (std::size_t i(0); i < storagedValues.size() - 1; ++i) { + BOOST_CHECK(storagedValues[i].weight <= storagedValues[i + 1].weight); + } + + std::sort( + storagedValues.begin(), storagedValues.end(), + Acts::CandidatesForMiddleSp::descendingByQuality); + // check values are sorted properly + for (std::size_t i(0); i < storagedValues.size() - 1; ++i) { + BOOST_CHECK(storagedValues[i].weight >= storagedValues[i + 1].weight); + } + // push again and check size + for (int i(0); i < 7; ++i) { + container.push(spacePoint, spacePoint, spacePoint, i, 2.15, false); + } + for (int i(0); i < 7; ++i) { + container.push(spacePoint, spacePoint, spacePoint, i, 2.15, true); + } + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 5); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 3); + container.clear(); + BOOST_CHECK_EQUAL(container.nLowQualityCandidates(), 0); + BOOST_CHECK_EQUAL(container.nHighQualityCandidates(), 0); +} + +} // namespace Acts::Test