Skip to content

Commit 11cd6c3

Browse files
authored
YT-CPPHGL-23: Implement hypergraph I/O functionality
- Refactored the options_manip class (previously stream_options_manipulator) to use a set and a clear mask instead of only the set mask - Aligned the options_manip constants and utility - Refined the GL module's I/O implementation and output formatting - Defined generic formatter types: range_formatter, implicit_range_formatter - Implemented the I/O utility for the HGL module - Added missing const qualifiers for hypergraph getter methods
1 parent 4ff0b14 commit 11cd6c3

41 files changed

Lines changed: 1967 additions & 844 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

include/gl/edge_descriptor.hpp

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44

55
#pragma once
66

7+
#include "gl/attributes/force_inline.hpp"
78
#include "gl/constants.hpp"
89
#include "gl/directional_tags.hpp"
9-
#include "gl/io/format.hpp"
10+
#include "gl/io/options.hpp"
11+
#include "gl/io/ranges.hpp"
1012
#include "gl/types/core.hpp"
1113
#include "gl/vertex_descriptor.hpp"
1214

15+
#include <format>
16+
1317
namespace gl {
1418

1519
template <
@@ -159,40 +163,70 @@ class edge_descriptor final {
159163
return this->_properties.get();
160164
}
161165

162-
friend inline std::ostream& operator<<(std::ostream& os, const edge_descriptor& edge) {
163-
edge._write(os);
164-
return os;
166+
friend std::ostream& operator<<(std::ostream& os, const edge_descriptor& edge) {
167+
using enum io::detail::option_bit;
168+
169+
if (io::is_option_set(os, verbose))
170+
return edge._verbose_write(os);
171+
else
172+
return edge._concise_write(os);
165173
}
166174

167175
private:
168-
void _write(std::ostream& os) const {
169-
if constexpr (not traits::c_writable<properties_type>) {
170-
this->_write_no_properties(os);
171-
return;
172-
}
173-
else {
174-
if (not io::is_option_set(os, io::graph_option::with_edge_properties)) {
175-
this->_write_no_properties(os);
176-
return;
177-
}
178-
179-
if (io::is_option_set(os, io::graph_option::verbose)) {
180-
os << "[source: " << this->_vertices.first << ", target: " << this->_vertices.second
181-
<< " | properties: " << this->_properties.get() << "]";
182-
}
183-
else {
184-
os << "[" << this->_vertices.first << ", " << this->_vertices.second << " | "
185-
<< this->_properties.get() << "]";
186-
}
187-
}
188-
}
189-
190-
void _write_no_properties(std::ostream& os) const {
191-
if (io::is_option_set(os, io::graph_option::verbose))
192-
os << "[source: " << this->_vertices.first << ", target: " << this->_vertices.first
193-
<< "]";
194-
else
195-
os << "[" << this->_vertices.first << ", " << this->_vertices.second << "]";
176+
std::ostream& _verbose_write(std::ostream& os) const
177+
requires std::same_as<directional_tag, undirected_t>
178+
{
179+
using enum io::detail::option_bit;
180+
181+
os << "[id: " << this->_id << " | endpoints: {" << this->_vertices.first << ", "
182+
<< this->_vertices.second << '}';
183+
if constexpr (traits::c_writable<properties_type>)
184+
if (io::is_option_set(os, with_connection_properties))
185+
os << " | " << this->_properties.get();
186+
os << ']';
187+
188+
return os;
189+
}
190+
191+
std::ostream& _concise_write(std::ostream& os) const
192+
requires std::same_as<directional_tag, undirected_t>
193+
{
194+
using enum io::detail::option_bit;
195+
196+
os << '{' << this->_vertices.first << ", " << this->_vertices.second << '}';
197+
if constexpr (traits::c_writable<properties_type>)
198+
if (io::is_option_set(os, with_vertex_properties))
199+
os << '[' << this->_properties.get() << ']';
200+
201+
return os;
202+
}
203+
204+
std::ostream& _verbose_write(std::ostream& os) const
205+
requires std::same_as<directional_tag, directed_t>
206+
{
207+
using enum io::detail::option_bit;
208+
209+
os << "[id: " << this->_id << " | source: " << this->_vertices.first
210+
<< ", target: " << this->_vertices.second;
211+
if constexpr (traits::c_writable<properties_type>)
212+
if (io::is_option_set(os, with_connection_properties))
213+
os << " | " << this->_properties.get();
214+
os << ']';
215+
216+
return os;
217+
}
218+
219+
std::ostream& _concise_write(std::ostream& os) const
220+
requires std::same_as<directional_tag, directed_t>
221+
{
222+
using enum io::detail::option_bit;
223+
224+
os << '(' << this->_vertices.first << ", " << this->_vertices.second << ')';
225+
if constexpr (traits::c_writable<properties_type>)
226+
if (io::is_option_set(os, with_vertex_properties))
227+
os << '[' << this->_properties.get() << ']';
228+
229+
return os;
196230
}
197231

198232
id_type _id;

include/gl/graph.hpp

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
#pragma once
66

7+
#include "gl/attributes/force_inline.hpp"
78
#include "gl/constants.hpp"
9+
#include "gl/directional_tags.hpp"
810
#include "gl/graph_traits.hpp"
911
#include "gl/impl/impl_tags.hpp"
10-
#include "gl/io/stream_options_manipulator.hpp"
12+
#include "gl/io/graph_fmt_traits.hpp"
13+
#include "gl/io/options.hpp"
14+
#include "gl/io/options_manip.hpp"
15+
#include "gl/traits.hpp"
1116
#include "gl/util/ranges.hpp"
1217

1318
#include <set>
@@ -207,14 +212,14 @@ class graph final {
207212

208213
// --- vertex getters ---
209214

210-
[[nodiscard]] gl_attr_force_inline auto vertices() const
215+
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
211216
requires(traits::c_empty_properties<vertex_properties_type>)
212217
{
213218
return this->vertex_ids()
214219
| std::views::transform([](const id_type id) { return vertex_descriptor{id}; });
215220
}
216221

217-
[[nodiscard]] gl_attr_force_inline auto vertices() const
222+
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
218223
requires(traits::c_non_empty_properties<vertex_properties_type>)
219224
{
220225
return this->_vertex_properties | std::views::enumerate
@@ -630,22 +635,19 @@ class graph final {
630635
// --- stream operators ---
631636

632637
friend std::ostream& operator<<(std::ostream& os, const graph& g) {
633-
if (io::is_option_set(os, io::graph_option::gsf)) {
634-
g._gsf_write(os);
635-
return os;
636-
}
638+
using io::detail::option_bit;
637639

638-
if (io::is_option_set(os, io::graph_option::verbose))
639-
g._verbose_write(os);
640-
else
641-
g._concise_write(os);
640+
if (io::is_option_set(os, option_bit::spec_fmt))
641+
return g._gsf_write(os);
642642

643-
return os;
643+
if (io::is_option_set(os, option_bit::verbose))
644+
return g._verbose_write(os);
645+
else
646+
return g._concise_write(os);
644647
}
645648

646-
friend inline std::istream& operator>>(std::istream& is, graph& g) {
647-
g._gsf_read(is);
648-
return is;
649+
friend gl_attr_force_inline std::istream& operator>>(std::istream& is, graph& g) {
650+
return g._gsf_read(is);
649651
}
650652

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

662664
private:
665+
using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;
666+
663667
graph(const graph& other)
664668
: _n_vertices{other._n_vertices}, _n_edges{other._n_edges}, _impl{other._impl} {
665669
// Deep copy vertex properties
@@ -679,11 +683,7 @@ class graph final {
679683
}
680684
}
681685

682-
[[nodiscard]] static constexpr std::string _directed_type_str() {
683-
return traits::c_directed_edge<edge_type> ? "directed" : "undirected";
684-
}
685-
686-
// --- graph element verification methods ---
686+
// --- element validation ---
687687

688688
gl_attr_force_inline void _verify_vertex_id(const id_type vertex_id) const {
689689
if (not this->has_vertex(vertex_id))
@@ -701,7 +701,7 @@ class graph final {
701701
));
702702
}
703703

704-
// --- vertex methods ---
704+
// --- vertex modifiers ---
705705

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

721-
// --- io methods ---
721+
// --- I/O utility ---
722722

723-
void _verbose_write(std::ostream& os) const {
724-
os << std::format(
725-
"type: {}\nnumber of vertices: {}\nnumber of edges: {}\nvertices:\n",
726-
_directed_type_str(),
727-
this->order(),
728-
this->size()
729-
);
723+
struct concise_target_formatter {
724+
edge_type edge;
725+
id_type src_id;
726+
bool with_props;
727+
728+
friend std::ostream& operator<<(std::ostream& os, const concise_target_formatter& proxy) {
729+
os << proxy.edge.other(proxy.src_id);
730+
if constexpr (traits::c_writable<edge_properties_type>)
731+
if (proxy.with_props)
732+
os << '[' << proxy.edge.properties() << ']';
733+
734+
return os;
735+
}
736+
};
730737

738+
std::ostream& _verbose_write(std::ostream& os) const {
739+
os << "type: " << fmt_traits::type << ", |V| = " << this->order()
740+
<< ", |E| = " << this->size() << '\n';
731741
for (const auto& vertex : this->vertices()) {
732-
os << "- " << vertex << "\n incident edges:\n";
742+
os << "- " << vertex << "\n " << fmt_traits::out_edges << ":\n";
733743
for (const auto& edge : this->out_edges(vertex.id()))
734744
os << "\t- " << edge << '\n';
735745
}
746+
return os;
736747
}
737748

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

741-
for (const auto& vertex : this->vertices()) {
742-
os << "- " << vertex << " :";
743-
for (const auto& edge : this->out_edges(vertex.id()))
744-
os << ' ' << edge;
745-
os << '\n';
752+
for (const auto& src : this->vertices()) {
753+
auto tgts = std::views::transform(
754+
this->out_edges(src.id()),
755+
[src_id = src.id(),
756+
with_props = io::is_option_set(os, with_connection_properties)](const auto& edge) {
757+
return concise_target_formatter{edge, src_id, with_props};
758+
}
759+
);
760+
os << src << " : " << io::range_formatter(tgts) << '\n';
746761
}
762+
763+
return os;
747764
}
748765

749-
void _gsf_write(std::ostream& os) const {
750-
const bool with_vertex_properties =
751-
io::is_option_set(os, io::graph_option::with_vertex_properties);
752-
const bool with_edge_properties =
753-
io::is_option_set(os, io::graph_option::with_edge_properties);
754-
755-
// print graph size
756-
os << std::format(
757-
"{} {} {} {} {}\n",
758-
static_cast<int>(traits::c_directed_edge<edge_type>),
759-
this->order(),
760-
this->size(),
761-
static_cast<int>(with_vertex_properties),
762-
static_cast<int>(with_edge_properties)
763-
);
766+
std::ostream& _gsf_write(std::ostream& os) const {
767+
using enum io::detail::option_bit;
764768

765-
if constexpr (traits::c_writable<typename vertex_type::properties_type>)
766-
if (with_vertex_properties)
769+
const bool with_v_props = io::is_option_set(os, with_vertex_properties);
770+
const bool with_e_props = io::is_option_set(os, with_connection_properties);
771+
772+
// print graph metadata
773+
os << traits::c_directed_edge<edge_type> << ' ' << this->order() << ' ' << this->size()
774+
<< ' ' << static_cast<int>(with_v_props) << ' ' << static_cast<int>(with_e_props)
775+
<< '\n';
776+
777+
if constexpr (traits::c_writable<vertex_properties_type>)
778+
if (with_v_props)
767779
for (const auto& vertex : this->vertices())
768780
os << vertex.properties() << '\n';
769781

770-
if constexpr (traits::c_writable<typename edge_type::properties_type>) {
771-
if (with_edge_properties) {
782+
if constexpr (traits::c_writable<edge_properties_type>) {
783+
if (with_e_props) {
772784
const auto print_out_edges = [this, &os](const id_type vertex_id) {
773785
for (const auto& edge : this->out_edges(vertex_id)) {
774-
if (edge.source() != vertex_id)
775-
continue; // vertex is not the source
786+
if constexpr (std::same_as<directional_tag, undirected_t>)
787+
if (edge.other(vertex_id) > vertex_id)
788+
continue; // deduplicate edges
776789
os << edge.source() << ' ' << edge.target() << ' ' << edge.properties()
777790
<< '\n';
778791
}
@@ -781,33 +794,40 @@ class graph final {
781794
for (const auto vertex_id : this->vertex_ids())
782795
print_out_edges(vertex_id);
783796

784-
return;
797+
return os;
785798
}
786799
}
787800

788801
const auto print_out_edges = [this, &os](const id_type vertex_id) {
789802
for (const auto& edge : this->out_edges(vertex_id)) {
790-
if (edge.source() != vertex_id)
791-
continue; // vertex is not the source
803+
if constexpr (std::same_as<directional_tag, undirected_t>)
804+
if (edge.other(vertex_id) > vertex_id)
805+
continue; // deduplicate edges
792806
os << edge.source() << ' ' << edge.target() << '\n';
793807
}
794808
};
795809

796810
for (const auto vertex_id : this->vertex_ids())
797811
print_out_edges(vertex_id);
812+
813+
return os;
798814
}
799815

800-
void _gsf_read(std::istream& is) {
801-
bool directed;
802-
is >> directed;
816+
std::istream& _gsf_read(std::istream& is) {
817+
using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;
803818

804-
if (directed != traits::c_directed_edge<edge_type>)
819+
int dir_discr;
820+
is >> dir_discr;
821+
822+
if (dir_discr != fmt_traits::discriminator)
805823
throw std::ios_base::failure(std::format(
806-
"Invalid graph specification: directional tag does not match - should be {}",
807-
_directed_type_str()
824+
"Invalid hypergraph specification: directional specifier {} does not match "
825+
"expected {}",
826+
dir_discr,
827+
fmt_traits::discriminator
808828
));
809829

810-
// read initial graph parameters
830+
// read graph metadata
811831
id_type n_vertices, n_edges;
812832
is >> n_vertices >> n_edges;
813833

@@ -861,6 +881,8 @@ class graph final {
861881
this->add_edge(source_id, target_id);
862882
}
863883
}
884+
885+
return is;
864886
}
865887

866888
size_type _n_vertices = 0uz;

0 commit comments

Comments
 (0)