From 457e10bdae6f8d00992f5bdcf49bf78418b51e13 Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:22:00 +0200 Subject: [PATCH 1/5] Update generate_api_docs.sh --- docs/api/generate_api_docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/generate_api_docs.sh b/docs/api/generate_api_docs.sh index da9d7adf..373f9963 100644 --- a/docs/api/generate_api_docs.sh +++ b/docs/api/generate_api_docs.sh @@ -10,7 +10,7 @@ python --version echo -e "Pip version:" pip --version -pip install -r ${APIDOC_DIR}/requirements.txt +pip install --break-system-packages -r ${APIDOC_DIR}/requirements.txt echo -e "Doxygen version:" doxygen --version From 51fc54734f87849f65c40282bedba4649509eddb Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:40:04 +0200 Subject: [PATCH 2/5] better permutation crossover implementation - ~10% for position on tsp439 - ~60% for cycle on tsp439 - ~2% for edge on tsp439 --- src/crossover/crossover_impl.hpp | 234 ++++++++++++------------------- src/crossover/neighbour_list.hpp | 139 ++++++++++++++++++ src/crossover/permutation.cpp | 6 - 3 files changed, 229 insertions(+), 150 deletions(-) create mode 100644 src/crossover/neighbour_list.hpp diff --git a/src/crossover/crossover_impl.hpp b/src/crossover/crossover_impl.hpp index a391958d..79aabaa7 100644 --- a/src/crossover/crossover_impl.hpp +++ b/src/crossover/crossover_impl.hpp @@ -1,15 +1,14 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_CROSSOVER_DTL_HPP -#define GA_CROSSOVER_DTL_HPP +#ifndef GAPP_CROSSOVER_DTL_HPP +#define GAPP_CROSSOVER_DTL_HPP +#include "neighbour_list.hpp" #include "../core/candidate.hpp" #include "../utility/small_vector.hpp" #include "../utility/dynamic_bitset.hpp" #include #include -#include -#include #include #include @@ -55,27 +54,19 @@ namespace gapp::crossover::dtl Candidate positionCrossoverImpl(const Candidate& parent1, const Candidate& parent2, std::span indices); - /* Find the indices of genes in the chromosomes chrom1 and chrom2 which belong to odd cycles. Used in the cycle crossover operator. */ + /* Find the indices of genes in the chromosomes chrom1 and chrom2 which belong to odd cycles. */ template std::vector findOddCycleIndices(const Chromosome& chrom1, const Chromosome& chrom2); + /* Find the indices of genes in the chromosomes chrom1 and chrom2 which belong to odd cycles. */ + template + std::vector findOddCycleIndices(const Chromosome& chrom1, const Chromosome& chrom2); + /* Implementation of the cycle crossover for any gene type. */ template CandidatePair cycleCrossoverImpl(const Candidate& parent1, const Candidate& parent2); - /* A list of neighbours for a gene. */ - template - class NeighbourList; - - /* A list of neighbours for an unsigned integer gene. */ - template - class NeighbourList; - - /* Conctruct the neighbour lists of each gene based on 2 chromosomes. The first and last elements are considered neighbours. */ - template, std::vector>, std::unordered_map>>> - R makeNeighbourLists(const Chromosome& chrom1, const Chromosome& chrom2); - /* Implementation of the edge crossover for any gene type, only generates a single child. */ template Candidate edgeCrossoverImpl(const Candidate& parent1, const Candidate& parent2); @@ -100,9 +91,7 @@ namespace gapp::crossover::dtl #include "../utility/algorithm.hpp" #include "../utility/functional.hpp" -#include "../utility/iterators.hpp" #include "../utility/utility.hpp" -#include #include #include @@ -174,6 +163,24 @@ namespace gapp::crossover::dtl } + template + bool isValidIntegerPermutation(const Chromosome& chrom) + { + if (chrom.empty()) return true; + + if (*std::min_element(chrom.begin(), chrom.end()) != 0) return false; + if (*std::max_element(chrom.begin(), chrom.end()) != chrom.size() - 1) return false; + + detail::dynamic_bitset present(chrom.size()); + for (const T& val : chrom) + { + if (present[val]) return false; + present[val] = true; + } + + return true; + } + template Candidate order1CrossoverImpl(const Candidate& parent1, const Candidate& parent2, size_t first, size_t last) { @@ -210,11 +217,10 @@ namespace gapp::crossover::dtl const size_t chrom_len = parent1.chromosome.size(); const size_t range_len = last - first; - /* The genes have to be unique unsigned integers in the range [0, chrom_len). */ GAPP_ASSERT(first <= last && last <= chrom_len); GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - GAPP_ASSERT(*std::min_element(parent1.chromosome.begin(), parent1.chromosome.end()) == 0); - GAPP_ASSERT(*std::max_element(parent1.chromosome.begin(), parent1.chromosome.end()) == chrom_len - 1); + GAPP_ASSERT(isValidIntegerPermutation(parent1.chromosome)); + GAPP_ASSERT(isValidIntegerPermutation(parent2.chromosome)); detail::dynamic_bitset is_direct(chrom_len); for (size_t idx = first; idx != last; idx++) is_direct[parent1.chromosome[idx]] = true; @@ -266,11 +272,10 @@ namespace gapp::crossover::dtl { const size_t chrom_len = parent1.chromosome.size(); - /* The genes have to be unique unsigned integers in the range [0, chrom_len). */ GAPP_ASSERT(first <= last && last <= chrom_len); GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - GAPP_ASSERT(*std::min_element(parent1.chromosome.begin(), parent1.chromosome.end()) == 0); - GAPP_ASSERT(*std::max_element(parent1.chromosome.begin(), parent1.chromosome.end()) == chrom_len - 1); + GAPP_ASSERT(isValidIntegerPermutation(parent1.chromosome)); + GAPP_ASSERT(isValidIntegerPermutation(parent2.chromosome)); detail::dynamic_bitset is_direct(chrom_len); for (size_t idx = first; idx != last; idx++) is_direct[parent1.chromosome[idx]] = true; @@ -317,24 +322,32 @@ namespace gapp::crossover::dtl Candidate positionCrossoverImpl(const Candidate& parent1, const Candidate& parent2, std::span indices) { const size_t chrom_len = parent1.chromosome.size(); - - /* The genes have to be unique unsigned integers in the range [0, chrom_len). */ + GAPP_ASSERT(std::all_of(indices.begin(), indices.end(), detail::between(0_sz, chrom_len - 1))); GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - GAPP_ASSERT(*std::min_element(parent1.chromosome.begin(), parent1.chromosome.end()) == 0); - GAPP_ASSERT(*std::max_element(parent1.chromosome.begin(), parent1.chromosome.end()) == chrom_len - 1); + GAPP_ASSERT(isValidIntegerPermutation(parent1.chromosome)); + GAPP_ASSERT(isValidIntegerPermutation(parent2.chromosome)); detail::dynamic_bitset is_direct(chrom_len); for (size_t idx : indices) is_direct[parent1.chromosome[idx]] = true; + small_vector next_indirect(chrom_len); + for (ptrdiff_t indirect = -1, i = chrom_len - 1; i >= 0; i--) + { + const T gene = parent1.chromosome[i]; + + indirect = is_direct[gene] ? indirect : i; + next_indirect[i] = indirect; + } + Candidate child = parent1; - for (auto child_pos = child.chromosome.begin(); const T& gene : parent2.chromosome) + for (size_t child_pos = 0; T gene : parent2.chromosome) { if (!is_direct[gene]) { - while (is_direct[*child_pos]) ++child_pos; - *child_pos++ = gene; + child_pos = next_indirect[child_pos]; + child.chromosome[child_pos++] = gene; } } @@ -379,137 +392,71 @@ namespace gapp::crossover::dtl return odd_indices; } - template - CandidatePair cycleCrossoverImpl(const Candidate& parent1, const Candidate& parent2) - { - GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - - const auto odd_cycle_idxs = dtl::findOddCycleIndices(parent1.chromosome, parent2.chromosome); - - Candidate child1 = parent1; - Candidate child2 = parent2; - - for (size_t idx : odd_cycle_idxs) - { - using std::swap; - swap(child1.chromosome[idx], child2.chromosome[idx]); - } - - return { std::move(child1), std::move(child2) }; - } - - - template - class NeighbourList : public detail::container_interface> + template + std::vector findOddCycleIndices(const Chromosome& chrom1, const Chromosome& chrom2) { - public: - using value_type = T; - using reference = T&; - using const_reference = const T&; + GAPP_ASSERT(chrom1.size() == chrom2.size()); + GAPP_ASSERT(isValidIntegerPermutation(chrom1)); + GAPP_ASSERT(isValidIntegerPermutation(chrom2)); - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + const size_t chrom_len = chrom1.size(); - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - using reverse_iterator = typename std::vector::reverse_iterator; - using const_reverse_iterator = typename std::vector::const_reverse_iterator; + std::vector odd_indices; + odd_indices.reserve(chrom_len / 2); - NeighbourList() { neighbours_.reserve(4); } + detail::dynamic_bitset deleted(chrom_len); + size_t num_deleted = 0; - void add(const T& value) + std::vector index_lookup(chrom_len, 0_sz); + for (size_t i = 0; i < chrom_len; i++) { - if (!detail::contains(neighbours_.cbegin(), neighbours_.cend(), value)) - { - neighbours_.push_back(value); - } + index_lookup[chrom1[i]] = i; } - void remove(const T& value) { detail::erase_first_stable(neighbours_, value); } - - constexpr auto begin() noexcept { return neighbours_.begin(); } - constexpr auto end() noexcept { return neighbours_.end(); } - constexpr auto begin() const noexcept { return neighbours_.begin(); } - constexpr auto end() const noexcept { return neighbours_.end(); } + for (bool odd_cycle = false; num_deleted < chrom_len; odd_cycle ^= 1) + { + size_t pos = deleted.find_first(false); + T cycle_start = chrom1[pos]; - private: - small_vector neighbours_; - }; + deleted[pos] = true; + num_deleted++; - template - class NeighbourList : public detail::iterator_interface> - { - public: - void add(T value) - { - GAPP_ASSERT(value != EMPTY); + if (odd_cycle) odd_indices.push_back(pos); - /* Assume that EMPTY values are at the back. */ - for (T& neighbour : neighbours_) + while (chrom2[pos] != cycle_start) { - if (neighbour == value) return; - if (neighbour == EMPTY) - { - neighbour = value; - return; - } - } - GAPP_UNREACHABLE(); - } + pos = index_lookup[chrom2[pos]]; - void remove(T value) - { - GAPP_ASSERT(value != EMPTY); + deleted[pos] = true; + num_deleted++; - for (T& neighbour : neighbours_) - { - neighbour = (neighbour == value) ? EMPTY : neighbour; + if (odd_cycle) odd_indices.push_back(pos); } } - constexpr size_t size() const noexcept - { - return std::count_if(neighbours_.begin(), neighbours_.end(), detail::not_equal_to(EMPTY)); - } - - constexpr bool empty() const noexcept { return size() == 0; } - - constexpr auto begin() noexcept { return neighbours_.begin(); } - constexpr auto end() noexcept { return neighbours_.end(); } - constexpr auto begin() const noexcept { return neighbours_.begin(); } - constexpr auto end() const noexcept { return neighbours_.end(); } - - static constexpr T EMPTY = T(-1); - - private: - std::array neighbours_{ EMPTY, EMPTY, EMPTY, EMPTY }; - }; + return odd_indices; + } - template - R makeNeighbourLists(const Chromosome& chrom1, const Chromosome& chrom2) + template + CandidatePair cycleCrossoverImpl(const Candidate& parent1, const Candidate& parent2) { - GAPP_ASSERT(chrom1.size() == chrom2.size()); + GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - R nb_lists(chrom1.size()); + const auto odd_cycle_idxs = dtl::findOddCycleIndices(parent1.chromosome, parent2.chromosome); - nb_lists[chrom1.front()].add(chrom1[1]); - nb_lists[chrom2.front()].add(chrom2[1]); + Candidate child1 = parent1; + Candidate child2 = parent2; - for (size_t i = 1; i < chrom1.size() - 1; i++) + for (size_t idx : odd_cycle_idxs) { - nb_lists[chrom1[i]].add(chrom1[i - 1]); - nb_lists[chrom1[i]].add(chrom1[i + 1]); - - nb_lists[chrom2[i]].add(chrom2[i - 1]); - nb_lists[chrom2[i]].add(chrom2[i + 1]); + using std::swap; + swap(child1.chromosome[idx], child2.chromosome[idx]); } - nb_lists[chrom1.back()].add(*(chrom1.end() - 2)); - nb_lists[chrom2.back()].add(*(chrom2.end() - 2)); - - return nb_lists; + return { std::move(child1), std::move(child2) }; } + template Candidate edgeCrossoverImpl(const Candidate& parent1, const Candidate& parent2) { @@ -551,10 +498,9 @@ namespace gapp::crossover::dtl { const size_t chrom_len = parent1.chromosome.size(); - /* The genes have to be unique unsigned integers in the range [0, chrom_len). */ GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); - GAPP_ASSERT(*std::min_element(parent1.chromosome.begin(), parent1.chromosome.end()) == 0); - GAPP_ASSERT(*std::max_element(parent1.chromosome.begin(), parent1.chromosome.end()) == chrom_len - 1); + GAPP_ASSERT(isValidIntegerPermutation(parent1.chromosome)); + GAPP_ASSERT(isValidIntegerPermutation(parent2.chromosome)); auto nb_lists = makeNeighbourLists(parent1.chromosome, parent2.chromosome); @@ -567,7 +513,7 @@ namespace gapp::crossover::dtl while (child.chromosome.size() != chrom_len) { T last_gene = child.chromosome.back(); - T next_gene = T(is_used.find_first(false)); + T next_gene = is_used.find_first(false); for (T neighbour : nb_lists[last_gene]) { @@ -588,6 +534,7 @@ namespace gapp::crossover::dtl return child; } + template Candidate pmxCrossoverImpl(const Candidate& parent1, const Candidate& parent2, size_t first, size_t last) { @@ -624,11 +571,10 @@ namespace gapp::crossover::dtl { const size_t chrom_len = parent1.chromosome.size(); - /* The genes have to be unique unsigned integers in the range [0, chrom_len). */ GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size()); GAPP_ASSERT(first <= last && last <= parent1.chromosome.size()); - GAPP_ASSERT(*std::min_element(parent1.chromosome.begin(), parent1.chromosome.end()) == 0); - GAPP_ASSERT(*std::max_element(parent1.chromosome.begin(), parent1.chromosome.end()) == chrom_len - 1); + GAPP_ASSERT(isValidIntegerPermutation(parent1.chromosome)); + GAPP_ASSERT(isValidIntegerPermutation(parent2.chromosome)); Candidate child = parent2; @@ -663,4 +609,4 @@ namespace gapp::crossover::dtl } // namespace gapp::crossover::dtl -#endif // !GA_CROSSOVER_DTL_HPP \ No newline at end of file +#endif // !GAPP_CROSSOVER_DTL_HPP \ No newline at end of file diff --git a/src/crossover/neighbour_list.hpp b/src/crossover/neighbour_list.hpp new file mode 100644 index 00000000..7e492436 --- /dev/null +++ b/src/crossover/neighbour_list.hpp @@ -0,0 +1,139 @@ +/* Copyright (c) 2024 Krisztián Rugási. Subject to the MIT License. */ + +#ifndef GAPP_CROSSOVER_NEIGHBOUR_LIST_HPP +#define GAPP_CROSSOVER_NEIGHBOUR_LIST_HPP + +#include "../core/candidate.hpp" +#include "../utility/algorithm.hpp" +#include "../utility/iterators.hpp" +#include "../utility/small_vector.hpp" +#include "../utility/utility.hpp" +#include +#include +#include +#include +#include + +namespace gapp::crossover::dtl +{ + template + class NeighbourList : public detail::container_interface> + { + public: + using value_type = T; + using reference = T&; + using const_reference = const T&; + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + using reverse_iterator = typename std::vector::reverse_iterator; + using const_reverse_iterator = typename std::vector::const_reverse_iterator; + + constexpr auto begin() noexcept { return neighbours_.begin(); } + constexpr auto end() noexcept { return neighbours_.end(); } + constexpr auto begin() const noexcept { return neighbours_.begin(); } + constexpr auto end() const noexcept { return neighbours_.end(); } + + constexpr void add(const T& value) + { + if (detail::contains(neighbours_.begin(), neighbours_.end(), value)) return; + neighbours_.push_back(value); + } + + constexpr void remove(const T& value) + { + detail::erase_first_stable(neighbours_, value); + } + + private: + small_vector neighbours_; + }; + + template + class NeighbourList : public detail::iterator_interface> + { + public: + constexpr auto begin() noexcept { return neighbours_.begin(); } + constexpr auto end() noexcept { return neighbours_.end(); } + constexpr auto begin() const noexcept { return neighbours_.begin(); } + constexpr auto end() const noexcept { return neighbours_.end(); } + + constexpr size_t size() const noexcept { return size_; } + constexpr bool empty() const noexcept { return size() == 0; } + + void add(T value) + { + GAPP_ASSERT(value != EMPTY); + + /* Assume that EMPTY values are at the back. */ + for (T& neighbour : neighbours_) + { + if (neighbour == value) return; + if (neighbour == EMPTY) + { + neighbour = value; + size_++; + return; + } + } + GAPP_UNREACHABLE(); + } + + void remove(T value) + { + GAPP_ASSERT(value != EMPTY); + + for (T& neighbour : neighbours_) + { + if (neighbour == value) + { + neighbour = EMPTY; + size_--; + } + } + } + + static constexpr T EMPTY = T(-1); + + private: + std::array neighbours_{ EMPTY, EMPTY, EMPTY, EMPTY }; + size_t size_ = 0; + }; + + + template + using NeighbourLists = + std::conditional_t, std::vector>, std::unordered_map>>; + + + template + NeighbourLists makeNeighbourLists(const Chromosome& chrom1, const Chromosome& chrom2) + { + GAPP_ASSERT(chrom1.size() == chrom2.size()); + + NeighbourLists nb_lists(chrom1.size()); + + nb_lists[chrom1.front()].add(chrom1[1]); + nb_lists[chrom2.front()].add(chrom2[1]); + + for (size_t i = 1; i < chrom1.size() - 1; i++) + { + nb_lists[chrom1[i]].add(chrom1[i - 1]); + nb_lists[chrom1[i]].add(chrom1[i + 1]); + + nb_lists[chrom2[i]].add(chrom2[i - 1]); + nb_lists[chrom2[i]].add(chrom2[i + 1]); + } + + nb_lists[chrom1.back()].add(*(chrom1.end() - 2)); + nb_lists[chrom2.back()].add(*(chrom2.end() - 2)); + + return nb_lists; + } + +} // namespace gapp::crossover::dtl + +#endif // !GAPP_CROSSOVER_NEIGHBOUR_LIST_HPP diff --git a/src/crossover/permutation.cpp b/src/crossover/permutation.cpp index 05486dad..a8fd03ff 100644 --- a/src/crossover/permutation.cpp +++ b/src/crossover/permutation.cpp @@ -15,7 +15,6 @@ namespace gapp::crossover::perm auto Order1::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); @@ -34,7 +33,6 @@ namespace gapp::crossover::perm auto Order2::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); @@ -53,7 +51,6 @@ namespace gapp::crossover::perm auto Position::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); @@ -71,7 +68,6 @@ namespace gapp::crossover::perm auto Cycle::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); @@ -83,7 +79,6 @@ namespace gapp::crossover::perm auto Edge::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); @@ -98,7 +93,6 @@ namespace gapp::crossover::perm auto PMX::crossover(const GA&, const Candidate& parent1, const Candidate& parent2) const -> CandidatePair { GAPP_ASSERT(parent1.chromosome.size() == parent2.chromosome.size(), "Mismatching parent chromosome lengths."); - /* The genes of the parent chromosomes must be unique. */ const size_t chrom_len = parent1.chromosome.size(); From 5fb339e4031b43caf443937883c452d2ec124b16 Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:01:22 +0200 Subject: [PATCH 3/5] use small_vector in more places --- .clang-tidy | 1 + .github/workflows/analysis.yml | 2 +- .github/workflows/macos.yml | 8 +- .github/workflows/windows.yml | 5 - CMakeLists.txt | 3 +- src/algorithm/algorithm_base.decl.hpp | 10 +- src/algorithm/algorithm_base.hpp | 6 +- src/algorithm/algorithm_base.impl.hpp | 12 +- src/algorithm/nd_sort.cpp | 2 +- src/algorithm/nsga2.cpp | 10 +- src/algorithm/nsga2.hpp | 11 +- src/algorithm/nsga3.cpp | 15 +- src/algorithm/nsga3.hpp | 12 +- src/algorithm/replacement_base.hpp | 10 +- src/algorithm/single_objective.cpp | 2 +- src/algorithm/single_objective.hpp | 12 +- src/algorithm/soga_replacement.cpp | 10 +- src/algorithm/soga_replacement.hpp | 16 +- src/core/candidate.hpp | 11 +- src/core/population.cpp | 19 +- src/core/population.hpp | 18 +- src/crossover/neighbour_list.hpp | 8 +- src/metrics/metric_set.hpp | 4 +- src/utility/algorithm.hpp | 39 +- src/utility/cone_tree.cpp | 22 +- src/utility/cone_tree.hpp | 6 +- src/utility/dynamic_bitset.hpp | 2 +- src/utility/matrix.hpp | 3 + src/utility/qrng.hpp | 6 +- src/utility/rng.hpp | 4 +- src/utility/small_vector.hpp | 43 +- src/utility/utility.hpp | 31 +- test/benchmark/thread_pool.cpp | 2 +- test/unit/algorithm.cpp | 84 +- test/unit/cone_tree.cpp | 6 +- test/unit/pareto_front.cpp | 13 +- test/unit/problems.cpp | 8 +- test/unit/replacement.cpp | 6 +- test/unit/small_vector.cpp | 1016 +++++++++++++++++++++++++ test/unit/utility.cpp | 97 +++ 40 files changed, 1328 insertions(+), 267 deletions(-) create mode 100644 test/unit/small_vector.cpp create mode 100644 test/unit/utility.cpp diff --git a/.clang-tidy b/.clang-tidy index 8dbf66c5..d012fa5b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -29,6 +29,7 @@ -readability-suspicious-call-argument, -readability-uppercase-literal-suffix, -readability-braces-around-statements, + -readability-qualified-auto, -bugprone-easily-swappable-parameters, -bugprone-unchecked-optional-access, -cppcoreguidelines-special-member-functions, diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 600d1a16..7650c618 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -44,7 +44,7 @@ jobs: { name: cppcheck, cmake-flag: CMAKE_CXX_CPPCHECK="cppcheck;--version;--verbose;--report-progress;--enable=all;--error-exitcode=1;--std=c++20;--suppressions-list=../.cppcheck-supressions" } ] include: - - pkgs: clang-18 clang-tools-18 clang-tidy-18 iwyu cppcheck + - pkgs: clang-18 clang-tools-18 clang-tidy-18 cppcheck cxx: clang++-18 defaults: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 07682f6f..9b059178 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -10,10 +10,10 @@ jobs: matrix: build-type: [ Release, RelWithDebInfo ] compiler: [ - { cxx: g++-11, pkgs: gcc@11, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, - { cxx: g++-12, pkgs: gcc@12, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, - { cxx: g++-13, pkgs: gcc@13, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, - { cxx: g++-14, pkgs: gcc@14, extra-flags: "-undefined dynamic_lookup -Wno-psabi" }, + { cxx: g++-11, pkgs: gcc@11, extra-flags: "-undefined dynamic_lookup -Wno-psabi -Wno-nonnull" }, + { cxx: g++-12, pkgs: gcc@12, extra-flags: "-undefined dynamic_lookup -Wno-psabi -Wno-nonnull" }, + { cxx: g++-13, pkgs: gcc@13, extra-flags: "-undefined dynamic_lookup -Wno-psabi -Wno-nonnull" }, + { cxx: g++-14, pkgs: gcc@14, extra-flags: "-undefined dynamic_lookup -Wno-psabi -Wno-nonnull" }, { cxx: $(brew --prefix llvm@15)/bin/clang++, pkgs: llvm@15 gcc@11, diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 76231d0b..88035bdb 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -17,8 +17,6 @@ jobs: ] build-shared: [ "ON", "OFF" ] exclude: - - platform: Win32 - compiler: ClangCL - platform: Win32 build-type: Release @@ -33,9 +31,6 @@ jobs: - name: checkout-repo uses: actions/checkout@v4 - #- name: setup-compiler - # run: choco install -y --allow-downgrade ${{ matrix.compiler.pkgs }} - - name: setup-catch env: CMAKE_GENERATOR: ${{ matrix.generator }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f095fe..5766bf42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,8 +100,7 @@ else() # GNU style compiler interface # gcc specific options if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - # -Warray-bounds, -Wstringop-overflow, -Wstringop-overread are regular false positives since g++-12 - set(GAPP_WARN_FLAGS "${GAPP_WARN_FLAGS} -Wlogical-op -Wno-array-bounds -Wno-stringop-overflow -Wno-stringop-overread") + set(GAPP_WARN_FLAGS "${GAPP_WARN_FLAGS} -Wlogical-op -Wno-array-bounds -Wno-stringop-overflow -Wno-stringop-overread -Wno-free-nonheap-object") endif() # clang specific options if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") diff --git a/src/algorithm/algorithm_base.decl.hpp b/src/algorithm/algorithm_base.decl.hpp index e89028ba..7bea06a3 100644 --- a/src/algorithm/algorithm_base.decl.hpp +++ b/src/algorithm/algorithm_base.decl.hpp @@ -1,12 +1,12 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_ALGORITHM_BASE_DECL_HPP -#define GA_ALGORITHM_ALGORITHM_BASE_DECL_HPP +#ifndef GAPP_ALGORITHM_ALGORITHM_BASE_DECL_HPP +#define GAPP_ALGORITHM_ALGORITHM_BASE_DECL_HPP #include "selection_base.hpp" #include "replacement_base.hpp" #include "../core/population.hpp" -#include +#include "../utility/small_vector.hpp" #include namespace gapp @@ -138,9 +138,9 @@ namespace gapp::algorithm * @param ga The %GA that uses the algorithm. * @returns The indices of the pareto optimal solutions in the current population. */ - virtual std::vector optimalSolutionsImpl(const GaInfo&) const { return {}; } + virtual small_vector optimalSolutionsImpl(const GaInfo&) const { return {}; } }; } // namespace gapp::algorithm -#endif // !GA_ALGORITHM_ALGORITHM_BASE_DECL_HPP \ No newline at end of file +#endif // !GAPP_ALGORITHM_ALGORITHM_BASE_DECL_HPP \ No newline at end of file diff --git a/src/algorithm/algorithm_base.hpp b/src/algorithm/algorithm_base.hpp index f455bc83..085d086f 100644 --- a/src/algorithm/algorithm_base.hpp +++ b/src/algorithm/algorithm_base.hpp @@ -1,9 +1,9 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_ALGORITHM_BASE_HPP -#define GA_ALGORITHM_ALGORITHM_BASE_HPP +#ifndef GAPP_ALGORITHM_ALGORITHM_BASE_HPP +#define GAPP_ALGORITHM_ALGORITHM_BASE_HPP #include "algorithm_base.decl.hpp" #include "algorithm_base.impl.hpp" -#endif //!GA_ALGORITHM_ALGORITHM_BASE_HPP \ No newline at end of file +#endif //!GAPP_ALGORITHM_ALGORITHM_BASE_HPP \ No newline at end of file diff --git a/src/algorithm/algorithm_base.impl.hpp b/src/algorithm/algorithm_base.impl.hpp index fc475f4d..4e15eda1 100644 --- a/src/algorithm/algorithm_base.impl.hpp +++ b/src/algorithm/algorithm_base.impl.hpp @@ -1,7 +1,7 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_ALGORITHM_BASE_IMPL_HPP -#define GA_ALGORITHM_ALGORITHM_BASE_IMPL_HPP +#ifndef GAPP_ALGORITHM_ALGORITHM_BASE_IMPL_HPP +#define GAPP_ALGORITHM_ALGORITHM_BASE_IMPL_HPP #include "algorithm_base.decl.hpp" #include "../core/ga_info.hpp" @@ -17,7 +17,7 @@ namespace gapp::algorithm { template - auto Algorithm::select(const GA& ga, const Population& pop, const FitnessMatrix& fmat) const -> const Candidate& + const Candidate& Algorithm::select(const GA& ga, const Population& pop, const FitnessMatrix& fmat) const { GAPP_ASSERT(ga.population_size() == pop.size()); GAPP_ASSERT(pop.size() == fmat.size()); @@ -30,7 +30,7 @@ namespace gapp::algorithm } template - auto Algorithm::nextPopulation(const GA& ga, Population parents, Population children) -> Population + Population Algorithm::nextPopulation(const GA& ga, Population parents, Population children) { GAPP_ASSERT(ga.population_size() == parents.size()); GAPP_ASSERT(ga.population_size() <= children.size()); @@ -48,7 +48,7 @@ namespace gapp::algorithm } template - auto Algorithm::optimalSolutions(const GA& ga, const Population& pop) const -> Candidates + Candidates Algorithm::optimalSolutions(const GA& ga, const Population& pop) const { GAPP_ASSERT(ga.population_size() == pop.size()); @@ -66,4 +66,4 @@ namespace gapp::algorithm } // namespace gapp::algorithm -#endif //!GA_ALGORITHM_ALGORITHM_BASE_IMPL_HPP +#endif //!GAPP_ALGORITHM_ALGORITHM_BASE_IMPL_HPP diff --git a/src/algorithm/nd_sort.cpp b/src/algorithm/nd_sort.cpp index 37df5156..3f411927 100644 --- a/src/algorithm/nd_sort.cpp +++ b/src/algorithm/nd_sort.cpp @@ -278,7 +278,7 @@ namespace gapp::algorithm::dtl pareto_fronts.reserve(popsize); size_t current_rank = 0; - std::vector removed_rows; + small_vector removed_rows; while (pareto_fronts.size() != popsize) { diff --git a/src/algorithm/nsga2.cpp b/src/algorithm/nsga2.cpp index 1be29687..0843784b 100644 --- a/src/algorithm/nsga2.cpp +++ b/src/algorithm/nsga2.cpp @@ -26,11 +26,11 @@ namespace gapp::algorithm GAPP_ASSERT(!fmat.empty()); GAPP_ASSERT(std::none_of(pareto_fronts.begin(), pareto_fronts.end(), detail::is_size(0))); - std::vector crowding_distances(fmat.size(), 0.0); + std::vector crowding_distances(fmat.size(), 0.0); for (size_t obj = 0; obj < fmat.ncols(); obj++) { - const FitnessVector fvec = fmat.column(obj); + const auto fvec = fmat.column(obj); for (const auto& front : pareto_fronts) { @@ -83,7 +83,7 @@ namespace gapp::algorithm return idx2; } - std::vector NSGA2::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector NSGA2::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(ga.num_objectives() > 1); GAPP_ASSERT(fmat.ncols() == ga.num_objectives()); @@ -103,7 +103,7 @@ namespace gapp::algorithm dists_ = crowdingDistances(fmat, pareto_fronts.fronts()); dists_.resize(popsize); - std::vector new_pop(popsize); + small_vector new_pop(popsize); for (size_t i = 0; i < popsize; i++) { new_pop[i] = pareto_fronts[i].idx; @@ -113,7 +113,7 @@ namespace gapp::algorithm return new_pop; } - std::vector NSGA2::optimalSolutionsImpl(const GaInfo&) const + small_vector NSGA2::optimalSolutionsImpl(const GaInfo&) const { return detail::find_indices(ranks_, detail::equal_to(0_sz)); } diff --git a/src/algorithm/nsga2.hpp b/src/algorithm/nsga2.hpp index e1e29219..3bd1d6e8 100644 --- a/src/algorithm/nsga2.hpp +++ b/src/algorithm/nsga2.hpp @@ -1,9 +1,10 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_NSGA2_HPP -#define GA_ALGORITHM_NSGA2_HPP +#ifndef GAPP_ALGORITHM_NSGA2_HPP +#define GAPP_ALGORITHM_NSGA2_HPP #include "algorithm_base.hpp" +#include "../utility/small_vector.hpp" #include #include @@ -38,9 +39,9 @@ namespace gapp::algorithm void prepareSelectionsImpl(const GaInfo&, const FitnessMatrix&) override {} size_t selectImpl(const GaInfo& ga, const FitnessMatrix& fmat) const override; - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; - std::vector optimalSolutionsImpl(const GaInfo& ga) const override; + small_vector optimalSolutionsImpl(const GaInfo& ga) const override; std::vector ranks_; std::vector dists_; @@ -48,4 +49,4 @@ namespace gapp::algorithm } // namespace gapp::algorithm -#endif // !GA_ALGORITHM_NSGA2_HPP \ No newline at end of file +#endif // !GAPP_ALGORITHM_NSGA2_HPP \ No newline at end of file diff --git a/src/algorithm/nsga3.cpp b/src/algorithm/nsga3.cpp index 481f6389..e40f7217 100644 --- a/src/algorithm/nsga3.cpp +++ b/src/algorithm/nsga3.cpp @@ -8,6 +8,7 @@ #include "../metrics/pop_stats.hpp" #include "../utility/algorithm.hpp" #include "../utility/functional.hpp" +#include "../utility/small_vector.hpp" #include "../utility/thread_pool.hpp" #include "../utility/math.hpp" #include "../utility/rng.hpp" @@ -42,11 +43,11 @@ namespace gapp::algorithm } /* Create a weight vector for the given axis (used in the ASF). */ - static inline std::vector weightVector(size_t dimensions, size_t axis) + static inline small_vector weightVector(size_t dimensions, size_t axis) { GAPP_ASSERT(dimensions > axis); - std::vector weights(dimensions, 1E-6); + small_vector weights(dimensions, 1E-6); weights[axis] = 1.0; return weights; @@ -127,7 +128,7 @@ namespace gapp::algorithm void incrementNicheCount(std::vector& refs, size_t ref); /* Create a new population from pareto_fronts. */ - std::vector createPopulation(std::span pareto_fronts); + small_vector createPopulation(std::span pareto_fronts); }; @@ -296,9 +297,9 @@ namespace gapp::algorithm std::iter_swap(current, std::prev(first_eq)); } - std::vector NSGA3::Impl::createPopulation(std::span pareto_fronts) + small_vector NSGA3::Impl::createPopulation(std::span pareto_fronts) { - std::vector new_pop; + small_vector new_pop; std::vector new_info; new_pop.reserve(pareto_fronts.size()); @@ -340,7 +341,7 @@ namespace gapp::algorithm pimpl_->recalcNicheCounts(pareto_fronts); } - std::vector NSGA3::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector NSGA3::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(ga.num_objectives() > 1); GAPP_ASSERT(fmat.ncols() == ga.num_objectives()); @@ -403,7 +404,7 @@ namespace gapp::algorithm return pimpl_->nichedCompare(idx1, idx2) ? idx1 : idx2; } - std::vector NSGA3::optimalSolutionsImpl(const GaInfo&) const + small_vector NSGA3::optimalSolutionsImpl(const GaInfo&) const { return detail::find_indices(pimpl_->sol_info_, [](const Impl::CandidateInfo& sol) { return sol.rank == 0; }); } diff --git a/src/algorithm/nsga3.hpp b/src/algorithm/nsga3.hpp index 24aaea07..f115b8fb 100644 --- a/src/algorithm/nsga3.hpp +++ b/src/algorithm/nsga3.hpp @@ -1,12 +1,12 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_NSGA3_HPP -#define GA_ALGORITHM_NSGA3_HPP +#ifndef GAPP_ALGORITHM_NSGA3_HPP +#define GAPP_ALGORITHM_NSGA3_HPP #include "algorithm_base.hpp" #include "reference_lines.hpp" #include "../utility/math.hpp" -#include +#include "../utility/small_vector.hpp" #include #include #include @@ -64,9 +64,9 @@ namespace gapp::algorithm void initializeImpl(const GaInfo& ga) override; size_t selectImpl(const GaInfo& ga, const FitnessMatrix& fmat) const override; - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; - std::vector optimalSolutionsImpl(const GaInfo& ga) const override; + small_vector optimalSolutionsImpl(const GaInfo& ga) const override; struct Impl; std::unique_ptr pimpl_; @@ -74,4 +74,4 @@ namespace gapp::algorithm } // namespace gapp::algorithm -#endif // !GA_ALGORITHM_NSGA2_HPP \ No newline at end of file +#endif // !GAPP_ALGORITHM_NSGA2_HPP \ No newline at end of file diff --git a/src/algorithm/replacement_base.hpp b/src/algorithm/replacement_base.hpp index 56a64132..4ef23ed1 100644 --- a/src/algorithm/replacement_base.hpp +++ b/src/algorithm/replacement_base.hpp @@ -1,10 +1,10 @@ /* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP -#define GA_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP +#ifndef GAPP_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP +#define GAPP_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP #include "../core/population.hpp" -#include +#include "../utility/small_vector.hpp" #include namespace gapp @@ -44,7 +44,7 @@ namespace gapp::replacement * @param fmat The fitness matrix of the combined parent and child populations. * @returns The indices of the candidates selected from the fitness matrix. */ - virtual std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) = 0; + virtual small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) = 0; /** Destructor. */ @@ -61,4 +61,4 @@ namespace gapp::replacement } // namespace gapp::replacement -#endif // !GA_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP \ No newline at end of file +#endif // !GAPP_ALGORITHM_SOGA_REPLACEMENT_BASE_HPP \ No newline at end of file diff --git a/src/algorithm/single_objective.cpp b/src/algorithm/single_objective.cpp index 8c3a5f63..491b0cc2 100644 --- a/src/algorithm/single_objective.cpp +++ b/src/algorithm/single_objective.cpp @@ -59,7 +59,7 @@ namespace gapp::algorithm selection_->initializeImpl(ga); } - std::vector SingleObjective::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector SingleObjective::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(replacement_); GAPP_ASSERT(ga.num_objectives() == 1, "The number of objectives must be 1 for the single-objective algorithms."); diff --git a/src/algorithm/single_objective.hpp b/src/algorithm/single_objective.hpp index 119ae469..a7e99595 100644 --- a/src/algorithm/single_objective.hpp +++ b/src/algorithm/single_objective.hpp @@ -1,15 +1,15 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_SINGLE_OBJECTIVE_HPP -#define GA_ALGORITHM_SINGLE_OBJECTIVE_HPP +#ifndef GAPP_ALGORITHM_SINGLE_OBJECTIVE_HPP +#define GAPP_ALGORITHM_SINGLE_OBJECTIVE_HPP #include "algorithm_base.decl.hpp" #include "selection_base.hpp" #include "replacement_base.hpp" #include "soga_selection.hpp" #include "soga_replacement.hpp" +#include "../utility/small_vector.hpp" #include "../utility/utility.hpp" -#include #include #include #include @@ -45,7 +45,7 @@ namespace gapp::algorithm * when not using a replacement policy derived from replacement::Replacement. * @see replacement_method() */ - using ReplacementCallable = std::function(const GaInfo&, const FitnessMatrix&)>; + using ReplacementCallable = std::function(const GaInfo&, const FitnessMatrix&)>; /** @@ -153,7 +153,7 @@ namespace gapp::algorithm void prepareSelectionsImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; size_t selectImpl(const GaInfo& ga, const FitnessMatrix& fmat) const override; - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; std::unique_ptr selection_; std::unique_ptr replacement_; @@ -204,4 +204,4 @@ namespace gapp::algorithm } // namespace gapp::algorithm -#endif // !GA_ALGORITHM_SINGLE_OBJECTIVE_HPP \ No newline at end of file +#endif // !GAPP_ALGORITHM_SINGLE_OBJECTIVE_HPP \ No newline at end of file diff --git a/src/algorithm/soga_replacement.cpp b/src/algorithm/soga_replacement.cpp index 92d24483..1afe23b1 100644 --- a/src/algorithm/soga_replacement.cpp +++ b/src/algorithm/soga_replacement.cpp @@ -12,12 +12,12 @@ namespace gapp::replacement { - std::vector KeepChildren::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix&) + small_vector KeepChildren::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix&) { return detail::index_vector(ga.population_size(), ga.population_size()); } - std::vector Elitism::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector Elitism::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(fmat.size() >= 2 * ga.population_size()); @@ -29,14 +29,14 @@ namespace gapp::replacement return math::paretoCompareLess(rhs, lhs); // descending }); - std::vector indices(ga.population_size()); + small_vector indices(ga.population_size()); std::copy(sorted_parent_indices.begin(), sorted_parent_indices.begin() + elite_count, indices.begin()); std::iota(indices.begin() + elite_count, indices.end(), ga.population_size()); return indices; } - std::vector KeepBest::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector KeepBest::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(fmat.size() >= ga.population_size()); @@ -58,7 +58,7 @@ namespace gapp::replacement replacement_ = std::move(f); } - std::vector Lambda::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) + small_vector Lambda::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(replacement_); diff --git a/src/algorithm/soga_replacement.hpp b/src/algorithm/soga_replacement.hpp index bed29b59..974490ee 100644 --- a/src/algorithm/soga_replacement.hpp +++ b/src/algorithm/soga_replacement.hpp @@ -1,11 +1,11 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_ALGORITHM_SOGA_REPLACEMENT_HPP -#define GA_ALGORITHM_SOGA_REPLACEMENT_HPP +#ifndef GAPP_ALGORITHM_SOGA_REPLACEMENT_HPP +#define GAPP_ALGORITHM_SOGA_REPLACEMENT_HPP #include "replacement_base.hpp" #include "../core/population.hpp" -#include +#include "../utility/small_vector.hpp" #include #include @@ -28,7 +28,7 @@ namespace gapp::replacement class KeepChildren final : public Replacement { private: - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; }; @@ -69,7 +69,7 @@ namespace gapp::replacement constexpr size_t elite_num() const noexcept { return n_; } private: - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; size_t n_; }; @@ -85,7 +85,7 @@ namespace gapp::replacement class KeepBest final : public Replacement { private: - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; }; @@ -96,12 +96,12 @@ namespace gapp::replacement class Lambda final : public Replacement { public: - using ReplacementCallable = std::function(const GaInfo&, const FitnessMatrix&)>; + using ReplacementCallable = std::function(const GaInfo&, const FitnessMatrix&)>; explicit Lambda(ReplacementCallable f) noexcept; private: - std::vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; + small_vector nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) override; ReplacementCallable replacement_; }; diff --git a/src/core/candidate.hpp b/src/core/candidate.hpp index 69476b6b..7ba1d5d3 100644 --- a/src/core/candidate.hpp +++ b/src/core/candidate.hpp @@ -1,9 +1,10 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_CORE_CANDIDATE_HPP -#define GA_CORE_CANDIDATE_HPP +#ifndef GAPP_CORE_CANDIDATE_HPP +#define GAPP_CORE_CANDIDATE_HPP #include "../utility/math.hpp" +#include "../utility/small_vector.hpp" #include "../utility/matrix.hpp" #include "../utility/concepts.hpp" #include @@ -14,7 +15,7 @@ namespace gapp { /** The class used to represent the fitness of the candidates. Contains a fitness value for each objective. */ - using FitnessVector = std::vector; + using FitnessVector = small_vector; /** * The class used to represent the fitness values of multiple candidates. @@ -128,8 +129,8 @@ namespace gapp Candidate& operator=(Candidate&&) = default; ~Candidate() = default; - Chromosome chromosome; /**< The chromosome encoding the solution. */ FitnessVector fitness; /**< The fitness values of the solution (for every objective). */ + Chromosome chromosome; /**< The chromosome encoding the solution. */ bool is_evaluated = false; /**< True if the candidate's fitness value doesn't need to be computed. */ }; @@ -215,4 +216,4 @@ namespace std } // namespace std -#endif // !GA_CORE_CANDIDATE_HPP \ No newline at end of file +#endif // !GAPP_CORE_CANDIDATE_HPP \ No newline at end of file diff --git a/src/core/population.cpp b/src/core/population.cpp index c7a716a8..9f84a92f 100644 --- a/src/core/population.cpp +++ b/src/core/population.cpp @@ -3,6 +3,7 @@ #include "population.hpp" #include "../utility/math.hpp" #include "../utility/algorithm.hpp" +#include "../utility/functional.hpp" #include #include #include @@ -13,19 +14,19 @@ namespace gapp::detail FitnessVector toFitnessVector(FitnessMatrix::const_iterator first, FitnessMatrix::const_iterator last) { FitnessVector fitness_vector(last - first); - std::transform(first, last, fitness_vector.begin(), [](const auto& row) { return row[0]; }); + std::transform(first, last, fitness_vector.begin(), detail::element_at(0)); return fitness_vector; } - std::vector findParetoFront(const FitnessMatrix& fmat) + small_vector findParetoFront(const FitnessMatrix& fmat) { if (fmat.empty()) return {}; return (fmat.ncols() == 1) ? findParetoFront1D(fmat) : findParetoFrontBest(fmat); } - std::vector findParetoFront1D(const FitnessMatrix& fmat) + small_vector findParetoFront1D(const FitnessMatrix& fmat) { const auto best = std::max_element(fmat.begin(), fmat.end(), [](const auto& lhs, const auto& rhs) noexcept @@ -41,7 +42,7 @@ namespace gapp::detail }); } - std::vector findParetoFrontSort(const FitnessMatrix& fmat) + small_vector findParetoFrontSort(const FitnessMatrix& fmat) { const auto indices = detail::argsort(fmat.begin(), fmat.end(), [](const auto& lhs, const auto& rhs) noexcept { @@ -52,7 +53,7 @@ namespace gapp::detail return false; }); - std::vector optimal_indices; + small_vector optimal_indices; for (size_t idx : indices) { @@ -67,14 +68,14 @@ namespace gapp::detail return optimal_indices; } - std::vector findParetoFrontBest(const FitnessMatrix& fmat) + small_vector findParetoFrontBest(const FitnessMatrix& fmat) { // Implementation of the BEST algorithm based on the description in: // Godfrey et al. "Algorithms and analyses for maximal vector computation." The VLDB Journal 16, no. 1 (2007): 5-28. auto indices = detail::index_vector(fmat.size()); - std::vector optimal_indices; + small_vector optimal_indices; optimal_indices.reserve(fmat.size()); auto first = indices.begin(); @@ -128,7 +129,7 @@ namespace gapp::detail return optimal_indices; } - std::vector findParetoFrontKungImpl(const FitnessMatrix& fmat, std::vector::const_iterator first, std::vector::const_iterator last) + small_vector findParetoFrontKungImpl(const FitnessMatrix& fmat, small_vector::const_iterator first, small_vector::const_iterator last) { if (std::distance(first, last) == 1) return { *first }; @@ -154,7 +155,7 @@ namespace gapp::detail return top_half; } - std::vector findParetoFrontKung(const FitnessMatrix& fmat) + small_vector findParetoFrontKung(const FitnessMatrix& fmat) { /* See: Kung et al. "On finding the maxima of a set of vectors." Journal of the ACM (JACM) 22.4 (1975): 469-476. */ /* Doesn't work for d = 1 (single-objective optimization). */ diff --git a/src/core/population.hpp b/src/core/population.hpp index e3844c63..46436b57 100644 --- a/src/core/population.hpp +++ b/src/core/population.hpp @@ -1,10 +1,10 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_CORE_POPULATION_HPP -#define GA_CORE_POPULATION_HPP +#ifndef GAPP_CORE_POPULATION_HPP +#define GAPP_CORE_POPULATION_HPP #include "candidate.hpp" -#include +#include "../utility/small_vector.hpp" #include namespace gapp @@ -32,12 +32,12 @@ namespace gapp::detail template Candidates findParetoFront(const Population& pop); - std::vector findParetoFront(const FitnessMatrix& fmat); + small_vector findParetoFront(const FitnessMatrix& fmat); - std::vector findParetoFront1D(const FitnessMatrix& fmat); - std::vector findParetoFrontSort(const FitnessMatrix& fmat); - std::vector findParetoFrontBest(const FitnessMatrix& fmat); - std::vector findParetoFrontKung(const FitnessMatrix& fmat); + small_vector findParetoFront1D(const FitnessMatrix& fmat); + small_vector findParetoFrontSort(const FitnessMatrix& fmat); + small_vector findParetoFrontBest(const FitnessMatrix& fmat); + small_vector findParetoFrontKung(const FitnessMatrix& fmat); /* Find the pareto-optimal solutions in the set (lhs U rhs), assuming both lhs and rhs are pareto sets. */ template @@ -169,4 +169,4 @@ namespace gapp::detail } // namespace gapp::detail -#endif // !GA_CORE_POPULATION_HPP \ No newline at end of file +#endif // !GAPP_CORE_POPULATION_HPP \ No newline at end of file diff --git a/src/crossover/neighbour_list.hpp b/src/crossover/neighbour_list.hpp index 7e492436..37298090 100644 --- a/src/crossover/neighbour_list.hpp +++ b/src/crossover/neighbour_list.hpp @@ -27,10 +27,10 @@ namespace gapp::crossover::dtl using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - using reverse_iterator = typename std::vector::reverse_iterator; - using const_reverse_iterator = typename std::vector::const_reverse_iterator; + using iterator = typename small_vector::iterator; + using const_iterator = typename small_vector::const_iterator; + using reverse_iterator = typename small_vector::reverse_iterator; + using const_reverse_iterator = typename small_vector::const_reverse_iterator; constexpr auto begin() noexcept { return neighbours_.begin(); } constexpr auto end() noexcept { return neighbours_.end(); } diff --git a/src/metrics/metric_set.hpp b/src/metrics/metric_set.hpp index 6d34bb55..bd4962d4 100644 --- a/src/metrics/metric_set.hpp +++ b/src/metrics/metric_set.hpp @@ -4,7 +4,7 @@ #define GA_METRICS_METRIC_SET_HPP #include "monitor_base.hpp" -#include +#include "../utility/small_vector.hpp" #include #include #include @@ -33,7 +33,7 @@ namespace gapp::detail void update(const GaInfo& ga); private: - std::vector> metrics_; + small_vector> metrics_; }; } // namespace gapp::detail diff --git a/src/utility/algorithm.hpp b/src/utility/algorithm.hpp index bee34fae..5ca02af7 100644 --- a/src/utility/algorithm.hpp +++ b/src/utility/algorithm.hpp @@ -1,8 +1,9 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_UTILITY_ALGORITHM_HPP -#define GA_UTILITY_ALGORITHM_HPP +#ifndef GAPP_UTILITY_ALGORITHM_HPP +#define GAPP_UTILITY_ALGORITHM_HPP +#include "small_vector.hpp" #include "type_traits.hpp" #include "utility.hpp" #include @@ -34,9 +35,9 @@ namespace gapp::detail return static_cast(u); } - inline std::vector index_vector(size_t n, size_t first = 0) + constexpr small_vector index_vector(size_t n, size_t first = 0) { - std::vector indices(n); + small_vector indices(n); std::iota(indices.begin(), indices.end(), first); return indices; @@ -44,7 +45,7 @@ namespace gapp::detail template>> requires std::strict_weak_order, std::iter_reference_t> - std::vector argsort(Iter first, Iter last, Comp&& comp = {}) + small_vector argsort(Iter first, Iter last, Comp&& comp = {}) { GAPP_ASSERT(std::distance(first, last) >= 0); @@ -71,7 +72,7 @@ namespace gapp::detail template>> requires std::strict_weak_order, std::iter_reference_t> - std::vector partial_argsort(Iter first, Iter middle, Iter last, Comp&& comp = {}) + small_vector partial_argsort(Iter first, Iter middle, Iter last, Comp&& comp = {}) { GAPP_ASSERT(std::distance(first, middle) >= 0); GAPP_ASSERT(std::distance(middle, last) >= 0); @@ -233,7 +234,7 @@ namespace gapp::detail using ValueType = std::remove_cvref_t>; - std::vector result; + small_vector result; result.reserve(last - first); for (; first != last; ++first) @@ -265,9 +266,9 @@ namespace gapp::detail template requires std::predicate> - std::vector find_indices(const R& range, Pred&& pred) + small_vector find_indices(const R& range, Pred&& pred) { - std::vector indices; + small_vector indices; for (size_t i = 0; i < range.size(); i++) { @@ -312,29 +313,27 @@ namespace gapp::detail } template - auto select(R&& range, const std::vector& indices) + auto select(R&& container, std::span indices) { - using ValueType = detail::value_t>; - - std::vector selected; + std::remove_cvref_t selected; selected.reserve(indices.size()); for (size_t idx : indices) { - selected.push_back(detail::forward_like(range[idx])); + selected.push_back(detail::forward_like(container[idx])); } return selected; } - template - constexpr void erase_duplicates(std::vector& container) + template + constexpr void erase_duplicates(R& range) { - std::sort(container.begin(), container.end()); - const auto last = std::unique(container.begin(), container.end()); - container.erase(last, container.end()); + std::sort(range.begin(), range.end()); + const auto last = std::unique(range.begin(), range.end()); + range.erase(last, range.end()); } } // namespace gapp::detail -#endif // !GA_UTILITY_ALGORITHM_HPP \ No newline at end of file +#endif // !GAPP_UTILITY_ALGORITHM_HPP \ No newline at end of file diff --git a/src/utility/cone_tree.cpp b/src/utility/cone_tree.cpp index 9ea42473..dccb94c7 100644 --- a/src/utility/cone_tree.cpp +++ b/src/utility/cone_tree.cpp @@ -15,11 +15,13 @@ namespace gapp::detail { - using iterator = ConeTree::iterator; + using iterator = ConeTree::iterator; using const_iterator = ConeTree::const_iterator; - using Point = ConeTree::Point; - using Node = ConeTree::Node; + using Point = ConeTree::Point; + using PointRef = ConeTree::PointRef; + + using Node = ConeTree::Node; using FindResult = ConeTree::FindResult; @@ -32,8 +34,7 @@ namespace gapp::detail for (const Point& point : points) points_.append_row(point); nodes_.reserve(4 * points_.size() / MAX_LEAF_ELEMENTS); - Node root{ .first = 0, .last = points_.size() }; - nodes_.push_back(root); + nodes_.push_back({ .first = 0, .last = points_.size() }); buildTree(); } @@ -64,20 +65,19 @@ namespace gapp::detail const ptrdiff_t range_len = std::distance(first, last); - Point center(first->begin(), first->end()); + Point center(*first); for (++first; first != last; ++first) { std::transform(center.begin(), center.end(), first->begin(), center.begin(), std::plus{}); } - std::transform(center.begin(), center.end(), center.begin(), detail::divide_by(range_len)); return center; } /* Find the Euclidean distance between the center point and the point in the range [first, last) furthest from it. */ - static inline double findRadius(const_iterator first, const_iterator last, const Point& center) + static inline double findRadius(const_iterator first, const_iterator last, PointRef center) { auto distance = std::bind_front(math::euclideanDistanceSq, center); auto furthest = detail::max_element(first, last, distance); @@ -92,7 +92,7 @@ namespace gapp::detail } /* Return the max possible inner product between the point and a point inside the node. */ - static inline double innerProductUpperBound(const Node& node, const Point& point, double point_norm) + static inline double innerProductUpperBound(const Node& node, PointRef point, double point_norm) { const double center_prod = std::inner_product(point.begin(), point.end(), node.center.begin(), 0.0); @@ -100,7 +100,7 @@ namespace gapp::detail } /* Find the best match in the range [first, last) using linear search. */ - static FindResult findBestMatchLinear(const Point& query_point, const_iterator first, const_iterator last) + static FindResult findBestMatchLinear(PointRef query_point, const_iterator first, const_iterator last) { GAPP_ASSERT(std::distance(first, last) > 0); GAPP_ASSERT(query_point.size() == first->size()); @@ -168,7 +168,7 @@ namespace gapp::detail } } - FindResult ConeTree::findBestMatch(const Point& query_point) const + FindResult ConeTree::findBestMatch(PointRef query_point) const { if (points_.empty()) return { points_.end(), 0.0 }; diff --git a/src/utility/cone_tree.hpp b/src/utility/cone_tree.hpp index 27ed7ddf..b2373ed1 100644 --- a/src/utility/cone_tree.hpp +++ b/src/utility/cone_tree.hpp @@ -4,6 +4,7 @@ #define GA_UTILITY_CONE_TREE_HPP #include "matrix.hpp" +#include "small_vector.hpp" #include #include #include @@ -24,7 +25,8 @@ namespace gapp::detail using iterator = Matrix::iterator; using const_iterator = Matrix::const_iterator; - using Point = std::vector; + using Point = small_vector; + using PointRef = std::span; struct FindResult { @@ -48,7 +50,7 @@ namespace gapp::detail explicit ConeTree(std::span points); /* Returns the closest point in the tree to the query point, and its distance. */ - FindResult findBestMatch(const Point& query_point) const; + FindResult findBestMatch(PointRef query_point) const; constexpr const_iterator begin() const noexcept { return points_.begin(); } constexpr const_iterator end() const noexcept { return points_.end(); } diff --git a/src/utility/dynamic_bitset.hpp b/src/utility/dynamic_bitset.hpp index 4fbb2c29..a00c74db 100644 --- a/src/utility/dynamic_bitset.hpp +++ b/src/utility/dynamic_bitset.hpp @@ -205,7 +205,7 @@ namespace gapp::detail } private: - small_vector blocks_; + small_vector blocks_; size_type size_ = 0; constexpr size_type find_first_one() const noexcept diff --git a/src/utility/matrix.hpp b/src/utility/matrix.hpp index 73636a4a..5a533e2c 100644 --- a/src/utility/matrix.hpp +++ b/src/utility/matrix.hpp @@ -3,6 +3,7 @@ #ifndef GA_UTILITY_MATRIX_HPP #define GA_UTILITY_MATRIX_HPP +#include "small_vector.hpp" #include "iterators.hpp" #include "functional.hpp" #include "type_traits.hpp" @@ -262,6 +263,8 @@ namespace gapp::detail template operator std::vector() const { return std::vector(begin(), end()); } + operator small_vector() const { return small_vector(begin(), end()); } + operator std::span>() const { return { begin(), end() }; } constexpr friend bool operator==(const Derived& lhs, const Derived& rhs) diff --git a/src/utility/qrng.hpp b/src/utility/qrng.hpp index 1868a081..97ad7387 100644 --- a/src/utility/qrng.hpp +++ b/src/utility/qrng.hpp @@ -4,7 +4,7 @@ #define GA_UTILITY_QRNG_HPP #include "bounded_value.hpp" -#include +#include "small_vector.hpp" #include #include @@ -18,8 +18,8 @@ namespace gapp::rng class QuasiRandom { public: - using result_type = std::vector; - using state_type = std::vector; + using result_type = small_vector; + using state_type = small_vector; using size_type = std::size_t; /** Create a quasi-random number generator in @p dim dimensions. */ diff --git a/src/utility/rng.hpp b/src/utility/rng.hpp index 7fa8a378..7ae23d42 100644 --- a/src/utility/rng.hpp +++ b/src/utility/rng.hpp @@ -475,12 +475,12 @@ namespace gapp::rng template small_vector sampleUnique(IntType lbound, IntType ubound, size_t count) { - const size_t range_len = detail::range_length(lbound, ubound); + const std::uint64_t range_len = detail::range_length(lbound, ubound); GAPP_ASSERT(ubound >= lbound); GAPP_ASSERT(range_len >= count); - const bool select_many = (count > 0.6 * range_len); + const bool select_many = (count >= std::uint64_t(0.6 * range_len)); const bool huge_range = (range_len >= 65536); if (huge_range) [[unlikely]] return rng::sampleUniqueSet(lbound, ubound, count); diff --git a/src/utility/small_vector.hpp b/src/utility/small_vector.hpp index f2995390..7e1408f2 100644 --- a/src/utility/small_vector.hpp +++ b/src/utility/small_vector.hpp @@ -5,7 +5,7 @@ #include "scope_exit.hpp" #include "utility.hpp" -#include +#include #include #include #include @@ -14,11 +14,10 @@ #include #include #include -#include #include #include -// NOLINTBEGIN(*pointer-arithmetic, *union-access) +// NOLINTBEGIN(*pointer-arithmetic, *union-access, *redundant-expression, *exception-escape) namespace gapp::detail { @@ -168,7 +167,7 @@ namespace gapp::detail { if (!std::is_constant_evaluated()) { - std::memset(first, 0, sizeof(T) * (last - first)); + std::fill((char*)first, (char*)last, 0); return; } } @@ -201,7 +200,8 @@ namespace gapp::detail { if (!std::is_constant_evaluated()) { - std::memcpy(first, std::to_address(src_first), sizeof(T) * (last - first)); + char* src = (char*)std::to_address(src_first); + std::copy(src, src + sizeof(T) * (last - first), (char*)first); return; } } @@ -221,7 +221,7 @@ namespace gapp::detail { if (!std::is_constant_evaluated()) { - std::memcpy(dest, first, sizeof(T) * (last - first)); + std::copy((char*)first, (char*)last, (char*)dest); return; } } @@ -241,7 +241,7 @@ namespace gapp::detail { if (!std::is_constant_evaluated()) { - std::memcpy(dest, first, sizeof(T) * (last - first)); + std::copy((char*)first, (char*)last, (char*)dest); return; } } @@ -305,15 +305,15 @@ namespace gapp::detail constexpr small_vector_buffer() noexcept {}; // NOLINT(*default) constexpr ~small_vector_buffer() noexcept {}; // NOLINT(*default) - constexpr auto begin() noexcept { return data_.data(); } - constexpr auto begin() const noexcept { return data_.data(); } + constexpr auto begin() noexcept { return std::begin(data_); } + constexpr auto begin() const noexcept { return std::begin(data_); } - constexpr auto end() noexcept { return data_.data() + Size; } - constexpr auto end() const noexcept { return data_.data() + Size; } + constexpr auto end() noexcept { return std::end(data_); } + constexpr auto end() const noexcept { return std::end(data_); } - constexpr std::size_t size() const noexcept { return Size; } + constexpr std::size_t size() const noexcept { return std::size(data_); } private: - union { std::array data_; }; + union { char dummy_ = {}; T data_[Size]; }; // NOLINT(*arrays) }; @@ -396,8 +396,6 @@ namespace gapp constexpr small_vector(Iter src_first, Iter src_last, const A& allocator = A()) : alloc_(allocator) { - if (src_first == src_last) return; - const auto src_len = std::distance(src_first, src_last); allocate_n(src_len); detail::scope_exit guard{ [&] { deallocate(); } }; @@ -441,9 +439,9 @@ namespace gapp } else { - std::swap(first_, other.first_); - std::swap(last_, other.last_); - std::swap(last_alloc_, other.last_alloc_); + first_ = std::exchange(other.first_, other.buffer_.begin()); + last_ = std::exchange(other.last_, other.buffer_.begin()); + last_alloc_ = std::exchange(other.last_alloc_, other.buffer_.end()); } } @@ -877,6 +875,8 @@ namespace gapp // OTHER // //-----------------------------------// + constexpr std::vector std_vec() const { return std::vector{ begin(), end() }; } + constexpr allocator_type get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) { return alloc_; } constexpr friend bool operator==(const small_vector& lhs, const small_vector& rhs) noexcept @@ -890,10 +890,7 @@ namespace gapp } private: - static constexpr std::size_t alignment = std::max(alignof(T), detail::cache_line_size); - - alignas(alignment) - GAPP_NO_UNIQUE_ADDRESS detail::small_vector_buffer buffer_; + detail::small_vector_buffer buffer_; pointer first_ = nullptr; pointer last_ = nullptr; pointer last_alloc_ = nullptr; @@ -1076,6 +1073,6 @@ namespace gapp } // namespace gapp -// NOLINTEND(*pointer-arithmetic, *union-access) +// NOLINTEND(*pointer-arithmetic, *union-access, *redundant-expression, *exception-escape) #endif // !GAPP_UTILITY_SMALL_VECTOR_HPP diff --git a/src/utility/utility.hpp b/src/utility/utility.hpp index a1fa17ff..2a2325a3 100644 --- a/src/utility/utility.hpp +++ b/src/utility/utility.hpp @@ -1,12 +1,13 @@ /* Copyright (c) 2022 Krisztián Rugási. Subject to the MIT License. */ -#ifndef GA_UTILITY_UTILITY_HPP -#define GA_UTILITY_UTILITY_HPP +#ifndef GAPP_UTILITY_UTILITY_HPP +#define GAPP_UTILITY_UTILITY_HPP #include #include #include #include +#include #include @@ -76,9 +77,9 @@ #if defined(__GNUC__) || defined(__clang__) -# define GAPP_NON_NULL __attribute__((nonnull)) +# define GAPP_MAY_ALIAS __attribute__((may_alias)) #else -# define GAPP_NON_NULL +# define GAPP_MAY_ALIAS #endif @@ -178,27 +179,13 @@ namespace gapp::detail temp.swap(vec); } - // returns true if the signs of the parameters are the same - template - constexpr bool same_sign(T left, T right) noexcept - { - return (left ^ right) >= 0; - } - - // returns the length of the range [low, high) without overflow + // returns the length of the range [low, high] without overflow template - constexpr size_t range_length(T low, T high) noexcept + constexpr std::uint64_t range_length(T low, T high) noexcept { GAPP_ASSERT(low <= high); - if constexpr (std::is_unsigned_v) - { - return high - low; - } - else - { - return same_sign(low, high) ? high - low : -(low + 1) + high + 1; - } + return std::uint64_t(high) - std::uint64_t(low); } template @@ -233,4 +220,4 @@ namespace gapp::detail } // namespace gapp::detail -#endif // !GA_UTILITY_UTILITY_HPP \ No newline at end of file +#endif // !GAPP_UTILITY_UTILITY_HPP diff --git a/test/benchmark/thread_pool.cpp b/test/benchmark/thread_pool.cpp index eddb1841..9a1efe34 100644 --- a/test/benchmark/thread_pool.cpp +++ b/test/benchmark/thread_pool.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */ +/* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */ #include #include diff --git a/test/unit/algorithm.cpp b/test/unit/algorithm.cpp index 3a0fbfdf..390c3996 100644 --- a/test/unit/algorithm.cpp +++ b/test/unit/algorithm.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "utility/small_vector.hpp" #include "utility/algorithm.hpp" #include "utility/rng.hpp" #include "utility/utility.hpp" @@ -19,74 +20,33 @@ static constexpr auto always_false = [](auto) { return false; }; using namespace gapp; -TEST_CASE("next_mod", "[algorithm]") -{ - REQUIRE(detail::next_mod(0, 3) == 1); - REQUIRE(detail::next_mod(1, 3) == 2); - REQUIRE(detail::next_mod(2, 3) == 0); -} - -TEST_CASE("prev_mod", "[algorithm]") -{ - REQUIRE(detail::prev_mod(0, 3) == 2); - REQUIRE(detail::prev_mod(1, 3) == 0); - REQUIRE(detail::prev_mod(2, 3) == 1); -} - -TEST_CASE("increment_mod", "[algorithm]") -{ - int n = 0; - - detail::increment_mod(n, 3); - REQUIRE(n == 1); - - detail::increment_mod(n, 3); - REQUIRE(n == 2); - - detail::increment_mod(n, 3); - REQUIRE(n == 0); -} - -TEST_CASE("decrement_mod", "[algorithm]") -{ - int n = 0; - - detail::decrement_mod(n, 3); - REQUIRE(n == 2); - - detail::decrement_mod(n, 3); - REQUIRE(n == 1); - - detail::decrement_mod(n, 3); - REQUIRE(n == 0); -} TEST_CASE("index_vector", "[algorithm]") { - REQUIRE(detail::index_vector(3) == std::vector{ 0, 1, 2 }); - REQUIRE(detail::index_vector(4, 2) == std::vector{ 2, 3, 4, 5 }); + REQUIRE(detail::index_vector(3) == small_vector{ 0, 1, 2 }); + REQUIRE(detail::index_vector(4, 2) == small_vector{ 2, 3, 4, 5 }); } TEST_CASE("argsort", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 1.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 1.0 }; SECTION("iterators") { const auto indices = detail::argsort(nums.begin(), nums.end()); - REQUIRE(indices == std::vector{ 1, 3, 2, 0 }); + REQUIRE(indices == small_vector{ 1, 3, 2, 0 }); } SECTION("reverse iterators") { const auto indices = detail::argsort(nums.rbegin(), nums.rend()); - REQUIRE(indices == std::vector{ 0, 2, 3, 1 }); + REQUIRE(indices == small_vector{ 0, 2, 3, 1 }); } SECTION("compare function") { const auto indices = detail::argsort(nums.begin(), nums.end(), std::greater<>{}); - REQUIRE(indices == std::vector{ 0, 2, 3, 1 }); + REQUIRE(indices == small_vector{ 0, 2, 3, 1 }); } SECTION("empty range") @@ -98,7 +58,7 @@ TEST_CASE("argsort", "[algorithm]") TEST_CASE("partial_argsort", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 1.0, 5.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 1.0, 5.0 }; SECTION("iterators") { @@ -136,7 +96,7 @@ TEST_CASE("partial_argsort", "[algorithm]") TEST_CASE("max_element", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; REQUIRE(*detail::max_element(nums.begin(), nums.end()) == 5.0); REQUIRE(*detail::max_element(nums.rbegin(), nums.rend()) == 5.0); @@ -148,7 +108,7 @@ TEST_CASE("max_element", "[algorithm]") TEST_CASE("min_element", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; REQUIRE(*detail::min_element(nums.begin(), nums.end()) == 0.0); REQUIRE(*detail::min_element(nums.rbegin(), nums.rend()) == 0.0); @@ -160,7 +120,7 @@ TEST_CASE("min_element", "[algorithm]") TEST_CASE("argmax", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; REQUIRE(detail::argmax(nums.begin(), nums.end()) == 3); REQUIRE(detail::argmax(nums.rbegin(), nums.rend()) == 3); @@ -224,7 +184,7 @@ TEST_CASE("partial_shuffle", "[algorithm]") TEST_CASE("contains", "[algorithm]") { - const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + const small_vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; REQUIRE(detail::contains(nums.begin(), nums.end(), 0.0)); REQUIRE(detail::contains(nums.begin(), nums.end(), 1.0)); @@ -235,10 +195,10 @@ TEST_CASE("contains", "[algorithm]") TEST_CASE("find_all", "[algorithm]") { - const std::vector nums = { 4, 0, 2, 5, 1 }; + const small_vector nums = { 4, 0, 2, 5, 1 }; const auto odd_nums = detail::find_all(nums.begin(), nums.end(), is_odd); - REQUIRE(odd_nums == std::vector{ 5, 1 }); + REQUIRE(odd_nums == small_vector{ 5, 1 }); const auto big_nums = detail::find_all(nums.begin(), nums.end(), is_big); REQUIRE(big_nums.empty()); @@ -249,16 +209,16 @@ TEST_CASE("find_all", "[algorithm]") TEST_CASE("find_indices", "[algorithm]") { - const std::vector nums = { 4, 0, 2, 5, 1 }; + const small_vector nums = { 4, 0, 2, 5, 1 }; const auto odd_num_idxs = detail::find_indices(nums, is_odd); - REQUIRE(odd_num_idxs == std::vector{ 3_sz, 4_sz }); + REQUIRE(odd_num_idxs == small_vector{ 3_sz, 4_sz }); const auto big_num_idxs = detail::find_indices(nums, is_big); REQUIRE(big_num_idxs.empty()); const auto all = detail::find_indices(nums, always_true); - REQUIRE(all == std::vector{ 0_sz, 1_sz, 2_sz, 3_sz, 4_sz }); + REQUIRE(all == small_vector{ 0_sz, 1_sz, 2_sz, 3_sz, 4_sz }); const auto none = detail::find_indices(nums, always_false); REQUIRE(none.empty()); @@ -266,7 +226,7 @@ TEST_CASE("find_indices", "[algorithm]") TEST_CASE("index_of", "[algorithm]") { - const std::vector nums = { 4, 0, 2, 5, 1 }; + const small_vector nums = { 4, 0, 2, 5, 1 }; REQUIRE(detail::index_of(nums, 4) == 0_sz); REQUIRE(detail::index_of(nums, 2) == 2_sz); @@ -276,7 +236,7 @@ TEST_CASE("index_of", "[algorithm]") TEST_CASE("find_index", "[algorithm]") { - const std::vector nums = { 4, 0, 2, 5, 1 }; + const small_vector nums = { 4, 0, 2, 5, 1 }; const auto first_idx = detail::find_index(nums, always_true); REQUIRE(first_idx == 0_sz); @@ -331,16 +291,16 @@ TEST_CASE("select", "[algorithm]") { const std::vector nums = { 4, 0, 2, 5, 1, 3, 1 }; - auto selected = detail::select(nums, { 0, 1, 4 }); + auto selected = detail::select(nums, std::vector{ 0_sz, 1_sz, 4_sz }); REQUIRE(selected == std::vector{ 4, 0, 1 }); - selected = detail::select(selected, { 2 }); + selected = detail::select(selected, std::vector{ 2_sz }); REQUIRE(selected == std::vector{ 1 }); selected = detail::select(nums, { }); REQUIRE(selected.empty()); - selected = detail::select(std::vector{ 1, 3, 5 }, { 0, 1 }); + selected = detail::select(std::vector{ 1, 3, 5 }, std::vector{ 0_sz, 1_sz }); REQUIRE(selected == std::vector{ 1, 3 }); } diff --git a/test/unit/cone_tree.cpp b/test/unit/cone_tree.cpp index 2bc2e1b7..1f94049f 100644 --- a/test/unit/cone_tree.cpp +++ b/test/unit/cone_tree.cpp @@ -52,10 +52,10 @@ TEST_CASE("cone_tree lookup", "[cone_tree]") ConeTree tree(points); - auto best = tree.findBestMatch({ 1.0, 1.0, 0.1 }); + auto best = tree.findBestMatch(ConeTree::Point{ 1.0, 1.0, 0.1 }); REQUIRE(*best.elem == ConeTree::Point{ 0.8, 0.8, 0.6 }); - best = tree.findBestMatch({ 0.1, 0.5, 0.8 }); + best = tree.findBestMatch(ConeTree::Point{ 0.1, 0.5, 0.8 }); REQUIRE(*best.elem == ConeTree::Point{ 0.6, 0.8, 0.8 }); } @@ -63,7 +63,7 @@ TEST_CASE("empty_cone_tree", "[cone_tree]") { ConeTree tree; - auto best = tree.findBestMatch({ 1.0, 1.0 }); + auto best = tree.findBestMatch(ConeTree::Point{ 1.0, 1.0 }); REQUIRE(best.elem == tree.end()); } \ No newline at end of file diff --git a/test/unit/pareto_front.cpp b/test/unit/pareto_front.cpp index 57110ae7..7768425a 100644 --- a/test/unit/pareto_front.cpp +++ b/test/unit/pareto_front.cpp @@ -4,6 +4,7 @@ #include #include #include "core/population.hpp" +#include "utility/small_vector.hpp" #include "utility/utility.hpp" #include @@ -22,7 +23,7 @@ TEST_CASE("find_pareto_front_1D", "[pareto_front]") auto optimal_indices = findParetoFront1D(fmat); - REQUIRE(optimal_indices == std::vector{ 1_sz }); + REQUIRE(optimal_indices == small_vector{ 1_sz }); } SECTION("multiple optimum") @@ -31,7 +32,7 @@ TEST_CASE("find_pareto_front_1D", "[pareto_front]") auto optimal_indices = findParetoFront1D(fmat); - REQUIRE(optimal_indices == std::vector{ 2_sz, 7_sz, 10_sz }); + REQUIRE(optimal_indices == small_vector{ 2_sz, 7_sz, 10_sz }); } SECTION("multiple optimum approx") @@ -40,7 +41,7 @@ TEST_CASE("find_pareto_front_1D", "[pareto_front]") auto optimal_indices = findParetoFront1D(fmat); - REQUIRE(optimal_indices == std::vector{ 2_sz, 3_sz, 5_sz, 7_sz, 10_sz }); + REQUIRE(optimal_indices == small_vector{ 2_sz, 3_sz, 5_sz, 7_sz, 10_sz }); } } @@ -70,14 +71,14 @@ TEMPLATE_TEST_CASE_SIG("find_pareto_front_nd", "[pareto_front]", ((auto F), F), auto optimal_indices = F(fmat); - REQUIRE(optimal_indices == std::vector{ 3 }); + REQUIRE(optimal_indices == small_vector{ 3 }); } SECTION("multiple optimum") { ScopedTolerances _(0.0, 0.0); - auto optimal_indices = F(fmat); + auto optimal_indices = F(fmat).std_vec(); REQUIRE_THAT(optimal_indices, Matchers::UnorderedEquals(std::vector{ 5, 9, 11, 13, 14 })); } @@ -86,7 +87,7 @@ TEMPLATE_TEST_CASE_SIG("find_pareto_front_nd", "[pareto_front]", ((auto F), F), { ScopedTolerances _(0.1, 0.0); - auto optimal_indices = F(fmat); + auto optimal_indices = F(fmat).std_vec(); REQUIRE_THAT(optimal_indices, Matchers::UnorderedEquals(std::vector{ 4, 5, 9, 11, 12, 13, 14 })); } diff --git a/test/unit/problems.cpp b/test/unit/problems.cpp index 497b7f24..efae98da 100644 --- a/test/unit/problems.cpp +++ b/test/unit/problems.cpp @@ -42,7 +42,7 @@ TEMPLATE_TEST_CASE("single_objective_problems", "[problems]", Sphere, Rastrigin, TestType func(var_count); - REQUIRE_THAT( func(func.optimum()), Approx(func.optimal_value()).margin(1E-6) ); + REQUIRE_THAT( func(func.optimum()).std_vec(), Approx(func.optimal_value().std_vec()).margin(1E-6) ); const auto random_chrom = randomChromosome(func.bounds()); @@ -58,7 +58,7 @@ TEST_CASE("kursawe", "[problems]") Kursawe func(var_count); - REQUIRE_THAT( func(func.optimum()), Approx(func.optimal_value()).margin(1E-6) ); + REQUIRE_THAT( func(func.optimum()).std_vec(), Approx(func.optimal_value().std_vec()).margin(1E-6)); REQUIRE( !paretoCompareLess(func.ideal_point(), func.nadir_point()) ); REQUIRE( !paretoCompareLess(func.optimal_value(), func.nadir_point()) ); @@ -80,7 +80,7 @@ TEMPLATE_TEST_CASE("zdt_suite", "[problems]", ZDT1, ZDT2, ZDT3, ZDT4, ZDT5, ZDT6 TestType func(var_count); - REQUIRE_THAT(func(func.optimum()), Approx(func.optimal_value()).margin(1E-6)); + REQUIRE_THAT(func(func.optimum()).std_vec(), Approx(func.optimal_value().std_vec()).margin(1E-6)); REQUIRE( !paretoCompareLess(func.ideal_point(), func.nadir_point()) ); REQUIRE( !paretoCompareLess(func.optimal_value(), func.nadir_point()) ); @@ -102,7 +102,7 @@ TEMPLATE_TEST_CASE("dtlz_suite", "[problems]", DTLZ1, DTLZ2, DTLZ3, DTLZ4, DTLZ5 TestType func(num_obj); - REQUIRE_THAT( func(func.optimum()), Approx(func.optimal_value()).margin(1E-6) ); + REQUIRE_THAT( func(func.optimum()).std_vec(), Approx(func.optimal_value().std_vec()).margin(1E-6) ); REQUIRE( !paretoCompareLess(func.ideal_point(), func.nadir_point()) ); REQUIRE( !paretoCompareLess(func.optimal_value(), func.nadir_point()) ); diff --git a/test/unit/replacement.cpp b/test/unit/replacement.cpp index 2a0945ae..1ea18d20 100644 --- a/test/unit/replacement.cpp +++ b/test/unit/replacement.cpp @@ -44,7 +44,7 @@ TEST_CASE("replacement_best", "[replacement][single-objective]") math::ScopedTolerances _(0.0, 0.0); std::unique_ptr replacement = std::make_unique(); - const auto indices = replacement->nextPopulationImpl(context, fitness_matrix); + const auto indices = replacement->nextPopulationImpl(context, fitness_matrix).std_vec(); const std::vector expected = { 0, 1, 4, 5, 9, 11, 12, 15, 16, 17 }; @@ -54,7 +54,7 @@ TEST_CASE("replacement_best", "[replacement][single-objective]") TEST_CASE("replacement_children", "[replacement][single-objective]") { std::unique_ptr replacement = std::make_unique(); - const auto indices = replacement->nextPopulationImpl(context, fitness_matrix); + const auto indices = replacement->nextPopulationImpl(context, fitness_matrix).std_vec(); const std::vector expected = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; @@ -64,7 +64,7 @@ TEST_CASE("replacement_children", "[replacement][single-objective]") TEST_CASE("replacement_elitism", "[replacement][single-objective]") { std::unique_ptr replacement = std::make_unique(2); - const auto indices = replacement->nextPopulationImpl(context, fitness_matrix); + const auto indices = replacement->nextPopulationImpl(context, fitness_matrix).std_vec(); const std::vector expected = { 0, 5, 10, 11, 12, 13, 14, 15, 16, 17 }; diff --git a/test/unit/small_vector.cpp b/test/unit/small_vector.cpp new file mode 100644 index 00000000..55210221 --- /dev/null +++ b/test/unit/small_vector.cpp @@ -0,0 +1,1016 @@ +/* Copyright (c) 2024 Krisztián Rugási. Subject to the MIT License. */ + +#include +#include +#include +#include "utility/small_vector.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace gapp; + +inline constexpr size_t EMPTY = 0; +inline constexpr size_t SMALL_SIZE = 4; +inline constexpr size_t LARGE_SIZE = 100; + +using TrivialType = int; + +struct NonDefaultConstructibleType +{ + NonDefaultConstructibleType(int i) : i_(i) {} + + friend bool operator==(const NonDefaultConstructibleType&, const NonDefaultConstructibleType&) = default; + + int i_ = 0; +}; + +struct MoveOnlyType +{ + MoveOnlyType() = default; + MoveOnlyType(int i) : i_(i) {} + + MoveOnlyType(const MoveOnlyType&) = delete; + MoveOnlyType(MoveOnlyType&& o) : i_(o.i_) {} + + MoveOnlyType& operator=(const MoveOnlyType&) = delete; + MoveOnlyType& operator=(MoveOnlyType&& o) { i_ = o.i_; return *this; } + + ~MoveOnlyType() noexcept {} + + friend bool operator==(const MoveOnlyType&, const MoveOnlyType&) = default; + + int i_ = 0; +}; + +struct ImmovableType +{ + ImmovableType() = default; + ImmovableType(int i) : i_(i) {} + + ImmovableType(const ImmovableType&) = delete; + ImmovableType(ImmovableType&&) = delete; + + ImmovableType& operator=(const ImmovableType&) = delete; + ImmovableType& operator=(ImmovableType&&) = delete; + + friend bool operator==(const ImmovableType&, const ImmovableType&) = default; + + int i_ = 0; +}; + +struct NonTrivialType +{ + NonTrivialType() {} + NonTrivialType(int i) : i_(i) {} + NonTrivialType(const NonTrivialType& o) : i_(o.i_) {} + NonTrivialType(NonTrivialType&& o) : i_(o.i_) {} + NonTrivialType& operator=(const NonTrivialType& o) { i_ = o.i_; return *this; } + NonTrivialType& operator=(NonTrivialType&& o) { i_ = o.i_; return *this; } + ~NonTrivialType() noexcept {} + + friend bool operator==(const NonTrivialType&, const NonTrivialType&) = default; + + int i_ = 0; +}; + +template +constexpr auto equal_to(T rhs) +{ + return [rhs = std::move(rhs)](const auto& lhs) { return lhs == rhs; }; +} + + + //-----------------------------------// + // OBJECT LAYOUT // + //-----------------------------------// + + +TEST_CASE("small_vector_size", "[object_layout][!mayfail]") // fails under clang-cl because of no support for no_unique_address +{ + STATIC_REQUIRE(std::is_standard_layout_v>); + + CHECK(sizeof(small_vector) == detail::cache_line_size); +} + + + //-----------------------------------// + // CONSTRUCTORS // + //-----------------------------------// + + +TEMPLATE_TEST_CASE("small_vector()", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType) +{ + small_vector vec; + + REQUIRE(vec.empty()); + REQUIRE(vec.begin() == vec.end()); + REQUIRE(vec.cbegin() == vec.end()); + + REQUIRE(vec.size() == 0); + REQUIRE(vec.capacity() > 0); +} + +TEMPLATE_TEST_CASE("small_vector(Alloc)", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType) +{ + small_vector vec(std::allocator{}); + + REQUIRE(vec.empty()); + REQUIRE(vec.begin() == vec.end()); + REQUIRE(vec.cbegin() == vec.end()); + + REQUIRE(vec.size() == 0); + REQUIRE(vec.capacity() > 0); +} + +TEMPLATE_TEST_CASE("small_vector(size_t)", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size); + + REQUIRE(vec.size() == size); + REQUIRE(vec.capacity() >= size); +} + +TEMPLATE_TEST_CASE("small_vector(size_t, Alloc)", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size, std::allocator{}); + + REQUIRE(vec.size() == size); + REQUIRE(vec.capacity() >= size); +} + +TEMPLATE_TEST_CASE("small_vector(size_t, const T&)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size, TestType{ 0 }); + + REQUIRE(vec.size() == size); + REQUIRE(vec.capacity() >= size); + REQUIRE(std::all_of(vec.begin(), vec.end(), equal_to(0))); +} + +TEMPLATE_TEST_CASE("small_vector(size_t, const T&, Alloc)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size, TestType{ 1 }, std::allocator{}); + + REQUIRE(vec.size() == size); + REQUIRE(vec.capacity() >= size); + REQUIRE(std::all_of(vec.begin(), vec.end(), equal_to(1))); +} + +TEMPLATE_TEST_CASE("small_vector(Iter, Iter)", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const std::vector source(size, 2); + + small_vector vec(source.begin(), source.end()); + + REQUIRE(vec.size() == source.size()); + REQUIRE(vec.capacity() >= source.size()); +} + +TEMPLATE_TEST_CASE("small_vector(FwdIter, FwdIter, Alloc)", "[constructor]", TrivialType, MoveOnlyType, ImmovableType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const std::vector source(size, 3); + + small_vector vec(source.begin(), source.end(), std::allocator{}); + + REQUIRE(vec.size() == source.size()); + REQUIRE(vec.capacity() >= source.size()); +} + +TEST_CASE("small_vector(InputIter, InputIter)", "[constructor]") +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + std::istringstream source(std::string(size, 'c')); + + small_vector vec{ std::istream_iterator(source), std::istream_iterator() }; + + REQUIRE(vec.size() == size); + REQUIRE(vec.capacity() >= size); + REQUIRE(std::all_of(vec.begin(), vec.end(), equal_to('c'))); +} + +TEST_CASE("small_vector(nullptr, nullptr)", "[constructor]") +{ + const small_vector vec(static_cast(nullptr), static_cast(nullptr)); + + REQUIRE(vec.empty()); + REQUIRE(vec.capacity() != 0); +} + +TEMPLATE_TEST_CASE("small_vector(initializer_list)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + small_vector vec{ TestType{ 1 }, TestType{ 4 }, TestType{ 2 } }; + + REQUIRE(vec.is_small()); + REQUIRE(vec.size() == 3); + REQUIRE(vec.capacity() >= 3); + REQUIRE(vec.back() == 2); +} + +TEMPLATE_TEST_CASE("small_vector(initializer_list, Alloc)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + small_vector vec({ TestType{ 1 }, TestType{ 4 }, TestType{ 2 } }, std::allocator{}); + + REQUIRE(vec.is_small()); + REQUIRE(vec.size() == 3); + REQUIRE(vec.capacity() >= 3); + REQUIRE(vec.back() == 2); +} + +TEMPLATE_TEST_CASE("small_vector(const small_vector&)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const small_vector source(size, TestType{ 26 }); + + small_vector vec(source); + + REQUIRE(vec.size() == source.size()); + REQUIRE(vec == source); + REQUIRE(vec.capacity() != 0); +} + +TEMPLATE_TEST_CASE("small_vector(const small_vector&, Alloc)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const small_vector source(size, TestType{ 26 }); + + small_vector vec(source, std::allocator{}); + + REQUIRE(vec.size() == source.size()); + REQUIRE(vec == source); + REQUIRE(vec.capacity() != 0); +} + +TEMPLATE_TEST_CASE("small_vector(small_vector&&)", "[constructor]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector source(size, TestType{ 26 }); + small_vector source_copy(source); + + small_vector vec(std::move(source)); + + REQUIRE(vec.size() == source_copy.size()); + REQUIRE(vec == source_copy); + + REQUIRE(source.empty()); + REQUIRE(source.capacity() != 0); + + source.push_back(TestType{ 11 }); + REQUIRE(source.size() == 1); +} + +TEMPLATE_TEST_CASE("small_vector(small_vector&&)", "[constructor]", MoveOnlyType) +{ + const size_t size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector source(size); + small_vector source_copy(size); + + small_vector vec(std::move(source)); + + REQUIRE(vec.size() == source_copy.size()); + REQUIRE(vec == source_copy); + + REQUIRE(source.empty()); + REQUIRE(source.capacity() != 0); + + source.push_back(TestType{ 11 }); + REQUIRE(source.size() == 1); +} + + //-----------------------------------// + // ASSIGNMENT // + //-----------------------------------// + +TEMPLATE_TEST_CASE("assign(size_t count, const T& val)", "[assignment]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t count = GENERATE(EMPTY, SMALL_SIZE - 1, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector dest(dest_size, TestType{ 2 }); + + dest.assign(count, TestType{ 3 }); + + REQUIRE(dest.size() == count); + REQUIRE(std::all_of(dest.begin(), dest.end(), equal_to(3))); +} + +TEMPLATE_TEST_CASE("assign(FwdIter, FwdIter)", "[assignment]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t src_size = GENERATE(EMPTY, SMALL_SIZE - 1, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const small_vector source(src_size, TestType{ 4 }); + small_vector dest(dest_size, TestType{ 3 }); + + dest.assign(source.begin(), source.end()); + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); + + + small_vector dest2 = dest; + dest2.reserve(2 * source.size()); + dest2.assign(source.begin(), source.end()); + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); +} + +TEST_CASE("assign(InputIter, InputIter)", "[assignment]") +{ + const size_t src_size = GENERATE(EMPTY, SMALL_SIZE - 1, LARGE_SIZE + 1); + const size_t dst_size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + std::istringstream source(std::string(src_size, 'c')); + small_vector dest(dst_size); + + dest.assign(std::istream_iterator(source), std::istream_iterator()); + + REQUIRE(dest.size() == src_size); + REQUIRE(dest.capacity() >= src_size); + REQUIRE(std::all_of(dest.begin(), dest.end(), equal_to('c'))); +} + +TEST_CASE("assign(nullptr, nullptr)", "[assignment]") +{ + small_vector vec(0, 2); + vec.assign(static_cast(nullptr), static_cast(nullptr)); + + REQUIRE(vec.empty()); +} + +TEMPLATE_TEST_CASE("operator=(const small_vector&)", "[assignment]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t src_size = GENERATE(EMPTY, SMALL_SIZE - 1, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + const small_vector source(src_size, TestType{ 4 }); + small_vector dest(dest_size, TestType{ 3 }); + + dest = source; + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); + + + small_vector dest2 = dest; + dest2.reserve(2 * source.size()); + dest2 = source; + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); +} + +TEMPLATE_TEST_CASE("operator=(small_vector&&)", "[assignment]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t src_size = GENERATE(EMPTY, SMALL_SIZE - 1, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(EMPTY, SMALL_SIZE, LARGE_SIZE); + + small_vector source(src_size, TestType{ 4 }); + const small_vector src_copy(source); + small_vector dest(dest_size, TestType{ 3 }); + + dest = std::move(source); + + REQUIRE(dest.size() == src_copy.size()); + REQUIRE(dest == src_copy); + + REQUIRE(source.capacity() != 0); + + source = dest; + REQUIRE(source == dest); +} + +TEMPLATE_TEST_CASE("operator=(initializer_list)", "[assignment]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t dest_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + const small_vector source{ TestType{ 1 }, TestType{ 2 }, TestType{ 4 } }; + small_vector dest(dest_size, TestType{ 0 }); + + dest = { TestType{ 1 }, TestType{ 2 }, TestType{ 4 } }; + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); +} + + //-----------------------------------// + // ITERATORS // + //-----------------------------------// + +TEMPLATE_TEST_CASE("forward_iteration", "[iterators]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const small_vector vec(size, TestType{ 1 }); + + REQUIRE(std::all_of(vec.begin(), vec.end(), equal_to(TestType{ 1 }))); +} + +TEMPLATE_TEST_CASE("reverse_iteration", "[iterators]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const small_vector vec(size, TestType{ 1 }); + + REQUIRE(std::all_of(vec.rbegin(), vec.rend(), equal_to(TestType{ 1 }))); +} + + //-----------------------------------// + // ELEMENT ACCESS // + //-----------------------------------// + +TEMPLATE_TEST_CASE("operator[]", "[element_access]", TrivialType, NonTrivialType, MoveOnlyType, ImmovableType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const small_vector vec(size); + + REQUIRE(vec[0] == vec[1]); + REQUIRE(vec[1] == TestType{}); +} + +TEMPLATE_TEST_CASE("at", "[element_access]", TrivialType, NonTrivialType, MoveOnlyType, ImmovableType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const small_vector vec(size); + + REQUIRE(vec.at(0) == vec.at(1)); + REQUIRE(vec.at(1) == TestType{}); + REQUIRE_THROWS(vec.at(size)); +} + +TEMPLATE_TEST_CASE("front/back", "[element_access]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size, TestType{ 2 }); + + vec.back() = TestType{ 3 }; + vec.front() = TestType{ 0 }; + + REQUIRE(vec.front() == TestType{ 0 }); + REQUIRE(vec.back() == TestType{ 3 }); +} + + //-----------------------------------// + // CAPACITY // + //-----------------------------------// + + +TEMPLATE_TEST_CASE("is_small", "[capacity]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size, TestType{ 2 }); + + REQUIRE(vec.is_small() == (size == SMALL_SIZE)); +} + +TEMPLATE_TEST_CASE("reserve", "[capacity]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size, TestType{ 2 }); + + vec.reserve(2 * LARGE_SIZE); + REQUIRE(vec.capacity() >= 2 * LARGE_SIZE); + + vec.reserve(SMALL_SIZE); + REQUIRE(vec.capacity() >= 2 * LARGE_SIZE); +} + +TEMPLATE_TEST_CASE("shrink_to_fit", "[capacity]", TrivialType, NonTrivialType) +{ + small_vector vec(LARGE_SIZE, TestType{ 2 }); + + vec.reserve(2 * LARGE_SIZE); + REQUIRE(vec.capacity() >= 2 * LARGE_SIZE); + + vec.shrink_to_fit(); + REQUIRE(vec.capacity() == LARGE_SIZE); +} + + //-----------------------------------// + // MODIFIERS // + //-----------------------------------// + +TEMPLATE_TEST_CASE("clear", "[modifiers]", TrivialType, NonTrivialType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size, TestType{ 1 }); + + vec.clear(); + + REQUIRE(vec.empty()); +} + +TEMPLATE_TEST_CASE("swap", "[modifiers]", TrivialType, NonTrivialType) +{ + const size_t left_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const size_t right_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector left(left_size, TestType{ 1 }); + const small_vector old_left(left); + small_vector right(right_size, TestType{ 2 }); + const small_vector old_right(right); + + using std::swap; + swap(left, right); + + REQUIRE(right == old_left); + REQUIRE(left == old_right); + + swap(left, right); + + REQUIRE(left == old_left); + REQUIRE(right == old_right); +} + +TEMPLATE_TEST_CASE("push_back(const T&)", "[modifiers]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size, TestType{ 0 }); + const TestType elem(1); + + for (size_t i = 0; i < LARGE_SIZE + 1; i++) vec.push_back(elem); + + REQUIRE(vec.size() == size + LARGE_SIZE + 1); + REQUIRE(vec.front() == TestType{ 0 }); + REQUIRE(vec.back() == TestType{ 1 }); +} + +TEMPLATE_TEST_CASE("push_back(T&&)", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size); + + for (size_t i = 0; i < LARGE_SIZE + 1; i++) vec.push_back(TestType{ 1 }); + + REQUIRE(vec.size() == size + LARGE_SIZE + 1); + REQUIRE(vec.front() == TestType{}); + REQUIRE(vec.back() == TestType{ 1 }); +} + +TEMPLATE_TEST_CASE("emplace_back(...)", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size); + + const TestType& val = vec.emplace_back(); + REQUIRE(val == TestType{}); + + for (size_t i = 0; i < LARGE_SIZE; i++) vec.emplace_back(1); + + REQUIRE(vec.size() == size + LARGE_SIZE + 1); + REQUIRE(vec.front() == TestType{}); + REQUIRE(vec.back() == TestType{ 1 }); +} + +TEMPLATE_TEST_CASE("pop_back()", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType, ImmovableType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size); + + vec.pop_back(); + REQUIRE(vec.size() == (size - 1)); + + vec.pop_back(); + REQUIRE(vec.size() == (size - 2)); +} + +TEMPLATE_TEST_CASE("resize(n)", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size); + + vec.resize(2 * LARGE_SIZE); + REQUIRE(vec.size() == 2 * LARGE_SIZE); + REQUIRE(vec.back() == TestType{}); + + vec.resize(0); + REQUIRE(vec.empty()); +} + +TEMPLATE_TEST_CASE("resize(n, value)", "[modifiers]", TrivialType, NonTrivialType, NonDefaultConstructibleType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + small_vector vec(size, TestType{ 1 }); + + vec.resize(2 * LARGE_SIZE, TestType{ 2 }); + REQUIRE(vec.size() == 2 * LARGE_SIZE); + REQUIRE(vec.back() == TestType{ 2 }); + + vec.resize(0, TestType{ 3 }); + REQUIRE(vec.empty()); +} + +TEMPLATE_TEST_CASE("erase(pos)", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType) +{ + const size_t size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector vec(size); + vec.front() = TestType{ 2 }; + + auto it1 = vec.erase(vec.begin()); + + REQUIRE(it1 == vec.begin()); + REQUIRE(vec.size() == size - 1); + REQUIRE(vec.front() == TestType{}); + + auto it2 = vec.erase(vec.end() - 1); + + REQUIRE(it2 == vec.end()); + REQUIRE(vec.size() == size - 2); +} + +TEMPLATE_TEST_CASE("erase(first, last)", "[modifiers]", TrivialType, NonTrivialType) +{ + small_vector vec{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 4 } }; + + SECTION("front") + { + auto it = vec.erase(vec.begin(), vec.begin() + 1); + + REQUIRE(vec == small_vector{ TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 4 } }); + REQUIRE(*it == TestType{ 1 }); + } + SECTION("back") + { + auto it = vec.erase(vec.end() - 1, vec.end()); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 } }); + REQUIRE(it == vec.end()); + } + SECTION("nothing") + { + auto it = vec.erase(vec.begin(), vec.begin()); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 4 } }); + REQUIRE(it == vec.begin()); + } + SECTION("everything") + { + auto it = vec.erase(vec.begin(), vec.end()); + + REQUIRE(vec.empty()); + REQUIRE(it == vec.end()); + + } + SECTION("middle") + { + auto it = vec.erase(vec.begin() + 1, vec.begin() + 3); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 3 }, TestType{ 4 } }); + REQUIRE(*it == TestType{ 3 }); + } +} + +TEMPLATE_TEST_CASE("insert(pos, const T&)", "[modifiers]", TrivialType, NonTrivialType) +{ + small_vector vec{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 } }; + const TestType value{ 21 }; + + SECTION("front") + { + auto it = vec.insert(vec.begin(), value); + + REQUIRE(vec == small_vector{ TestType{ 21 }, TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 } }); + REQUIRE(*it == value); + } + SECTION("back") + { + auto it = vec.insert(vec.end(), value); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 21 } }); + REQUIRE(*it == value); + } + SECTION("middle") + { + auto it = vec.insert(vec.begin() + 2, value); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 21 }, TestType{ 2 }, TestType{ 3 } }); + REQUIRE(*it == value); + } + SECTION("many") + { + for (size_t i = 0; i < 100; i++) vec.insert(vec.begin(), value); + + REQUIRE(vec.size() == 104); + REQUIRE(vec.front() == TestType{ 21 }); + REQUIRE(vec.back() == TestType{ 3 }); + } +} + +TEMPLATE_TEST_CASE("insert(pos, T&&)", "[modifiers]", TrivialType, NonTrivialType) +{ + small_vector vec{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 } }; + + SECTION("front") + { + auto it = vec.insert(vec.begin(), TestType{ 21 }); + + REQUIRE(vec == small_vector{ TestType{ 21 }, TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 } }); + REQUIRE(*it == TestType{ 21 }); + + } + SECTION("back") + { + auto it = vec.insert(vec.end(), TestType{ 21 }); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 21 } }); + REQUIRE(*it == TestType{ 21 }); + } + SECTION("middle") + { + auto it = vec.insert(vec.begin() + 2, TestType{ 21 }); + + REQUIRE(vec == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 21 }, TestType{ 2 }, TestType{ 3 } }); + REQUIRE(*it == TestType{ 21 }); + } + SECTION("many") + { + for (size_t i = 0; i < 100; i++) vec.insert(vec.begin(), TestType{ 21 }); + + REQUIRE(vec.size() == 104); + REQUIRE(vec.front() == TestType{ 21 }); + REQUIRE(vec.back() == TestType{ 3 }); + } +} + +TEMPLATE_TEST_CASE("insert(pos, count, const T&)", "[modifiers]", TrivialType, NonTrivialType) +{ + small_vector dest{ TestType{ 0 }, TestType{ 1 } }; + + SECTION("front") + { + auto it = dest.insert(dest.begin(), 3, TestType{ 4 }); + + REQUIRE(dest == small_vector{ TestType{ 4 }, TestType{ 4 }, TestType{ 4 }, TestType{ 0 }, TestType{ 1 } }); + REQUIRE(it == dest.begin()); + REQUIRE(*it == TestType{ 4 }); + } + SECTION("back") + { + auto it = dest.insert(dest.end(), 3, TestType{ 4 }); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 4 }, TestType{ 4 }, TestType{ 4 } }); + REQUIRE(it == (dest.begin() + 2)); + REQUIRE(*it == TestType{ 4 }); + } + SECTION("middle") + { + auto it = dest.insert(dest.begin() + 1, 3, TestType{ 4 }); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 4 }, TestType{ 4 }, TestType{ 4 }, TestType{ 1 } }); + REQUIRE(it == (dest.begin() + 1)); + REQUIRE(*it == TestType{ 4 }); + } + SECTION("nothing") + { + auto it = dest.insert(dest.end(), 0, TestType{ 4 }); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 1 } }); + REQUIRE(it == dest.end()); + } + SECTION("many") + { + for (size_t i = 0; i < 100; i++) dest.insert(dest.begin(), 3, TestType{ 4 }); + + REQUIRE(dest.size() == 302); + REQUIRE(dest.front() == TestType{ 4 }); + REQUIRE(dest.back() == TestType{ 1 }); + } +} + +TEMPLATE_TEST_CASE("insert(pos, FwdIter, FwdIter)", "[modifiers]", TrivialType, NonTrivialType) +{ + small_vector dest{ TestType{ 0 }, TestType{ 1 } }; + const small_vector src{ TestType{ 2 }, TestType{ 3 }, TestType{ 4 } }; + + SECTION("front") + { + auto it = dest.insert(dest.begin(), src.begin(), src.end()); + + REQUIRE(dest == small_vector{ TestType{ 2 }, TestType{ 3 }, TestType{ 4 }, TestType{ 0 }, TestType{ 1 } }); + REQUIRE(*it == TestType{ 2 }); + } + SECTION("back") + { + auto it = dest.insert(dest.end(), src.begin(), src.end()); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 1 }, TestType{ 2 }, TestType{ 3 }, TestType{ 4 } }); + REQUIRE(*it == TestType{ 2 }); + } + SECTION("middle") + { + auto it = dest.insert(dest.begin() + 1, src.begin(), src.end()); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 2 }, TestType{ 3 }, TestType{ 4 }, TestType{ 1 } }); + REQUIRE(*it == TestType{ 2 }); + } + SECTION("empty") + { + auto it = dest.insert(dest.end(), src.begin(), src.begin()); + + REQUIRE(dest == small_vector{ TestType{ 0 }, TestType{ 1 } }); + REQUIRE(it == dest.end()); + } + SECTION("many") + { + for (size_t i = 0; i < 100; i++) dest.insert(dest.begin(), src.begin(), src.end()); + + REQUIRE(dest.size() == 100 * src.size() + 2); + REQUIRE(dest.front() == TestType{ 2 }); + REQUIRE(dest.back() == TestType{ 1 }); + } +} + +TEST_CASE("insert(pos, InputIter, InputIter)", "[modifiers]") +{ + small_vector dest(2, 'a'); + std::istringstream src(std::string(3, 'c')); + + SECTION("front") + { + auto it = dest.insert(dest.begin(), std::istream_iterator(src), std::istream_iterator()); + + REQUIRE(dest == small_vector{ 'c', 'c', 'c', 'a', 'a' }); + REQUIRE(it == dest.begin()); + } + SECTION("back") + { + auto it = dest.insert(dest.end(), std::istream_iterator(src), std::istream_iterator()); + + REQUIRE(dest == small_vector{ 'a', 'a', 'c', 'c', 'c' }); + REQUIRE(it == (dest.begin() + 2)); + REQUIRE(*it == 'c'); + } + SECTION("middle") + { + auto it = dest.insert(dest.begin() + 1, std::istream_iterator(src), std::istream_iterator()); + + REQUIRE(dest == small_vector{ 'a', 'c', 'c', 'c', 'a' }); + REQUIRE(it == (dest.begin() + 1)); + REQUIRE(*it == 'c'); + } + SECTION("empty") + { + auto it = dest.insert(dest.end(), std::istream_iterator(), std::istream_iterator()); + + REQUIRE(dest == small_vector{ 'a', 'a' }); + REQUIRE(it == dest.end()); + } + SECTION("many") + { + for (size_t i = 0; i < 100; i++) + { + std::istringstream input(std::string(3, 'c')); + dest.insert(dest.begin(), std::istream_iterator(input), std::istream_iterator()); + } + + REQUIRE(dest.size() == 302); + REQUIRE(dest.front() == 'c'); + REQUIRE(dest.back() == 'a'); + } +} + +TEMPLATE_TEST_CASE("emplace(pos, Args&&...)", "[modifiers]", TrivialType, NonTrivialType, MoveOnlyType) +{ + small_vector vec(2); + + auto it1 = vec.emplace(vec.begin(), 1); + + REQUIRE(*it1 == 1); + REQUIRE(vec.size() == 3); + REQUIRE(vec.front() == TestType{ 1 }); + + auto it2 = vec.emplace(vec.end(), 2); + + REQUIRE(*it2 == 2); + REQUIRE(vec.size() == 4); + REQUIRE(vec.back() == TestType{ 2 }); + + auto it3 = vec.emplace(vec.begin() + 1); + + REQUIRE(*it3 == TestType{}); + REQUIRE(vec.size() == 5); + + for (size_t i = 0; i < 100; i++) vec.emplace(vec.begin()); + REQUIRE(vec.size() == 105); + REQUIRE(vec.front() == TestType{}); + REQUIRE(vec.back() == TestType{ 2 }); +} + + //-----------------------------------// + // ALLOCATOR PROPAGATION // + //-----------------------------------// + +template +struct DummyAllocator +{ + using value_type = T; + + T* allocate(std::size_t size) const + { + if (size == 0) return nullptr; + + const std::size_t bytes_needed = size * sizeof(value_type); + void* const storage = ::operator new[](bytes_needed); + + return static_cast(storage); + } + + void deallocate(T* storage, std::size_t) const noexcept + { + ::operator delete[](storage); + } + + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + + using is_always_equal = std::false_type; + friend bool operator==(const DummyAllocator&, const DummyAllocator&) { return false; } +}; + + +template +using small_vector2 = small_vector>; + + +TEMPLATE_TEST_CASE("propagate_on_copy_assignment", "[allocators]", TrivialType, NonTrivialType) +{ + const size_t src_size = GENERATE(SMALL_SIZE, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + const small_vector2 source(src_size, 4); + small_vector2 dest(dest_size, 3); + + dest = source; + + REQUIRE(dest.size() == source.size()); + REQUIRE(dest == source); +} + +TEMPLATE_TEST_CASE("propagate_on_move_assignment", "[allocators]", TrivialType, NonTrivialType) +{ + const size_t src_size = GENERATE(SMALL_SIZE, LARGE_SIZE + 1); + const size_t dest_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector2 source(src_size, 4); + const small_vector2 src_copy(source); + small_vector2 dest(dest_size, 3); + + dest = std::move(source); + + REQUIRE(dest.size() == src_copy.size()); + REQUIRE(dest == src_copy); + + REQUIRE(source.capacity() != 0); + + source = dest; + REQUIRE(source == dest); +} + +TEMPLATE_TEST_CASE("propagate_on_swap", "[allocators]", TrivialType, NonTrivialType) +{ + const size_t left_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + const size_t right_size = GENERATE(SMALL_SIZE, LARGE_SIZE); + + small_vector2 left(left_size, 1); + const small_vector2 old_left(left); + small_vector2 right(right_size, 2); + const small_vector2 old_right(right); + + using std::swap; + swap(left, right); + + REQUIRE(right == old_left); + REQUIRE(left == old_right); + + swap(left, right); + + REQUIRE(left == old_left); + REQUIRE(right == old_right); +} diff --git a/test/unit/utility.cpp b/test/unit/utility.cpp new file mode 100644 index 00000000..472a95aa --- /dev/null +++ b/test/unit/utility.cpp @@ -0,0 +1,97 @@ +/* Copyright (c) 2024 Krisztián Rugási. Subject to the MIT License. */ + +#include +#include +#include "utility/utility.hpp" +#include +#include +#include + +using namespace gapp; +using namespace gapp::detail; + + +TEMPLATE_TEST_CASE("range_length_signed", "[utility]", std::int8_t, std::int16_t, std::int32_t, std::int64_t) +{ + using IntType = TestType; + using UIntType = std::make_unsigned_t; + + constexpr IntType small = std::numeric_limits::min(); + constexpr IntType large = std::numeric_limits::max(); + + STATIC_REQUIRE(range_length(IntType{ 0 }, IntType{ 0 }) == 0); + STATIC_REQUIRE(range_length(IntType{ -1 }, IntType{ -1 }) == 0); + STATIC_REQUIRE(range_length(IntType{ 1 }, IntType{ 1 }) == 0); + + STATIC_REQUIRE(range_length(IntType{ 0 }, IntType{ 1 }) == 1); + STATIC_REQUIRE(range_length(IntType{ -1 }, IntType{ 0 }) == 1); + + STATIC_REQUIRE(range_length(IntType{ -1 }, IntType{ 1 }) == 2); + + STATIC_REQUIRE(range_length(small, small) == 0); + STATIC_REQUIRE(range_length(large, large) == 0); + + STATIC_REQUIRE(range_length(small, IntType{ 0 }) == (UIntType(large) + 1)); + STATIC_REQUIRE(range_length(IntType{ 0 }, large) == UIntType(large)); + STATIC_REQUIRE(range_length(small, large) == std::numeric_limits::max()); +} + +TEMPLATE_TEST_CASE("range_length_unsigned", "[utility]", std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) +{ + using IntType = TestType; + + constexpr IntType small = std::numeric_limits::min(); + constexpr IntType large = std::numeric_limits::max(); + + STATIC_REQUIRE(range_length(IntType{ 0 }, IntType{ 0 }) == 0); + STATIC_REQUIRE(range_length(IntType{ 1 }, IntType{ 1 }) == 0); + + STATIC_REQUIRE(range_length(IntType{ 0 }, IntType{ 1 }) == 1); + + STATIC_REQUIRE(range_length(small, small) == 0); + STATIC_REQUIRE(range_length(large, large) == 0); + + STATIC_REQUIRE(range_length(small, large) == large); +} + +TEST_CASE("next_mod", "[algorithm]") +{ + REQUIRE(detail::next_mod(0, 3) == 1); + REQUIRE(detail::next_mod(1, 3) == 2); + REQUIRE(detail::next_mod(2, 3) == 0); +} + +TEST_CASE("prev_mod", "[algorithm]") +{ + REQUIRE(detail::prev_mod(0, 3) == 2); + REQUIRE(detail::prev_mod(1, 3) == 0); + REQUIRE(detail::prev_mod(2, 3) == 1); +} + +TEST_CASE("increment_mod", "[algorithm]") +{ + int n = 0; + + detail::increment_mod(n, 3); + REQUIRE(n == 1); + + detail::increment_mod(n, 3); + REQUIRE(n == 2); + + detail::increment_mod(n, 3); + REQUIRE(n == 0); +} + +TEST_CASE("decrement_mod", "[algorithm]") +{ + int n = 0; + + detail::decrement_mod(n, 3); + REQUIRE(n == 2); + + detail::decrement_mod(n, 3); + REQUIRE(n == 1); + + detail::decrement_mod(n, 3); + REQUIRE(n == 0); +} From 04c93ccd68eb0fabce5052fd1eb1240ab1881eba Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:35:18 +0200 Subject: [PATCH 4/5] fix some clang tidy warnings --- .clang-tidy | 1 - src/algorithm/soga_replacement.cpp | 8 +++----- src/algorithm/soga_selection.cpp | 8 +++----- src/core/fitness_function.hpp | 8 +++----- src/crossover/lambda.hpp | 8 +++----- src/mutation/lambda.hpp | 8 +++----- src/stop_condition/stop_condition_base.hpp | 8 +++----- src/utility/bounded_value.hpp | 7 +++---- 8 files changed, 21 insertions(+), 35 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index d012fa5b..536ff2d8 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -20,7 +20,6 @@ -modernize-use-nodiscard, -modernize-use-auto, -modernize-use-trailing-return-type, - -performance-noexcept-move-constructor, -readability-identifier-length, -readability-magic-numbers, -readability-named-parameter, diff --git a/src/algorithm/soga_replacement.cpp b/src/algorithm/soga_replacement.cpp index 1afe23b1..710ef587 100644 --- a/src/algorithm/soga_replacement.cpp +++ b/src/algorithm/soga_replacement.cpp @@ -51,17 +51,15 @@ namespace gapp::replacement } - Lambda::Lambda(ReplacementCallable f) noexcept + Lambda::Lambda(ReplacementCallable f) noexcept : + replacement_(std::move(f)) { - GAPP_ASSERT(f, "The population replacement method can't be a nullptr."); - - replacement_ = std::move(f); + GAPP_ASSERT(replacement_, "The population replacement method can't be a nullptr."); } small_vector Lambda::nextPopulationImpl(const GaInfo& ga, const FitnessMatrix& fmat) { GAPP_ASSERT(replacement_); - return replacement_(ga, fmat); } diff --git a/src/algorithm/soga_selection.cpp b/src/algorithm/soga_selection.cpp index 61caa5cc..a868b83a 100644 --- a/src/algorithm/soga_selection.cpp +++ b/src/algorithm/soga_selection.cpp @@ -178,17 +178,15 @@ namespace gapp::selection } - Lambda::Lambda(SelectionCallable f) noexcept + Lambda::Lambda(SelectionCallable f) noexcept : + selection_(std::move(f)) { - GAPP_ASSERT(f, "The selection method can't be a nullptr."); - - selection_ = std::move(f); + GAPP_ASSERT(selection_, "The selection method can't be a nullptr."); } size_t Lambda::selectImpl(const GaInfo& ga, const FitnessMatrix& fmat) const { GAPP_ASSERT(selection_); - return selection_(ga, fmat); } diff --git a/src/core/fitness_function.hpp b/src/core/fitness_function.hpp index e8ddd00f..47ead32a 100644 --- a/src/core/fitness_function.hpp +++ b/src/core/fitness_function.hpp @@ -154,18 +154,16 @@ namespace gapp::detail using FitnessCallable = std::function&)>; FitnessLambda(size_t chrom_len, FitnessCallable f) noexcept : - FitnessFunctionBase(chrom_len) + FitnessFunctionBase(chrom_len), + fitness_function_(std::move(f)) { - GAPP_ASSERT(f, "The fitness function can't be a nullptr."); - - fitness_function_ = std::move(f); + GAPP_ASSERT(fitness_function_, "The fitness function can't be a nullptr."); } private: FitnessVector invoke(const Chromosome& chrom) const override { GAPP_ASSERT(fitness_function_); - return fitness_function_(chrom); } diff --git a/src/crossover/lambda.hpp b/src/crossover/lambda.hpp index c6d4a95a..271fddf1 100644 --- a/src/crossover/lambda.hpp +++ b/src/crossover/lambda.hpp @@ -21,11 +21,10 @@ namespace gapp::crossover public: using CrossoverCallable = std::function(const GA&, const Candidate&, const Candidate&)>; - constexpr explicit Lambda(CrossoverCallable f) noexcept + constexpr explicit Lambda(CrossoverCallable f) noexcept : + crossover_(std::move(f)) { - GAPP_ASSERT(f, "The crossover method can't be a nullptr."); - - crossover_ = std::move(f); + GAPP_ASSERT(crossover_, "The crossover method can't be a nullptr."); } private: @@ -34,7 +33,6 @@ namespace gapp::crossover CandidatePair crossover(const GA& ga, const Candidate& parent1, const Candidate& parent2) const override { GAPP_ASSERT(crossover_); - return crossover_(ga, parent1, parent2); } }; diff --git a/src/mutation/lambda.hpp b/src/mutation/lambda.hpp index f064f0a7..f4fa2f71 100644 --- a/src/mutation/lambda.hpp +++ b/src/mutation/lambda.hpp @@ -22,11 +22,10 @@ namespace gapp::mutation using MutationCallable = std::function&, const Candidate&, Chromosome&)>; constexpr explicit Lambda(MutationCallable f) noexcept : - Mutation(0.01) + Mutation(0.01), + mutate_(std::move(f)) { - GAPP_ASSERT(f, "The mutation method can't be a nullptr."); - - mutate_ = std::move(f); + GAPP_ASSERT(mutate_, "The mutation method can't be a nullptr."); } private: @@ -35,7 +34,6 @@ namespace gapp::mutation void mutate(const GA& ga, const Candidate& candidate, Chromosome& chromosome) const override { GAPP_ASSERT(mutate_); - mutate_(ga, candidate, chromosome); } }; diff --git a/src/stop_condition/stop_condition_base.hpp b/src/stop_condition/stop_condition_base.hpp index 8fb63041..a78f6ecb 100644 --- a/src/stop_condition/stop_condition_base.hpp +++ b/src/stop_condition/stop_condition_base.hpp @@ -86,11 +86,10 @@ namespace gapp::stopping public: using StopConditionCallable = std::function; - explicit Lambda(StopConditionCallable f) noexcept + explicit Lambda(StopConditionCallable f) noexcept : + stop_condition_(std::move(f)) { - GAPP_ASSERT(f, "The stop condition can't be a nullptr."); - - stop_condition_ = std::move(f); + GAPP_ASSERT(stop_condition_, "The stop condition can't be a nullptr."); } private: @@ -99,7 +98,6 @@ namespace gapp::stopping bool stop_condition(const GaInfo& ga) override { GAPP_ASSERT(stop_condition_); - return stop_condition_(ga); } }; diff --git a/src/utility/bounded_value.hpp b/src/utility/bounded_value.hpp index 3bab5af4..da915403 100644 --- a/src/utility/bounded_value.hpp +++ b/src/utility/bounded_value.hpp @@ -37,11 +37,10 @@ namespace gapp::detail public: using value_type = T; - constexpr /* implicit */ BoundedValue(value_type value) noexcept + constexpr /* implicit */ BoundedValue(value_type value) noexcept : + value_(std::move(value)) { - GAPP_ASSERT(I.contains(value), "The value is outside of the allowed interval."); - - value_ = std::move(value); + GAPP_ASSERT(I.contains(value_), "The value is outside of the allowed interval."); } constexpr /* implicit */ operator value_type() const noexcept { return value_; } From 6016c601be7f601085f67b495c205d0fd7c99d9a Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:36:08 +0200 Subject: [PATCH 5/5] replace CMakeSettings.json with CMakePresets.json --- CMakePresets.json | 247 +++++++++++++++++++++++++++++++++++++++++++++ CMakeSettings.json | 126 ----------------------- 2 files changed, 247 insertions(+), 126 deletions(-) create mode 100644 CMakePresets.json delete mode 100644 CMakeSettings.json diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..601e4f8c --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,247 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/build/${presetName}", + "installDir": "${sourceDir}/install/${presetName}", + "cacheVariables": { + "GAPP_USE_WERROR": "ON", + "GAPP_BUILD_TESTS": "ON", + "GAPP_BUILD_BENCHMARKS": "ON", + "GAPP_BUILD_EXAMPLES": "ON", + "GAPP_USE_LTO": "ON" + } + }, + { + "name": "windows-base", + "inherits": "base", + "hidden": true, + "generator": "Ninja", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "linux-base", + "inherits": "base", + "hidden": true, + "generator": "Ninja", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "msvc-debug", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "msvc-relwithdebinfo", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "msvc-release", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "msvc-dll", + "inherits": "msvc-release", + "cacheVariables": { + "BUILD_SHARED_LIBS": "ON" + } + }, + { + "name": "msvc-x86", + "inherits": "msvc-release", + "architecture": { + "value": "x86", + "strategy": "external" + } + }, + { + "name": "clang-cl-debug", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "clang-cl" + } + }, + { + "name": "clang-cl-relwithdebinfo", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_COMPILER": "clang-cl" + } + }, + { + "name": "clang-cl-release", + "inherits": "windows-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "clang-cl" + } + }, + { + "name": "clang-cl-dll", + "inherits": "clang-cl-release", + "cacheVariables": { + "BUILD_SHARED_LIBS": "ON" + } + }, + { + "name": "gcc-debug", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "gcc-relwithdebinfo", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "gcc-release", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "clang-debug", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "clang-relwithdebinfo", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "clang-release", + "inherits": "linux-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "clang-tidy", + "inherits": "clang-release", + "cacheVariables": { + "CMAKE_CXX_CLANG_TIDY": "clang-tidy" + } + }, + { + "name": "asan", + "inherits": "clang-release", + "cacheVariables": { + "GAPP_CXX_FLAGS": "-fsanitize=address -g -fno-omit-frame-pointer" + } + }, + { + "name": "ubsan", + "inherits": "clang-release", + "cacheVariables": { + "GAPP_CXX_FLAGS": "-fsanitize=undefined -g -fno-omit-frame-pointer" + } + }, + { + "name": "tsan", + "inherits": "clang-release", + "cacheVariables": { + "GAPP_CXX_FLAGS": "-fsanitize=thread -g -fno-omit-frame-pointer" + } + }, + { + "name": "coverage", + "inherits": "gcc-debug", + "cacheVariables": { + "GAPP_CXX_FLAGS": "--coverage", + "CMAKE_EXE_LINKER_FLAGS": "--coverage", + "GAPP_BUILD_BENCHMARKS": "OFF", + "GAPP_BUILD_EXAMPLES": "OFF" + } + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "base", + "jobs": 0 + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "base", + "execution": { + "scheduleRandom": true + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "asan", + "inherits": "default", + "configurePreset": "asan", + "environment": { + "ASAN_OPTIONS": "check_initialization_order=1:strict_init_order=1:detect_stack_use_after_return=1:detect_leaks=1" + } + }, + { + "name": "ubsan", + "inherits": "default", + "configurePreset": "ubsan", + "environment": { + "UBSAN_OPTIONS": "print_stacktrace=1:print_summary=1" + } + }, + { + "name": "tsan", + "inherits": "default", + "configurePreset": "tsan", + "environment": { + "TSAN_OPTIONS": "verbosity=1:force_seq_cst_atomics=0" + } + } + ] +} diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index e58d6afd..00000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "configurations": [ - { - "name": "msvc-debug", - "generator": "Ninja", - "configurationType": "Debug", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_CXX_FLAGS=/analyze:WX- -DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": false, - "inheritEnvironments": [ "msvc_x64_x64" ] - }, - { - "name": "msvc-release", - "generator": "Ninja", - "configurationType": "Release", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_CXX_FLAGS=-analyze:WX- -DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": false, - "inheritEnvironments": [ "msvc_x64_x64" ] - }, - { - "name": "msvc-relWithDebInfo", - "generator": "Ninja", - "configurationType": "RelWithDebInfo", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_CXX_FLAGS=-analyze:WX- -DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": false, - "inheritEnvironments": [ "msvc_x64_x64" ] - }, - { - "name": "clang-debug", - "generator": "Ninja", - "configurationType": "Debug", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "enableClangTidyCodeAnalysis": false, - "inheritEnvironments": [ "clang_cl_x64_x64" ] - }, - { - "name": "clang-release", - "generator": "Ninja", - "configurationType": "Release", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "inheritEnvironments": [ "clang_cl_x64_x64" ] - }, - { - "name": "clang-relWithDebInfo", - "generator": "Ninja", - "configurationType": "RelWithDebInfo", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "inheritEnvironments": [ "clang_cl_x64_x64" ] - }, - { - "name": "msvc-dll", - "generator": "Ninja", - "configurationType": "Release", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_CXX_FLAGS=-analyze:WX- -DGAPP_USE_WERROR=ON -DBUILD_SHARED_LIBS=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": false, - "inheritEnvironments": [ "msvc_x64_x64" ] - }, - { - "name": "clang-dll", - "generator": "Ninja", - "configurationType": "Release", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_USE_WERROR=ON -DBUILD_SHARED_LIBS=ON -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON -DGAPP_USE_LTO=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": false, - "inheritEnvironments": [ "clang_cl_x64_x64" ] - }, - { - "name": "clang-tidy", - "generator": "Ninja", - "configurationType": "Debug", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "inheritEnvironments": [ "clang_cl_x64_x64" ], - "variables": [ - { - "name": "CMAKE_CXX_CLANG_TIDY", - "value": "clang-tidy", - "type": "STRING" - } - ] - }, - { - "name": "core-guidelines-check", - "generator": "Ninja", - "configurationType": "Debug", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DGAPP_CXX_FLAGS=/analyze:WX- -DGAPP_USE_WERROR=ON -DGAPP_BUILD_TESTS=ON", - "ctestCommandArgs": "--output-on-failure --schedule-random", - "codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset", - "enableMicrosoftCodeAnalysis": true, - "disableExternalAnalysis": true, - "inheritEnvironments": [ "msvc_x64_x64" ], - "environments": [ { "CAExcludePath": "${projectDir}\\src\\algorithm\\reference_lines.cpp" } ] - } - ] -} \ No newline at end of file