return norm2(dx);
}
-/*! \brief Append ListOfLists exclusion objects 1 to nsrc in \p src to \p *dest */
-static void combineExclusions(ListOfLists<int>* dest, gmx::ArrayRef<const thread_work_t> src)
-{
- for (gmx::index s = 1; s < src.ssize(); s++)
- {
- dest->appendListOfLists(src[s].excl);
- }
-}
-
/*! \brief Append t_idef structures 1 to nsrc in src to *dest */
static void combine_idef(t_idef* dest, gmx::ArrayRef<const thread_work_t> src)
{
if (izone < nzone_excl)
{
- if (rt->th_work.size() > 1)
+ for (std::size_t th = 1; th < rt->th_work.size(); th++)
{
- combineExclusions(lexcls, rt->th_work);
+ lexcls->appendListOfLists(rt->th_work[th].excl);
}
-
for (const thread_work_t& th_work : rt->th_work)
{
*excl_count += th_work.excl_count;
excls->clear();
- std::vector<int> exclusionsForAtom;
for (i = 0; (i < nnb->nr); i++)
{
/* calculate the total number of exclusions for atom i */
prints("after rm-double", j_index, s);
/* put the sorted exclusions in the target list */
- exclusionsForAtom.clear();
+ excls->pushBackListOfSize(nr_of_sortables);
+ gmx::ArrayRef<int> exclusionsForAtom = excls->back();
for (nrs = 0; (nrs < nr_of_sortables); nrs++)
{
- exclusionsForAtom.push_back(s[nrs].aj);
+ exclusionsForAtom[nrs] = s[nrs].aj;
}
- excls->pushBack(exclusionsForAtom);
/* cleanup temporary space */
sfree(s);
#include "bench_system.h"
+#include <numeric>
#include <vector>
#include "gromacs/math/vec.h"
atomInfoAllVdw.resize(numAtoms);
atomInfoOxygenVdw.resize(numAtoms);
- std::vector<int> exclusionsForAtom;
for (int a = 0; a < numAtoms; a++)
{
if (a % numAtomsInMolecule == 0)
SET_CGINFO_HAS_Q(atomInfoAllVdw[a]);
SET_CGINFO_HAS_Q(atomInfoOxygenVdw[a]);
- exclusionsForAtom.clear();
- const int firstAtomInMolecule = a - (a % numAtomsInMolecule);
- for (int aj = 0; aj < numAtomsInMolecule; aj++)
- {
- exclusionsForAtom.push_back(firstAtomInMolecule + aj);
- }
- excls.pushBack(exclusionsForAtom);
+ excls.pushBackListOfSize(numAtomsInMolecule);
+ gmx::ArrayRef<int> exclusionsForAtom = excls.back();
+ const int firstAtomInMolecule = a - (a % numAtomsInMolecule);
+ std::iota(exclusionsForAtom.begin(), exclusionsForAtom.end(), firstAtomInMolecule);
}
forceRec.ntype = numAtomTypes;
// particles would be higher, or where the exclusions would not be random,
// to make a higher percentage of the exclusions to actually be within the
// cutoff.
- std::vector<int> exclusionsForAtom;
for (int i = 0; i < testPosCount_; ++i)
{
- exclusionsForAtom.clear();
+ excls_.pushBackListOfSize(20);
+ gmx::ArrayRef<int> exclusionsForAtom = excls_.back();
for (int j = 0; j < 20; ++j)
{
- exclusionsForAtom.push_back(i + j * 3);
+ exclusionsForAtom[j] = i + j * 3;
}
- excls_.pushBack(exclusionsForAtom);
}
}
#include "gromacs/topology/exclusionblocks.h"
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "gromacs/topology/block.h"
for (index i = 0; i < ssize(b_); i++)
{
gmx::ArrayRef<const int> jList = list_[i];
- EXPECT_EQ(b_[i].nra(), jList.ssize()) << "Block size mismatch at " << i << ".";
- for (int j = 0; j < b_[i].nra(); j++)
- {
- EXPECT_EQ(b_[i].atomNumber[j], jList[j]) << "Block mismatch at " << i << " , " << j << ".";
- }
+ ASSERT_EQ(b_[i].nra(), jList.ssize()) << "Block size mismatch at " << i << ".";
+ EXPECT_THAT(b_[i].atomNumber, ::testing::Pointwise(::testing::Eq(), jList));
}
}
* lists concatenated. List i is stored in entries listRanges_[i] to
* listRanges_[i+1] in elements_.
*
+ * \note This class is currently limited to arithmetic types, mainly because
+ * this should only be used for performance critical applications.
+ * When performance is not critical, a std::vector of std::vector can be used.
+ *
* \tparam T value type
*/
+
template<typename T>
class ListOfLists
{
+ static_assert(std::is_arithmetic<T>::value, "This class is limited to arithmetic types");
+
public:
//! Constructs an empty list of lists
ListOfLists() = default;
listRanges_.push_back(int(elements_.size()));
}
+ //! Appends a new list with \p numElements elements
+ void pushBackListOfSize(int numElements)
+ {
+ // With arithmetic types enforced, this assertion is always true
+ static_assert(std::is_default_constructible<T>::value,
+ "pushBackListOfSize should only be called with default constructable types");
+ elements_.resize(elements_.size() + numElements);
+ listRanges_.push_back(int(elements_.size()));
+ }
+
//! Returns an ArrayRef to the elements of the list with the given index
ArrayRef<const T> operator[](std::size_t listIndex) const
{
elements_.data() + listRanges_.at(listIndex + 1));
}
+ /*! \brief Returns a reference to the first list
+ *
+ * \returns a reference to the first list
+ */
+ ArrayRef<T> front()
+ {
+ GMX_ASSERT(size() > 0, "Must contain a list if front() is called");
+ auto beginPtr = elements_.data();
+ auto endPtr = beginPtr + listRanges_[1];
+ return { beginPtr, endPtr };
+ }
+ /*! \brief Returns a reference to the final list
+ *
+ * \returns a reference to the final list
+ */
+ ArrayRef<T> back()
+ {
+ GMX_ASSERT(size() > 0, "Must contain a list if bank() is called");
+ auto endIndex = *(listRanges_.end() - 1);
+ auto beginIndex = *(listRanges_.end() - 2);
+ return { elements_.data() + beginIndex, elements_.data() + endIndex };
+ }
+
//! Clears the list
void clear()
{
elements_.clear();
}
- //! Appends a ListOfLists at the end
- void appendListOfLists(const ListOfLists& listOfLists)
+ //! Appends a ListOfLists at the end and increments the appended elements by \p offset
+ void appendListOfLists(const ListOfLists& listOfLists, const T offset = 0)
{
- const std::size_t oldNumLists = size();
listRanges_.insert(listRanges_.end(), listOfLists.listRanges_.begin() + 1,
listOfLists.listRanges_.end());
const int oldNumElements = elements_.size();
- for (std::size_t i = oldNumLists + 1; i < listRanges_.size(); i++)
+ for (std::size_t i = listRanges_.size() - listOfLists.size(); i < listRanges_.size(); i++)
{
listRanges_[i] += oldNumElements;
}
elements_.insert(elements_.end(), listOfLists.elements_.begin(), listOfLists.elements_.end());
- }
- /*! \brief Appends a ListOfLists at the end and increments the appended elements by \p offset
- *
- * \tparam U Type which should be the same as \p T
- *
- * Note that we can not rely on SFINAE for this void function without additional templating.
- * So to enable compilation of ListOfLists for all types, we use a second template parameter
- * which can be automatically deduced from \p listOfLists.
- */
- template<typename U>
- std::enable_if_t<std::is_same<U, T>::value && std::is_arithmetic<T>::value, void>
- appendListOfLists(const ListOfLists<U>& listOfLists, const T offset)
- {
- const std::size_t oldNumElements = elements_.size();
- appendListOfLists(listOfLists);
- for (std::size_t i = oldNumElements; i < elements_.size(); i++)
+ if (offset != 0)
{
- elements_[i] += offset;
+ for (std::size_t i = elements_.size() - listOfLists.elements_.size(); i < elements_.size(); i++)
+ {
+ elements_[i] += offset;
+ }
}
}
#include "gromacs/utility/listoflists.h"
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testutils/testasserts.h"
-#include "testutils/testmatchers.h"
namespace gmx
{
namespace
{
+using ::testing::Eq;
+using ::testing::Pointwise;
+
//! Compares all element between two lists of lists
template<typename T>
void compareLists(const ListOfLists<T>& list, const std::vector<std::vector<T>>& v)
{
- using ::testing::Eq;
- using ::testing::Pointwise;
-
ASSERT_EQ(list.size(), v.size());
for (std::size_t i = 0; i < list.size(); i++)
{
compareLists(list, inputLists);
}
-TEST(ListOfLists, AppendPodWorks)
+TEST(ListOfLists, AppendWorks)
{
const std::vector<std::vector<char>> v = { { 5, 3 }, { char(-1), 7, 4 } };
checkAppend(v);
}
-TEST(ListOfLists, AppendNonpodWorks)
-{
- const std::vector<std::vector<std::string>> v = { { "Will", "this" },
- { "test", "work", "", "?" } };
-
- checkAppend(v);
-}
-
TEST(ListOfLists, EmptyListWorks)
{
ListOfLists<char> list;
EXPECT_EQ(a.empty(), true);
}
+TEST(ListOfLists, AppendAccessWorks)
+{
+ const std::vector<std::vector<char>> v = { { 5, 3 }, { char(-1), 4 } };
+
+ ListOfLists<char> list;
+ list.pushBack(v[0]);
+ list.pushBackListOfSize(v[1].size());
+ std::copy(v[1].begin(), v[1].end(), list.back().begin());
+ compareLists(list, v);
+}
+
TEST(ListOfLists, ClearWorks)
{
ListOfLists<char> list;
EXPECT_THROW(list.at(1), std::out_of_range);
}
+TEST(ListOfLists, FrontAndBackWork)
+{
+ ListOfLists<char> list1;
+ std::vector<char> v1{ { 3, 4 } };
+ list1.pushBack(v1);
+ EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+ EXPECT_THAT(list1.back(), Pointwise(Eq(), v1));
+
+ std::vector<char> v2{ { 12, 63, 1 } };
+ list1.pushBack(v2);
+ EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+ EXPECT_THAT(list1.back(), Pointwise(Eq(), v2));
+
+ list1.pushBack({});
+ EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+ EXPECT_THAT(list1.back(), Pointwise(Eq(), std::vector<char>{}));
+
+ std::vector<char> v3{ { 99, 0, char(-1) } };
+ list1.pushBack(v3);
+ EXPECT_THAT(list1.front(), Pointwise(Eq(), v1));
+ EXPECT_THAT(list1.back(), Pointwise(Eq(), v3));
+
+ ListOfLists<char> list2;
+ list2.pushBack(v2);
+ EXPECT_THAT(list2.front(), Pointwise(Eq(), v2));
+ EXPECT_THAT(list2.back(), Pointwise(Eq(), v2));
+
+ list2.appendListOfLists(list1);
+ EXPECT_THAT(list2.front(), Pointwise(Eq(), v2));
+ EXPECT_THAT(list2.back(), Pointwise(Eq(), v3));
+ EXPECT_EQ(list2.back().size(), v3.size());
+
+ list2.pushBackListOfSize(1);
+ EXPECT_EQ(list2.back().size(), 1);
+}
+
TEST(ListOfLists, ExtractsAndRestores)
{
const std::vector<std::vector<char>> v({ { 5, 3 }, {}, { char(-1), 4 } });
compareLists(list1, v);
}
-TEST(ListOfLists, AppendsListOfListsNonpod)
-{
- std::vector<std::vector<std::string>> v({ { "a", "bc" }, { "d" }, {}, { "efg", "h" } });
-
- ListOfLists<std::string> list1;
- ListOfLists<std::string> list2;
-
- list1.pushBack(v[0]);
- list1.pushBack(v[1]);
- list2.pushBack(v[2]);
- list2.pushBack(v[3]);
- list1.appendListOfLists(list2);
- compareLists(list1, v);
-}
-
} // namespace
} // namespace gmx