Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 66 additions & 32 deletions include/gl/edge_descriptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

#pragma once

#include "gl/attributes/force_inline.hpp"
#include "gl/constants.hpp"
#include "gl/directional_tags.hpp"
#include "gl/io/format.hpp"
#include "gl/io/options.hpp"
#include "gl/io/ranges.hpp"
#include "gl/types/core.hpp"
#include "gl/vertex_descriptor.hpp"

#include <format>

namespace gl {

template <
Expand Down Expand Up @@ -159,40 +163,70 @@ class edge_descriptor final {
return this->_properties.get();
}

friend inline std::ostream& operator<<(std::ostream& os, const edge_descriptor& edge) {
edge._write(os);
return os;
friend std::ostream& operator<<(std::ostream& os, const edge_descriptor& edge) {
using enum io::detail::option_bit;

if (io::is_option_set(os, verbose))
return edge._verbose_write(os);
else
return edge._concise_write(os);
}

private:
void _write(std::ostream& os) const {
if constexpr (not traits::c_writable<properties_type>) {
this->_write_no_properties(os);
return;
}
else {
if (not io::is_option_set(os, io::graph_option::with_edge_properties)) {
this->_write_no_properties(os);
return;
}

if (io::is_option_set(os, io::graph_option::verbose)) {
os << "[source: " << this->_vertices.first << ", target: " << this->_vertices.second
<< " | properties: " << this->_properties.get() << "]";
}
else {
os << "[" << this->_vertices.first << ", " << this->_vertices.second << " | "
<< this->_properties.get() << "]";
}
}
}

void _write_no_properties(std::ostream& os) const {
if (io::is_option_set(os, io::graph_option::verbose))
os << "[source: " << this->_vertices.first << ", target: " << this->_vertices.first
<< "]";
else
os << "[" << this->_vertices.first << ", " << this->_vertices.second << "]";
std::ostream& _verbose_write(std::ostream& os) const
requires std::same_as<directional_tag, undirected_t>
{
using enum io::detail::option_bit;

os << "[id: " << this->_id << " | endpoints: {" << this->_vertices.first << ", "
<< this->_vertices.second << '}';
if constexpr (traits::c_writable<properties_type>)
if (io::is_option_set(os, with_connection_properties))
os << " | " << this->_properties.get();
os << ']';

return os;
}

std::ostream& _concise_write(std::ostream& os) const
requires std::same_as<directional_tag, undirected_t>
{
using enum io::detail::option_bit;

os << '{' << this->_vertices.first << ", " << this->_vertices.second << '}';
if constexpr (traits::c_writable<properties_type>)
if (io::is_option_set(os, with_vertex_properties))
os << '[' << this->_properties.get() << ']';

return os;
}

std::ostream& _verbose_write(std::ostream& os) const
requires std::same_as<directional_tag, directed_t>
{
using enum io::detail::option_bit;

os << "[id: " << this->_id << " | source: " << this->_vertices.first
<< ", target: " << this->_vertices.second;
if constexpr (traits::c_writable<properties_type>)
if (io::is_option_set(os, with_connection_properties))
os << " | " << this->_properties.get();
os << ']';

return os;
}

std::ostream& _concise_write(std::ostream& os) const
requires std::same_as<directional_tag, directed_t>
{
using enum io::detail::option_bit;

os << '(' << this->_vertices.first << ", " << this->_vertices.second << ')';
if constexpr (traits::c_writable<properties_type>)
if (io::is_option_set(os, with_vertex_properties))
os << '[' << this->_properties.get() << ']';

return os;
}

id_type _id;
Expand Down
158 changes: 90 additions & 68 deletions include/gl/graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

#pragma once

#include "gl/attributes/force_inline.hpp"
#include "gl/constants.hpp"
#include "gl/directional_tags.hpp"
#include "gl/graph_traits.hpp"
#include "gl/impl/impl_tags.hpp"
#include "gl/io/stream_options_manipulator.hpp"
#include "gl/io/graph_fmt_traits.hpp"
#include "gl/io/options.hpp"
#include "gl/io/options_manip.hpp"
#include "gl/traits.hpp"
#include "gl/util/ranges.hpp"

#include <set>
Expand Down Expand Up @@ -207,14 +212,14 @@ class graph final {

// --- vertex getters ---

[[nodiscard]] gl_attr_force_inline auto vertices() const
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
requires(traits::c_empty_properties<vertex_properties_type>)
{
return this->vertex_ids()
| std::views::transform([](const id_type id) { return vertex_descriptor{id}; });
}

[[nodiscard]] gl_attr_force_inline auto vertices() const
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
requires(traits::c_non_empty_properties<vertex_properties_type>)
{
return this->_vertex_properties | std::views::enumerate
Expand Down Expand Up @@ -630,22 +635,19 @@ class graph final {
// --- stream operators ---

friend std::ostream& operator<<(std::ostream& os, const graph& g) {
if (io::is_option_set(os, io::graph_option::gsf)) {
g._gsf_write(os);
return os;
}
using io::detail::option_bit;

if (io::is_option_set(os, io::graph_option::verbose))
g._verbose_write(os);
else
g._concise_write(os);
if (io::is_option_set(os, option_bit::spec_fmt))
return g._gsf_write(os);

return os;
if (io::is_option_set(os, option_bit::verbose))
return g._verbose_write(os);
else
return g._concise_write(os);
}

friend inline std::istream& operator>>(std::istream& is, graph& g) {
g._gsf_read(is);
return is;
friend gl_attr_force_inline std::istream& operator>>(std::istream& is, graph& g) {
return g._gsf_read(is);
}

// --- friend declarations ---
Expand All @@ -660,6 +662,8 @@ class graph final {
friend struct detail::to_impl;

private:
using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;

graph(const graph& other)
: _n_vertices{other._n_vertices}, _n_edges{other._n_edges}, _impl{other._impl} {
// Deep copy vertex properties
Expand All @@ -679,11 +683,7 @@ class graph final {
}
}

[[nodiscard]] static constexpr std::string _directed_type_str() {
return traits::c_directed_edge<edge_type> ? "directed" : "undirected";
}

// --- graph element verification methods ---
// --- element validation ---

gl_attr_force_inline void _verify_vertex_id(const id_type vertex_id) const {
if (not this->has_vertex(vertex_id))
Expand All @@ -701,7 +701,7 @@ class graph final {
));
}

// --- vertex methods ---
// --- vertex modifiers ---

void _remove_vertex_impl(const id_type vertex_id) {
const auto removed_edge_ids = this->_impl.remove_vertex(vertex_id);
Expand All @@ -718,61 +718,74 @@ class graph final {
}
}

// --- io methods ---
// --- I/O utility ---

void _verbose_write(std::ostream& os) const {
os << std::format(
"type: {}\nnumber of vertices: {}\nnumber of edges: {}\nvertices:\n",
_directed_type_str(),
this->order(),
this->size()
);
struct concise_target_formatter {
edge_type edge;
id_type src_id;
bool with_props;

friend std::ostream& operator<<(std::ostream& os, const concise_target_formatter& proxy) {
os << proxy.edge.other(proxy.src_id);
if constexpr (traits::c_writable<edge_properties_type>)
if (proxy.with_props)
os << '[' << proxy.edge.properties() << ']';

return os;
}
};

std::ostream& _verbose_write(std::ostream& os) const {
os << "type: " << fmt_traits::type << ", |V| = " << this->order()
<< ", |E| = " << this->size() << '\n';
for (const auto& vertex : this->vertices()) {
os << "- " << vertex << "\n incident edges:\n";
os << "- " << vertex << "\n " << fmt_traits::out_edges << ":\n";
for (const auto& edge : this->out_edges(vertex.id()))
os << "\t- " << edge << '\n';
}
return os;
}

void _concise_write(std::ostream& os) const {
os << std::format("{} {} {}\n", _directed_type_str(), this->order(), this->size());
std::ostream& _concise_write(std::ostream& os) const {
using enum io::detail::option_bit;

for (const auto& vertex : this->vertices()) {
os << "- " << vertex << " :";
for (const auto& edge : this->out_edges(vertex.id()))
os << ' ' << edge;
os << '\n';
for (const auto& src : this->vertices()) {
auto tgts = std::views::transform(
this->out_edges(src.id()),
[src_id = src.id(),
with_props = io::is_option_set(os, with_connection_properties)](const auto& edge) {
return concise_target_formatter{edge, src_id, with_props};
}
);
os << src << " : " << io::range_formatter(tgts) << '\n';
}

return os;
}

void _gsf_write(std::ostream& os) const {
const bool with_vertex_properties =
io::is_option_set(os, io::graph_option::with_vertex_properties);
const bool with_edge_properties =
io::is_option_set(os, io::graph_option::with_edge_properties);

// print graph size
os << std::format(
"{} {} {} {} {}\n",
static_cast<int>(traits::c_directed_edge<edge_type>),
this->order(),
this->size(),
static_cast<int>(with_vertex_properties),
static_cast<int>(with_edge_properties)
);
std::ostream& _gsf_write(std::ostream& os) const {
using enum io::detail::option_bit;

if constexpr (traits::c_writable<typename vertex_type::properties_type>)
if (with_vertex_properties)
const bool with_v_props = io::is_option_set(os, with_vertex_properties);
const bool with_e_props = io::is_option_set(os, with_connection_properties);

// print graph metadata
os << traits::c_directed_edge<edge_type> << ' ' << this->order() << ' ' << this->size()
<< ' ' << static_cast<int>(with_v_props) << ' ' << static_cast<int>(with_e_props)
<< '\n';

if constexpr (traits::c_writable<vertex_properties_type>)
if (with_v_props)
for (const auto& vertex : this->vertices())
os << vertex.properties() << '\n';

if constexpr (traits::c_writable<typename edge_type::properties_type>) {
if (with_edge_properties) {
if constexpr (traits::c_writable<edge_properties_type>) {
if (with_e_props) {
const auto print_out_edges = [this, &os](const id_type vertex_id) {
for (const auto& edge : this->out_edges(vertex_id)) {
if (edge.source() != vertex_id)
continue; // vertex is not the source
if constexpr (std::same_as<directional_tag, undirected_t>)
if (edge.other(vertex_id) > vertex_id)
continue; // deduplicate edges
os << edge.source() << ' ' << edge.target() << ' ' << edge.properties()
<< '\n';
}
Expand All @@ -781,33 +794,40 @@ class graph final {
for (const auto vertex_id : this->vertex_ids())
print_out_edges(vertex_id);

return;
return os;
}
}

const auto print_out_edges = [this, &os](const id_type vertex_id) {
for (const auto& edge : this->out_edges(vertex_id)) {
if (edge.source() != vertex_id)
continue; // vertex is not the source
if constexpr (std::same_as<directional_tag, undirected_t>)
if (edge.other(vertex_id) > vertex_id)
continue; // deduplicate edges
os << edge.source() << ' ' << edge.target() << '\n';
}
};

for (const auto vertex_id : this->vertex_ids())
print_out_edges(vertex_id);

return os;
}

void _gsf_read(std::istream& is) {
bool directed;
is >> directed;
std::istream& _gsf_read(std::istream& is) {
using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;

if (directed != traits::c_directed_edge<edge_type>)
int dir_discr;
is >> dir_discr;

if (dir_discr != fmt_traits::discriminator)
throw std::ios_base::failure(std::format(
"Invalid graph specification: directional tag does not match - should be {}",
_directed_type_str()
"Invalid hypergraph specification: directional specifier {} does not match "
"expected {}",
dir_discr,
fmt_traits::discriminator
));

// read initial graph parameters
// read graph metadata
id_type n_vertices, n_edges;
is >> n_vertices >> n_edges;

Expand Down Expand Up @@ -861,6 +881,8 @@ class graph final {
this->add_edge(source_id, target_id);
}
}

return is;
}

size_type _n_vertices = 0uz;
Expand Down
Loading
Loading