241 my_input(std::move(input)),
242 my_nthreads(options.num_threads),
243 my_block_size(options.block_size)
247 std::unique_ptr<Input_> my_input;
249 std::size_t my_block_size;
251 Index my_current_line = 0;
254 template<
typename Input2_>
255 static bool chomp(Input2_& input) {
257 char x = input.get();
258 if (x !=
' ' && x !=
'\t') {
261 if (!(input.advance())) {
268 template<
typename Input2_>
269 static bool advance_and_chomp(Input2_& input) {
272 if (!(input.advance())) {
278 template<
typename Input2_>
279 static bool skip_lines(Input2_& input,
Index& current_line) {
282 char x = input.get();
285 if (!(input.advance())) {
288 }
while (input.get() !=
'\n');
289 }
else if (x !=
'\n') {
293 if (!input.advance()) {
302 bool my_passed_banner =
false;
304 struct ExpectedMatch {
305 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
306 ExpectedMatch() : ExpectedMatch(false, false, false) {}
312 ExpectedMatch advance_past_expected_string() {
313 if (!(my_input->advance())) {
314 return ExpectedMatch(
true,
false,
false);
317 char next = my_input->get();
318 if (next ==
' ' || next ==
'\t') {
319 if (!advance_and_chomp(*my_input)) {
320 return ExpectedMatch(
true,
false,
false);
322 if (my_input->get() ==
'\n') {
323 bool remaining = my_input->advance();
324 return ExpectedMatch(
true,
true, remaining);
326 return ExpectedMatch(
true,
false,
true);
328 }
else if (next ==
'\n') {
329 bool remaining = my_input->advance();
330 return ExpectedMatch(
true,
true, remaining);
334 return ExpectedMatch(
false,
true,
true);
337 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
343 for (std::size_t i = start; i < len; ++i) {
344 if (!my_input->advance()) {
345 return ExpectedMatch(
false,
false,
false);
347 if (my_input->get() != ptr[i]) {
348 return ExpectedMatch(
false,
false,
true);
351 return advance_past_expected_string();
354 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
357 return is_expected_string(ptr, len, 1);
360 bool parse_banner_object() {
363 char x = my_input->get();
365 res = is_expected_string(
"matrix", 6);
366 my_details.
object = Object::MATRIX;
367 }
else if (x ==
'v') {
368 res = is_expected_string(
"vector", 6);
369 my_details.
object = Object::VECTOR;
373 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
375 if (!res.remaining) {
376 throw std::runtime_error(
"end of file reached after the first banner field");
382 bool parse_banner_format() {
385 char x = my_input->get();
387 res = is_expected_string(
"coordinate", 10);
388 my_details.
format = Format::COORDINATE;
389 }
else if (x ==
'a') {
390 res = is_expected_string(
"array", 5);
391 my_details.
format = Format::ARRAY;
395 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
397 if (!res.remaining) {
398 throw std::runtime_error(
"end of file reached after the second banner field");
404 bool parse_banner_field() {
407 char x = my_input->get();
409 res = is_expected_string(
"integer", 7);
410 my_details.
field = Field::INTEGER;
411 }
else if (x ==
'd') {
412 res = is_expected_string(
"double", 6);
413 my_details.
field = Field::DOUBLE;
414 }
else if (x ==
'c') {
415 res = is_expected_string(
"complex", 7);
416 my_details.
field = Field::COMPLEX;
417 }
else if (x ==
'p') {
418 res = is_expected_string(
"pattern", 7);
419 my_details.
field = Field::PATTERN;
420 }
else if (x ==
'r') {
421 res = is_expected_string(
"real", 4);
422 my_details.
field = Field::REAL;
426 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
428 if (!res.remaining) {
429 throw std::runtime_error(
"end of file reached after the third banner field");
435 bool parse_banner_symmetry() {
438 char x = my_input->get();
440 res = is_expected_string(
"general", 7);
441 my_details.
symmetry = Symmetry::GENERAL;
442 }
else if (x ==
'h') {
443 res = is_expected_string(
"hermitian", 9);
444 my_details.
symmetry = Symmetry::HERMITIAN;
445 }
else if (x ==
's') {
446 if (my_input->advance()) {
447 char x = my_input->get();
449 res = is_expected_string(
"skew-symmetric", 14, 2);
450 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
452 res = is_expected_string(
"symmetric", 9, 2);
453 my_details.
symmetry = Symmetry::SYMMETRIC;
459 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
461 if (!res.remaining) {
462 throw std::runtime_error(
"end of file reached after the fourth banner field");
469 if (my_passed_banner) {
470 throw std::runtime_error(
"banner has already been scanned");
472 if (!(my_input->valid())) {
473 throw std::runtime_error(
"failed to find banner line before end of file");
475 if (my_input->get() !=
'%') {
476 throw std::runtime_error(
"first line of the file should be the banner");
479 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
480 if (!found_banner.remaining) {
481 throw std::runtime_error(
"end of file reached before matching the banner");
483 if (!found_banner.found) {
484 throw std::runtime_error(
"first line of the file should be the banner");
486 if (found_banner.newline) {
487 throw std::runtime_error(
"end of line reached before matching the banner");
490 if (parse_banner_object()) {
491 throw std::runtime_error(
"end of line reached after the first banner field");
493 if (parse_banner_format()) {
494 throw std::runtime_error(
"end of line reached after the second banner field");
498 if (my_details.
object == Object::MATRIX) {
499 if (parse_banner_field()) {
500 throw std::runtime_error(
"end of line reached after the third banner field");
502 eol = parse_banner_symmetry();
507 my_details.
symmetry = Symmetry::GENERAL;
510 eol = parse_banner_field();
513 my_passed_banner =
true;
520 if (!(my_input->advance())) {
521 throw std::runtime_error(
"end of file reached before the end of the banner line");
523 }
while (my_input->get() !=
'\n');
538 if (!my_passed_banner) {
539 throw std::runtime_error(
"banner has not yet been scanned");
547 struct NotLastSizeInfo {
551 struct LastSizeInfo {
553 bool remaining =
false;
557 using SizeInfo =
typename std::conditional<last_, LastSizeInfo, NotLastSizeInfo>::type;
559 template<
bool last_,
class Input2_>
560 static SizeInfo<last_> scan_integer_field(
bool size, Input2_& input,
Index overall_line_count) {
561 SizeInfo<last_> output;
564 auto what = [&]() -> std::string {
573 char x = input.get();
575 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
578 output.index += x -
'0';
587 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
589 if constexpr(last_) {
590 output.remaining = input.advance();
593 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
595 if (!advance_and_chomp(input)) {
596 if constexpr(last_) {
599 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
602 if constexpr(last_) {
603 if (input.get() !=
'\n') {
604 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
606 output.remaining = input.advance();
610 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
613 if (!(input.advance())) {
614 if constexpr(last_) {
617 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
625 template<
bool last_,
class Input2_>
626 static SizeInfo<last_> scan_size_field(Input2_& input,
Index overall_line_count) {
627 return scan_integer_field<last_>(
true, input, overall_line_count);
630 template<
bool last_,
class Input2_>
631 static SizeInfo<last_> scan_index_field(Input2_& input,
Index overall_line_count) {
632 return scan_integer_field<last_>(
false, input, overall_line_count);
636 bool my_passed_size =
false;
637 Index my_nrows = 0, my_ncols = 0, my_nlines = 0;
640 if (!(my_input->valid())) {
641 throw std::runtime_error(
"failed to find size line before end of file");
645 if (!skip_lines(*my_input, my_current_line)) {
646 throw std::runtime_error(
"failed to find size line before end of file");
648 if (!chomp(*my_input)) {
649 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
652 if (my_details.
object == Object::MATRIX) {
653 if (my_details.
format == Format::COORDINATE) {
654 auto first_field = scan_size_field<false>(*my_input, my_current_line);
655 my_nrows = first_field.index;
657 auto second_field = scan_size_field<false>(*my_input, my_current_line);
658 my_ncols = second_field.index;
660 auto third_field = scan_size_field<true>(*my_input, my_current_line);
661 my_nlines = third_field.index;
664 auto first_field = scan_size_field<false>(*my_input, my_current_line);
665 my_nrows = first_field.index;
667 auto second_field = scan_size_field<true>(*my_input, my_current_line);
668 my_ncols = second_field.index;
669 my_nlines = my_nrows * my_ncols;
673 if (my_details.
format == Format::COORDINATE) {
674 auto first_field = scan_size_field<false>(*my_input, my_current_line);
675 my_nrows = first_field.index;
677 auto second_field = scan_size_field<true>(*my_input, my_current_line);
678 my_nlines = second_field.index;
681 auto first_field = scan_size_field<true>(*my_input, my_current_line);
682 my_nlines = first_field.index;
683 my_nrows = my_nlines;
688 my_passed_size =
true;
700 if (!my_passed_size) {
701 throw std::runtime_error(
"size line has not yet been scanned");
714 if (!my_passed_size) {
715 throw std::runtime_error(
"size line has not yet been scanned");
728 if (!my_passed_size) {
729 throw std::runtime_error(
"size line has not yet been scanned");
746 template<
typename Type_>
752 template<
typename Workspace_>
753 bool configure_parallel_workspace(Workspace_& work) {
754 bool available = fill_to_next_newline(*my_input, work.buffer, my_block_size);
755 work.contents.clear();
756 work.overall_line = my_current_line;
757 my_current_line += count_newlines(work.buffer);
761 void check_num_lines_loop(
Index data_line_count)
const {
762 if (data_line_count >= my_nlines) {
763 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
767 void check_num_lines_final(
bool finished,
Index data_line_count)
const {
769 if (data_line_count != my_nlines) {
771 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
777 void check_matrix_coordinate_line(
Index currow,
Index curcol,
Index overall_line_count)
const {
779 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
781 if (currow > my_nrows) {
782 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
785 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
787 if (curcol > my_ncols) {
788 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
792 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
793 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
794 bool valid = input.valid();
797 if (!skip_lines(input, overall_line_count)) {
801 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
804 auto first_field = scan_index_field<false>(input, overall_line_count);
805 auto second_field = scan_index_field<false>(input, overall_line_count);
806 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
809 ParseInfo<Type_> res = fparser(input, overall_line_count);
810 if (!wstore(first_field.index, second_field.index, res.value)) {
813 ++overall_line_count;
814 valid = res.remaining;
820 template<
typename Type_,
class FieldParser_,
class Store_>
821 bool scan_matrix_coordinate_non_pattern(Store_ store) {
822 bool finished =
false;
823 Index current_data_line = 0;
825 if (my_nthreads == 1) {
826 FieldParser_ fparser;
827 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
832 check_num_lines_loop(current_data_line);
834 return store(r, c, value);
840 std::vector<char> buffer;
841 FieldParser_ fparser;
842 std::vector<std::tuple<Index, Index, Type_> > contents;
846 ThreadPool<Workspace> tp(
847 [&](Workspace& work) ->
bool {
848 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
850 return scan_matrix_coordinate_non_pattern_base<Type_>(
855 work.contents.emplace_back(r, c, value);
864 [&](Workspace& work) ->
bool {
865 return configure_parallel_workspace(work);
867 [&](Workspace& work) ->
bool {
868 for (
const auto& con : work.contents) {
869 check_num_lines_loop(current_data_line);
870 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
880 check_num_lines_final(finished, current_data_line);
885 template<
class Input2_,
class WrappedStore_>
886 bool scan_matrix_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
887 bool valid = input.valid();
890 if (!skip_lines(input, overall_line_count)) {
894 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
897 auto first_field = scan_index_field<false>(input, overall_line_count);
898 auto second_field = scan_index_field<true>(input, overall_line_count);
899 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
901 if (!wstore(first_field.index, second_field.index)) {
904 ++overall_line_count;
905 valid = second_field.remaining;
911 template<
class Store_>
912 bool scan_matrix_coordinate_pattern(Store_ store) {
913 bool finished =
false;
914 Index current_data_line = 0;
916 if (my_nthreads == 1) {
917 finished = scan_matrix_coordinate_pattern_base(
921 check_num_lines_loop(current_data_line);
929 std::vector<char> buffer;
930 std::vector<std::tuple<Index, Index> > contents;
934 ThreadPool<Workspace> tp(
935 [&](Workspace& work) ->
bool {
936 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
938 return scan_matrix_coordinate_pattern_base(
942 work.contents.emplace_back(r, c);
951 [&](Workspace& work) ->
bool {
952 return configure_parallel_workspace(work);
954 [&](Workspace& work) ->
bool {
955 for (
const auto& con : work.contents) {
956 check_num_lines_loop(current_data_line);
957 if (!store(std::get<0>(con), std::get<1>(con))) {
967 check_num_lines_final(finished, current_data_line);
972 void check_vector_coordinate_line(
Index currow,
Index overall_line_count)
const {
974 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
976 if (currow > my_nrows) {
977 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
981 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
982 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
983 bool valid = input.valid();
986 if (!skip_lines(input, overall_line_count)) {
990 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
993 auto first_field = scan_index_field<false>(input, overall_line_count);
994 check_vector_coordinate_line(first_field.index, overall_line_count);
997 ParseInfo<Type_> res = fparser(input, overall_line_count);
998 if (!wstore(first_field.index, res.value)) {
1001 ++overall_line_count;
1002 valid = res.remaining;
1008 template<
typename Type_,
class FieldParser_,
class Store_>
1009 bool scan_vector_coordinate_non_pattern(Store_ store) {
1010 bool finished =
false;
1011 Index current_data_line = 0;
1013 if (my_nthreads == 1) {
1014 FieldParser_ fparser;
1015 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1019 [&](
Index r, Type_ value) ->
bool {
1020 check_num_lines_loop(current_data_line);
1021 ++current_data_line;
1022 return store(r, 1, value);
1028 std::vector<char> buffer;
1029 FieldParser_ fparser;
1030 std::vector<std::tuple<Index, Type_> > contents;
1034 ThreadPool<Workspace> tp(
1035 [&](Workspace& work) ->
bool {
1036 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1038 return scan_vector_coordinate_non_pattern_base<Type_>(
1042 [&](
Index r, Type_ value) ->
bool {
1043 work.contents.emplace_back(r, value);
1052 [&](Workspace& work) ->
bool {
1053 return configure_parallel_workspace(work);
1055 [&](Workspace& work) ->
bool {
1056 for (
const auto& con : work.contents) {
1057 check_num_lines_loop(current_data_line);
1058 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1061 ++current_data_line;
1068 check_num_lines_final(finished, current_data_line);
1073 template<
class Input2_,
class WrappedStore_>
1074 bool scan_vector_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
1075 bool valid = input.valid();
1078 if (!skip_lines(input, overall_line_count)) {
1081 if (!chomp(input)) {
1082 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1085 auto first_field = scan_index_field<true>(input, overall_line_count);
1086 check_vector_coordinate_line(first_field.index, overall_line_count);
1088 if (!wstore(first_field.index)) {
1091 ++overall_line_count;
1092 valid = first_field.remaining;
1098 template<
class Store_>
1099 bool scan_vector_coordinate_pattern(Store_ store) {
1100 bool finished =
false;
1101 Index current_data_line = 0;
1103 if (my_nthreads == 1) {
1104 finished = scan_vector_coordinate_pattern_base(
1107 [&](
Index r) ->
bool {
1108 check_num_lines_loop(current_data_line);
1109 ++current_data_line;
1116 std::vector<char> buffer;
1117 std::vector<Index> contents;
1121 ThreadPool<Workspace> tp(
1122 [&](Workspace& work) ->
bool {
1123 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1125 return scan_vector_coordinate_pattern_base(
1128 [&](
Index r) ->
bool {
1129 work.contents.emplace_back(r);
1138 [&](Workspace& work) ->
bool {
1139 return configure_parallel_workspace(work);
1141 [&](Workspace& work) ->
bool {
1142 for (
const auto& r : work.contents) {
1143 check_num_lines_loop(current_data_line);
1147 ++current_data_line;
1154 check_num_lines_final(finished, current_data_line);
1159 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1160 bool scan_matrix_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1161 bool valid = input.valid();
1164 if (!skip_lines(input, overall_line_count)) {
1167 if (!chomp(input)) {
1168 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1172 ParseInfo<Type_> res = fparser(input, overall_line_count);
1173 if (!wstore(res.value)) {
1176 ++overall_line_count;
1177 valid = res.remaining;
1183 template<
typename Type_,
class FieldParser_,
class Store_>
1184 bool scan_matrix_array(Store_ store) {
1185 bool finished =
false;
1186 Index current_data_line = 0;
1188 Index currow = 1, curcol = 1;
1189 auto increment = [&]() {
1191 if (currow > my_nrows) {
1197 if (my_nthreads == 1) {
1198 FieldParser_ fparser;
1199 finished = scan_matrix_array_base<Type_>(
1203 [&](Type_ value) ->
bool {
1204 check_num_lines_loop(current_data_line);
1205 if (!store(currow, curcol, value)) {
1208 ++current_data_line;
1216 std::vector<char> buffer;
1217 FieldParser_ fparser;
1218 std::vector<Type_> contents;
1222 ThreadPool<Workspace> tp(
1223 [&](Workspace& work) ->
bool {
1224 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1226 return scan_matrix_array_base<Type_>(
1230 [&](Type_ value) ->
bool {
1231 work.contents.emplace_back(value);
1240 [&](Workspace& work) ->
bool {
1241 return configure_parallel_workspace(work);
1243 [&](Workspace& work) ->
bool {
1244 for (
const auto& val : work.contents) {
1245 check_num_lines_loop(current_data_line);
1246 if (!store(currow, curcol, val)) {
1249 ++current_data_line;
1257 check_num_lines_final(finished, current_data_line);
1262 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1263 bool scan_vector_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1264 bool valid = input.valid();
1267 if (!skip_lines(input, overall_line_count)) {
1270 if (!chomp(input)) {
1271 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1275 ParseInfo<Type_> res = fparser(input, overall_line_count);
1276 if (!wstore(res.value)) {
1279 ++overall_line_count;
1280 valid = res.remaining;
1286 template<
typename Type_,
class FieldParser_,
class Store_>
1287 bool scan_vector_array(Store_ store) {
1288 bool finished =
false;
1289 Index current_data_line = 0;
1290 if (my_nthreads == 1) {
1291 FieldParser_ fparser;
1292 finished = scan_vector_array_base<Type_>(
1296 [&](Type_ value) ->
bool {
1297 check_num_lines_loop(current_data_line);
1298 ++current_data_line;
1299 return store(current_data_line, 1, value);
1305 std::vector<char> buffer;
1306 FieldParser_ fparser;
1307 std::vector<Type_> contents;
1311 ThreadPool<Workspace> tp(
1312 [&](Workspace& work) ->
bool {
1313 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1315 return scan_vector_array_base<Type_>(
1319 [&](Type_ value) ->
bool {
1320 work.contents.emplace_back(value);
1329 [&](Workspace& work) ->
bool {
1330 return configure_parallel_workspace(work);
1332 [&](Workspace& work) ->
bool {
1333 for (
const auto& val : work.contents) {
1334 check_num_lines_loop(current_data_line);
1335 ++current_data_line;
1336 if (!store(current_data_line, 1, val)) {
1345 check_num_lines_final(finished, current_data_line);
1350 void check_preamble()
const {
1351 if (!my_passed_banner || !my_passed_size) {
1352 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1356 template<
typename Type_>
1357 class IntegerFieldParser {
1359 template<
class Input2_>
1360 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1361 bool negative = (input.get() ==
'-');
1363 if (!(input.advance())) {
1364 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1369 auto finish = [&](
bool valid) -> ParseInfo<Type_> {
1370 ParseInfo<Type_> output;
1371 output.remaining = valid;
1381 char x = input.get();
1383 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1388 case ' ':
case '\t':
1389 if (!advance_and_chomp(input)) {
1390 return finish(
false);
1392 if (input.get() !=
'\n') {
1393 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1395 return finish(input.advance());
1403 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1405 return finish(input.advance());
1407 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1410 if (!(input.advance())) {
1415 return finish(
false);
1433 template<
typename Type_ =
int,
class Store_>
1437 auto wrapped_store = [&](
Index r,
Index c, Type_ val) ->
bool {
1438 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1439 return store(r, c, val);
1446 if (my_details.
format == Format::COORDINATE) {
1447 if (my_details.
object == Object::MATRIX) {
1448 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1450 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1453 if (my_details.
object == Object::MATRIX) {
1454 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1456 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1462 template<
typename Type_>
1463 static Type_ convert_to_real(
const std::string& temporary,
Index overall_line_count) {
1468 if constexpr(std::is_same<Type_, float>::value) {
1469 output = std::stof(temporary, &n);
1470 }
else if constexpr(std::is_same<Type_, long double>::value) {
1471 output = std::stold(temporary, &n);
1473 output = std::stod(temporary, &n);
1475 }
catch (std::invalid_argument& e) {
1476 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1479 if (n != temporary.size()) {
1480 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1486 template<
typename Type_>
1487 class RealFieldParser {
1489 template<
class Input2_>
1490 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1491 ParseInfo<Type_> output;
1492 output.remaining =
true;
1495 char x = input.get();
1498 if (temporary.empty()) {
1500 throw std::runtime_error(
"empty number field on line " + std::to_string(overall_line_count + 1));
1502 output.remaining = input.advance();
1506 if (!(input.advance())) {
1507 output.remaining =
false;
1513 if (x ==
' ' || x ==
'\t') {
1514 if (!advance_and_chomp(input)) {
1515 output.remaining =
false;
1518 if (input.get() !=
'\n') {
1519 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1521 output.remaining = input.advance();
1526 output.value = convert_to_real<Type_>(temporary, overall_line_count);
1531 std::string temporary;
1548 template<
typename Type_ =
double,
class Store_>
1552 auto store_real = [&](
Index r,
Index c, Type_ val) ->
bool {
1553 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1554 return store(r, c, val);
1561 if (my_details.
format == Format::COORDINATE) {
1562 if (my_details.
object == Object::MATRIX) {
1563 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1565 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1568 if (my_details.
object == Object::MATRIX) {
1569 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1571 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1590 template<
typename Type_ =
double,
class Store_>
1596 template<
typename InnerType_>
1597 class ComplexFieldParser {
1599 template<
typename Input2_>
1600 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
Index overall_line_count) {
1601 ParseInfo<std::complex<InnerType_> > output;
1602 output.remaining =
true;
1603 std::complex<InnerType_> holding;
1607 char x = input.get();
1610 if (temporary.empty()) {
1612 throw std::runtime_error(
"empty real field on line " + std::to_string(overall_line_count + 1));
1614 throw std::runtime_error(
"missing the imaginary part on line " + std::to_string(overall_line_count + 1));
1619 if (!(input.advance())) {
1620 throw std::runtime_error(
"missing the imaginary part on line " + std::to_string(overall_line_count + 1));
1626 if (x ==
' ' || x ==
'\t') {
1627 if (!advance_and_chomp(input)) {
1628 throw std::runtime_error(
"missing the imaginary part on line " + std::to_string(overall_line_count + 1));
1630 if (input.get() ==
'\n') {
1631 throw std::runtime_error(
"missing the imaginary part on line " + std::to_string(overall_line_count + 1));
1636 holding.real(convert_to_real<InnerType_>(temporary, overall_line_count));
1643 if (!(input.advance())) {
1644 output.remaining =
false;
1652 output.remaining = input.advance();
1658 if (x ==
' ' || x ==
'\t') {
1659 if (!advance_and_chomp(input)) {
1660 output.remaining =
false;
1663 if (input.get() !=
'\n') {
1664 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1666 output.remaining = input.advance();
1670 holding.imag(convert_to_real<InnerType_>(temporary, overall_line_count));
1672 output.value = holding;
1677 std::string temporary;
1694 template<
typename Type_ =
double,
class Store_>
1698 typedef std::complex<Type_> FullType;
1699 auto store_comp = [&](
Index r,
Index c, FullType val) ->
bool {
1700 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, FullType>::type,
bool>::value) {
1701 return store(r, c, val);
1708 if (my_details.
format == Format::COORDINATE) {
1709 if (my_details.
object == Object::MATRIX) {
1710 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1712 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1715 if (my_details.
object == Object::MATRIX) {
1716 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1718 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1738 template<
typename Type_ =
bool,
class Store_>
1741 if (my_details.
format != Format::COORDINATE) {
1742 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1745 auto store_pat = [&](
Index r,
Index c) ->
bool {
1746 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, bool>::type,
bool>::value) {
1747 return store(r, c,
true);
1754 if (my_details.
object == Object::MATRIX) {
1755 return scan_matrix_coordinate_pattern(std::move(store_pat));
1757 return scan_vector_coordinate_pattern(std::move(store_pat));