22#include "absl/strings/str_join.h"
23#include "absl/strings/str_split.h"
24#include "absl/types/span.h"
32void CarpParser::Initialize() {
36 number_of_edges_with_servicing_ = 0;
37 number_of_edges_without_servicing_ = 0;
38 total_servicing_cost_ = 0;
40 traversing_costs_.clear();
41 servicing_demands_.clear();
49 return ParseFile(file_name);
52bool CarpParser::ParseFile(
const std::string& file_name) {
53 static auto section_headers = std::array<const char*, 12>({
61 "TIPO_COSTES_ARISTAS",
64 "LISTA_ARISTAS_NOREQ",
68 for (
const std::string&
line :
70 const std::vector<std::string> words =
71 absl::StrSplit(
line, absl::ByAnyChar(
" :\t"), absl::SkipEmpty());
73 if (absl::c_linear_search(section_headers, words[0])) {
75 if (words[0] ==
"LISTA_ARISTAS_REQ") {
78 section_ = ARCS_WITH_SERVICING;
79 }
else if (words[0] ==
"LISTA_ARISTAS_NOREQ") {
81 section_ = ARCS_WITHOUT_SERVICING;
83 if (!ParseMetadataLine(words)) {
84 LOG(ERROR) <<
"Error when parsing the following metadata line: "
92 case ARCS_WITH_SERVICING:
93 if (!ParseEdge(
line,
true)) {
94 LOG(ERROR) <<
"Could not parse line in LISTA_ARISTAS_REQ: " <<
line;
98 case ARCS_WITHOUT_SERVICING:
99 if (!ParseEdge(
line,
false)) {
100 LOG(ERROR) <<
"Could not parse line in LISTA_ARISTAS_NOREQ: "
106 LOG(ERROR) <<
"Could not parse line outside edge lists: " <<
line;
112 return !servicing_demands_.empty();
116std::optional<int64_t> ParseNodeIndex(std::string_view text);
119bool CarpParser::ParseMetadataLine(absl::Span<const std::string> words) {
120 if (words[0] ==
"NOMBRE") {
121 name_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
122 }
else if (words[0] ==
"COMENTARIO") {
123 comment_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
124 }
else if (words[0] ==
"VERTICES") {
126 if (number_of_nodes_ <= 0) {
127 LOG(ERROR) <<
"Error when parsing the number of nodes: " << words[1];
130 }
else if (words[0] ==
"ARISTAS_REQ") {
131 number_of_edges_with_servicing_ =
133 if (number_of_edges_with_servicing_ <= 0) {
134 LOG(ERROR) <<
"Error when parsing the number of edges with servicing: "
138 }
else if (words[0] ==
"ARISTAS_NOREQ") {
139 number_of_edges_without_servicing_ =
141 if (number_of_edges_without_servicing_ < 0) {
144 LOG(ERROR) <<
"Error when parsing the number of edges without servicing: "
148 }
else if (words[0] ==
"VEHICULOS") {
150 if (n_vehicles_ <= 0) {
151 LOG(ERROR) <<
"Error when parsing the number of vehicles: " << words[1];
154 }
else if (words[0] ==
"CAPACIDAD") {
156 if (capacity_ <= 0) {
157 LOG(ERROR) <<
"Error when parsing the capacity: " << words[1];
160 }
else if (words[0] ==
"TIPO_COSTES_ARISTAS") {
161 if (words[1] !=
"EXPLICITOS") {
163 LOG(ERROR) <<
"Value of TIPO_COSTES_ARISTAS is unexpected, only "
164 "EXPLICITOS is supported, but "
165 << words[1] <<
" was found";
168 }
else if (words[0] ==
"COSTE_TOTAL_REQ") {
170 if (total_servicing_cost_ == -1) {
171 LOG(ERROR) <<
"Error when parsing the total servicing cost: " << words[1];
174 }
else if (words[0] ==
"DEPOSITO") {
176 const std::optional<int64_t>
depot = ParseNodeIndex(words[1]);
177 if (!
depot.has_value()) {
178 LOG(ERROR) <<
"Error when parsing the depot: " << words[1];
181 depot_ =
depot.value();
186bool CarpParser::ParseEdge(std::string_view
line,
bool with_servicing) {
187 const std::vector<std::string> words =
188 absl::StrSplit(
line, absl::ByAnyChar(
" :\t(),"), absl::SkipEmpty());
191 std::optional<int64_t> opt_head = ParseNodeIndex(words[0]);
192 if (!opt_head.has_value()) {
193 LOG(ERROR) <<
"Error when parsing the head node: " << words[0];
196 const int64_t
head = opt_head.value();
198 std::optional<int64_t> opt_tail = ParseNodeIndex(words[1]);
199 if (!opt_tail.has_value()) {
200 LOG(ERROR) <<
"Error when parsing the tail node: " << words[1];
203 const int64_t
tail = opt_tail.value();
206 LOG(ERROR) <<
"The head and tail nodes are identical: " <<
line;
211 if (words[2] !=
"coste") {
212 LOG(ERROR) <<
"Unexpected keyword: " << words[2];
216 traversing_costs_[{
tail,
head}] = cost;
219 if (with_servicing) {
220 if (words[4] !=
"demanda") {
221 LOG(ERROR) <<
"Unexpected keyword: " << words[2];
225 servicing_demands_[{
tail,
head}] = servicing;
229 const int64_t next_id = (with_servicing) ? 6 : 4;
230 if (words.size() > next_id) {
231 LOG(ERROR) <<
"Extraneous elements in line, starting with: "
240std::optional<int64_t> ParseNodeIndex(std::string_view text) {
243 LOG(ERROR) <<
"Could not parse node index: " << text;
bool LoadFile(const std::string &file_name)
Loads instance from a file into this parser object.
int64_t NumberOfEdgesWithServicing() const
int64_t depot() const
Returns the index of the depot.
int64_t NumberOfEdges() const
In SWIG mode, we don't want anything besides these top-level includes.
int64_t ParseLeadingInt64Value(const char *str, int64_t deflt)