34 Parser(Pointer_ r) : input(std::move(r)) {}
37 typename std::conditional<parallel_, byteme::PerByteParallel<char, Pointer_>, byteme::PerByte<char, Pointer_> >::type input;
38 size_t current_line = 0;
41 bool passed_banner =
false;
44 void parse_banner(
const std::string& contents) {
47 auto obj_str = contents.c_str() + pos;
48 if (std::strncmp(obj_str,
"matrix ", 7) == 0) {
49 details.
object = Object::MATRIX;
51 }
else if (std::strncmp(obj_str,
"vector ", 7) == 0) {
52 details.
object = Object::VECTOR;
55 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
58 auto format_str = contents.c_str() + pos;
59 if (std::strncmp(format_str,
"coordinate ", 11) == 0) {
60 details.
format = Format::COORDINATE;
62 }
else if (std::strncmp(format_str,
"array ", 6) == 0) {
63 details.
format = Format::ARRAY;
66 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
69 auto field_str = contents.c_str() + pos;
70 if (std::strncmp(field_str,
"integer ", 8) == 0) {
71 details.
field = Field::INTEGER;
73 }
else if (std::strncmp(field_str,
"double ", 7) == 0) {
74 details.
field = Field::DOUBLE;
76 }
else if (std::strncmp(field_str,
"complex ", 8) == 0) {
77 details.
field = Field::COMPLEX;
79 }
else if (std::strncmp(field_str,
"pattern ", 8) == 0) {
80 details.
field = Field::PATTERN;
82 }
else if (std::strncmp(field_str,
"real ", 5) == 0) {
83 details.
field = Field::REAL;
86 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
89 auto sym_str = contents.c_str() + pos;
90 if (std::strcmp(sym_str,
"general") == 0) {
91 details.
symmetry = Symmetry::GENERAL;
92 }
else if (std::strcmp(sym_str,
"symmetric") == 0) {
93 details.
symmetry = Symmetry::SYMMETRIC;
94 }
else if (std::strcmp(sym_str,
"skew-symmetric") == 0) {
95 details.
symmetry = Symmetry::SKEW_SYMMETRIC;
96 }
else if (std::strcmp(sym_str,
"hermitian") == 0) {
97 details.
symmetry = Symmetry::HERMITIAN;
99 throw std::runtime_error(
"fourth banner field should be one of 'general', 'symmetric', 'skew-symemtric' or 'hermitian'");
104 std::string contents;
105 bool valid = input.valid();
107 throw std::runtime_error(
"banner has already been scanned");
111 if (input.get() !=
'%') {
112 throw std::runtime_error(
"failed to find banner line before non-commented line " + std::to_string(current_line + 1));
117 valid = input.advance();
118 }
while (valid && input.get() ==
'%');
121 while (valid && input.get() !=
'\n') {
122 contents += input.get();
123 valid = input.advance();
130 valid = input.advance();
132 if (contents.rfind(
"MatrixMarket ", 0) == 0) {
133 parse_banner(contents);
134 passed_banner =
true;
140 throw std::runtime_error(
"failed to find banner line before end of file");
151 if (!passed_banner) {
152 throw std::runtime_error(
"banner has not yet been scanned");
158 bool passed_size =
false;
159 size_t nrows = 0, ncols = 0, nlines = 0;
161 void check_size(
int onto,
bool non_empty) {
163 throw std::runtime_error(
"detected an empty size field on line " + std::to_string(current_line + 1));
166 if (details.
object == Object::MATRIX) {
167 if (details.
format == Format::COORDINATE) {
169 throw std::runtime_error(
"expected three size fields for coordinate matrices on line " + std::to_string(current_line + 1));
171 }
else if (details.
format == Format::ARRAY) {
173 throw std::runtime_error(
"expected two size fields for array matrices on line " + std::to_string(current_line + 1));
175 nlines = nrows * ncols;
178 if (details.
format == Format::COORDINATE) {
180 throw std::runtime_error(
"expected two size fields for coordinate vectors on line " + std::to_string(current_line + 1));
183 }
else if (details.
format == Format::ARRAY) {
185 throw std::runtime_error(
"expected one size field for array vectors on line " + std::to_string(current_line + 1));
197 bool non_empty =
false;
198 bool valid = input.valid();
201 if (input.get() ==
'%') {
204 valid = input.advance();
205 }
while (valid && input.get() !=
'\n');
211 valid = input.advance();
218 char current = input.get();
222 throw std::runtime_error(
"detected an empty size field on line " + std::to_string(current_line + 1));
229 valid = input.advance();
230 check_size(onto, non_empty);
233 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
236 auto delta = current -
'0';
255 throw std::runtime_error(
"only non-negative integers should be present on line " + std::to_string(current_line + 1));
258 valid = input.advance();
262 check_size(onto, non_empty);
275 throw std::runtime_error(
"size line has not yet been scanned");
289 throw std::runtime_error(
"size line has not yet been scanned");
303 throw std::runtime_error(
"size line has not yet been scanned");
320 void check_coordinate_common(
size_t currow,
size_t current_data_line,
bool non_empty)
const {
322 throw std::runtime_error(
"empty field detected on line " + std::to_string(current_line + 1));
325 if (current_data_line >= nlines) {
326 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(nlines) +
")");
330 throw std::runtime_error(
"row index must be positive on line " + std::to_string(current_line + 1));
332 if (currow > nrows) {
333 throw std::runtime_error(
"row index out of range on line " + std::to_string(current_line + 1));
337 template<Field field_>
338 void check_coordinate_matrix(
size_t currow,
size_t curcol,
size_t current_data_line,
int onto,
bool non_empty)
const {
339 check_coordinate_common(currow, current_data_line, non_empty);
341 if constexpr(field_ == Field::REAL || field_ == Field::DOUBLE || field_ == Field::INTEGER) {
343 throw std::runtime_error(
"expected 3 fields on line " + std::to_string(current_line + 1));
345 }
else if constexpr(field_ == Field::PATTERN) {
347 throw std::runtime_error(
"expected 2 fields on line " + std::to_string(current_line + 1));
349 }
else if constexpr(field_ == Field::COMPLEX) {
351 throw std::runtime_error(
"expected 4 fields on line " + std::to_string(current_line + 1));
356 throw std::runtime_error(
"column index must be positive on line " + std::to_string(current_line + 1));
358 if (curcol > ncols) {
359 throw std::runtime_error(
"column index out of range on line " + std::to_string(current_line + 1));
363 template<Field field_>
364 void check_coordinate_vector(
size_t currow,
size_t current_data_line,
int onto,
bool non_empty)
const {
365 check_coordinate_common(currow, current_data_line, non_empty);
367 if constexpr(field_ == Field::REAL || field_ == Field::DOUBLE || field_ == Field::INTEGER) {
369 throw std::runtime_error(
"expected 2 fields on line " + std::to_string(current_line + 1));
371 }
else if constexpr(field_ == Field::PATTERN) {
373 throw std::runtime_error(
"expected 1 field on line " + std::to_string(current_line + 1));
375 }
else if constexpr(field_ == Field::COMPLEX) {
377 throw std::runtime_error(
"expected 3 fields on line " + std::to_string(current_line + 1));
382 template<Object
object_, Field field_,
class Store_,
class Compose_,
class Bump_,
class Finish_>
383 bool scan_coordinate(Store_ store, Compose_ compose, Bump_ bump, Finish_ finish) {
384 if (!passed_banner || !passed_size) {
385 throw std::runtime_error(
"banner or size lines have not yet been parsed");
388 size_t current_data_line = 0;
389 size_t currow = 0, curcol = 0;
391 bool non_empty =
false;
392 bool valid = input.valid();
394 if constexpr(object_ == Object::VECTOR) {
398 typedef typename std::invoke_result<Finish_, size_t>::type finish_value;
399 constexpr bool can_quit = std::is_same<typename std::invoke_result<Store_, size_t, size_t, finish_value>::type,
bool>::value;
402 if (input.get() ==
'%') {
405 valid = input.advance();
406 }
while (valid && input.get() !=
'\n');
412 valid = input.advance();
418 char current = input.get();
419 if (current ==
' ') {
421 throw std::runtime_error(
"detected empty field on line " + std::to_string(current_line + 1));
423 if constexpr(object_ == Object::MATRIX) {
435 }
else if (current ==
'\n') {
436 if constexpr(object_ == Object::MATRIX) {
437 check_coordinate_matrix<field_>(currow, curcol, current_data_line, onto, non_empty);
439 check_coordinate_vector<field_>(currow, current_data_line, onto, non_empty);
442 if constexpr(can_quit) {
443 if (!store(currow, curcol, finish(current_line))) {
447 store(currow, curcol, finish(current_line));
451 if constexpr(object_ == Object::MATRIX) {
458 valid = input.advance();
464 if (current <
'0' || current >
'9') {
465 throw std::runtime_error(
"row index should be a non-negative integer on line " + std::to_string(current_line + 1));
468 currow += current -
'0';
472 if constexpr(object_ == Object::MATRIX) {
473 if (current <
'0' || current >
'9') {
474 throw std::runtime_error(
"column index should be a non-negative integer on line " + std::to_string(current_line + 1));
477 curcol += current -
'0';
479 compose(current, current_line);
484 compose(current, current_line);
490 valid = input.advance();
497 if (onto != 0 || non_empty) {
498 if constexpr(object_ == Object::MATRIX) {
499 check_coordinate_matrix<field_>(currow, curcol, current_data_line, onto, non_empty);
501 check_coordinate_vector<field_>(currow, current_data_line, onto, non_empty);
504 if constexpr(can_quit) {
505 if (!store(currow, curcol, finish(current_line))) {
509 store(currow, curcol, finish(current_line));
515 if (current_data_line != nlines) {
516 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(nlines) +
")");
522 template<Field field_>
523 void check_array(
size_t current_data_line,
int onto,
bool non_empty)
const {
525 throw std::runtime_error(
"empty field detected on line " + std::to_string(current_line + 1));
528 if constexpr(field_ == Field::REAL || field_ == Field::DOUBLE || field_ == Field::INTEGER) {
530 throw std::runtime_error(
"expected 1 field on line " + std::to_string(current_line + 1));
532 }
else if constexpr(field_ == Field::COMPLEX) {
534 throw std::runtime_error(
"expected 2 fields on line " + std::to_string(current_line + 1));
538 if (current_data_line >= nlines) {
539 throw std::runtime_error(
"more lines present than expected for an array format (" + std::to_string(nlines) +
")");
543 template<Field field_,
class Store_,
class Compose_,
class Bump_,
class Finish_>
544 bool scan_array(Store_ store, Compose_ compose, Bump_ bump, Finish_ finish) {
545 if (!passed_banner || !passed_size) {
546 throw std::runtime_error(
"banner or size lines have not yet been parsed");
549 size_t current_data_line = 0;
550 size_t currow = 1, curcol = 1;
552 bool non_empty =
false;
553 bool valid = input.valid();
555 typedef typename std::invoke_result<Finish_, size_t>::type finish_value;
556 constexpr bool can_quit = std::is_same<typename std::invoke_result<Store_, size_t, size_t, finish_value>::type,
bool>::value;
559 if (input.get() ==
'%') {
562 valid = input.advance();
563 }
while (valid && input.get() !=
'\n');
569 valid = input.advance();
575 char current = input.get();
576 if (current ==
' ') {
578 throw std::runtime_error(
"detected empty field on line " + std::to_string(current_line + 1));
584 }
else if (current ==
'\n') {
585 check_array<field_>(current_data_line, onto, non_empty);
586 if constexpr(can_quit) {
587 if (!store(currow, curcol, finish(current_line))) {
591 store(currow, curcol, finish(current_line));
595 if (currow > nrows) {
604 valid = input.advance();
608 compose(current, current_line);
612 valid = input.advance();
619 if (onto != 0 || non_empty) {
620 check_array<field_>(current_data_line, onto, non_empty);
621 if constexpr(can_quit) {
622 if (!store(currow, curcol, finish(current_line))) {
626 store(currow, curcol, finish(current_line));
631 if (current_data_line != nlines) {
632 throw std::runtime_error(
"fewer lines present than expected for an array format (" + std::to_string(nlines) +
")");
638 template<
typename Type_>
639 static Type_ convert_to_real(
const std::string& temporary,
size_t line) {
644 if constexpr(std::is_same<Type_, float>::value) {
645 output = std::stof(temporary, &n);
646 }
else if constexpr(std::is_same<Type_, long double>::value) {
647 output = std::stold(temporary, &n);
649 output = std::stod(temporary, &n);
651 }
catch (std::invalid_argument& e) {
652 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(line + 1));
655 if (n != temporary.size()) {
656 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(line + 1));
676 template<
typename Type_ =
int,
class Store_>
679 bool negative =
false;
682 auto compose = [&](
char x,
size_t line) ->
void {
690 if (x <
'0' || x >
'9') {
691 throw std::runtime_error(
"expected an integer value on line " + std::to_string(line + 1));
697 auto bump = [](size_t) ->
void {};
699 auto finish = [&](size_t) -> Type_ {
711 if (details.
format == Format::COORDINATE) {
712 if (details.
object == Object::MATRIX) {
713 return scan_coordinate<Object::MATRIX, Field::INTEGER>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
715 return scan_coordinate<Object::VECTOR, Field::INTEGER>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
718 return scan_array<Field::INTEGER>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
735 template<
typename Type_ =
double,
class Store_>
737 std::string temporary;
739 auto compose = [&](
char x,
size_t line) ->
void {
740 if (std::isspace(x)) {
741 throw std::runtime_error(
"detected whitespace in value on line " + std::to_string(line + 1));
746 auto bump = [](size_t) ->
void {};
748 auto finish = [&](
size_t line) ->
double {
749 double output = convert_to_real<Type_>(temporary, line);
754 if (details.
format == Format::COORDINATE) {
755 if (details.
object == Object::MATRIX) {
756 return scan_coordinate<Object::MATRIX, Field::REAL>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
758 return scan_coordinate<Object::VECTOR, Field::REAL>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
761 return scan_array<Field::REAL>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
779 template<
typename Type_ =
double,
class Store_>
781 return scan_real<Type_, Store_>(std::forward<Store_>(store));
797 template<
typename Type_ =
double,
class Store_>
799 std::string temporary;
800 std::complex<Type_> holding;
802 auto compose = [&](
char x,
size_t line) ->
void {
803 if (std::isspace(x)) {
804 throw std::runtime_error(
"detected whitespace in value on line " + std::to_string(line + 1));
809 auto bump = [&](
size_t line) ->
void {
810 holding.real(convert_to_real<Type_>(temporary, line));
814 auto finish = [&](
size_t line) -> std::complex<Type_> {
815 holding.imag(convert_to_real<Type_>(temporary, line));
820 if (details.
format == Format::COORDINATE) {
821 if (details.
object == Object::MATRIX) {
822 return scan_coordinate<Object::MATRIX, Field::COMPLEX>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
824 return scan_coordinate<Object::VECTOR, Field::COMPLEX>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
827 return scan_array<Field::COMPLEX>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
846 template<
typename Type_ =
bool,
class Store_>
848 auto compose = [](char, size_t) ->
void {};
849 auto bump = [](size_t) ->
void {};
850 auto finish = [](size_t) -> Type_ {
854 if (details.
format != Format::COORDINATE) {
855 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
858 if (details.
object == Object::MATRIX) {
859 return scan_coordinate<Object::MATRIX, Field::PATTERN>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));
861 return scan_coordinate<Object::VECTOR, Field::PATTERN>(std::forward<Store_>(store), std::move(compose), std::move(bump), std::move(finish));