311 my_input(std::move(input), options.buffer_size),
312 my_nthreads(options.num_threads),
313 my_buffer_size(options.buffer_size)
315 sanisizer::as_size_type<std::vector<char> >(my_buffer_size);
322 std::size_t my_buffer_size;
327 template<
typename Input2_>
328 static bool chomp(Input2_& input) {
330 char x = input.get();
331 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
334 if (!(input.advance())) {
341 template<
typename Input2_>
342 static bool advance_and_chomp(Input2_& input) {
345 if (!(input.advance())) {
351 template<
typename Input2_>
352 static bool skip_lines(Input2_& input,
LineIndex& current_line) {
355 char x = input.get();
358 if (!(input.advance())) {
361 }
while (input.get() !=
'\n');
362 }
else if (x !=
'\n') {
366 if (!input.advance()) {
375 bool my_passed_banner =
false;
377 struct ExpectedMatch {
378 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
379 ExpectedMatch() : ExpectedMatch(false, false, false) {}
385 ExpectedMatch advance_past_expected_string() {
387 return ExpectedMatch(
true,
false,
false);
390 char next = my_input.
get();
391 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
392 if (!advance_and_chomp(my_input)) {
393 return ExpectedMatch(
true,
false,
false);
395 if (my_input.
get() ==
'\n') {
396 bool remaining = my_input.
advance();
397 return ExpectedMatch(
true,
true, remaining);
399 return ExpectedMatch(
true,
false,
true);
401 }
else if (next ==
'\n') {
402 bool remaining = my_input.
advance();
403 return ExpectedMatch(
true,
true, remaining);
407 return ExpectedMatch(
false,
true,
true);
410 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
416 for (std::size_t i = start; i < len; ++i) {
418 return ExpectedMatch(
false,
false,
false);
420 if (my_input.
get() != ptr[i]) {
421 return ExpectedMatch(
false,
false,
true);
424 return advance_past_expected_string();
427 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
430 return is_expected_string(ptr, len, 1);
433 bool parse_banner_object() {
436 char x = my_input.
get();
438 res = is_expected_string(
"matrix", 6);
439 my_details.
object = Object::MATRIX;
440 }
else if (x ==
'v') {
441 res = is_expected_string(
"vector", 6);
442 my_details.
object = Object::VECTOR;
446 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
448 if (!res.remaining) {
449 throw std::runtime_error(
"end of file reached after the first banner field");
455 bool parse_banner_format() {
458 char x = my_input.
get();
460 res = is_expected_string(
"coordinate", 10);
461 my_details.
format = Format::COORDINATE;
462 }
else if (x ==
'a') {
463 res = is_expected_string(
"array", 5);
464 my_details.
format = Format::ARRAY;
468 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
470 if (!res.remaining) {
471 throw std::runtime_error(
"end of file reached after the second banner field");
477 bool parse_banner_field() {
480 char x = my_input.
get();
482 res = is_expected_string(
"integer", 7);
483 my_details.
field = Field::INTEGER;
484 }
else if (x ==
'd') {
485 res = is_expected_string(
"double", 6);
486 my_details.
field = Field::DOUBLE;
487 }
else if (x ==
'c') {
488 res = is_expected_string(
"complex", 7);
489 my_details.
field = Field::COMPLEX;
490 }
else if (x ==
'p') {
491 res = is_expected_string(
"pattern", 7);
492 my_details.
field = Field::PATTERN;
493 }
else if (x ==
'r') {
494 res = is_expected_string(
"real", 4);
495 my_details.
field = Field::REAL;
499 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
501 if (!res.remaining) {
502 throw std::runtime_error(
"end of file reached after the third banner field");
508 bool parse_banner_symmetry() {
511 char x = my_input.
get();
513 res = is_expected_string(
"general", 7);
514 my_details.
symmetry = Symmetry::GENERAL;
515 }
else if (x ==
'h') {
516 res = is_expected_string(
"hermitian", 9);
517 my_details.
symmetry = Symmetry::HERMITIAN;
518 }
else if (x ==
's') {
520 char x = my_input.
get();
522 res = is_expected_string(
"skew-symmetric", 14, 2);
523 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
525 res = is_expected_string(
"symmetric", 9, 2);
526 my_details.
symmetry = Symmetry::SYMMETRIC;
532 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
534 if (!res.remaining) {
535 throw std::runtime_error(
"end of file reached after the fourth banner field");
542 if (my_passed_banner) {
543 throw std::runtime_error(
"banner has already been scanned");
545 if (!(my_input.
valid())) {
546 throw std::runtime_error(
"failed to find banner line before end of file");
548 if (my_input.
get() !=
'%') {
549 throw std::runtime_error(
"first line of the file should be the banner");
552 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
553 if (!found_banner.remaining) {
554 throw std::runtime_error(
"end of file reached before matching the banner");
556 if (!found_banner.found) {
557 throw std::runtime_error(
"first line of the file should be the banner");
559 if (found_banner.newline) {
560 throw std::runtime_error(
"end of line reached before matching the banner");
563 if (parse_banner_object()) {
564 throw std::runtime_error(
"end of line reached after the first banner field");
566 if (parse_banner_format()) {
567 throw std::runtime_error(
"end of line reached after the second banner field");
571 if (my_details.
object == Object::MATRIX) {
572 if (parse_banner_field()) {
573 throw std::runtime_error(
"end of line reached after the third banner field");
575 eol = parse_banner_symmetry();
580 my_details.
symmetry = Symmetry::GENERAL;
583 eol = parse_banner_field();
586 my_passed_banner =
true;
594 throw std::runtime_error(
"end of file reached before the end of the banner line");
596 }
while (my_input.
get() !=
'\n');
612 if (!my_passed_banner) {
613 throw std::runtime_error(
"banner has not yet been scanned");
621 template<
typename Integer_>
622 struct NotLastSizeInfo {
626 template<
typename Integer_>
627 struct LastSizeInfo {
629 bool remaining =
false;
632 template<
bool last_,
typename Integer_>
633 using SizeInfo =
typename std::conditional<last_, LastSizeInfo<Integer_>, NotLastSizeInfo<Integer_> >::type;
635 template<
bool last_,
typename Integer_,
class Input2_>
636 static SizeInfo<last_, Integer_> scan_integer_field(
bool size, Input2_& input,
LineIndex overall_line_count) {
637 SizeInfo<last_, Integer_> output;
640 auto what = [&]() -> std::string {
648 constexpr Integer_ max_limit = std::numeric_limits<Integer_>::max();
649 constexpr Integer_ max_limit_before_mult = max_limit / 10;
650 constexpr Integer_ max_limit_mod = max_limit % 10;
653 char x = input.get();
655 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
657 Integer_ delta = x -
'0';
659 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
660 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
663 output.index += delta;
674 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
676 if constexpr(last_) {
677 output.remaining = input.advance();
680 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
681 case ' ':
case '\t':
case '\r':
682 if (!advance_and_chomp(input)) {
683 if constexpr(last_) {
686 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
689 if constexpr(last_) {
690 if (input.get() !=
'\n') {
691 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
693 output.remaining = input.advance();
697 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
700 if (!(input.advance())) {
701 if constexpr(last_) {
704 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
712 template<
bool last_,
class Input2_>
713 static SizeInfo<last_, Index_> scan_size_field(Input2_& input,
LineIndex overall_line_count) {
714 return scan_integer_field<last_, Index_>(
true, input, overall_line_count);
717 template<
class Input2_>
718 static SizeInfo<true, LineIndex> scan_line_count_field(Input2_& input,
LineIndex overall_line_count) {
719 return scan_integer_field<true, LineIndex>(
true, input, overall_line_count);
722 template<
bool last_,
class Input2_>
723 static SizeInfo<last_, Index_> scan_index_field(Input2_& input,
LineIndex overall_line_count) {
724 return scan_integer_field<last_, Index_>(
false, input, overall_line_count);
728 bool my_passed_size =
false;
729 Index_ my_nrows = 0, my_ncols = 0;
733 if (!(my_input.
valid())) {
734 throw std::runtime_error(
"failed to find size line before end of file");
738 if (!skip_lines(my_input, my_current_line)) {
739 throw std::runtime_error(
"failed to find size line before end of file");
741 if (!chomp(my_input)) {
742 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
745 if (my_details.
object == Object::MATRIX) {
746 if (my_details.
format == Format::COORDINATE) {
747 auto first_field = scan_size_field<false>(my_input, my_current_line);
748 my_nrows = first_field.index;
750 auto second_field = scan_size_field<false>(my_input, my_current_line);
751 my_ncols = second_field.index;
753 auto third_field = scan_line_count_field(my_input, my_current_line);
754 my_nlines = third_field.index;
757 auto first_field = scan_size_field<false>(my_input, my_current_line);
758 my_nrows = first_field.index;
760 auto second_field = scan_size_field<true>(my_input, my_current_line);
761 my_ncols = second_field.index;
762 my_nlines = sanisizer::product<LineIndex>(my_nrows, my_ncols);
766 if (my_details.
format == Format::COORDINATE) {
767 auto first_field = scan_size_field<false>(my_input, my_current_line);
768 my_nrows = first_field.index;
770 auto second_field = scan_line_count_field(my_input, my_current_line);
771 my_nlines = second_field.index;
774 auto first_field = scan_size_field<true>(my_input, my_current_line);
775 my_nlines = first_field.index;
776 my_nrows = my_nlines;
782 my_passed_size =
true;
794 if (!my_passed_size) {
795 throw std::runtime_error(
"size line has not yet been scanned");
808 if (!my_passed_size) {
809 throw std::runtime_error(
"size line has not yet been scanned");
822 if (!my_passed_size) {
823 throw std::runtime_error(
"size line has not yet been scanned");
840 template<
typename Type_>
842 ParseInfo() =
default;
843 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
848 template<
typename Workspace_>
849 bool configure_parallel_workspace(Workspace_& work) {
850 bool available = fill_to_next_newline(my_input, work.buffer, my_buffer_size);
851 work.contents.clear();
852 work.overall_line = my_current_line;
853 my_current_line += count_newlines(work.buffer);
857 void check_num_lines_loop(
LineIndex data_line_count)
const {
858 if (data_line_count >= my_nlines) {
859 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
863 void check_num_lines_final(
bool finished,
LineIndex data_line_count)
const {
865 if (data_line_count != my_nlines) {
867 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
873 void check_matrix_coordinate_line(Index_ currow, Index_ curcol,
LineIndex overall_line_count)
const {
875 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
877 if (currow > my_nrows) {
878 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
881 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
883 if (curcol > my_ncols) {
884 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
888 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
889 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
890 bool valid = input.valid();
893 if (!skip_lines(input, overall_line_count)) {
897 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
900 auto first_field = scan_index_field<false>(input, overall_line_count);
901 auto second_field = scan_index_field<false>(input, overall_line_count);
902 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
905 ParseInfo<Type_> res = fparser(input, overall_line_count);
906 if (!wstore(first_field.index, second_field.index, res.value)) {
909 ++overall_line_count;
910 valid = res.remaining;
916 template<
typename Type_,
class FieldParser_,
class Store_>
917 bool scan_matrix_coordinate_non_pattern(Store_ store) {
918 bool finished =
false;
921 if (my_nthreads == 1) {
922 FieldParser_ fparser;
923 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
927 [&](Index_ r, Index_ c, Type_ value) ->
bool {
928 check_num_lines_loop(current_data_line);
930 return store(r, c, value);
936 std::vector<char> buffer;
937 FieldParser_ fparser;
938 std::vector<std::tuple<Index_, Index_, Type_> > contents;
942 ThreadPool<Workspace> tp(
943 [&](Workspace& work) ->
bool {
944 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
945 return scan_matrix_coordinate_non_pattern_base<Type_>(
949 [&](Index_ r, Index_ c, Type_ value) ->
bool {
950 work.contents.emplace_back(r, c, value);
959 [&](Workspace& work) ->
bool {
960 return configure_parallel_workspace(work);
962 [&](Workspace& work) ->
bool {
963 for (
const auto& con : work.contents) {
964 check_num_lines_loop(current_data_line);
965 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
975 check_num_lines_final(finished, current_data_line);
980 template<
class Input2_,
class WrappedStore_>
981 bool scan_matrix_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
982 bool valid = input.valid();
985 if (!skip_lines(input, overall_line_count)) {
989 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
992 auto first_field = scan_index_field<false>(input, overall_line_count);
993 auto second_field = scan_index_field<true>(input, overall_line_count);
994 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
996 if (!wstore(first_field.index, second_field.index)) {
999 ++overall_line_count;
1000 valid = second_field.remaining;
1006 template<
class Store_>
1007 bool scan_matrix_coordinate_pattern(Store_ store) {
1008 bool finished =
false;
1011 if (my_nthreads == 1) {
1012 finished = scan_matrix_coordinate_pattern_base(
1015 [&](Index_ r, Index_ c) ->
bool {
1016 check_num_lines_loop(current_data_line);
1017 ++current_data_line;
1024 std::vector<char> buffer;
1025 std::vector<std::tuple<Index_, Index_> > contents;
1029 ThreadPool<Workspace> tp(
1030 [&](Workspace& work) ->
bool {
1031 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1032 return scan_matrix_coordinate_pattern_base(
1035 [&](Index_ r, Index_ c) ->
bool {
1036 work.contents.emplace_back(r, c);
1045 [&](Workspace& work) ->
bool {
1046 return configure_parallel_workspace(work);
1048 [&](Workspace& work) ->
bool {
1049 for (
const auto& con : work.contents) {
1050 check_num_lines_loop(current_data_line);
1051 if (!store(std::get<0>(con), std::get<1>(con))) {
1054 ++current_data_line;
1061 check_num_lines_final(finished, current_data_line);
1066 void check_vector_coordinate_line(Index_ currow,
LineIndex overall_line_count)
const {
1068 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1070 if (currow > my_nrows) {
1071 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1075 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1076 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1077 bool valid = input.valid();
1080 if (!skip_lines(input, overall_line_count)) {
1083 if (!chomp(input)) {
1084 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1087 auto first_field = scan_index_field<false>(input, overall_line_count);
1088 check_vector_coordinate_line(first_field.index, overall_line_count);
1091 ParseInfo<Type_> res = fparser(input, overall_line_count);
1092 if (!wstore(first_field.index, res.value)) {
1095 ++overall_line_count;
1096 valid = res.remaining;
1102 template<
typename Type_,
class FieldParser_,
class Store_>
1103 bool scan_vector_coordinate_non_pattern(Store_ store) {
1104 bool finished =
false;
1107 if (my_nthreads == 1) {
1108 FieldParser_ fparser;
1109 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1113 [&](Index_ r, Type_ value) ->
bool {
1114 check_num_lines_loop(current_data_line);
1115 ++current_data_line;
1116 return store(r, 1, value);
1122 std::vector<char> buffer;
1123 FieldParser_ fparser;
1124 std::vector<std::tuple<Index_, Type_> > contents;
1128 ThreadPool<Workspace> tp(
1129 [&](Workspace& work) ->
bool {
1130 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1131 return scan_vector_coordinate_non_pattern_base<Type_>(
1135 [&](Index_ r, Type_ value) ->
bool {
1136 work.contents.emplace_back(r, value);
1145 [&](Workspace& work) ->
bool {
1146 return configure_parallel_workspace(work);
1148 [&](Workspace& work) ->
bool {
1149 for (
const auto& con : work.contents) {
1150 check_num_lines_loop(current_data_line);
1151 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1154 ++current_data_line;
1161 check_num_lines_final(finished, current_data_line);
1166 template<
class Input2_,
class WrappedStore_>
1167 bool scan_vector_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
1168 bool valid = input.valid();
1171 if (!skip_lines(input, overall_line_count)) {
1174 if (!chomp(input)) {
1175 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1178 auto first_field = scan_index_field<true>(input, overall_line_count);
1179 check_vector_coordinate_line(first_field.index, overall_line_count);
1181 if (!wstore(first_field.index)) {
1184 ++overall_line_count;
1185 valid = first_field.remaining;
1191 template<
class Store_>
1192 bool scan_vector_coordinate_pattern(Store_ store) {
1193 bool finished =
false;
1196 if (my_nthreads == 1) {
1197 finished = scan_vector_coordinate_pattern_base(
1200 [&](Index_ r) ->
bool {
1201 check_num_lines_loop(current_data_line);
1202 ++current_data_line;
1209 std::vector<char> buffer;
1210 std::vector<Index_> contents;
1214 ThreadPool<Workspace> tp(
1215 [&](Workspace& work) ->
bool {
1216 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1217 return scan_vector_coordinate_pattern_base(
1220 [&](Index_ r) ->
bool {
1221 work.contents.emplace_back(r);
1230 [&](Workspace& work) ->
bool {
1231 return configure_parallel_workspace(work);
1233 [&](Workspace& work) ->
bool {
1234 for (
const auto& r : work.contents) {
1235 check_num_lines_loop(current_data_line);
1239 ++current_data_line;
1246 check_num_lines_final(finished, current_data_line);
1251 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1252 bool scan_matrix_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1253 bool valid = input.valid();
1256 if (!skip_lines(input, overall_line_count)) {
1259 if (!chomp(input)) {
1260 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1264 ParseInfo<Type_> res = fparser(input, overall_line_count);
1265 if (!wstore(res.value)) {
1268 ++overall_line_count;
1269 valid = res.remaining;
1275 template<
typename Type_,
class FieldParser_,
class Store_>
1276 bool scan_matrix_array(Store_ store) {
1277 bool finished =
false;
1280 Index_ currow = 1, curcol = 1;
1281 auto increment = [&]() {
1283 if (currow > my_nrows) {
1289 if (my_nthreads == 1) {
1290 FieldParser_ fparser;
1291 finished = scan_matrix_array_base<Type_>(
1295 [&](Type_ value) ->
bool {
1296 check_num_lines_loop(current_data_line);
1297 if (!store(currow, curcol, value)) {
1300 ++current_data_line;
1308 std::vector<char> buffer;
1309 FieldParser_ fparser;
1310 std::vector<Type_> contents;
1314 ThreadPool<Workspace> tp(
1315 [&](Workspace& work) ->
bool {
1316 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1317 return scan_matrix_array_base<Type_>(
1321 [&](Type_ value) ->
bool {
1322 work.contents.emplace_back(value);
1331 [&](Workspace& work) ->
bool {
1332 return configure_parallel_workspace(work);
1334 [&](Workspace& work) ->
bool {
1335 for (
const auto& val : work.contents) {
1336 check_num_lines_loop(current_data_line);
1337 if (!store(currow, curcol, val)) {
1340 ++current_data_line;
1348 check_num_lines_final(finished, current_data_line);
1353 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1354 bool scan_vector_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1355 bool valid = input.valid();
1358 if (!skip_lines(input, overall_line_count)) {
1361 if (!chomp(input)) {
1362 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1366 ParseInfo<Type_> res = fparser(input, overall_line_count);
1367 if (!wstore(res.value)) {
1370 ++overall_line_count;
1371 valid = res.remaining;
1377 template<
typename Type_,
class FieldParser_,
class Store_>
1378 bool scan_vector_array(Store_ store) {
1379 bool finished =
false;
1381 if (my_nthreads == 1) {
1382 FieldParser_ fparser;
1383 finished = scan_vector_array_base<Type_>(
1387 [&](Type_ value) ->
bool {
1388 check_num_lines_loop(current_data_line);
1389 ++current_data_line;
1390 return store(current_data_line, 1, value);
1396 std::vector<char> buffer;
1397 FieldParser_ fparser;
1398 std::vector<Type_> contents;
1402 ThreadPool<Workspace> tp(
1403 [&](Workspace& work) ->
bool {
1404 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1405 return scan_vector_array_base<Type_>(
1409 [&](Type_ value) ->
bool {
1410 work.contents.emplace_back(value);
1419 [&](Workspace& work) ->
bool {
1420 return configure_parallel_workspace(work);
1422 [&](Workspace& work) ->
bool {
1423 for (
const auto& val : work.contents) {
1424 check_num_lines_loop(current_data_line);
1425 ++current_data_line;
1426 if (!store(current_data_line, 1, val)) {
1435 check_num_lines_final(finished, current_data_line);
1440 void check_preamble()
const {
1441 if (!my_passed_banner || !my_passed_size) {
1442 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1446 template<
typename Type_>
1447 class IntegerFieldParser {
1449 template<
class Input2_>
1450 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1451 char firstchar = input.get();
1452 bool negative = (firstchar ==
'-');
1453 if (negative || firstchar ==
'+') {
1454 if (!(input.advance())) {
1455 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1459 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1460 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1461 constexpr Type_ upper_limit_mod = upper_limit % 10;
1462 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1463 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1464 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1469 char x = input.get();
1471 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1473 Type_ delta = x -
'0';
1477 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1478 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1483 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1484 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1492 case ' ':
case '\t':
case '\r':
1493 if (!advance_and_chomp(input)) {
1494 return ParseInfo<Type_>(val,
false);
1496 if (input.get() !=
'\n') {
1497 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1499 return ParseInfo<Type_>(val, input.advance());
1507 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1509 return ParseInfo<Type_>(val, input.advance());
1511 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1514 if (!(input.advance())) {
1519 return ParseInfo<Type_>(val,
false);
1537 template<
typename Type_ =
int,
class Store_>
1540 static_assert(std::is_integral<Type_>::value);
1542 auto wrapped_store = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1543 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1544 return store(r, c, val);
1551 if (my_details.
format == Format::COORDINATE) {
1552 if (my_details.
object == Object::MATRIX) {
1553 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1555 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1558 if (my_details.
object == Object::MATRIX) {
1559 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1561 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1567 template<
bool last_,
typename Type_,
typename Input2_>
1568 static ParseInfo<Type_> parse_real(Input2_& input, std::string& temporary, Index overall_line_count) {
1570 ParseInfo<Type_> output(0,
true);
1573 char x = input.get();
1576 if constexpr(last_) {
1580 if (temporary.empty()) {
1581 throw std::runtime_error(
"empty number field on line " + std::to_string(overall_line_count + 1));
1583 output.remaining = input.advance();
1585 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1587 goto final_processing;
1589 case ' ':
case '\t':
case '\r':
1590 if constexpr(last_) {
1591 if (!advance_and_chomp(input)) {
1592 output.remaining =
false;
1594 if (input.get() !=
'\n') {
1595 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1597 output.remaining = input.advance();
1600 if (!advance_and_chomp(input)) {
1601 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1603 if (input.get() ==
'\n') {
1604 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1607 goto final_processing;
1614 if (!(input.advance())) {
1615 if constexpr(last_) {
1616 output.remaining =
false;
1617 goto final_processing;
1619 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1624 if (temporary.size() >= 2 && temporary[0] ==
'0' && (temporary[1] ==
'x' || temporary[1] ==
'X')) {
1625 throw std::runtime_error(
"hexadecimal numbers are not allowed on line " + std::to_string(overall_line_count + 1));
1630 if constexpr(std::is_same<Type_, float>::value) {
1631 output.value = std::stof(temporary, &n);
1632 }
else if constexpr(std::is_same<Type_, long double>::value) {
1633 output.value = std::stold(temporary, &n);
1635 output.value = std::stod(temporary, &n);
1637 }
catch (std::invalid_argument& e) {
1638 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1641 if (n != temporary.size()) {
1642 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1647 template<
typename Type_>
1648 class RealFieldParser {
1650 template<
class Input2_>
1651 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1652 return parse_real<true, Type_>(input, my_temporary, overall_line_count);
1655 std::string my_temporary;
1672 template<
typename Type_ =
double,
class Store_>
1675 static_assert(std::is_floating_point<Type_>::value);
1677 auto store_real = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1678 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1679 return store(r, c, val);
1686 if (my_details.
format == Format::COORDINATE) {
1687 if (my_details.
object == Object::MATRIX) {
1688 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1690 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1693 if (my_details.
object == Object::MATRIX) {
1694 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1696 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1715 template<
typename Type_ =
double,
class Store_>
1721 template<
typename InnerType_>
1722 class ComplexFieldParser {
1724 template<
typename Input2_>
1725 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
LineIndex overall_line_count) {
1726 auto first = parse_real<false, InnerType_>(input, my_temporary, overall_line_count);
1727 auto second = parse_real<true, InnerType_>(input, my_temporary, overall_line_count);
1728 ParseInfo<std::complex<InnerType_> > output;
1729 output.value.real(first.value);
1730 output.value.imag(second.value);
1731 output.remaining = second.remaining;
1735 std::string my_temporary;
1752 template<
typename Type_ =
double,
class Store_>
1755 static_assert(std::is_floating_point<Type_>::value);
1757 typedef std::complex<Type_> FullType;
1758 auto store_comp = [&](Index_ r, Index_ c, FullType val) ->
bool {
1759 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, FullType>::type,
bool>::value) {
1760 return store(r, c, val);
1767 if (my_details.
format == Format::COORDINATE) {
1768 if (my_details.
object == Object::MATRIX) {
1769 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1771 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1774 if (my_details.
object == Object::MATRIX) {
1775 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1777 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1797 template<
typename Type_ =
bool,
class Store_>
1800 if (my_details.
format != Format::COORDINATE) {
1801 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1804 auto store_pat = [&](Index_ r, Index_ c) ->
bool {
1805 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, bool>::type,
bool>::value) {
1806 return store(r, c,
true);
1813 if (my_details.
object == Object::MATRIX) {
1814 return scan_matrix_coordinate_pattern(std::move(store_pat));
1816 return scan_vector_coordinate_pattern(std::move(store_pat));