Some renaming in the utility library (#2387)

Rename vector_set > flat_set, move util_compare > util/compare, move util_cast > util/cast
This commit is contained in:
Antoine Prouvost 2023-03-17 09:06:31 +01:00 committed by GitHub
parent aa8d28c6c5
commit 07477a25fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 973 additions and 937 deletions

View File

@ -191,6 +191,11 @@ endforeach()
set(LIBMAMBA_PUBLIC_HEADERS
${LIBMAMBA_INCLUDE_DIR}/mamba/version.hpp
# Utility library
${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_set.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/graph.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/compare.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/cast.hpp
# Implementation of version and matching specs
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/version.hpp
# Core API (low-level)
@ -230,13 +235,10 @@ set(LIBMAMBA_PUBLIC_HEADERS
${LIBMAMBA_INCLUDE_DIR}/mamba/core/url.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/fsutil.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_graph.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_os.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_random.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_scope.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_string.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_cast.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_compare.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/validate.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/virtual_packages.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/env_lockfile.hpp

View File

@ -22,7 +22,7 @@ extern "C" // Incomplete header
#include "mamba/core/package_info.hpp"
#include "mamba/core/pool.hpp"
#include "mamba/core/util_graph.hpp"
#include "mamba/util/graph.hpp"
namespace mamba
{
@ -73,7 +73,7 @@ namespace mamba
{
public:
using dependency_graph = DiGraph<PackageInfo>;
using dependency_graph = util::DiGraph<PackageInfo>;
query_result(QueryType type, const std::string& query, dependency_graph&& dep_graph);

View File

@ -23,7 +23,8 @@
#include "mamba/core/match_spec.hpp"
#include "mamba/core/package_info.hpp"
#include "mamba/core/util_graph.hpp"
#include "mamba/util/flat_set.hpp"
#include "mamba/util/graph.hpp"
namespace mamba
{
@ -32,11 +33,11 @@ namespace mamba
class MPool;
template <typename T>
class conflict_map : private std::unordered_map<T, vector_set<T>>
class conflict_map : private std::unordered_map<T, util::flat_set<T>>
{
public:
using Base = std::unordered_map<T, vector_set<T>>;
using Base = std::unordered_map<T, util::flat_set<T>>;
using typename Base::const_iterator;
using typename Base::key_type;
using typename Base::value_type;
@ -47,7 +48,7 @@ namespace mamba
using Base::empty;
using Base::size;
bool has_conflict(const key_type& a) const;
auto conflicts(const key_type& a) const -> const vector_set<T>&;
auto conflicts(const key_type& a) const -> const util::flat_set<T>&;
bool in_conflict(const key_type& a, const key_type& b) const;
using Base::cbegin;
@ -89,7 +90,7 @@ namespace mamba
using edge_t = MatchSpec;
using graph_t = DiGraph<node_t, edge_t>;
using graph_t = util::DiGraph<node_t, edge_t>;
using node_id = graph_t::node_id;
using conflicts_t = conflict_map<node_id>;
@ -142,11 +143,11 @@ namespace mamba
* specialization for needed types.
*/
template <typename T, typename Allocator = std::allocator<T>>
class NamedList : private vector_set<T, RoughCompare<T>, Allocator>
class NamedList : private util::flat_set<T, RoughCompare<T>, Allocator>
{
public:
using Base = vector_set<T, RoughCompare<T>, Allocator>;
using Base = util::flat_set<T, RoughCompare<T>, Allocator>;
using typename Base::allocator_type;
using typename Base::const_iterator;
using typename Base::const_reverse_iterator;
@ -213,7 +214,7 @@ namespace mamba
using edge_t = NamedList<MatchSpec>;
using graph_t = DiGraph<node_t, edge_t>;
using graph_t = util::DiGraph<node_t, edge_t>;
using node_id = graph_t::node_id;
using conflicts_t = conflict_map<node_id>;
@ -275,7 +276,7 @@ namespace mamba
}
template <typename T>
auto conflict_map<T>::conflicts(const key_type& a) const -> const vector_set<T>&
auto conflict_map<T>::conflicts(const key_type& a) const -> const util::flat_set<T>&
{
return Base::at(a);
}

View File

@ -19,7 +19,7 @@
#include <utility>
#include <vector>
#include "mamba/core/util_compare.hpp"
#include "mamba/util/compare.hpp"
namespace mamba
{

View File

@ -13,7 +13,7 @@
#include <fmt/format.h>
#include "util_compare.hpp"
#include "mamba/util/compare.hpp"
namespace mamba::util
{

View File

@ -0,0 +1,309 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#ifndef MAMBA_UTILFLAT_SET_HPP
#define MAMBA_UTILFLAT_SET_HPP
#include <algorithm>
#include <utility>
#include <vector>
namespace mamba::util
{
/**
* A sorted vector behaving like a set.
*
* Like, ``std::set``, uniqueness is determined by using the equivalence relation.
* In imprecise terms, two objects ``a`` and ``b`` are considered equivalent if neither
* compares less than the other: ``!comp(a, b) && !comp(b, a)``
*
* @todo C++23 This is implemented in <flat_set>
*/
template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
class flat_set : private std::vector<Key, Allocator>
{
public:
using Base = std::vector<Key, Allocator>;
using typename Base::allocator_type;
using typename Base::const_iterator;
using typename Base::const_reverse_iterator;
using typename Base::size_type;
using typename Base::value_type;
using key_compare = Compare;
using value_compare = Compare;
using Base::cbegin;
using Base::cend;
using Base::crbegin;
using Base::crend;
using Base::clear;
using Base::empty;
using Base::reserve;
using Base::size;
flat_set() = default;
flat_set(
std::initializer_list<value_type> il,
key_compare compare = key_compare(),
const allocator_type& alloc = allocator_type()
);
template <typename InputIterator>
flat_set(
InputIterator first,
InputIterator last,
key_compare compare = key_compare(),
const allocator_type& alloc = Allocator()
);
flat_set(const flat_set&) = default;
flat_set(flat_set&&) = default;
explicit flat_set(std::vector<Key, Allocator>&& other, key_compare compare = key_compare());
explicit flat_set(const std::vector<Key, Allocator>& other, key_compare compare = key_compare());
flat_set& operator=(const flat_set&) = default;
flat_set& operator=(flat_set&&) = default;
bool contains(const value_type&) const;
const value_type& front() const noexcept;
const value_type& back() const noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
/** Insert an element in the set.
*
* Like std::vector and unlike std::set, inserting an element invalidates iterators.
*/
std::pair<const_iterator, bool> insert(value_type&& value);
std::pair<const_iterator, bool> insert(const value_type& value);
template <typename InputIterator>
void insert(InputIterator first, InputIterator last);
const_iterator erase(const_iterator pos);
const_iterator erase(const_iterator first, const_iterator last);
size_type erase(const value_type& value);
private:
key_compare m_compare;
bool key_eq(const value_type& a, const value_type& b) const;
template <typename U>
std::pair<const_iterator, bool> insert_impl(U&& value);
void sort_and_remove_duplicates();
template <typename K, typename C, typename A>
friend bool operator==(const flat_set<K, C, A>& lhs, const flat_set<K, C, A>& rhs);
};
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
flat_set(std::initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> flat_set<Key, Compare, Allocator>;
template <
class InputIt,
class Comp = std::less<typename std::iterator_traits<InputIt>::value_type>,
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
flat_set(InputIt, InputIt, Comp = Comp(), Alloc = Alloc())
-> flat_set<typename std::iterator_traits<InputIt>::value_type, Comp, Alloc>;
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
flat_set(std::vector<Key, Allocator>&&, Compare compare = Compare())
-> flat_set<Key, Compare, Allocator>;
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
flat_set(const std::vector<Key, Allocator>&, Compare compare = Compare())
-> flat_set<Key, Compare, Allocator>;
template <typename Key, typename Compare, typename Allocator>
bool
operator==(const flat_set<Key, Compare, Allocator>& lhs, const flat_set<Key, Compare, Allocator>& rhs);
template <typename Key, typename Compare, typename Allocator>
bool
operator!=(const flat_set<Key, Compare, Allocator>& lhs, const flat_set<Key, Compare, Allocator>& rhs);
/*******************************
* vector_set Implementation *
*******************************/
template <typename K, typename C, typename A>
flat_set<K, C, A>::flat_set(
std::initializer_list<value_type> il,
key_compare compare,
const allocator_type& alloc
)
: Base(std::move(il), alloc)
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
template <typename InputIterator>
flat_set<K, C, A>::flat_set(
InputIterator first,
InputIterator last,
key_compare compare,
const allocator_type& alloc
)
: Base(first, last, alloc)
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
flat_set<K, C, A>::flat_set(std::vector<K, A>&& other, C compare)
: Base(std::move(other))
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
flat_set<K, C, A>::flat_set(const std::vector<K, A>& other, C compare)
: Base(std::move(other))
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::contains(const value_type& value) const -> bool
{
return std::binary_search(begin(), end(), value);
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::front() const noexcept -> const value_type&
{
return Base::front();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::back() const noexcept -> const value_type&
{
return Base::back();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::begin() const noexcept -> const_iterator
{
return Base::begin();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::end() const noexcept -> const_iterator
{
return Base::end();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::rbegin() const noexcept -> const_reverse_iterator
{
return Base::rbegin();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::rend() const noexcept -> const_reverse_iterator
{
return Base::rend();
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::insert(const value_type& value) -> std::pair<const_iterator, bool>
{
return insert_impl(value);
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::insert(value_type&& value) -> std::pair<const_iterator, bool>
{
return insert_impl(std::move(value));
}
template <typename K, typename C, typename A>
bool flat_set<K, C, A>::key_eq(const value_type& a, const value_type& b) const
{
return !m_compare(a, b) && !m_compare(b, a);
}
template <typename K, typename C, typename A>
void flat_set<K, C, A>::sort_and_remove_duplicates()
{
std::sort(Base::begin(), Base::end(), m_compare);
auto is_eq = [this](const value_type& a, const value_type& b) { return key_eq(a, b); };
Base::erase(std::unique(Base::begin(), Base::end(), is_eq), Base::end());
}
template <typename K, typename C, typename A>
template <typename InputIterator>
void flat_set<K, C, A>::insert(InputIterator first, InputIterator last)
{
Base::insert(Base::end(), first, last);
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
template <typename U>
auto flat_set<K, C, A>::insert_impl(U&& value) -> std::pair<const_iterator, bool>
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it != end()) && (key_eq(*it, value)))
{
return { it, false };
}
it = Base::insert(it, std::forward<U>(value));
return { it, true };
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::erase(const_iterator pos) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(pos);
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::erase(const_iterator first, const_iterator last) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(first, last);
}
template <typename K, typename C, typename A>
auto flat_set<K, C, A>::erase(const value_type& value) -> size_type
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it == end()) || (!(key_eq(*it, value))))
{
return 0;
}
erase(it);
return 1;
}
template <typename K, typename C, typename A>
bool operator==(const flat_set<K, C, A>& lhs, const flat_set<K, C, A>& rhs)
{
auto is_eq = [&lhs](const auto& a, const auto& b) { return lhs.key_eq(a, b); };
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), is_eq);
}
template <typename K, typename C, typename A>
bool operator!=(const flat_set<K, C, A>& lhs, const flat_set<K, C, A>& rhs)
{
return !(lhs == rhs);
}
}
#endif

View File

@ -4,8 +4,8 @@
//
// The full license is in the file LICENSE, distributed with this software.
#ifndef MAMBA_CORE_GRAPH_UTIL_HPP
#define MAMBA_CORE_GRAPH_UTIL_HPP
#ifndef MAMBA_UTIL_GRAPH_HPP
#define MAMBA_UTIL_GRAPH_HPP
#include <algorithm>
#include <functional>
@ -14,127 +14,10 @@
#include <utility>
#include <vector>
namespace mamba
#include "flat_set.hpp"
namespace mamba::util
{
/**
* A sorted vector behaving like a set.
*
* Like, ``std::set``, uniqueness is determined by using the equivalence relation.
* In imprecise terms, two objects ``a`` and ``b`` are considered equivalent if neither
* compares less than the other: ``!comp(a, b) && !comp(b, a)``
*/
template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
class vector_set : private std::vector<Key, Allocator>
{
public:
using Base = std::vector<Key, Allocator>;
using typename Base::allocator_type;
using typename Base::const_iterator;
using typename Base::const_reverse_iterator;
using typename Base::size_type;
using typename Base::value_type;
using key_compare = Compare;
using value_compare = Compare;
using Base::cbegin;
using Base::cend;
using Base::crbegin;
using Base::crend;
using Base::clear;
using Base::empty;
using Base::reserve;
using Base::size;
vector_set() = default;
vector_set(
std::initializer_list<value_type> il,
key_compare compare = key_compare(),
const allocator_type& alloc = allocator_type()
);
template <typename InputIterator>
vector_set(
InputIterator first,
InputIterator last,
key_compare compare = key_compare(),
const allocator_type& alloc = Allocator()
);
vector_set(const vector_set&) = default;
vector_set(vector_set&&) = default;
explicit vector_set(std::vector<Key, Allocator>&& other, key_compare compare = key_compare());
explicit vector_set(const std::vector<Key, Allocator>& other, key_compare compare = key_compare());
vector_set& operator=(const vector_set&) = default;
vector_set& operator=(vector_set&&) = default;
bool contains(const value_type&) const;
const value_type& front() const noexcept;
const value_type& back() const noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
/** Insert an element in the set.
*
* Like std::vector and unlike std::set, inserting an element invalidates iterators.
*/
std::pair<const_iterator, bool> insert(value_type&& value);
std::pair<const_iterator, bool> insert(const value_type& value);
template <typename InputIterator>
void insert(InputIterator first, InputIterator last);
const_iterator erase(const_iterator pos);
const_iterator erase(const_iterator first, const_iterator last);
size_type erase(const value_type& value);
private:
key_compare m_compare;
bool key_eq(const value_type& a, const value_type& b) const;
template <typename U>
std::pair<const_iterator, bool> insert_impl(U&& value);
void sort_and_remove_duplicates();
template <typename K, typename C, typename A>
friend bool operator==(const vector_set<K, C, A>& lhs, const vector_set<K, C, A>& rhs);
};
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
vector_set(std::initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> vector_set<Key, Compare, Allocator>;
template <
class InputIt,
class Comp = std::less<typename std::iterator_traits<InputIt>::value_type>,
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
vector_set(InputIt, InputIt, Comp = Comp(), Alloc = Alloc())
-> vector_set<typename std::iterator_traits<InputIt>::value_type, Comp, Alloc>;
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
vector_set(std::vector<Key, Allocator>&&, Compare compare = Compare())
-> vector_set<Key, Compare, Allocator>;
template <class Key, class Compare = std::less<Key>, class Allocator = std::allocator<Key>>
vector_set(const std::vector<Key, Allocator>&, Compare compare = Compare())
-> vector_set<Key, Compare, Allocator>;
template <typename Key, typename Compare, typename Allocator>
bool operator==(
const vector_set<Key, Compare, Allocator>& lhs,
const vector_set<Key, Compare, Allocator>& rhs
);
template <typename Key, typename Compare, typename Allocator>
bool operator!=(
const vector_set<Key, Compare, Allocator>& lhs,
const vector_set<Key, Compare, Allocator>& rhs
);
// Simplified implementation of a directed graph
template <typename Node, typename Derived>
class DiGraphBase
@ -144,7 +27,7 @@ namespace mamba
using node_t = Node;
using node_id = std::size_t;
using node_map = std::map<node_id, node_t>;
using node_id_list = vector_set<node_id>;
using node_id_list = flat_set<node_id>;
using adjacency_list = std::vector<node_id_list>;
node_id add_node(const node_t& value);
@ -337,180 +220,6 @@ namespace mamba
{
};
/*******************************
* vector_set Implementation *
*******************************/
template <typename K, typename C, typename A>
vector_set<K, C, A>::vector_set(
std::initializer_list<value_type> il,
key_compare compare,
const allocator_type& alloc
)
: Base(std::move(il), alloc)
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
template <typename InputIterator>
vector_set<K, C, A>::vector_set(
InputIterator first,
InputIterator last,
key_compare compare,
const allocator_type& alloc
)
: Base(first, last, alloc)
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
vector_set<K, C, A>::vector_set(std::vector<K, A>&& other, C compare)
: Base(std::move(other))
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
vector_set<K, C, A>::vector_set(const std::vector<K, A>& other, C compare)
: Base(std::move(other))
, m_compare(std::move(compare))
{
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::contains(const value_type& value) const -> bool
{
return std::binary_search(begin(), end(), value);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::front() const noexcept -> const value_type&
{
return Base::front();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::back() const noexcept -> const value_type&
{
return Base::back();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::begin() const noexcept -> const_iterator
{
return Base::begin();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::end() const noexcept -> const_iterator
{
return Base::end();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::rbegin() const noexcept -> const_reverse_iterator
{
return Base::rbegin();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::rend() const noexcept -> const_reverse_iterator
{
return Base::rend();
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::insert(const value_type& value) -> std::pair<const_iterator, bool>
{
return insert_impl(value);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::insert(value_type&& value) -> std::pair<const_iterator, bool>
{
return insert_impl(std::move(value));
}
template <typename K, typename C, typename A>
bool vector_set<K, C, A>::key_eq(const value_type& a, const value_type& b) const
{
return !m_compare(a, b) && !m_compare(b, a);
}
template <typename K, typename C, typename A>
void vector_set<K, C, A>::sort_and_remove_duplicates()
{
std::sort(Base::begin(), Base::end(), m_compare);
auto is_eq = [this](const value_type& a, const value_type& b) { return key_eq(a, b); };
Base::erase(std::unique(Base::begin(), Base::end(), is_eq), Base::end());
}
template <typename K, typename C, typename A>
template <typename InputIterator>
void vector_set<K, C, A>::insert(InputIterator first, InputIterator last)
{
Base::insert(Base::end(), first, last);
sort_and_remove_duplicates();
}
template <typename K, typename C, typename A>
template <typename U>
auto vector_set<K, C, A>::insert_impl(U&& value) -> std::pair<const_iterator, bool>
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it != end()) && (key_eq(*it, value)))
{
return { it, false };
}
it = Base::insert(it, std::forward<U>(value));
return { it, true };
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const_iterator pos) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(pos);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const_iterator first, const_iterator last) -> const_iterator
{
// No need to sort or remove duplicates again
return Base::erase(first, last);
}
template <typename K, typename C, typename A>
auto vector_set<K, C, A>::erase(const value_type& value) -> size_type
{
auto it = std::lower_bound(begin(), end(), value, m_compare);
if ((it == end()) || (!(key_eq(*it, value))))
{
return 0;
}
erase(it);
return 1;
}
template <typename K, typename C, typename A>
bool operator==(const vector_set<K, C, A>& lhs, const vector_set<K, C, A>& rhs)
{
auto is_eq = [&lhs](const auto& a, const auto& b) { return lhs.key_eq(a, b); };
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), is_eq);
}
template <typename K, typename C, typename A>
bool operator!=(const vector_set<K, C, A>& lhs, const vector_set<K, C, A>& rhs)
{
return !(lhs == rhs);
}
/********************************
* DiGraphBase Implementation *
********************************/
@ -950,7 +659,5 @@ namespace mamba
{
return edge({ from, to });
}
} // namespace mamba
#endif // INCLUDE_GRAPH_UTIL_HPP
}
#endif

View File

@ -20,9 +20,9 @@ extern "C" // Incomplete header
#include "mamba/core/context.hpp"
#include "mamba/core/output.hpp"
#include "mamba/core/pool.hpp"
#include "mamba/core/util_cast.hpp"
#include "mamba/core/util_compare.hpp"
#include "mamba/core/util_string.hpp"
#include "mamba/util/cast.hpp"
#include "mamba/util/compare.hpp"
#include "solv-cpp/queue.hpp"
namespace mamba

View File

@ -9,7 +9,7 @@
#include "mamba/core/context.hpp"
#include "mamba/core/execution.hpp"
#include "mamba/core/util_compare.hpp"
#include "mamba/util/compare.hpp"
#include "progress_bar_impl.hpp"

View File

@ -582,11 +582,11 @@ namespace mamba
using node_id = ProblemsGraph::node_id;
const auto& g = pbs.graph();
auto is_leaf = [&g](node_id n) -> bool { return g.successors(n).size() == 0; };
auto leaves_from = [&g](node_id n) -> vector_set<node_id>
auto leaves_from = [&g](node_id n) -> util::flat_set<node_id>
{
auto leaves = std::vector<node_id>();
g.for_each_leaf_id_from(n, [&leaves](node_id m) { leaves.push_back(m); });
return vector_set(std::move(leaves));
return util::flat_set(std::move(leaves));
};
return (node_name(g.node(n1)) == node_name(g.node(n2)))
// Merging conflicts would be counter-productive in explaining problems
@ -1126,7 +1126,7 @@ namespace mamba
using TreeNodeList = std::vector<TreeNode>;
using TreeNodeIter = typename TreeNodeList::iterator;
vector_set<node_id> leaf_installables = {};
util::flat_set<node_id> leaf_installables = {};
std::map<node_id, std::optional<Status>> m_node_visited = {};
const CompressedProblemsGraph& m_pbs;

View File

@ -51,10 +51,10 @@ extern "C"
#include "mamba/core/thread_utils.hpp"
#include "mamba/core/url.hpp"
#include "mamba/core/util.hpp"
#include "mamba/core/util_compare.hpp"
#include "mamba/core/util_os.hpp"
#include "mamba/core/util_random.hpp"
#include "mamba/core/util_string.hpp"
#include "mamba/util/compare.hpp"
namespace mamba
{

View File

@ -12,9 +12,9 @@
#include <tuple>
#include "mamba/core/error_handling.hpp"
#include "mamba/core/util_cast.hpp"
#include "mamba/core/util_string.hpp"
#include "mamba/specs/version.hpp"
#include "mamba/util/cast.hpp"
namespace mamba::specs
{

View File

@ -13,6 +13,11 @@ mamba_target_add_compile_warnings(testing_libmamba_lock WARNING_AS_ERROR ${MAMBA
set(LIBMAMBA_TEST_SRCS
# C++ wrapping of libsolv
src/solv-cpp/test_queue.cpp
# Utility library
src/util/test_flat_set.cpp
src/util/test_graph.cpp
src/util/test_compare.cpp
src/util/test_cast.cpp
# Implementation of version and matching specs
src/specs/test_version.cpp
@ -35,10 +40,7 @@ set(LIBMAMBA_TEST_SRCS
src/core/test_validate.cpp
src/core/test_virtual_packages.cpp
src/core/test_util.cpp
src/core/test_util_cast.cpp
src/core/test_util_compare.cpp
src/core/test_util_string.cpp
src/core/test_util_graph.cpp
src/core/test_env_lockfile.cpp
src/core/test_execution.cpp
src/core/test_invoke.cpp

View File

@ -1,94 +0,0 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <cmath>
#include <limits>
#include <utility>
#include <gtest/gtest.h>
#include "mamba/core/util_cast.hpp"
namespace mamba::util
{
template <typename T>
struct cast_valid : ::testing::Test
{
using First = typename T::first_type;
using Second = typename T::second_type;
};
using WidenTypes = ::testing::Types<
// integers
std::pair<char, int>,
std::pair<unsigned char, int>,
std::pair<unsigned char, unsigned int>,
std::pair<int, long long int>,
std::pair<unsigned int, long long int>,
std::pair<unsigned int, unsigned long long int>,
// floats
std::pair<float, double>,
// Mixed
std::pair<char, float>,
std::pair<unsigned char, float>,
std::pair<int, double>,
std::pair<unsigned int, double>>;
TYPED_TEST_SUITE(cast_valid, WidenTypes);
TYPED_TEST(cast_valid, checked_exact_num_cast_widen)
{
using From = typename TestFixture::First;
using To = typename TestFixture::Second;
static constexpr auto from_lowest = std::numeric_limits<From>::lowest();
static constexpr auto from_max = std::numeric_limits<From>::max();
EXPECT_EQ(safe_num_cast<To>(From(0)), To(0));
EXPECT_EQ(safe_num_cast<To>(From(1)), To(1));
EXPECT_EQ(safe_num_cast<To>(from_lowest), static_cast<To>(from_lowest));
EXPECT_EQ(safe_num_cast<To>(from_max), static_cast<To>(from_max));
}
TYPED_TEST(cast_valid, checked_exact_num_cast_narrow)
{
using From = typename TestFixture::Second; // inversed
using To = typename TestFixture::First; // inversed
EXPECT_EQ(safe_num_cast<To>(From(0)), To(0));
EXPECT_EQ(safe_num_cast<To>(From(1)), To(1));
}
template <typename T>
struct cast_overflow_lowest : ::testing::Test
{
using First = typename T::first_type;
using Second = typename T::second_type;
};
using OverflowLowestTypes = ::testing::Types<
// integers
std::pair<char, unsigned char>,
std::pair<char, unsigned int>,
std::pair<int, char>,
std::pair<int, unsigned long long int>,
// floats
std::pair<double, float>,
// mixed
std::pair<double, int>,
std::pair<float, char>>;
TYPED_TEST_SUITE(cast_overflow_lowest, OverflowLowestTypes);
TYPED_TEST(cast_overflow_lowest, checked_exact_num_cast)
{
using From = typename TestFixture::First;
using To = typename TestFixture::Second;
static constexpr auto from_lowest = std::numeric_limits<From>::lowest();
EXPECT_THROW(safe_num_cast<To>(from_lowest), std::overflow_error);
}
TEST(cast, precision)
{
EXPECT_THROW(safe_num_cast<int>(1.1), std::runtime_error);
EXPECT_THROW(safe_num_cast<float>(std::nextafter(double(1), 2)), std::runtime_error);
}
}

View File

@ -1,87 +0,0 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <limits>
#include <utility>
#include <gtest/gtest.h>
#include "mamba/core/util_compare.hpp"
namespace mamba::util
{
TEST(compare, equal)
{
EXPECT_TRUE(cmp_equal(char{ 0 }, char{ 0 }));
EXPECT_TRUE(cmp_equal(char{ 1 }, char{ 1 }));
EXPECT_TRUE(cmp_equal(char{ -1 }, char{ -1 }));
EXPECT_TRUE(cmp_equal(int{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(int{ 1 }, int{ 1 }));
EXPECT_TRUE(cmp_equal(int{ -1 }, int{ -1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, std::size_t{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_equal(char{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(char{ 1 }, int{ 1 }));
EXPECT_TRUE(cmp_equal(char{ -1 }, int{ -1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, char{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, char{ 1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 0 }, char{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 1 }, char{ -1 }));
EXPECT_FALSE(cmp_equal(int{ 0 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(int{ -1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 0 }, std::size_t{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 0 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_equal(char{ -1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_equal(static_cast<std::size_t>(-1), int{ -1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_equal(std::numeric_limits<std::size_t>::max(), int{ 0 }));
}
TEST(compare, less)
{
EXPECT_TRUE(cmp_less(char{ 0 }, char{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, char{ 0 }));
EXPECT_TRUE(cmp_less(int{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(int{ -1 }, int{ 1 }));
EXPECT_TRUE(cmp_less(std::size_t{ 0 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_less(char{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, int{ 0 }));
EXPECT_TRUE(cmp_less(char{ -1 }, int{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_less(std::size_t{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(std::numeric_limits<int>::min(), char{ 0 }));
EXPECT_TRUE(cmp_less(std::numeric_limits<int>::min(), std::size_t{ 0 }));
EXPECT_TRUE(cmp_less(int{ -1 }, std::numeric_limits<std::size_t>::max()));
EXPECT_TRUE(cmp_less(std::size_t{ 1 }, std::numeric_limits<int>::max()));
EXPECT_FALSE(cmp_less(char{ 1 }, char{ 0 }));
EXPECT_FALSE(cmp_less(char{ 1 }, char{ 1 }));
EXPECT_FALSE(cmp_less(char{ 0 }, char{ -1 }));
EXPECT_FALSE(cmp_less(int{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_less(int{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_less(std::size_t{ 1 }, std::size_t{ 0 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ 1 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_less(char{ 0 }, int{ -1 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ -11 }));
EXPECT_FALSE(cmp_less(std::size_t{ 1 }, char{ -1 }));
EXPECT_FALSE(cmp_less(int{ 1 }, std::size_t{ 0 }));
EXPECT_FALSE(cmp_less(char{ 0 }, std::numeric_limits<int>::min()));
EXPECT_FALSE(cmp_less(std::size_t{ 0 }, std::numeric_limits<int>::min()));
EXPECT_FALSE(cmp_less(std::numeric_limits<std::size_t>::max(), int{ -1 }));
EXPECT_FALSE(cmp_less(std::numeric_limits<int>::max(), std::size_t{ 1 }));
}
}

View File

@ -1,425 +0,0 @@
#include <type_traits>
#include <gtest/gtest.h>
#include "mamba/core/util_graph.hpp"
namespace mamba
{
TEST(vector_set, constructor)
{
const auto s1 = vector_set<int>();
EXPECT_EQ(s1.size(), 0);
auto s2 = vector_set<int>({ 1, 2 });
EXPECT_EQ(s2.size(), 2);
const auto s3 = vector_set<int>{ s2 };
EXPECT_EQ(s3.size(), 2);
const auto s4 = vector_set<int>{ std::move(s2) };
EXPECT_EQ(s4.size(), 2);
// CTAD
auto s5 = vector_set({ 1, 2 });
EXPECT_EQ(s5.size(), 2);
static_assert(std::is_same_v<decltype(s5)::value_type, int>);
auto s6 = vector_set(s5.begin(), s5.end(), std::greater{});
EXPECT_EQ(s6.size(), s5.size());
static_assert(std::is_same_v<decltype(s6)::value_type, decltype(s5)::value_type>);
}
TEST(vector_set, equality)
{
EXPECT_EQ(vector_set<int>(), vector_set<int>());
EXPECT_EQ(vector_set<int>({ 1, 2 }), vector_set<int>({ 1, 2 }));
EXPECT_EQ(vector_set<int>({ 1, 2 }), vector_set<int>({ 2, 1 }));
EXPECT_EQ(vector_set<int>({ 1, 2, 1 }), vector_set<int>({ 2, 2, 1 }));
EXPECT_NE(vector_set<int>({ 1, 2 }), vector_set<int>({ 1, 2, 3 }));
EXPECT_NE(vector_set<int>({ 2 }), vector_set<int>({}));
}
TEST(vector_set, insert)
{
auto s = vector_set<int>();
s.insert(33);
EXPECT_EQ(s, vector_set<int>({ 33 }));
s.insert(33);
s.insert(17);
EXPECT_EQ(s, vector_set<int>({ 17, 33 }));
s.insert(22);
EXPECT_EQ(s, vector_set<int>({ 17, 22, 33 }));
s.insert(33);
EXPECT_EQ(s, vector_set<int>({ 17, 22, 33 }));
auto v = std::vector<int>({ 33, 22, 17, 0 });
s.insert(v.begin(), v.end());
EXPECT_EQ(s, vector_set<int>({ 0, 17, 22, 33 }));
}
TEST(vector_set, erase)
{
auto s = vector_set<int>{ 4, 3, 2, 1 };
EXPECT_EQ(s.erase(4), 1);
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
EXPECT_EQ(s.erase(4), 0);
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
const auto it = s.erase(s.begin());
EXPECT_EQ(it, s.begin());
EXPECT_EQ(s, vector_set<int>({ 2, 3 }));
}
TEST(vector_set, contains)
{
const auto s = vector_set<int>({ 1, 3, 4, 5 });
EXPECT_FALSE(s.contains(0));
EXPECT_TRUE(s.contains(1));
EXPECT_FALSE(s.contains(2));
EXPECT_TRUE(s.contains(3));
EXPECT_TRUE(s.contains(4));
EXPECT_TRUE(s.contains(5));
EXPECT_FALSE(s.contains(6));
}
TEST(vector_set, key_compare)
{
auto s = vector_set({ 1, 3, 4, 5 }, std::greater{});
EXPECT_EQ(s.front(), 5);
EXPECT_EQ(s.back(), 1);
s.insert(6);
EXPECT_EQ(s.front(), 6);
}
DiGraph<double> build_graph()
{
DiGraph<double> g;
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
const auto n3 = g.add_node(3.5);
const auto n4 = g.add_node(4.5);
const auto n5 = g.add_node(5.5);
const auto n6 = g.add_node(6.5);
g.add_edge(n0, n1);
g.add_edge(n0, n2);
g.add_edge(n1, n3);
g.add_edge(n1, n4);
g.add_edge(n2, n3);
g.add_edge(n2, n5);
g.add_edge(n3, n6);
return g;
}
DiGraph<double> build_cyclic_graph()
{
DiGraph<double> g;
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
const auto n3 = g.add_node(3.5);
const auto n4 = g.add_node(4.5);
g.add_edge(n0, n1);
g.add_edge(n0, n3);
g.add_edge(n1, n2);
g.add_edge(n2, n0);
g.add_edge(n3, n4);
return g;
}
DiGraph<double, const char*> build_edge_data_graph()
{
auto g = DiGraph<double, const char*>{};
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
g.add_edge(n0, n1, "n0->n1");
g.add_edge(n1, n2, "n1->n2");
return g;
}
template <class G>
class test_visitor : private default_visitor<G>
{
public:
using base_type = default_visitor<G>;
using node_id = typename base_type::node_id;
using predecessor_map = std::map<node_id, node_id>;
using edge_map = std::map<node_id, node_id>;
using base_type::finish_edge;
using base_type::finish_node;
using base_type::start_edge;
using base_type::start_node;
using base_type::tree_edge;
void back_edge(node_id from, node_id to, const G&)
{
m_back_edges[from] = to;
}
void forward_or_cross_edge(node_id from, node_id to, const G&)
{
m_cross_edges[from] = to;
}
const edge_map& get_back_edge_map() const
{
return m_back_edges;
}
const edge_map get_cross_edge_map() const
{
return m_cross_edges;
}
private:
edge_map m_back_edges;
edge_map m_cross_edges;
};
TEST(graph, build_simple)
{
const auto g = build_graph();
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 7ul);
EXPECT_EQ(g.number_of_edges(), 7ul);
EXPECT_EQ(
g.nodes(),
node_map(
{ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 }, { 3, 3.5 }, { 4, 4.5 }, { 5, 5.5 }, { 6, 6.5 } }
)
);
EXPECT_EQ(g.successors(0u), node_id_list({ 1u, 2u }));
EXPECT_EQ(g.successors(1u), node_id_list({ 3u, 4u }));
EXPECT_EQ(g.successors(2u), node_id_list({ 3u, 5u }));
EXPECT_EQ(g.successors(3u), node_id_list({ 6u }));
EXPECT_EQ(g.predecessors(0u), node_id_list());
EXPECT_EQ(g.predecessors(1u), node_id_list({ 0u }));
EXPECT_EQ(g.predecessors(2u), node_id_list({ 0u }));
EXPECT_EQ(g.predecessors(3u), node_id_list({ 1u, 2u }));
}
TEST(graph, build_edge_data)
{
const auto g = build_edge_data_graph();
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 3ul);
EXPECT_EQ(g.number_of_edges(), 2ul);
EXPECT_EQ(g.nodes(), node_map({ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 } }));
EXPECT_EQ(g.successors(0ul), node_id_list({ 1ul }));
EXPECT_EQ(g.successors(1ul), node_id_list({ 2ul }));
EXPECT_EQ(g.successors(2ul), node_id_list());
EXPECT_EQ(g.predecessors(0ul), node_id_list());
EXPECT_EQ(g.predecessors(1ul), node_id_list({ 0ul }));
EXPECT_EQ(g.predecessors(2ul), node_id_list({ 1ul }));
using edge_map = decltype(g)::edge_map;
EXPECT_EQ(g.edges(), edge_map({ { { 0ul, 1ul }, "n0->n1" }, { { 1ul, 2ul }, "n1->n2" } }));
}
TEST(graph, has_node_edge)
{
const auto g = build_graph();
EXPECT_TRUE(g.has_node(1ul));
EXPECT_TRUE(g.has_node(4ul));
EXPECT_FALSE(g.has_node(g.number_of_nodes()));
EXPECT_TRUE(g.has_edge(1ul, 4ul));
EXPECT_FALSE(g.has_edge(4ul, 1ul));
EXPECT_TRUE(g.has_edge(0ul, 2ul));
EXPECT_FALSE(g.has_edge(0ul, 5ul));
EXPECT_FALSE(g.has_edge(0ul, g.number_of_nodes()));
EXPECT_FALSE(g.has_edge(g.number_of_nodes(), 1ul));
}
TEST(graph, data_modifier)
{
auto g = build_edge_data_graph();
static constexpr auto new_node_val = -1.5;
EXPECT_NE(g.node(0ul), new_node_val);
g.node(0ul) = new_node_val;
EXPECT_EQ(g.node(0ul), new_node_val);
static constexpr auto new_edge_val = "data";
EXPECT_NE(g.edge(0ul, 1ul), new_edge_val);
g.edge(0ul, 1ul) = new_edge_val;
EXPECT_EQ(g.edge(0ul, 1ul), new_edge_val);
}
TEST(graph, remove_edge)
{
auto g = build_edge_data_graph();
const auto n_edges_init = g.number_of_edges();
ASSERT_FALSE(g.has_edge(1, 0));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_FALSE(g.remove_edge(1, 0));
EXPECT_EQ(g.number_of_edges(), n_edges_init);
EXPECT_FALSE(g.has_edge(1, 0));
EXPECT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_TRUE(g.remove_edge(0, 1));
EXPECT_EQ(g.number_of_edges(), n_edges_init - 1u);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_EQ(g.edges().count({ 0, 1 }), 0);
}
TEST(graph, remove_node)
{
auto g = build_edge_data_graph();
ASSERT_TRUE(g.has_node(0));
ASSERT_TRUE(g.has_node(1));
ASSERT_TRUE(g.has_node(2));
ASSERT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(1, 2));
const auto n_edges_init = g.number_of_edges();
const auto n_nodes_init = g.number_of_nodes();
const auto node_1_degree = g.in_degree(1) + g.out_degree(1);
EXPECT_TRUE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
EXPECT_TRUE(g.has_node(0));
EXPECT_FALSE(g.has_node(1));
EXPECT_TRUE(g.has_node(2));
EXPECT_EQ(g.in_degree(1), 0);
EXPECT_EQ(g.out_degree(1), 0);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_FALSE(g.has_edge(1, 2));
g.for_each_node_id([&](auto id) { EXPECT_TRUE(g.has_node(id)); });
EXPECT_FALSE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
const auto new_id = g.add_node(.7);
EXPECT_EQ(new_id, n_nodes_init); // Ids are not invalidated so new id is used
EXPECT_FALSE(g.has_node(1)); // Old id is not being confused
EXPECT_EQ(g.number_of_nodes(), n_nodes_init);
}
TEST(graph, degree)
{
const auto g = build_graph();
EXPECT_EQ(g.out_degree(0), 2);
EXPECT_EQ(g.out_degree(1), 2);
EXPECT_EQ(g.out_degree(6), 0);
EXPECT_EQ(g.in_degree(0), 0);
EXPECT_EQ(g.in_degree(3), 2);
EXPECT_EQ(g.in_degree(6), 1);
}
TEST(graph, for_each_node)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_nodes = 0;
g.for_each_node_id(
[&](node_id id)
{
EXPECT_TRUE(g.has_node(id));
++n_nodes;
}
);
EXPECT_EQ(n_nodes, g.number_of_nodes());
}
TEST(graph, for_each_edge)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_edges = 0;
g.for_each_edge_id(
[&g, &n_edges](node_id from, node_id to)
{
EXPECT_TRUE(g.has_edge(from, to));
++n_edges;
}
);
EXPECT_EQ(n_edges, g.number_of_edges());
}
TEST(graph, for_each_leaf)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf_id([&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 4ul, 5ul, 6ul }));
}
TEST(graph, for_each_leaf_from)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 5ul, 6ul }));
}
TEST(graph, for_each_root)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto roots = node_id_list();
g.for_each_root_id([&roots](node_id root) { roots.insert(root); });
EXPECT_EQ(roots, node_id_list({ 0ul }));
}
TEST(graph, for_each_root_from)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_root_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 0ul }));
}
TEST(graph, depth_first_search)
{
const auto g = build_graph();
test_visitor<DiGraph<double>> vis;
g.depth_first_search(vis);
EXPECT_TRUE(vis.get_back_edge_map().empty());
EXPECT_EQ(vis.get_cross_edge_map().find(2u)->second, 3u);
}
TEST(graph, dfs_cyclic)
{
const auto g = build_cyclic_graph();
test_visitor<DiGraph<double>> vis;
g.depth_first_search(vis);
EXPECT_EQ(vis.get_back_edge_map().find(2u)->second, 0u);
EXPECT_TRUE(vis.get_cross_edge_map().empty());
}
TEST(graph, dfs_empty)
{
DiGraph<int> g;
test_visitor<DiGraph<int>> vis;
g.depth_first_search(vis);
EXPECT_TRUE(vis.get_back_edge_map().empty());
EXPECT_TRUE(vis.get_cross_edge_map().empty());
}
TEST(graph_algorithm, is_reachable)
{
auto graph = build_graph();
EXPECT_TRUE(is_reachable(graph, 0, 6));
EXPECT_FALSE(is_reachable(graph, 6, 0));
}
} // namespace mamba

View File

@ -0,0 +1,93 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <cmath>
#include <limits>
#include <utility>
#include <gtest/gtest.h>
#include "mamba/util/cast.hpp"
using namespace mamba::util;
template <typename T>
struct cast_valid : ::testing::Test
{
using First = typename T::first_type;
using Second = typename T::second_type;
};
using WidenTypes = ::testing::Types<
// integers
std::pair<char, int>,
std::pair<unsigned char, int>,
std::pair<unsigned char, unsigned int>,
std::pair<int, long long int>,
std::pair<unsigned int, long long int>,
std::pair<unsigned int, unsigned long long int>,
// floats
std::pair<float, double>,
// Mixed
std::pair<char, float>,
std::pair<unsigned char, float>,
std::pair<int, double>,
std::pair<unsigned int, double>>;
TYPED_TEST_SUITE(cast_valid, WidenTypes);
TYPED_TEST(cast_valid, checked_exact_num_cast_widen)
{
using From = typename TestFixture::First;
using To = typename TestFixture::Second;
static constexpr auto from_lowest = std::numeric_limits<From>::lowest();
static constexpr auto from_max = std::numeric_limits<From>::max();
EXPECT_EQ(safe_num_cast<To>(From(0)), To(0));
EXPECT_EQ(safe_num_cast<To>(From(1)), To(1));
EXPECT_EQ(safe_num_cast<To>(from_lowest), static_cast<To>(from_lowest));
EXPECT_EQ(safe_num_cast<To>(from_max), static_cast<To>(from_max));
}
TYPED_TEST(cast_valid, checked_exact_num_cast_narrow)
{
using From = typename TestFixture::Second; // inversed
using To = typename TestFixture::First; // inversed
EXPECT_EQ(safe_num_cast<To>(From(0)), To(0));
EXPECT_EQ(safe_num_cast<To>(From(1)), To(1));
}
template <typename T>
struct cast_overflow_lowest : ::testing::Test
{
using First = typename T::first_type;
using Second = typename T::second_type;
};
using OverflowLowestTypes = ::testing::Types<
// integers
std::pair<char, unsigned char>,
std::pair<char, unsigned int>,
std::pair<int, char>,
std::pair<int, unsigned long long int>,
// floats
std::pair<double, float>,
// mixed
std::pair<double, int>,
std::pair<float, char>>;
TYPED_TEST_SUITE(cast_overflow_lowest, OverflowLowestTypes);
TYPED_TEST(cast_overflow_lowest, checked_exact_num_cast)
{
using From = typename TestFixture::First;
using To = typename TestFixture::Second;
static constexpr auto from_lowest = std::numeric_limits<From>::lowest();
EXPECT_THROW(safe_num_cast<To>(from_lowest), std::overflow_error);
}
TEST(cast, precision)
{
EXPECT_THROW(safe_num_cast<int>(1.1), std::runtime_error);
EXPECT_THROW(safe_num_cast<float>(std::nextafter(double(1), 2)), std::runtime_error);
}

View File

@ -0,0 +1,86 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <limits>
#include <utility>
#include <gtest/gtest.h>
#include "mamba/util/compare.hpp"
using namespace mamba::util;
TEST(compare, equal)
{
EXPECT_TRUE(cmp_equal(char{ 0 }, char{ 0 }));
EXPECT_TRUE(cmp_equal(char{ 1 }, char{ 1 }));
EXPECT_TRUE(cmp_equal(char{ -1 }, char{ -1 }));
EXPECT_TRUE(cmp_equal(int{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(int{ 1 }, int{ 1 }));
EXPECT_TRUE(cmp_equal(int{ -1 }, int{ -1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, std::size_t{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_equal(char{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(char{ 1 }, int{ 1 }));
EXPECT_TRUE(cmp_equal(char{ -1 }, int{ -1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, char{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, char{ 1 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 0 }, int{ 0 }));
EXPECT_TRUE(cmp_equal(std::size_t{ 1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 0 }, char{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 1 }, char{ -1 }));
EXPECT_FALSE(cmp_equal(int{ 0 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(int{ -1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 0 }, std::size_t{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 0 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(char{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_equal(char{ -1 }, int{ 1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_equal(static_cast<std::size_t>(-1), int{ -1 }));
EXPECT_FALSE(cmp_equal(std::size_t{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_equal(std::numeric_limits<std::size_t>::max(), int{ 0 }));
}
TEST(compare, less)
{
EXPECT_TRUE(cmp_less(char{ 0 }, char{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, char{ 0 }));
EXPECT_TRUE(cmp_less(int{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(int{ -1 }, int{ 1 }));
EXPECT_TRUE(cmp_less(std::size_t{ 0 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_less(char{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, int{ 0 }));
EXPECT_TRUE(cmp_less(char{ -1 }, int{ 1 }));
EXPECT_TRUE(cmp_less(char{ -1 }, std::size_t{ 1 }));
EXPECT_TRUE(cmp_less(std::size_t{ 0 }, int{ 1 }));
EXPECT_TRUE(cmp_less(std::numeric_limits<int>::min(), char{ 0 }));
EXPECT_TRUE(cmp_less(std::numeric_limits<int>::min(), std::size_t{ 0 }));
EXPECT_TRUE(cmp_less(int{ -1 }, std::numeric_limits<std::size_t>::max()));
EXPECT_TRUE(cmp_less(std::size_t{ 1 }, std::numeric_limits<int>::max()));
EXPECT_FALSE(cmp_less(char{ 1 }, char{ 0 }));
EXPECT_FALSE(cmp_less(char{ 1 }, char{ 1 }));
EXPECT_FALSE(cmp_less(char{ 0 }, char{ -1 }));
EXPECT_FALSE(cmp_less(int{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_less(int{ 1 }, int{ -1 }));
EXPECT_FALSE(cmp_less(std::size_t{ 1 }, std::size_t{ 0 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ 1 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ 0 }));
EXPECT_FALSE(cmp_less(char{ 0 }, int{ -1 }));
EXPECT_FALSE(cmp_less(char{ 1 }, int{ -11 }));
EXPECT_FALSE(cmp_less(std::size_t{ 1 }, char{ -1 }));
EXPECT_FALSE(cmp_less(int{ 1 }, std::size_t{ 0 }));
EXPECT_FALSE(cmp_less(char{ 0 }, std::numeric_limits<int>::min()));
EXPECT_FALSE(cmp_less(std::size_t{ 0 }, std::numeric_limits<int>::min()));
EXPECT_FALSE(cmp_less(std::numeric_limits<std::size_t>::max(), int{ -1 }));
EXPECT_FALSE(cmp_less(std::numeric_limits<int>::max(), std::size_t{ 1 }));
}

View File

@ -0,0 +1,94 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <type_traits>
#include <gtest/gtest.h>
#include "mamba/util/flat_set.hpp"
using namespace mamba::util;
TEST(flat_set, constructor)
{
const auto s1 = flat_set<int>();
EXPECT_EQ(s1.size(), 0);
auto s2 = flat_set<int>({ 1, 2 });
EXPECT_EQ(s2.size(), 2);
const auto s3 = flat_set<int>{ s2 };
EXPECT_EQ(s3.size(), 2);
const auto s4 = flat_set<int>{ std::move(s2) };
EXPECT_EQ(s4.size(), 2);
// CTAD
auto s5 = flat_set({ 1, 2 });
EXPECT_EQ(s5.size(), 2);
static_assert(std::is_same_v<decltype(s5)::value_type, int>);
auto s6 = flat_set(s5.begin(), s5.end(), std::greater{});
EXPECT_EQ(s6.size(), s5.size());
static_assert(std::is_same_v<decltype(s6)::value_type, decltype(s5)::value_type>);
}
TEST(flat_set, equality)
{
EXPECT_EQ(flat_set<int>(), flat_set<int>());
EXPECT_EQ(flat_set<int>({ 1, 2 }), flat_set<int>({ 1, 2 }));
EXPECT_EQ(flat_set<int>({ 1, 2 }), flat_set<int>({ 2, 1 }));
EXPECT_EQ(flat_set<int>({ 1, 2, 1 }), flat_set<int>({ 2, 2, 1 }));
EXPECT_NE(flat_set<int>({ 1, 2 }), flat_set<int>({ 1, 2, 3 }));
EXPECT_NE(flat_set<int>({ 2 }), flat_set<int>({}));
}
TEST(flat_set, insert)
{
auto s = flat_set<int>();
s.insert(33);
EXPECT_EQ(s, flat_set<int>({ 33 }));
s.insert(33);
s.insert(17);
EXPECT_EQ(s, flat_set<int>({ 17, 33 }));
s.insert(22);
EXPECT_EQ(s, flat_set<int>({ 17, 22, 33 }));
s.insert(33);
EXPECT_EQ(s, flat_set<int>({ 17, 22, 33 }));
auto v = std::vector<int>({ 33, 22, 17, 0 });
s.insert(v.begin(), v.end());
EXPECT_EQ(s, flat_set<int>({ 0, 17, 22, 33 }));
}
TEST(flat_set, erase)
{
auto s = flat_set<int>{ 4, 3, 2, 1 };
EXPECT_EQ(s.erase(4), 1);
EXPECT_EQ(s, flat_set<int>({ 1, 2, 3 }));
EXPECT_EQ(s.erase(4), 0);
EXPECT_EQ(s, flat_set<int>({ 1, 2, 3 }));
const auto it = s.erase(s.begin());
EXPECT_EQ(it, s.begin());
EXPECT_EQ(s, flat_set<int>({ 2, 3 }));
}
TEST(flat_set, contains)
{
const auto s = flat_set<int>({ 1, 3, 4, 5 });
EXPECT_FALSE(s.contains(0));
EXPECT_TRUE(s.contains(1));
EXPECT_FALSE(s.contains(2));
EXPECT_TRUE(s.contains(3));
EXPECT_TRUE(s.contains(4));
EXPECT_TRUE(s.contains(5));
EXPECT_FALSE(s.contains(6));
}
TEST(flat_set, key_compare)
{
auto s = flat_set({ 1, 3, 4, 5 }, std::greater{});
EXPECT_EQ(s.front(), 5);
EXPECT_EQ(s.back(), 1);
s.insert(6);
EXPECT_EQ(s.front(), 6);
}

View File

@ -0,0 +1,348 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <gtest/gtest.h>
#include "mamba/util/graph.hpp"
using namespace mamba::util;
auto
build_graph() -> DiGraph<double>
{
DiGraph<double> g;
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
const auto n3 = g.add_node(3.5);
const auto n4 = g.add_node(4.5);
const auto n5 = g.add_node(5.5);
const auto n6 = g.add_node(6.5);
g.add_edge(n0, n1);
g.add_edge(n0, n2);
g.add_edge(n1, n3);
g.add_edge(n1, n4);
g.add_edge(n2, n3);
g.add_edge(n2, n5);
g.add_edge(n3, n6);
return g;
}
auto
build_cyclic_graph() -> DiGraph<double>
{
DiGraph<double> g;
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
const auto n3 = g.add_node(3.5);
const auto n4 = g.add_node(4.5);
g.add_edge(n0, n1);
g.add_edge(n0, n3);
g.add_edge(n1, n2);
g.add_edge(n2, n0);
g.add_edge(n3, n4);
return g;
}
auto
build_edge_data_graph() -> DiGraph<double, const char*>
{
auto g = DiGraph<double, const char*>{};
const auto n0 = g.add_node(0.5);
const auto n1 = g.add_node(1.5);
const auto n2 = g.add_node(2.5);
g.add_edge(n0, n1, "n0->n1");
g.add_edge(n1, n2, "n1->n2");
return g;
}
template <class G>
class test_visitor : private default_visitor<G>
{
public:
using base_type = default_visitor<G>;
using node_id = typename base_type::node_id;
using predecessor_map = std::map<node_id, node_id>;
using edge_map = std::map<node_id, node_id>;
using base_type::finish_edge;
using base_type::finish_node;
using base_type::start_edge;
using base_type::start_node;
using base_type::tree_edge;
void back_edge(node_id from, node_id to, const G&)
{
m_back_edges[from] = to;
}
void forward_or_cross_edge(node_id from, node_id to, const G&)
{
m_cross_edges[from] = to;
}
auto get_back_edge_map() const -> const edge_map&
{
return m_back_edges;
}
auto get_cross_edge_map() const -> const edge_map
{
return m_cross_edges;
}
private:
edge_map m_back_edges;
edge_map m_cross_edges;
};
TEST(graph, build_simple)
{
const auto g = build_graph();
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 7ul);
EXPECT_EQ(g.number_of_edges(), 7ul);
EXPECT_EQ(
g.nodes(),
node_map({ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 }, { 3, 3.5 }, { 4, 4.5 }, { 5, 5.5 }, { 6, 6.5 } })
);
EXPECT_EQ(g.successors(0u), node_id_list({ 1u, 2u }));
EXPECT_EQ(g.successors(1u), node_id_list({ 3u, 4u }));
EXPECT_EQ(g.successors(2u), node_id_list({ 3u, 5u }));
EXPECT_EQ(g.successors(3u), node_id_list({ 6u }));
EXPECT_EQ(g.predecessors(0u), node_id_list());
EXPECT_EQ(g.predecessors(1u), node_id_list({ 0u }));
EXPECT_EQ(g.predecessors(2u), node_id_list({ 0u }));
EXPECT_EQ(g.predecessors(3u), node_id_list({ 1u, 2u }));
}
TEST(graph, build_edge_data)
{
const auto g = build_edge_data_graph();
using node_map = decltype(g)::node_map;
using node_id_list = decltype(g)::node_id_list;
EXPECT_EQ(g.number_of_nodes(), 3ul);
EXPECT_EQ(g.number_of_edges(), 2ul);
EXPECT_EQ(g.nodes(), node_map({ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 } }));
EXPECT_EQ(g.successors(0ul), node_id_list({ 1ul }));
EXPECT_EQ(g.successors(1ul), node_id_list({ 2ul }));
EXPECT_EQ(g.successors(2ul), node_id_list());
EXPECT_EQ(g.predecessors(0ul), node_id_list());
EXPECT_EQ(g.predecessors(1ul), node_id_list({ 0ul }));
EXPECT_EQ(g.predecessors(2ul), node_id_list({ 1ul }));
using edge_map = decltype(g)::edge_map;
EXPECT_EQ(g.edges(), edge_map({ { { 0ul, 1ul }, "n0->n1" }, { { 1ul, 2ul }, "n1->n2" } }));
}
TEST(graph, has_node_edge)
{
const auto g = build_graph();
EXPECT_TRUE(g.has_node(1ul));
EXPECT_TRUE(g.has_node(4ul));
EXPECT_FALSE(g.has_node(g.number_of_nodes()));
EXPECT_TRUE(g.has_edge(1ul, 4ul));
EXPECT_FALSE(g.has_edge(4ul, 1ul));
EXPECT_TRUE(g.has_edge(0ul, 2ul));
EXPECT_FALSE(g.has_edge(0ul, 5ul));
EXPECT_FALSE(g.has_edge(0ul, g.number_of_nodes()));
EXPECT_FALSE(g.has_edge(g.number_of_nodes(), 1ul));
}
TEST(graph, data_modifier)
{
auto g = build_edge_data_graph();
static constexpr auto new_node_val = -1.5;
EXPECT_NE(g.node(0ul), new_node_val);
g.node(0ul) = new_node_val;
EXPECT_EQ(g.node(0ul), new_node_val);
static constexpr auto new_edge_val = "data";
EXPECT_NE(g.edge(0ul, 1ul), new_edge_val);
g.edge(0ul, 1ul) = new_edge_val;
EXPECT_EQ(g.edge(0ul, 1ul), new_edge_val);
}
TEST(graph, remove_edge)
{
auto g = build_edge_data_graph();
const auto n_edges_init = g.number_of_edges();
ASSERT_FALSE(g.has_edge(1, 0));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_FALSE(g.remove_edge(1, 0));
EXPECT_EQ(g.number_of_edges(), n_edges_init);
EXPECT_FALSE(g.has_edge(1, 0));
EXPECT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(0, 1));
EXPECT_TRUE(g.remove_edge(0, 1));
EXPECT_EQ(g.number_of_edges(), n_edges_init - 1u);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_EQ(g.edges().count({ 0, 1 }), 0);
}
TEST(graph, remove_node)
{
auto g = build_edge_data_graph();
ASSERT_TRUE(g.has_node(0));
ASSERT_TRUE(g.has_node(1));
ASSERT_TRUE(g.has_node(2));
ASSERT_TRUE(g.has_edge(0, 1));
ASSERT_TRUE(g.has_edge(1, 2));
const auto n_edges_init = g.number_of_edges();
const auto n_nodes_init = g.number_of_nodes();
const auto node_1_degree = g.in_degree(1) + g.out_degree(1);
EXPECT_TRUE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
EXPECT_TRUE(g.has_node(0));
EXPECT_FALSE(g.has_node(1));
EXPECT_TRUE(g.has_node(2));
EXPECT_EQ(g.in_degree(1), 0);
EXPECT_EQ(g.out_degree(1), 0);
EXPECT_FALSE(g.has_edge(0, 1));
EXPECT_FALSE(g.has_edge(1, 2));
g.for_each_node_id([&](auto id) { EXPECT_TRUE(g.has_node(id)); });
EXPECT_FALSE(g.remove_node(1));
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
EXPECT_EQ(g.number_of_edges(), g.edges().size());
const auto new_id = g.add_node(.7);
EXPECT_EQ(new_id, n_nodes_init); // Ids are not invalidated so new id is used
EXPECT_FALSE(g.has_node(1)); // Old id is not being confused
EXPECT_EQ(g.number_of_nodes(), n_nodes_init);
}
TEST(graph, degree)
{
const auto g = build_graph();
EXPECT_EQ(g.out_degree(0), 2);
EXPECT_EQ(g.out_degree(1), 2);
EXPECT_EQ(g.out_degree(6), 0);
EXPECT_EQ(g.in_degree(0), 0);
EXPECT_EQ(g.in_degree(3), 2);
EXPECT_EQ(g.in_degree(6), 1);
}
TEST(graph, for_each_node)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_nodes = 0;
g.for_each_node_id(
[&](node_id id)
{
EXPECT_TRUE(g.has_node(id));
++n_nodes;
}
);
EXPECT_EQ(n_nodes, g.number_of_nodes());
}
TEST(graph, for_each_edge)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
std::size_t n_edges = 0;
g.for_each_edge_id(
[&g, &n_edges](node_id from, node_id to)
{
EXPECT_TRUE(g.has_edge(from, to));
++n_edges;
}
);
EXPECT_EQ(n_edges, g.number_of_edges());
}
TEST(graph, for_each_leaf)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf_id([&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 4ul, 5ul, 6ul }));
}
TEST(graph, for_each_leaf_from)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_leaf_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 5ul, 6ul }));
}
TEST(graph, for_each_root)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto roots = node_id_list();
g.for_each_root_id([&roots](node_id root) { roots.insert(root); });
EXPECT_EQ(roots, node_id_list({ 0ul }));
}
TEST(graph, for_each_root_from)
{
const auto g = build_graph();
using node_id = decltype(g)::node_id;
using node_id_list = decltype(g)::node_id_list;
auto leaves = node_id_list();
g.for_each_root_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
EXPECT_EQ(leaves, node_id_list({ 0ul }));
}
TEST(graph, depth_first_search)
{
const auto g = build_graph();
test_visitor<DiGraph<double>> vis;
g.depth_first_search(vis);
EXPECT_TRUE(vis.get_back_edge_map().empty());
EXPECT_EQ(vis.get_cross_edge_map().find(2u)->second, 3u);
}
TEST(graph, dfs_cyclic)
{
const auto g = build_cyclic_graph();
test_visitor<DiGraph<double>> vis;
g.depth_first_search(vis);
EXPECT_EQ(vis.get_back_edge_map().find(2u)->second, 0u);
EXPECT_TRUE(vis.get_cross_edge_map().empty());
}
TEST(graph, dfs_empty)
{
DiGraph<int> g;
test_visitor<DiGraph<int>> vis;
g.depth_first_search(vis);
EXPECT_TRUE(vis.get_back_edge_map().empty());
EXPECT_TRUE(vis.get_cross_edge_map().empty());
}
TEST(graph_algorithm, is_reachable)
{
auto graph = build_graph();
EXPECT_TRUE(is_reachable(graph, 0, 6));
EXPECT_FALSE(is_reachable(graph, 6, 0));
}

View File

@ -30,10 +30,10 @@
#include "mamba/core/subdirdata.hpp"
#include "mamba/core/transaction.hpp"
#include "mamba/core/url.hpp"
#include "mamba/core/util_graph.hpp"
#include "mamba/core/util_string.hpp"
#include "mamba/core/validate.hpp"
#include "mamba/core/virtual_packages.hpp"
#include "mamba/util/flat_set.hpp"
namespace py = pybind11;
@ -54,8 +54,8 @@ namespace PYBIND11_NAMESPACE
namespace detail
{
template <typename Key, typename Compare, typename Allocator>
struct type_caster<mamba::vector_set<Key, Compare, Allocator>>
: set_caster<mamba::vector_set<Key, Compare, Allocator>, Key>
struct type_caster<mamba::util::flat_set<Key, Compare, Allocator>>
: set_caster<mamba::util::flat_set<Key, Compare, Allocator>, Key>
{
};
}