313 my_input(std::move(input), options.buffer_size),
314 my_nthreads(options.num_threads),
315 my_buffer_size(options.buffer_size)
317 sanisizer::as_size_type<std::vector<char> >(my_buffer_size);
324 std::size_t my_buffer_size;
329 template<
typename Input2_>
330 static bool chomp(Input2_& input) {
332 char x = input.get();
333 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
336 if (!(input.advance())) {
343 template<
typename Input2_>
344 static bool advance_and_chomp(Input2_& input) {
347 if (!(input.advance())) {
353 template<
typename Input2_>
354 static bool skip_lines(Input2_& input,
LineIndex& current_line) {
357 char x = input.get();
360 if (!(input.advance())) {
363 }
while (input.get() !=
'\n');
364 }
else if (x !=
'\n') {
368 if (!input.advance()) {
377 bool my_passed_banner =
false;
379 struct ExpectedMatch {
380 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
381 ExpectedMatch() : ExpectedMatch(false, false, false) {}
387 ExpectedMatch advance_past_expected_string() {
389 return ExpectedMatch(
true,
false,
false);
392 char next = my_input.
get();
393 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
394 if (!advance_and_chomp(my_input)) {
395 return ExpectedMatch(
true,
false,
false);
397 if (my_input.
get() ==
'\n') {
398 bool remaining = my_input.
advance();
399 return ExpectedMatch(
true,
true, remaining);
401 return ExpectedMatch(
true,
false,
true);
403 }
else if (next ==
'\n') {
404 bool remaining = my_input.
advance();
405 return ExpectedMatch(
true,
true, remaining);
409 return ExpectedMatch(
false,
true,
true);
412 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
418 for (std::size_t i = start; i < len; ++i) {
420 return ExpectedMatch(
false,
false,
false);
422 if (my_input.
get() != ptr[i]) {
423 return ExpectedMatch(
false,
false,
true);
426 return advance_past_expected_string();
429 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
432 return is_expected_string(ptr, len, 1);
435 bool parse_banner_object() {
438 char x = my_input.
get();
440 res = is_expected_string(
"matrix", 6);
441 my_details.
object = Object::MATRIX;
442 }
else if (x ==
'v') {
443 res = is_expected_string(
"vector", 6);
444 my_details.
object = Object::VECTOR;
448 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
450 if (!res.remaining) {
451 throw std::runtime_error(
"end of file reached after the first banner field");
457 bool parse_banner_format() {
460 char x = my_input.
get();
462 res = is_expected_string(
"coordinate", 10);
463 my_details.
format = Format::COORDINATE;
464 }
else if (x ==
'a') {
465 res = is_expected_string(
"array", 5);
466 my_details.
format = Format::ARRAY;
470 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
472 if (!res.remaining) {
473 throw std::runtime_error(
"end of file reached after the second banner field");
479 bool parse_banner_field() {
482 char x = my_input.
get();
484 res = is_expected_string(
"integer", 7);
485 my_details.
field = Field::INTEGER;
486 }
else if (x ==
'd') {
487 res = is_expected_string(
"double", 6);
488 my_details.
field = Field::DOUBLE;
489 }
else if (x ==
'c') {
490 res = is_expected_string(
"complex", 7);
491 my_details.
field = Field::COMPLEX;
492 }
else if (x ==
'p') {
493 res = is_expected_string(
"pattern", 7);
494 my_details.
field = Field::PATTERN;
495 }
else if (x ==
'r') {
496 res = is_expected_string(
"real", 4);
497 my_details.
field = Field::REAL;
501 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
503 if (!res.remaining) {
504 throw std::runtime_error(
"end of file reached after the third banner field");
510 bool parse_banner_symmetry() {
513 char x = my_input.
get();
515 res = is_expected_string(
"general", 7);
516 my_details.
symmetry = Symmetry::GENERAL;
517 }
else if (x ==
'h') {
518 res = is_expected_string(
"hermitian", 9);
519 my_details.
symmetry = Symmetry::HERMITIAN;
520 }
else if (x ==
's') {
522 char x = my_input.
get();
524 res = is_expected_string(
"skew-symmetric", 14, 2);
525 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
527 res = is_expected_string(
"symmetric", 9, 2);
528 my_details.
symmetry = Symmetry::SYMMETRIC;
534 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
536 if (!res.remaining) {
537 throw std::runtime_error(
"end of file reached after the fourth banner field");
544 if (my_passed_banner) {
545 throw std::runtime_error(
"banner has already been scanned");
547 if (!(my_input.
valid())) {
548 throw std::runtime_error(
"failed to find banner line before end of file");
550 if (my_input.
get() !=
'%') {
551 throw std::runtime_error(
"first line of the file should be the banner");
554 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
555 if (!found_banner.remaining) {
556 throw std::runtime_error(
"end of file reached before matching the banner");
558 if (!found_banner.found) {
559 throw std::runtime_error(
"first line of the file should be the banner");
561 if (found_banner.newline) {
562 throw std::runtime_error(
"end of line reached before matching the banner");
565 if (parse_banner_object()) {
566 throw std::runtime_error(
"end of line reached after the first banner field");
568 if (parse_banner_format()) {
569 throw std::runtime_error(
"end of line reached after the second banner field");
573 if (my_details.
object == Object::MATRIX) {
574 if (parse_banner_field()) {
575 throw std::runtime_error(
"end of line reached after the third banner field");
577 eol = parse_banner_symmetry();
582 my_details.
symmetry = Symmetry::GENERAL;
585 eol = parse_banner_field();
588 my_passed_banner =
true;
596 throw std::runtime_error(
"end of file reached before the end of the banner line");
598 }
while (my_input.
get() !=
'\n');
614 if (!my_passed_banner) {
615 throw std::runtime_error(
"banner has not yet been scanned");
623 template<
typename Integer_>
624 struct NotLastSizeInfo {
628 template<
typename Integer_>
629 struct LastSizeInfo {
631 bool remaining =
false;
634 template<
bool last_,
typename Integer_>
635 using SizeInfo =
typename std::conditional<last_, LastSizeInfo<Integer_>, NotLastSizeInfo<Integer_> >::type;
637 template<
bool last_,
typename Integer_,
class Input2_>
638 static SizeInfo<last_, Integer_> scan_integer_field(
bool size, Input2_& input,
LineIndex overall_line_count) {
639 SizeInfo<last_, Integer_> output;
642 auto what = [&]() -> std::string {
650 constexpr Integer_ max_limit = std::numeric_limits<Integer_>::max();
651 constexpr Integer_ max_limit_before_mult = max_limit / 10;
652 constexpr Integer_ max_limit_mod = max_limit % 10;
655 char x = input.get();
657 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
659 Integer_ delta = x -
'0';
661 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
662 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
665 output.index += delta;
676 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
678 if constexpr(last_) {
679 output.remaining = input.advance();
682 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
683 case ' ':
case '\t':
case '\r':
684 if (!advance_and_chomp(input)) {
685 if constexpr(last_) {
688 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
691 if constexpr(last_) {
692 if (input.get() !=
'\n') {
693 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
695 output.remaining = input.advance();
699 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
702 if (!(input.advance())) {
703 if constexpr(last_) {
706 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
714 template<
bool last_,
class Input2_>
715 static SizeInfo<last_, Index_> scan_size_field(Input2_& input,
LineIndex overall_line_count) {
716 return scan_integer_field<last_, Index_>(
true, input, overall_line_count);
719 template<
class Input2_>
720 static SizeInfo<true, LineIndex> scan_line_count_field(Input2_& input,
LineIndex overall_line_count) {
721 return scan_integer_field<true, LineIndex>(
true, input, overall_line_count);
724 template<
bool last_,
class Input2_>
725 static SizeInfo<last_, Index_> scan_index_field(Input2_& input,
LineIndex overall_line_count) {
726 return scan_integer_field<last_, Index_>(
false, input, overall_line_count);
730 bool my_passed_size =
false;
731 Index_ my_nrows = 0, my_ncols = 0;
735 if (!(my_input.
valid())) {
736 throw std::runtime_error(
"failed to find size line before end of file");
740 if (!skip_lines(my_input, my_current_line)) {
741 throw std::runtime_error(
"failed to find size line before end of file");
743 if (!chomp(my_input)) {
744 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
747 if (my_details.
object == Object::MATRIX) {
748 if (my_details.
format == Format::COORDINATE) {
749 auto first_field = scan_size_field<false>(my_input, my_current_line);
750 my_nrows = first_field.index;
752 auto second_field = scan_size_field<false>(my_input, my_current_line);
753 my_ncols = second_field.index;
755 auto third_field = scan_line_count_field(my_input, my_current_line);
756 my_nlines = third_field.index;
759 auto first_field = scan_size_field<false>(my_input, my_current_line);
760 my_nrows = first_field.index;
762 auto second_field = scan_size_field<true>(my_input, my_current_line);
763 my_ncols = second_field.index;
764 my_nlines = sanisizer::product<LineIndex>(my_nrows, my_ncols);
768 if (my_details.
format == Format::COORDINATE) {
769 auto first_field = scan_size_field<false>(my_input, my_current_line);
770 my_nrows = first_field.index;
772 auto second_field = scan_line_count_field(my_input, my_current_line);
773 my_nlines = second_field.index;
776 auto first_field = scan_size_field<true>(my_input, my_current_line);
777 my_nlines = first_field.index;
778 my_nrows = my_nlines;
784 my_passed_size =
true;
796 if (!my_passed_size) {
797 throw std::runtime_error(
"size line has not yet been scanned");
810 if (!my_passed_size) {
811 throw std::runtime_error(
"size line has not yet been scanned");
824 if (!my_passed_size) {
825 throw std::runtime_error(
"size line has not yet been scanned");
842 template<
typename Type_>
844 ParseInfo() =
default;
845 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
850 template<
typename Workspace_>
851 bool configure_parallel_workspace(Workspace_& work) {
852 bool available = fill_to_next_newline(my_input, work.buffer, my_buffer_size);
853 work.contents.clear();
854 work.overall_line = my_current_line;
855 my_current_line += count_newlines(work.buffer);
859 void check_num_lines_loop(
LineIndex data_line_count)
const {
860 if (data_line_count >= my_nlines) {
861 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
865 void check_num_lines_final(
bool finished,
LineIndex data_line_count)
const {
867 if (data_line_count != my_nlines) {
869 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
875 void check_matrix_coordinate_line(Index_ currow, Index_ curcol,
LineIndex overall_line_count)
const {
877 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
879 if (currow > my_nrows) {
880 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
883 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
885 if (curcol > my_ncols) {
886 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
890 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
891 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
892 bool valid = input.valid();
895 if (!skip_lines(input, overall_line_count)) {
899 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
902 auto first_field = scan_index_field<false>(input, overall_line_count);
903 auto second_field = scan_index_field<false>(input, overall_line_count);
904 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
907 ParseInfo<Type_> res = fparser(input, overall_line_count);
908 if (!wstore(first_field.index, second_field.index, res.value)) {
911 ++overall_line_count;
912 valid = res.remaining;
918 template<
typename Type_,
class FieldParser_,
class Store_>
919 bool scan_matrix_coordinate_non_pattern(Store_ store) {
920 bool finished =
false;
923 if (my_nthreads == 1) {
924 FieldParser_ fparser;
925 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
929 [&](Index_ r, Index_ c, Type_ value) ->
bool {
930 check_num_lines_loop(current_data_line);
932 return store(r, c, value);
938 std::vector<char> buffer;
939 FieldParser_ fparser;
940 std::vector<std::tuple<Index_, Index_, Type_> > contents;
944 ThreadPool<Workspace> tp(
945 [&](Workspace& work) ->
bool {
946 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
947 return scan_matrix_coordinate_non_pattern_base<Type_>(
951 [&](Index_ r, Index_ c, Type_ value) ->
bool {
952 work.contents.emplace_back(r, c, value);
961 [&](Workspace& work) ->
bool {
962 return configure_parallel_workspace(work);
964 [&](Workspace& work) ->
bool {
965 for (
const auto& con : work.contents) {
966 check_num_lines_loop(current_data_line);
967 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
977 check_num_lines_final(finished, current_data_line);
982 template<
class Input2_,
class WrappedStore_>
983 bool scan_matrix_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
984 bool valid = input.valid();
987 if (!skip_lines(input, overall_line_count)) {
991 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
994 auto first_field = scan_index_field<false>(input, overall_line_count);
995 auto second_field = scan_index_field<true>(input, overall_line_count);
996 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
998 if (!wstore(first_field.index, second_field.index)) {
1001 ++overall_line_count;
1002 valid = second_field.remaining;
1008 template<
class Store_>
1009 bool scan_matrix_coordinate_pattern(Store_ store) {
1010 bool finished =
false;
1013 if (my_nthreads == 1) {
1014 finished = scan_matrix_coordinate_pattern_base(
1017 [&](Index_ r, Index_ c) ->
bool {
1018 check_num_lines_loop(current_data_line);
1019 ++current_data_line;
1026 std::vector<char> buffer;
1027 std::vector<std::tuple<Index_, Index_> > contents;
1031 ThreadPool<Workspace> tp(
1032 [&](Workspace& work) ->
bool {
1033 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1034 return scan_matrix_coordinate_pattern_base(
1037 [&](Index_ r, Index_ c) ->
bool {
1038 work.contents.emplace_back(r, c);
1047 [&](Workspace& work) ->
bool {
1048 return configure_parallel_workspace(work);
1050 [&](Workspace& work) ->
bool {
1051 for (
const auto& con : work.contents) {
1052 check_num_lines_loop(current_data_line);
1053 if (!store(std::get<0>(con), std::get<1>(con))) {
1056 ++current_data_line;
1063 check_num_lines_final(finished, current_data_line);
1068 void check_vector_coordinate_line(Index_ currow,
LineIndex overall_line_count)
const {
1070 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1072 if (currow > my_nrows) {
1073 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1077 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1078 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1079 bool valid = input.valid();
1082 if (!skip_lines(input, overall_line_count)) {
1085 if (!chomp(input)) {
1086 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1089 auto first_field = scan_index_field<false>(input, overall_line_count);
1090 check_vector_coordinate_line(first_field.index, overall_line_count);
1093 ParseInfo<Type_> res = fparser(input, overall_line_count);
1094 if (!wstore(first_field.index, res.value)) {
1097 ++overall_line_count;
1098 valid = res.remaining;
1104 template<
typename Type_,
class FieldParser_,
class Store_>
1105 bool scan_vector_coordinate_non_pattern(Store_ store) {
1106 bool finished =
false;
1109 if (my_nthreads == 1) {
1110 FieldParser_ fparser;
1111 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1115 [&](Index_ r, Type_ value) ->
bool {
1116 check_num_lines_loop(current_data_line);
1117 ++current_data_line;
1118 return store(r, 1, value);
1124 std::vector<char> buffer;
1125 FieldParser_ fparser;
1126 std::vector<std::tuple<Index_, Type_> > contents;
1130 ThreadPool<Workspace> tp(
1131 [&](Workspace& work) ->
bool {
1132 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1133 return scan_vector_coordinate_non_pattern_base<Type_>(
1137 [&](Index_ r, Type_ value) ->
bool {
1138 work.contents.emplace_back(r, value);
1147 [&](Workspace& work) ->
bool {
1148 return configure_parallel_workspace(work);
1150 [&](Workspace& work) ->
bool {
1151 for (
const auto& con : work.contents) {
1152 check_num_lines_loop(current_data_line);
1153 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1156 ++current_data_line;
1163 check_num_lines_final(finished, current_data_line);
1168 template<
class Input2_,
class WrappedStore_>
1169 bool scan_vector_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
1170 bool valid = input.valid();
1173 if (!skip_lines(input, overall_line_count)) {
1176 if (!chomp(input)) {
1177 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1180 auto first_field = scan_index_field<true>(input, overall_line_count);
1181 check_vector_coordinate_line(first_field.index, overall_line_count);
1183 if (!wstore(first_field.index)) {
1186 ++overall_line_count;
1187 valid = first_field.remaining;
1193 template<
class Store_>
1194 bool scan_vector_coordinate_pattern(Store_ store) {
1195 bool finished =
false;
1198 if (my_nthreads == 1) {
1199 finished = scan_vector_coordinate_pattern_base(
1202 [&](Index_ r) ->
bool {
1203 check_num_lines_loop(current_data_line);
1204 ++current_data_line;
1211 std::vector<char> buffer;
1212 std::vector<Index_> contents;
1216 ThreadPool<Workspace> tp(
1217 [&](Workspace& work) ->
bool {
1218 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1219 return scan_vector_coordinate_pattern_base(
1222 [&](Index_ r) ->
bool {
1223 work.contents.emplace_back(r);
1232 [&](Workspace& work) ->
bool {
1233 return configure_parallel_workspace(work);
1235 [&](Workspace& work) ->
bool {
1236 for (
const auto& r : work.contents) {
1237 check_num_lines_loop(current_data_line);
1241 ++current_data_line;
1248 check_num_lines_final(finished, current_data_line);
1253 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1254 bool scan_matrix_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1255 bool valid = input.valid();
1258 if (!skip_lines(input, overall_line_count)) {
1261 if (!chomp(input)) {
1262 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1266 ParseInfo<Type_> res = fparser(input, overall_line_count);
1267 if (!wstore(res.value)) {
1270 ++overall_line_count;
1271 valid = res.remaining;
1277 template<
typename Type_,
class FieldParser_,
class Store_>
1278 bool scan_matrix_array(Store_ store) {
1279 bool finished =
false;
1282 Index_ currow = 1, curcol = 1;
1283 auto increment = [&]() {
1285 if (currow > my_nrows) {
1291 if (my_nthreads == 1) {
1292 FieldParser_ fparser;
1293 finished = scan_matrix_array_base<Type_>(
1297 [&](Type_ value) ->
bool {
1298 check_num_lines_loop(current_data_line);
1299 if (!store(currow, curcol, value)) {
1302 ++current_data_line;
1310 std::vector<char> buffer;
1311 FieldParser_ fparser;
1312 std::vector<Type_> contents;
1316 ThreadPool<Workspace> tp(
1317 [&](Workspace& work) ->
bool {
1318 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1319 return scan_matrix_array_base<Type_>(
1323 [&](Type_ value) ->
bool {
1324 work.contents.emplace_back(value);
1333 [&](Workspace& work) ->
bool {
1334 return configure_parallel_workspace(work);
1336 [&](Workspace& work) ->
bool {
1337 for (
const auto& val : work.contents) {
1338 check_num_lines_loop(current_data_line);
1339 if (!store(currow, curcol, val)) {
1342 ++current_data_line;
1350 check_num_lines_final(finished, current_data_line);
1355 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1356 bool scan_vector_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1357 bool valid = input.valid();
1360 if (!skip_lines(input, overall_line_count)) {
1363 if (!chomp(input)) {
1364 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1368 ParseInfo<Type_> res = fparser(input, overall_line_count);
1369 if (!wstore(res.value)) {
1372 ++overall_line_count;
1373 valid = res.remaining;
1379 template<
typename Type_,
class FieldParser_,
class Store_>
1380 bool scan_vector_array(Store_ store) {
1381 bool finished =
false;
1383 if (my_nthreads == 1) {
1384 FieldParser_ fparser;
1385 finished = scan_vector_array_base<Type_>(
1389 [&](Type_ value) ->
bool {
1390 check_num_lines_loop(current_data_line);
1391 ++current_data_line;
1392 return store(current_data_line, 1, value);
1398 std::vector<char> buffer;
1399 FieldParser_ fparser;
1400 std::vector<Type_> contents;
1404 ThreadPool<Workspace> tp(
1405 [&](Workspace& work) ->
bool {
1406 DirectBufferedReader bufreader(work.buffer.data(), work.buffer.size());
1407 return scan_vector_array_base<Type_>(
1411 [&](Type_ value) ->
bool {
1412 work.contents.emplace_back(value);
1421 [&](Workspace& work) ->
bool {
1422 return configure_parallel_workspace(work);
1424 [&](Workspace& work) ->
bool {
1425 for (
const auto& val : work.contents) {
1426 check_num_lines_loop(current_data_line);
1427 ++current_data_line;
1428 if (!store(current_data_line, 1, val)) {
1437 check_num_lines_final(finished, current_data_line);
1442 void check_preamble()
const {
1443 if (!my_passed_banner || !my_passed_size) {
1444 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1448 template<
typename Type_>
1449 class IntegerFieldParser {
1451 template<
class Input2_>
1452 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1453 char firstchar = input.get();
1454 bool negative = (firstchar ==
'-');
1455 if (negative || firstchar ==
'+') {
1456 if (!(input.advance())) {
1457 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1461 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1462 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1463 constexpr Type_ upper_limit_mod = upper_limit % 10;
1464 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1465 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1466 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1471 char x = input.get();
1473 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1475 Type_ delta = x -
'0';
1479 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1480 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1485 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1486 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1494 case ' ':
case '\t':
case '\r':
1495 if (!advance_and_chomp(input)) {
1496 return ParseInfo<Type_>(val,
false);
1498 if (input.get() !=
'\n') {
1499 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1501 return ParseInfo<Type_>(val, input.advance());
1509 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1511 return ParseInfo<Type_>(val, input.advance());
1513 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1516 if (!(input.advance())) {
1521 return ParseInfo<Type_>(val,
false);
1539 template<
typename Type_ =
int,
class Store_>
1542 static_assert(std::is_integral<Type_>::value);
1544 auto wrapped_store = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1545 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1546 return store(r, c, val);
1553 if (my_details.
format == Format::COORDINATE) {
1554 if (my_details.
object == Object::MATRIX) {
1555 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1557 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1560 if (my_details.
object == Object::MATRIX) {
1561 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1563 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1569 template<
bool last_,
typename Type_,
typename Input2_>
1570 static ParseInfo<Type_> parse_real(Input2_& input, std::string& temporary, Index overall_line_count) {
1572 ParseInfo<Type_> output(0,
true);
1575 char x = input.get();
1578 if constexpr(last_) {
1582 if (temporary.empty()) {
1583 throw std::runtime_error(
"empty number field on line " + std::to_string(overall_line_count + 1));
1585 output.remaining = input.advance();
1587 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1589 goto final_processing;
1591 case ' ':
case '\t':
case '\r':
1592 if constexpr(last_) {
1593 if (!advance_and_chomp(input)) {
1594 output.remaining =
false;
1596 if (input.get() !=
'\n') {
1597 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1599 output.remaining = input.advance();
1602 if (!advance_and_chomp(input)) {
1603 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1605 if (input.get() ==
'\n') {
1606 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1609 goto final_processing;
1616 if (!(input.advance())) {
1617 if constexpr(last_) {
1618 output.remaining =
false;
1619 goto final_processing;
1621 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1626 if (temporary.size() >= 2 && temporary[0] ==
'0' && (temporary[1] ==
'x' || temporary[1] ==
'X')) {
1627 throw std::runtime_error(
"hexadecimal numbers are not allowed on line " + std::to_string(overall_line_count + 1));
1632 if constexpr(std::is_same<Type_, float>::value) {
1633 output.value = std::stof(temporary, &n);
1634 }
else if constexpr(std::is_same<Type_, long double>::value) {
1635 output.value = std::stold(temporary, &n);
1637 output.value = std::stod(temporary, &n);
1639 }
catch (std::invalid_argument& e) {
1640 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1643 if (n != temporary.size()) {
1644 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1649 template<
typename Type_>
1650 class RealFieldParser {
1652 template<
class Input2_>
1653 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1654 return parse_real<true, Type_>(input, my_temporary, overall_line_count);
1657 std::string my_temporary;
1674 template<
typename Type_ =
double,
class Store_>
1677 static_assert(std::is_floating_point<Type_>::value);
1679 auto store_real = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1680 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1681 return store(r, c, val);
1688 if (my_details.
format == Format::COORDINATE) {
1689 if (my_details.
object == Object::MATRIX) {
1690 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1692 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1695 if (my_details.
object == Object::MATRIX) {
1696 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1698 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1717 template<
typename Type_ =
double,
class Store_>
1723 template<
typename InnerType_>
1724 class ComplexFieldParser {
1726 template<
typename Input2_>
1727 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
LineIndex overall_line_count) {
1728 auto first = parse_real<false, InnerType_>(input, my_temporary, overall_line_count);
1729 auto second = parse_real<true, InnerType_>(input, my_temporary, overall_line_count);
1730 ParseInfo<std::complex<InnerType_> > output;
1731 output.value.real(first.value);
1732 output.value.imag(second.value);
1733 output.remaining = second.remaining;
1737 std::string my_temporary;
1754 template<
typename Type_ =
double,
class Store_>
1757 static_assert(std::is_floating_point<Type_>::value);
1759 typedef std::complex<Type_> FullType;
1760 auto store_comp = [&](Index_ r, Index_ c, FullType val) ->
bool {
1761 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, FullType>::type,
bool>::value) {
1762 return store(r, c, val);
1769 if (my_details.
format == Format::COORDINATE) {
1770 if (my_details.
object == Object::MATRIX) {
1771 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1773 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1776 if (my_details.
object == Object::MATRIX) {
1777 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1779 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1799 template<
typename Type_ =
bool,
class Store_>
1802 if (my_details.
format != Format::COORDINATE) {
1803 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1806 auto store_pat = [&](Index_ r, Index_ c) ->
bool {
1807 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, bool>::type,
bool>::value) {
1808 return store(r, c,
true);
1815 if (my_details.
object == Object::MATRIX) {
1816 return scan_matrix_coordinate_pattern(std::move(store_pat));
1818 return scan_vector_coordinate_pattern(std::move(store_pat));