|
11 | 11 | #include "hgl/directional_tags.hpp" |
12 | 12 | #include "hgl/hypergraph_traits.hpp" |
13 | 13 | #include "hgl/impl/impl_tags.hpp" |
14 | | -#include "hgl/io.hpp" |
| 14 | +#include "hgl/io/core.hpp" |
| 15 | +#include "hgl/io/hypergraph_fmt_traits.hpp" |
15 | 16 | #include "hgl/util.hpp" |
16 | 17 |
|
17 | 18 | #include <algorithm> |
@@ -819,15 +820,21 @@ class hypergraph final { |
819 | 820 | } |
820 | 821 |
|
821 | 822 | friend std::ostream& operator<<(std::ostream& os, const hypergraph& hg) { |
822 | | - // if (gl::io::is_option_set(os, gl::io::detail::option_bit::gsf)) |
823 | | - // return hg._gsf_write(os); |
| 823 | + using enum io::detail::option_bit; |
| 824 | + |
| 825 | + if (gl::io::is_option_set(os, spec_fmt)) |
| 826 | + return hg._hgsf_write(os); |
824 | 827 |
|
825 | | - if (gl::io::is_option_set(os, gl::io::detail::option_bit::verbose)) |
| 828 | + if (gl::io::is_option_set(os, verbose)) |
826 | 829 | return hg._verbose_write(os); |
827 | 830 |
|
828 | 831 | return hg._concise_write(os); |
829 | 832 | } |
830 | 833 |
|
| 834 | + friend gl_attr_force_inline std::istream& operator>>(std::istream& is, hypergraph& hg) { |
| 835 | + return hg._hgsf_read(is); |
| 836 | + } |
| 837 | + |
831 | 838 | // --- friend declarations --- |
832 | 839 |
|
833 | 840 | template <traits::c_hypergraph Hypergraph> |
@@ -991,6 +998,170 @@ class hypergraph final { |
991 | 998 | return os; |
992 | 999 | } |
993 | 1000 |
|
| 1001 | + std::ostream& _hgsf_write(std::ostream& os) const { |
| 1002 | + using enum io::detail::option_bit; |
| 1003 | + using fmt_traits = io::detail::hypergraph_fmt_traits<directional_tag>; |
| 1004 | + |
| 1005 | + const bool with_v_props = io::is_option_set(os, with_vertex_properties); |
| 1006 | + const bool with_he_props = io::is_option_set(os, with_connection_properties); |
| 1007 | + |
| 1008 | + // print hypergraph metadata |
| 1009 | + os << fmt_traits::discriminator << ' ' << this->order() << ' ' << this->size() << ' ' |
| 1010 | + << static_cast<int>(with_v_props) << ' ' << static_cast<int>(with_he_props) << '\n'; |
| 1011 | + |
| 1012 | + if constexpr (traits::c_writable<vertex_properties_type>) |
| 1013 | + if (with_v_props) |
| 1014 | + for (const auto& vertex : this->vertices()) |
| 1015 | + os << vertex.properties() << '\n'; |
| 1016 | + |
| 1017 | + for (const auto& hyperedge : this->hyperedges()) { |
| 1018 | + const auto he_id = hyperedge.id(); |
| 1019 | + |
| 1020 | + if constexpr (std::same_as<directional_tag, undirected_t>) { |
| 1021 | + os << this->hyperedge_size(he_id); |
| 1022 | + for (const auto v : this->incident_vertex_ids(he_id)) |
| 1023 | + os << ' ' << v; |
| 1024 | + } |
| 1025 | + else if constexpr (std::same_as<directional_tag, bf_directed_t>) { |
| 1026 | + os << this->tail_size(he_id) << ' ' << this->head_size(he_id); |
| 1027 | + for (const auto v : this->tail_vertex_ids(he_id)) |
| 1028 | + os << ' ' << v; |
| 1029 | + for (const auto v : this->head_vertex_ids(he_id)) |
| 1030 | + os << ' ' << v; |
| 1031 | + } |
| 1032 | + |
| 1033 | + if constexpr (traits::c_writable<hyperedge_properties_type>) { |
| 1034 | + if (with_he_props) |
| 1035 | + os << ' ' << hyperedge.properties(); |
| 1036 | + } |
| 1037 | + |
| 1038 | + os << '\n'; |
| 1039 | + } |
| 1040 | + |
| 1041 | + return os; |
| 1042 | + } |
| 1043 | + |
| 1044 | + std::istream& _hgsf_read(std::istream& is) { |
| 1045 | + using fmt_traits = io::detail::hypergraph_fmt_traits<directional_tag>; |
| 1046 | + |
| 1047 | + int dir_discr; |
| 1048 | + is >> dir_discr; |
| 1049 | + |
| 1050 | + if (dir_discr != fmt_traits::discriminator) |
| 1051 | + throw std::ios_base::failure(std::format( |
| 1052 | + "Invalid hypergraph specification: directional specifier {} does not match " |
| 1053 | + "expected {}", |
| 1054 | + dir_discr, |
| 1055 | + fmt_traits::discriminator |
| 1056 | + )); |
| 1057 | + |
| 1058 | + // read hypergraph metadata |
| 1059 | + id_type n_vertices, n_hyperedges; |
| 1060 | + is >> n_vertices >> n_hyperedges; |
| 1061 | + |
| 1062 | + bool with_v_props, with_he_props; |
| 1063 | + is >> with_v_props >> with_he_props; |
| 1064 | + |
| 1065 | + if (with_v_props) { |
| 1066 | + if constexpr (not traits::c_readable<vertex_properties_type>) { |
| 1067 | + throw std::ios_base::failure( |
| 1068 | + "Invalid hypergraph specification: vertex_properties=true " |
| 1069 | + "when vertex_properties_type is not readable" |
| 1070 | + ); |
| 1071 | + } |
| 1072 | + else { |
| 1073 | + std::vector<vertex_properties_type> vertex_properties(n_vertices); |
| 1074 | + for (auto i = 0uz; i < n_vertices; ++i) |
| 1075 | + is >> vertex_properties[i]; |
| 1076 | + this->add_vertices_with(vertex_properties); |
| 1077 | + } |
| 1078 | + } |
| 1079 | + else { |
| 1080 | + this->add_vertices(n_vertices); |
| 1081 | + } |
| 1082 | + |
| 1083 | + if (with_he_props) { |
| 1084 | + if constexpr (not traits::c_readable<hyperedge_properties_type>) { |
| 1085 | + throw std::ios_base::failure( |
| 1086 | + "Invalid hypergraph specification: hyperedge_properties=true " |
| 1087 | + "when hyperedge_properties_type is not readable" |
| 1088 | + ); |
| 1089 | + } |
| 1090 | + } |
| 1091 | + |
| 1092 | + this->_read_hyperedges(is, n_hyperedges, with_he_props); |
| 1093 | + |
| 1094 | + return is; |
| 1095 | + } |
| 1096 | + |
| 1097 | + void _read_hyperedges(std::istream& is, const size_type n_hyperedges, const bool with_he_props) |
| 1098 | + requires std::same_as<directional_tag, undirected_t> |
| 1099 | + { |
| 1100 | + for (auto _ = 0uz; _ < n_hyperedges; ++_) { |
| 1101 | + size_type size; |
| 1102 | + is >> size; |
| 1103 | + |
| 1104 | + std::vector<id_type> v_ids(size); |
| 1105 | + for (auto i = 0uz; i < size; ++i) |
| 1106 | + is >> v_ids[i]; |
| 1107 | + |
| 1108 | + id_type new_he_id; |
| 1109 | + if constexpr (traits::c_readable<hyperedge_properties_type>) { |
| 1110 | + if (with_he_props) { |
| 1111 | + hyperedge_properties_type props; |
| 1112 | + is >> props; |
| 1113 | + new_he_id = this->add_hyperedge_with(std::move(props)).id(); |
| 1114 | + } |
| 1115 | + else { |
| 1116 | + new_he_id = this->add_hyperedge().id(); |
| 1117 | + } |
| 1118 | + } |
| 1119 | + else { |
| 1120 | + new_he_id = this->add_hyperedge().id(); |
| 1121 | + } |
| 1122 | + |
| 1123 | + for (const auto v_id : v_ids) |
| 1124 | + this->bind(v_id, new_he_id); |
| 1125 | + } |
| 1126 | + } |
| 1127 | + |
| 1128 | + void _read_hyperedges(std::istream& is, const size_type n_hyperedges, const bool with_he_props) |
| 1129 | + requires std::same_as<directional_tag, bf_directed_t> |
| 1130 | + { |
| 1131 | + for (auto _ = 0uz; _ < n_hyperedges; ++_) { |
| 1132 | + size_type tail_size, head_size; |
| 1133 | + is >> tail_size >> head_size; |
| 1134 | + |
| 1135 | + std::vector<id_type> tail_ids(tail_size); |
| 1136 | + for (auto i = 0uz; i < tail_size; ++i) |
| 1137 | + is >> tail_ids[i]; |
| 1138 | + |
| 1139 | + std::vector<id_type> head_ids(head_size); |
| 1140 | + for (auto i = 0uz; i < head_size; ++i) |
| 1141 | + is >> head_ids[i]; |
| 1142 | + |
| 1143 | + id_type new_he_id; |
| 1144 | + if constexpr (traits::c_readable<hyperedge_properties_type>) { |
| 1145 | + if (with_he_props) { |
| 1146 | + hyperedge_properties_type props; |
| 1147 | + is >> props; |
| 1148 | + new_he_id = this->add_hyperedge_with(std::move(props)).id(); |
| 1149 | + } |
| 1150 | + else { |
| 1151 | + new_he_id = this->add_hyperedge().id(); |
| 1152 | + } |
| 1153 | + } |
| 1154 | + else { |
| 1155 | + new_he_id = this->add_hyperedge().id(); |
| 1156 | + } |
| 1157 | + |
| 1158 | + for (const auto v_id : tail_ids) |
| 1159 | + this->bind_tail(v_id, new_he_id); |
| 1160 | + for (const auto v_id : head_ids) |
| 1161 | + this->bind_head(v_id, new_he_id); |
| 1162 | + } |
| 1163 | + } |
| 1164 | + |
994 | 1165 | // --- data members --- |
995 | 1166 |
|
996 | 1167 | size_type _n_vertices = 0uz; |
|
0 commit comments