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
662664private:
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: {}\n number of vertices: {}\n number of edges: {}\n vertices:\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