275 my_input(std::move(input)),
276 my_nthreads(options.num_threads),
277 my_block_size(options.block_size)
281 std::unique_ptr<Input_> my_input;
283 std::size_t my_block_size;
285 Index my_current_line = 0;
288 template<
typename Input2_>
289 static bool chomp(Input2_& input) {
291 char x = input.get();
292 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
295 if (!(input.advance())) {
302 template<
typename Input2_>
303 static bool advance_and_chomp(Input2_& input) {
306 if (!(input.advance())) {
312 template<
typename Input2_>
313 static bool skip_lines(Input2_& input,
Index& current_line) {
316 char x = input.get();
319 if (!(input.advance())) {
322 }
while (input.get() !=
'\n');
323 }
else if (x !=
'\n') {
327 if (!input.advance()) {
336 bool my_passed_banner =
false;
338 struct ExpectedMatch {
339 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
340 ExpectedMatch() : ExpectedMatch(false, false, false) {}
346 ExpectedMatch advance_past_expected_string() {
347 if (!(my_input->advance())) {
348 return ExpectedMatch(
true,
false,
false);
351 char next = my_input->get();
352 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
353 if (!advance_and_chomp(*my_input)) {
354 return ExpectedMatch(
true,
false,
false);
356 if (my_input->get() ==
'\n') {
357 bool remaining = my_input->advance();
358 return ExpectedMatch(
true,
true, remaining);
360 return ExpectedMatch(
true,
false,
true);
362 }
else if (next ==
'\n') {
363 bool remaining = my_input->advance();
364 return ExpectedMatch(
true,
true, remaining);
368 return ExpectedMatch(
false,
true,
true);
371 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
377 for (std::size_t i = start; i < len; ++i) {
378 if (!my_input->advance()) {
379 return ExpectedMatch(
false,
false,
false);
381 if (my_input->get() != ptr[i]) {
382 return ExpectedMatch(
false,
false,
true);
385 return advance_past_expected_string();
388 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
391 return is_expected_string(ptr, len, 1);
394 bool parse_banner_object() {
397 char x = my_input->get();
399 res = is_expected_string(
"matrix", 6);
400 my_details.
object = Object::MATRIX;
401 }
else if (x ==
'v') {
402 res = is_expected_string(
"vector", 6);
403 my_details.
object = Object::VECTOR;
407 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
409 if (!res.remaining) {
410 throw std::runtime_error(
"end of file reached after the first banner field");
416 bool parse_banner_format() {
419 char x = my_input->get();
421 res = is_expected_string(
"coordinate", 10);
422 my_details.
format = Format::COORDINATE;
423 }
else if (x ==
'a') {
424 res = is_expected_string(
"array", 5);
425 my_details.
format = Format::ARRAY;
429 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
431 if (!res.remaining) {
432 throw std::runtime_error(
"end of file reached after the second banner field");
438 bool parse_banner_field() {
441 char x = my_input->get();
443 res = is_expected_string(
"integer", 7);
444 my_details.
field = Field::INTEGER;
445 }
else if (x ==
'd') {
446 res = is_expected_string(
"double", 6);
447 my_details.
field = Field::DOUBLE;
448 }
else if (x ==
'c') {
449 res = is_expected_string(
"complex", 7);
450 my_details.
field = Field::COMPLEX;
451 }
else if (x ==
'p') {
452 res = is_expected_string(
"pattern", 7);
453 my_details.
field = Field::PATTERN;
454 }
else if (x ==
'r') {
455 res = is_expected_string(
"real", 4);
456 my_details.
field = Field::REAL;
460 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
462 if (!res.remaining) {
463 throw std::runtime_error(
"end of file reached after the third banner field");
469 bool parse_banner_symmetry() {
472 char x = my_input->get();
474 res = is_expected_string(
"general", 7);
475 my_details.
symmetry = Symmetry::GENERAL;
476 }
else if (x ==
'h') {
477 res = is_expected_string(
"hermitian", 9);
478 my_details.
symmetry = Symmetry::HERMITIAN;
479 }
else if (x ==
's') {
480 if (my_input->advance()) {
481 char x = my_input->get();
483 res = is_expected_string(
"skew-symmetric", 14, 2);
484 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
486 res = is_expected_string(
"symmetric", 9, 2);
487 my_details.
symmetry = Symmetry::SYMMETRIC;
493 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
495 if (!res.remaining) {
496 throw std::runtime_error(
"end of file reached after the fourth banner field");
503 if (my_passed_banner) {
504 throw std::runtime_error(
"banner has already been scanned");
506 if (!(my_input->valid())) {
507 throw std::runtime_error(
"failed to find banner line before end of file");
509 if (my_input->get() !=
'%') {
510 throw std::runtime_error(
"first line of the file should be the banner");
513 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
514 if (!found_banner.remaining) {
515 throw std::runtime_error(
"end of file reached before matching the banner");
517 if (!found_banner.found) {
518 throw std::runtime_error(
"first line of the file should be the banner");
520 if (found_banner.newline) {
521 throw std::runtime_error(
"end of line reached before matching the banner");
524 if (parse_banner_object()) {
525 throw std::runtime_error(
"end of line reached after the first banner field");
527 if (parse_banner_format()) {
528 throw std::runtime_error(
"end of line reached after the second banner field");
532 if (my_details.
object == Object::MATRIX) {
533 if (parse_banner_field()) {
534 throw std::runtime_error(
"end of line reached after the third banner field");
536 eol = parse_banner_symmetry();
541 my_details.
symmetry = Symmetry::GENERAL;
544 eol = parse_banner_field();
547 my_passed_banner =
true;
554 if (!(my_input->advance())) {
555 throw std::runtime_error(
"end of file reached before the end of the banner line");
557 }
while (my_input->get() !=
'\n');
573 if (!my_passed_banner) {
574 throw std::runtime_error(
"banner has not yet been scanned");
582 struct NotLastSizeInfo {
586 struct LastSizeInfo {
588 bool remaining =
false;
592 using SizeInfo =
typename std::conditional<last_, LastSizeInfo, NotLastSizeInfo>::type;
594 template<
bool last_,
class Input2_>
595 static SizeInfo<last_> scan_integer_field(
bool size, Input2_& input,
Index overall_line_count) {
596 SizeInfo<last_> output;
599 auto what = [&]() -> std::string {
607 constexpr Index max_limit = std::numeric_limits<Index>::max();
608 constexpr Index max_limit_before_mult = max_limit / 10;
609 constexpr Index max_limit_mod = max_limit % 10;
612 char x = input.get();
614 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
616 Index delta = x -
'0';
618 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
619 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
622 output.index += delta;
633 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
635 if constexpr(last_) {
636 output.remaining = input.advance();
639 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
640 case ' ':
case '\t':
case '\r':
641 if (!advance_and_chomp(input)) {
642 if constexpr(last_) {
645 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
648 if constexpr(last_) {
649 if (input.get() !=
'\n') {
650 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
652 output.remaining = input.advance();
656 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
659 if (!(input.advance())) {
660 if constexpr(last_) {
663 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
671 template<
bool last_,
class Input2_>
672 static SizeInfo<last_> scan_size_field(Input2_& input,
Index overall_line_count) {
673 return scan_integer_field<last_>(
true, input, overall_line_count);
676 template<
bool last_,
class Input2_>
677 static SizeInfo<last_> scan_index_field(Input2_& input,
Index overall_line_count) {
678 return scan_integer_field<last_>(
false, input, overall_line_count);
682 bool my_passed_size =
false;
683 Index my_nrows = 0, my_ncols = 0, my_nlines = 0;
686 if (!(my_input->valid())) {
687 throw std::runtime_error(
"failed to find size line before end of file");
691 if (!skip_lines(*my_input, my_current_line)) {
692 throw std::runtime_error(
"failed to find size line before end of file");
694 if (!chomp(*my_input)) {
695 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
698 if (my_details.
object == Object::MATRIX) {
699 if (my_details.
format == Format::COORDINATE) {
700 auto first_field = scan_size_field<false>(*my_input, my_current_line);
701 my_nrows = first_field.index;
703 auto second_field = scan_size_field<false>(*my_input, my_current_line);
704 my_ncols = second_field.index;
706 auto third_field = scan_size_field<true>(*my_input, my_current_line);
707 my_nlines = third_field.index;
710 auto first_field = scan_size_field<false>(*my_input, my_current_line);
711 my_nrows = first_field.index;
713 auto second_field = scan_size_field<true>(*my_input, my_current_line);
714 my_ncols = second_field.index;
715 my_nlines = my_nrows * my_ncols;
719 if (my_details.
format == Format::COORDINATE) {
720 auto first_field = scan_size_field<false>(*my_input, my_current_line);
721 my_nrows = first_field.index;
723 auto second_field = scan_size_field<true>(*my_input, my_current_line);
724 my_nlines = second_field.index;
727 auto first_field = scan_size_field<true>(*my_input, my_current_line);
728 my_nlines = first_field.index;
729 my_nrows = my_nlines;
735 my_passed_size =
true;
747 if (!my_passed_size) {
748 throw std::runtime_error(
"size line has not yet been scanned");
761 if (!my_passed_size) {
762 throw std::runtime_error(
"size line has not yet been scanned");
775 if (!my_passed_size) {
776 throw std::runtime_error(
"size line has not yet been scanned");
793 template<
typename Type_>
795 ParseInfo() =
default;
796 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
801 template<
typename Workspace_>
802 bool configure_parallel_workspace(Workspace_& work) {
803 bool available = fill_to_next_newline(*my_input, work.buffer, my_block_size);
804 work.contents.clear();
805 work.overall_line = my_current_line;
806 my_current_line += count_newlines(work.buffer);
810 void check_num_lines_loop(
Index data_line_count)
const {
811 if (data_line_count >= my_nlines) {
812 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
816 void check_num_lines_final(
bool finished,
Index data_line_count)
const {
818 if (data_line_count != my_nlines) {
820 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
826 void check_matrix_coordinate_line(
Index currow,
Index curcol,
Index overall_line_count)
const {
828 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
830 if (currow > my_nrows) {
831 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
834 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
836 if (curcol > my_ncols) {
837 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
841 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
842 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
843 bool valid = input.valid();
846 if (!skip_lines(input, overall_line_count)) {
850 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
853 auto first_field = scan_index_field<false>(input, overall_line_count);
854 auto second_field = scan_index_field<false>(input, overall_line_count);
855 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
858 ParseInfo<Type_> res = fparser(input, overall_line_count);
859 if (!wstore(first_field.index, second_field.index, res.value)) {
862 ++overall_line_count;
863 valid = res.remaining;
869 template<
typename Type_,
class FieldParser_,
class Store_>
870 bool scan_matrix_coordinate_non_pattern(Store_ store) {
871 bool finished =
false;
872 Index current_data_line = 0;
874 if (my_nthreads == 1) {
875 FieldParser_ fparser;
876 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
881 check_num_lines_loop(current_data_line);
883 return store(r, c, value);
889 std::vector<char> buffer;
890 FieldParser_ fparser;
891 std::vector<std::tuple<Index, Index, Type_> > contents;
895 ThreadPool<Workspace> tp(
896 [&](Workspace& work) ->
bool {
897 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
899 return scan_matrix_coordinate_non_pattern_base<Type_>(
904 work.contents.emplace_back(r, c, value);
913 [&](Workspace& work) ->
bool {
914 return configure_parallel_workspace(work);
916 [&](Workspace& work) ->
bool {
917 for (
const auto& con : work.contents) {
918 check_num_lines_loop(current_data_line);
919 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
929 check_num_lines_final(finished, current_data_line);
934 template<
class Input2_,
class WrappedStore_>
935 bool scan_matrix_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
936 bool valid = input.valid();
939 if (!skip_lines(input, overall_line_count)) {
943 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
946 auto first_field = scan_index_field<false>(input, overall_line_count);
947 auto second_field = scan_index_field<true>(input, overall_line_count);
948 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
950 if (!wstore(first_field.index, second_field.index)) {
953 ++overall_line_count;
954 valid = second_field.remaining;
960 template<
class Store_>
961 bool scan_matrix_coordinate_pattern(Store_ store) {
962 bool finished =
false;
963 Index current_data_line = 0;
965 if (my_nthreads == 1) {
966 finished = scan_matrix_coordinate_pattern_base(
970 check_num_lines_loop(current_data_line);
978 std::vector<char> buffer;
979 std::vector<std::tuple<Index, Index> > contents;
983 ThreadPool<Workspace> tp(
984 [&](Workspace& work) ->
bool {
985 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
987 return scan_matrix_coordinate_pattern_base(
991 work.contents.emplace_back(r, c);
1000 [&](Workspace& work) ->
bool {
1001 return configure_parallel_workspace(work);
1003 [&](Workspace& work) ->
bool {
1004 for (
const auto& con : work.contents) {
1005 check_num_lines_loop(current_data_line);
1006 if (!store(std::get<0>(con), std::get<1>(con))) {
1009 ++current_data_line;
1016 check_num_lines_final(finished, current_data_line);
1021 void check_vector_coordinate_line(
Index currow,
Index overall_line_count)
const {
1023 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1025 if (currow > my_nrows) {
1026 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1030 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1031 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1032 bool valid = input.valid();
1035 if (!skip_lines(input, overall_line_count)) {
1038 if (!chomp(input)) {
1039 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1042 auto first_field = scan_index_field<false>(input, overall_line_count);
1043 check_vector_coordinate_line(first_field.index, overall_line_count);
1046 ParseInfo<Type_> res = fparser(input, overall_line_count);
1047 if (!wstore(first_field.index, res.value)) {
1050 ++overall_line_count;
1051 valid = res.remaining;
1057 template<
typename Type_,
class FieldParser_,
class Store_>
1058 bool scan_vector_coordinate_non_pattern(Store_ store) {
1059 bool finished =
false;
1060 Index current_data_line = 0;
1062 if (my_nthreads == 1) {
1063 FieldParser_ fparser;
1064 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1068 [&](
Index r, Type_ value) ->
bool {
1069 check_num_lines_loop(current_data_line);
1070 ++current_data_line;
1071 return store(r, 1, value);
1077 std::vector<char> buffer;
1078 FieldParser_ fparser;
1079 std::vector<std::tuple<Index, Type_> > contents;
1083 ThreadPool<Workspace> tp(
1084 [&](Workspace& work) ->
bool {
1085 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1087 return scan_vector_coordinate_non_pattern_base<Type_>(
1091 [&](
Index r, Type_ value) ->
bool {
1092 work.contents.emplace_back(r, value);
1101 [&](Workspace& work) ->
bool {
1102 return configure_parallel_workspace(work);
1104 [&](Workspace& work) ->
bool {
1105 for (
const auto& con : work.contents) {
1106 check_num_lines_loop(current_data_line);
1107 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1110 ++current_data_line;
1117 check_num_lines_final(finished, current_data_line);
1122 template<
class Input2_,
class WrappedStore_>
1123 bool scan_vector_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
1124 bool valid = input.valid();
1127 if (!skip_lines(input, overall_line_count)) {
1130 if (!chomp(input)) {
1131 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1134 auto first_field = scan_index_field<true>(input, overall_line_count);
1135 check_vector_coordinate_line(first_field.index, overall_line_count);
1137 if (!wstore(first_field.index)) {
1140 ++overall_line_count;
1141 valid = first_field.remaining;
1147 template<
class Store_>
1148 bool scan_vector_coordinate_pattern(Store_ store) {
1149 bool finished =
false;
1150 Index current_data_line = 0;
1152 if (my_nthreads == 1) {
1153 finished = scan_vector_coordinate_pattern_base(
1156 [&](
Index r) ->
bool {
1157 check_num_lines_loop(current_data_line);
1158 ++current_data_line;
1165 std::vector<char> buffer;
1166 std::vector<Index> contents;
1170 ThreadPool<Workspace> tp(
1171 [&](Workspace& work) ->
bool {
1172 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1174 return scan_vector_coordinate_pattern_base(
1177 [&](
Index r) ->
bool {
1178 work.contents.emplace_back(r);
1187 [&](Workspace& work) ->
bool {
1188 return configure_parallel_workspace(work);
1190 [&](Workspace& work) ->
bool {
1191 for (
const auto& r : work.contents) {
1192 check_num_lines_loop(current_data_line);
1196 ++current_data_line;
1203 check_num_lines_final(finished, current_data_line);
1208 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1209 bool scan_matrix_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1210 bool valid = input.valid();
1213 if (!skip_lines(input, overall_line_count)) {
1216 if (!chomp(input)) {
1217 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1221 ParseInfo<Type_> res = fparser(input, overall_line_count);
1222 if (!wstore(res.value)) {
1225 ++overall_line_count;
1226 valid = res.remaining;
1232 template<
typename Type_,
class FieldParser_,
class Store_>
1233 bool scan_matrix_array(Store_ store) {
1234 bool finished =
false;
1235 Index current_data_line = 0;
1237 Index currow = 1, curcol = 1;
1238 auto increment = [&]() {
1240 if (currow > my_nrows) {
1246 if (my_nthreads == 1) {
1247 FieldParser_ fparser;
1248 finished = scan_matrix_array_base<Type_>(
1252 [&](Type_ value) ->
bool {
1253 check_num_lines_loop(current_data_line);
1254 if (!store(currow, curcol, value)) {
1257 ++current_data_line;
1265 std::vector<char> buffer;
1266 FieldParser_ fparser;
1267 std::vector<Type_> contents;
1271 ThreadPool<Workspace> tp(
1272 [&](Workspace& work) ->
bool {
1273 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1275 return scan_matrix_array_base<Type_>(
1279 [&](Type_ value) ->
bool {
1280 work.contents.emplace_back(value);
1289 [&](Workspace& work) ->
bool {
1290 return configure_parallel_workspace(work);
1292 [&](Workspace& work) ->
bool {
1293 for (
const auto& val : work.contents) {
1294 check_num_lines_loop(current_data_line);
1295 if (!store(currow, curcol, val)) {
1298 ++current_data_line;
1306 check_num_lines_final(finished, current_data_line);
1311 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1312 bool scan_vector_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1313 bool valid = input.valid();
1316 if (!skip_lines(input, overall_line_count)) {
1319 if (!chomp(input)) {
1320 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1324 ParseInfo<Type_> res = fparser(input, overall_line_count);
1325 if (!wstore(res.value)) {
1328 ++overall_line_count;
1329 valid = res.remaining;
1335 template<
typename Type_,
class FieldParser_,
class Store_>
1336 bool scan_vector_array(Store_ store) {
1337 bool finished =
false;
1338 Index current_data_line = 0;
1339 if (my_nthreads == 1) {
1340 FieldParser_ fparser;
1341 finished = scan_vector_array_base<Type_>(
1345 [&](Type_ value) ->
bool {
1346 check_num_lines_loop(current_data_line);
1347 ++current_data_line;
1348 return store(current_data_line, 1, value);
1354 std::vector<char> buffer;
1355 FieldParser_ fparser;
1356 std::vector<Type_> contents;
1360 ThreadPool<Workspace> tp(
1361 [&](Workspace& work) ->
bool {
1362 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1364 return scan_vector_array_base<Type_>(
1368 [&](Type_ value) ->
bool {
1369 work.contents.emplace_back(value);
1378 [&](Workspace& work) ->
bool {
1379 return configure_parallel_workspace(work);
1381 [&](Workspace& work) ->
bool {
1382 for (
const auto& val : work.contents) {
1383 check_num_lines_loop(current_data_line);
1384 ++current_data_line;
1385 if (!store(current_data_line, 1, val)) {
1394 check_num_lines_final(finished, current_data_line);
1399 void check_preamble()
const {
1400 if (!my_passed_banner || !my_passed_size) {
1401 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1405 template<
typename Type_>
1406 class IntegerFieldParser {
1408 template<
class Input2_>
1409 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1410 char firstchar = input.get();
1411 bool negative = (firstchar ==
'-');
1412 if (negative || firstchar ==
'+') {
1413 if (!(input.advance())) {
1414 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1418 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1419 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1420 constexpr Type_ upper_limit_mod = upper_limit % 10;
1421 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1422 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1423 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1428 char x = input.get();
1430 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1432 Type_ delta = x -
'0';
1436 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1437 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1442 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1443 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1451 case ' ':
case '\t':
case '\r':
1452 if (!advance_and_chomp(input)) {
1453 return ParseInfo<Type_>(val,
false);
1455 if (input.get() !=
'\n') {
1456 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1458 return ParseInfo<Type_>(val, input.advance());
1466 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1468 return ParseInfo<Type_>(val, input.advance());
1470 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1473 if (!(input.advance())) {
1478 return ParseInfo<Type_>(val,
false);
1496 template<
typename Type_ =
int,
class Store_>
1500 auto wrapped_store = [&](
Index r,
Index c, Type_ val) ->
bool {
1501 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1502 return store(r, c, val);
1509 if (my_details.
format == Format::COORDINATE) {
1510 if (my_details.
object == Object::MATRIX) {
1511 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1513 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1516 if (my_details.
object == Object::MATRIX) {
1517 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1519 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1525 template<
bool last_,
typename Type_,
typename Input2_>
1526 static typename std::conditional<last_, ParseInfo<Type_>, Type_>::type parse_special(Input2_& input,
bool negative,
bool check_inf,
Index overall_line_count) {
1527 auto what = [&]() -> std::string {
1529 return std::string(
"infinity");
1531 return std::string(
"NaN");
1535 auto check = [&](
char lower,
char upper) ->
void {
1536 if (!input.advance()) {
1537 throw std::runtime_error(
"unexpected termination of " + what() +
" on line " + std::to_string(overall_line_count + 1));
1539 char current = input.get();
1540 if (current != lower && current != upper) {
1541 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1545 bool remaining =
true;
1552 remaining = input.advance();
1554 char current = input.get();
1555 if (current !=
'\n' && current !=
' ' && current !=
'\t' && current !=
'\r') {
1556 if (current !=
'i' && current !=
'I') {
1557 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1563 remaining = input.advance();
1570 remaining = input.advance();
1575 switch(input.get()) {
1576 case ' ':
case '\t':
case '\r':
1577 if (!advance_and_chomp(input)) {
1578 if constexpr(last_) {
1582 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1584 if constexpr(last_) {
1585 if (input.get() !=
'\n') {
1586 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1588 remaining = input.advance();
1592 if constexpr(last_) {
1593 remaining = input.advance();
1596 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1598 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1601 if constexpr(!last_) {
1602 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1608 if constexpr(!std::numeric_limits<Type_>::has_infinity) {
1609 throw std::runtime_error(
"requested type does not support " + what());
1611 value = std::numeric_limits<Type_>::infinity();
1613 if constexpr(!std::numeric_limits<Type_>::has_quiet_NaN) {
1614 throw std::runtime_error(
"requested type does not support " + what());
1616 value = std::numeric_limits<Type_>::quiet_NaN();
1622 if constexpr(last_) {
1623 ParseInfo<Type_> output;
1624 output.value = value;
1625 output.remaining = remaining;
1632 template<
bool last_,
typename Type_,
typename Input2_>
1633 static typename std::conditional<last_, ParseInfo<Type_>, Type_>::type parse_real(Input2_& input,
Index overall_line_count) {
1634 char firstchar = input.get();
1635 bool negative = (firstchar ==
'-');
1636 if (negative || firstchar ==
'+') {
1637 if (!(input.advance())) {
1638 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1643 switch (input.get()) {
1645 return parse_special<last_, Type_>(input, negative,
true, overall_line_count);
1647 return parse_special<last_, Type_>(input, negative,
false, overall_line_count);
1653 bool remaining =
true;
1656 char val = input.get();
1658 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1663 case ' ':
case '\t':
case '\r':
1664 if (!advance_and_chomp(input)) {
1665 if constexpr(last_) {
1667 goto final_processing;
1669 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1671 if constexpr(last_) {
1672 if (input.get() !=
'\n') {
1673 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1675 remaining = input.advance();
1677 goto final_processing;
1679 if constexpr(last_) {
1680 remaining = input.advance();
1681 goto final_processing;
1683 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1685 if (!input.advance()) {
1686 if constexpr(last_) {
1688 goto final_processing;
1690 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1692 goto decimal_processing;
1694 if (!input.advance()) {
1695 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1697 goto exponent_processing;
1699 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1702 if (!(input.advance())) {
1703 if constexpr(last_) {
1704 remaining = input.advance();
1705 goto final_processing;
1707 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1714 Type_ multiplier = 1;
1716 char val = input.get();
1718 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1720 value += (val -
'0') / multiplier;
1723 case ' ':
case '\t':
case '\r':
1724 if (!advance_and_chomp(input)) {
1725 if constexpr(last_) {
1727 goto final_processing;
1729 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1731 if constexpr(last_) {
1732 if (input.get() !=
'\n') {
1733 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1735 remaining = input.advance();
1737 goto final_processing;
1739 if constexpr(last_) {
1740 remaining = input.advance();
1741 goto final_processing;
1743 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1745 if (!input.advance()) {
1746 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1748 goto exponent_processing;
1750 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1753 if (!(input.advance())) {
1754 if constexpr(last_) {
1755 remaining = input.advance();
1756 goto final_processing;
1758 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1766 bool expnegative = (input.get() ==
'-');
1767 if (expnegative || input.get() ==
'+') {
1768 if (!(input.advance())) {
1769 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1774 bool expfound =
false;
1776 char val = input.get();
1778 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1780 exponent += (val -
'0');
1783 case ' ':
case '\t':
case '\r':
1784 if (!advance_and_chomp(input)) {
1785 if constexpr(last_) {
1787 goto exponent_processing_finish;
1789 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1791 if constexpr(last_) {
1792 if (input.get() !=
'\n') {
1793 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1795 remaining = input.advance();
1797 goto exponent_processing_finish;
1799 if constexpr(last_) {
1800 remaining = input.advance();
1801 goto exponent_processing_finish;
1803 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1805 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1808 if (!(input.advance())) {
1809 if constexpr(last_) {
1810 remaining = input.advance();
1811 goto exponent_processing_finish;
1813 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1817exponent_processing_finish:
1819 throw std::runtime_error(
"no digits in the decimal exponent on line " + std::to_string(overall_line_count + 1));
1824 value *= std::pow(10.0, exponent);
1829 throw std::runtime_error(
"no digits in real number on line " + std::to_string(overall_line_count + 1));
1835 if constexpr(last_) {
1836 ParseInfo<Type_> output;
1837 output.value = value;
1838 output.remaining = remaining;
1845 template<
typename Type_>
1846 class RealFieldParser {
1848 template<
class Input2_>
1849 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1850 return parse_real<true, Type_>(input, overall_line_count);
1868 template<
typename Type_ =
double,
class Store_>
1872 auto store_real = [&](
Index r,
Index c, Type_ val) ->
bool {
1873 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1874 return store(r, c, val);
1881 if (my_details.
format == Format::COORDINATE) {
1882 if (my_details.
object == Object::MATRIX) {
1883 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1885 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1888 if (my_details.
object == Object::MATRIX) {
1889 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1891 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1910 template<
typename Type_ =
double,
class Store_>
1916 template<
typename InnerType_>
1917 class ComplexFieldParser {
1919 template<
typename Input2_>
1920 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
Index overall_line_count) {
1921 auto first = parse_real<false, InnerType_>(input, overall_line_count);
1922 auto second = parse_real<true, InnerType_>(input, overall_line_count);
1923 ParseInfo<std::complex<InnerType_> > output;
1924 output.value.real(first);
1925 output.value.imag(second.value);
1926 output.remaining = second.remaining;
1945 template<
typename Type_ =
double,
class Store_>
1949 typedef std::complex<Type_> FullType;
1950 auto store_comp = [&](
Index r,
Index c, FullType val) ->
bool {
1951 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, FullType>::type,
bool>::value) {
1952 return store(r, c, val);
1959 if (my_details.
format == Format::COORDINATE) {
1960 if (my_details.
object == Object::MATRIX) {
1961 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1963 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1966 if (my_details.
object == Object::MATRIX) {
1967 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1969 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1989 template<
typename Type_ =
bool,
class Store_>
1992 if (my_details.
format != Format::COORDINATE) {
1993 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1996 auto store_pat = [&](
Index r,
Index c) ->
bool {
1997 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, bool>::type,
bool>::value) {
1998 return store(r, c,
true);
2005 if (my_details.
object == Object::MATRIX) {
2006 return scan_matrix_coordinate_pattern(std::move(store_pat));
2008 return scan_vector_coordinate_pattern(std::move(store_pat));