278 my_input(std::move(input)),
279 my_nthreads(options.num_threads),
280 my_block_size(options.block_size)
282 sanisizer::cast<typename std::vector<char>::size_type>(my_block_size);
286 std::unique_ptr<Input_> my_input;
288 std::size_t my_block_size;
290 Index my_current_line = 0;
293 template<
typename Input2_>
294 static bool chomp(Input2_& input) {
296 char x = input.get();
297 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
300 if (!(input.advance())) {
307 template<
typename Input2_>
308 static bool advance_and_chomp(Input2_& input) {
311 if (!(input.advance())) {
317 template<
typename Input2_>
318 static bool skip_lines(Input2_& input,
Index& current_line) {
321 char x = input.get();
324 if (!(input.advance())) {
327 }
while (input.get() !=
'\n');
328 }
else if (x !=
'\n') {
332 if (!input.advance()) {
341 bool my_passed_banner =
false;
343 struct ExpectedMatch {
344 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
345 ExpectedMatch() : ExpectedMatch(false, false, false) {}
351 ExpectedMatch advance_past_expected_string() {
352 if (!(my_input->advance())) {
353 return ExpectedMatch(
true,
false,
false);
356 char next = my_input->get();
357 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
358 if (!advance_and_chomp(*my_input)) {
359 return ExpectedMatch(
true,
false,
false);
361 if (my_input->get() ==
'\n') {
362 bool remaining = my_input->advance();
363 return ExpectedMatch(
true,
true, remaining);
365 return ExpectedMatch(
true,
false,
true);
367 }
else if (next ==
'\n') {
368 bool remaining = my_input->advance();
369 return ExpectedMatch(
true,
true, remaining);
373 return ExpectedMatch(
false,
true,
true);
376 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
382 for (std::size_t i = start; i < len; ++i) {
383 if (!my_input->advance()) {
384 return ExpectedMatch(
false,
false,
false);
386 if (my_input->get() != ptr[i]) {
387 return ExpectedMatch(
false,
false,
true);
390 return advance_past_expected_string();
393 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
396 return is_expected_string(ptr, len, 1);
399 bool parse_banner_object() {
402 char x = my_input->get();
404 res = is_expected_string(
"matrix", 6);
405 my_details.
object = Object::MATRIX;
406 }
else if (x ==
'v') {
407 res = is_expected_string(
"vector", 6);
408 my_details.
object = Object::VECTOR;
412 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
414 if (!res.remaining) {
415 throw std::runtime_error(
"end of file reached after the first banner field");
421 bool parse_banner_format() {
424 char x = my_input->get();
426 res = is_expected_string(
"coordinate", 10);
427 my_details.
format = Format::COORDINATE;
428 }
else if (x ==
'a') {
429 res = is_expected_string(
"array", 5);
430 my_details.
format = Format::ARRAY;
434 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
436 if (!res.remaining) {
437 throw std::runtime_error(
"end of file reached after the second banner field");
443 bool parse_banner_field() {
446 char x = my_input->get();
448 res = is_expected_string(
"integer", 7);
449 my_details.
field = Field::INTEGER;
450 }
else if (x ==
'd') {
451 res = is_expected_string(
"double", 6);
452 my_details.
field = Field::DOUBLE;
453 }
else if (x ==
'c') {
454 res = is_expected_string(
"complex", 7);
455 my_details.
field = Field::COMPLEX;
456 }
else if (x ==
'p') {
457 res = is_expected_string(
"pattern", 7);
458 my_details.
field = Field::PATTERN;
459 }
else if (x ==
'r') {
460 res = is_expected_string(
"real", 4);
461 my_details.
field = Field::REAL;
465 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
467 if (!res.remaining) {
468 throw std::runtime_error(
"end of file reached after the third banner field");
474 bool parse_banner_symmetry() {
477 char x = my_input->get();
479 res = is_expected_string(
"general", 7);
480 my_details.
symmetry = Symmetry::GENERAL;
481 }
else if (x ==
'h') {
482 res = is_expected_string(
"hermitian", 9);
483 my_details.
symmetry = Symmetry::HERMITIAN;
484 }
else if (x ==
's') {
485 if (my_input->advance()) {
486 char x = my_input->get();
488 res = is_expected_string(
"skew-symmetric", 14, 2);
489 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
491 res = is_expected_string(
"symmetric", 9, 2);
492 my_details.
symmetry = Symmetry::SYMMETRIC;
498 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
500 if (!res.remaining) {
501 throw std::runtime_error(
"end of file reached after the fourth banner field");
508 if (my_passed_banner) {
509 throw std::runtime_error(
"banner has already been scanned");
511 if (!(my_input->valid())) {
512 throw std::runtime_error(
"failed to find banner line before end of file");
514 if (my_input->get() !=
'%') {
515 throw std::runtime_error(
"first line of the file should be the banner");
518 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
519 if (!found_banner.remaining) {
520 throw std::runtime_error(
"end of file reached before matching the banner");
522 if (!found_banner.found) {
523 throw std::runtime_error(
"first line of the file should be the banner");
525 if (found_banner.newline) {
526 throw std::runtime_error(
"end of line reached before matching the banner");
529 if (parse_banner_object()) {
530 throw std::runtime_error(
"end of line reached after the first banner field");
532 if (parse_banner_format()) {
533 throw std::runtime_error(
"end of line reached after the second banner field");
537 if (my_details.
object == Object::MATRIX) {
538 if (parse_banner_field()) {
539 throw std::runtime_error(
"end of line reached after the third banner field");
541 eol = parse_banner_symmetry();
546 my_details.
symmetry = Symmetry::GENERAL;
549 eol = parse_banner_field();
552 my_passed_banner =
true;
559 if (!(my_input->advance())) {
560 throw std::runtime_error(
"end of file reached before the end of the banner line");
562 }
while (my_input->get() !=
'\n');
578 if (!my_passed_banner) {
579 throw std::runtime_error(
"banner has not yet been scanned");
587 struct NotLastSizeInfo {
591 struct LastSizeInfo {
593 bool remaining =
false;
597 using SizeInfo =
typename std::conditional<last_, LastSizeInfo, NotLastSizeInfo>::type;
599 template<
bool last_,
class Input2_>
600 static SizeInfo<last_> scan_integer_field(
bool size, Input2_& input,
Index overall_line_count) {
601 SizeInfo<last_> output;
604 auto what = [&]() -> std::string {
612 constexpr Index max_limit = std::numeric_limits<Index>::max();
613 constexpr Index max_limit_before_mult = max_limit / 10;
614 constexpr Index max_limit_mod = max_limit % 10;
617 char x = input.get();
619 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
621 Index delta = x -
'0';
623 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
624 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
627 output.index += delta;
638 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
640 if constexpr(last_) {
641 output.remaining = input.advance();
644 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
645 case ' ':
case '\t':
case '\r':
646 if (!advance_and_chomp(input)) {
647 if constexpr(last_) {
650 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
653 if constexpr(last_) {
654 if (input.get() !=
'\n') {
655 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
657 output.remaining = input.advance();
661 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
664 if (!(input.advance())) {
665 if constexpr(last_) {
668 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
676 template<
bool last_,
class Input2_>
677 static SizeInfo<last_> scan_size_field(Input2_& input,
Index overall_line_count) {
678 return scan_integer_field<last_>(
true, input, overall_line_count);
681 template<
bool last_,
class Input2_>
682 static SizeInfo<last_> scan_index_field(Input2_& input,
Index overall_line_count) {
683 return scan_integer_field<last_>(
false, input, overall_line_count);
687 bool my_passed_size =
false;
688 Index my_nrows = 0, my_ncols = 0, my_nlines = 0;
691 if (!(my_input->valid())) {
692 throw std::runtime_error(
"failed to find size line before end of file");
696 if (!skip_lines(*my_input, my_current_line)) {
697 throw std::runtime_error(
"failed to find size line before end of file");
699 if (!chomp(*my_input)) {
700 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
703 if (my_details.
object == Object::MATRIX) {
704 if (my_details.
format == Format::COORDINATE) {
705 auto first_field = scan_size_field<false>(*my_input, my_current_line);
706 my_nrows = first_field.index;
708 auto second_field = scan_size_field<false>(*my_input, my_current_line);
709 my_ncols = second_field.index;
711 auto third_field = scan_size_field<true>(*my_input, my_current_line);
712 my_nlines = third_field.index;
715 auto first_field = scan_size_field<false>(*my_input, my_current_line);
716 my_nrows = first_field.index;
718 auto second_field = scan_size_field<true>(*my_input, my_current_line);
719 my_ncols = second_field.index;
720 my_nlines = my_nrows * my_ncols;
724 if (my_details.
format == Format::COORDINATE) {
725 auto first_field = scan_size_field<false>(*my_input, my_current_line);
726 my_nrows = first_field.index;
728 auto second_field = scan_size_field<true>(*my_input, my_current_line);
729 my_nlines = second_field.index;
732 auto first_field = scan_size_field<true>(*my_input, my_current_line);
733 my_nlines = first_field.index;
734 my_nrows = my_nlines;
740 my_passed_size =
true;
752 if (!my_passed_size) {
753 throw std::runtime_error(
"size line has not yet been scanned");
766 if (!my_passed_size) {
767 throw std::runtime_error(
"size line has not yet been scanned");
780 if (!my_passed_size) {
781 throw std::runtime_error(
"size line has not yet been scanned");
798 template<
typename Type_>
800 ParseInfo() =
default;
801 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
806 template<
typename Workspace_>
807 bool configure_parallel_workspace(Workspace_& work) {
808 bool available = fill_to_next_newline(*my_input, work.buffer, my_block_size);
809 work.contents.clear();
810 work.overall_line = my_current_line;
811 my_current_line += count_newlines(work.buffer);
815 void check_num_lines_loop(
Index data_line_count)
const {
816 if (data_line_count >= my_nlines) {
817 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
821 void check_num_lines_final(
bool finished,
Index data_line_count)
const {
823 if (data_line_count != my_nlines) {
825 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
831 void check_matrix_coordinate_line(
Index currow,
Index curcol,
Index overall_line_count)
const {
833 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
835 if (currow > my_nrows) {
836 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
839 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
841 if (curcol > my_ncols) {
842 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
846 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
847 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
848 bool valid = input.valid();
851 if (!skip_lines(input, overall_line_count)) {
855 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
858 auto first_field = scan_index_field<false>(input, overall_line_count);
859 auto second_field = scan_index_field<false>(input, overall_line_count);
860 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
863 ParseInfo<Type_> res = fparser(input, overall_line_count);
864 if (!wstore(first_field.index, second_field.index, res.value)) {
867 ++overall_line_count;
868 valid = res.remaining;
874 template<
typename Type_,
class FieldParser_,
class Store_>
875 bool scan_matrix_coordinate_non_pattern(Store_ store) {
876 bool finished =
false;
877 Index current_data_line = 0;
879 if (my_nthreads == 1) {
880 FieldParser_ fparser;
881 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
886 check_num_lines_loop(current_data_line);
888 return store(r, c, value);
894 std::vector<char> buffer;
895 FieldParser_ fparser;
896 std::vector<std::tuple<Index, Index, Type_> > contents;
900 ThreadPool<Workspace> tp(
901 [&](Workspace& work) ->
bool {
902 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
904 return scan_matrix_coordinate_non_pattern_base<Type_>(
909 work.contents.emplace_back(r, c, value);
918 [&](Workspace& work) ->
bool {
919 return configure_parallel_workspace(work);
921 [&](Workspace& work) ->
bool {
922 for (
const auto& con : work.contents) {
923 check_num_lines_loop(current_data_line);
924 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
934 check_num_lines_final(finished, current_data_line);
939 template<
class Input2_,
class WrappedStore_>
940 bool scan_matrix_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
941 bool valid = input.valid();
944 if (!skip_lines(input, overall_line_count)) {
948 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
951 auto first_field = scan_index_field<false>(input, overall_line_count);
952 auto second_field = scan_index_field<true>(input, overall_line_count);
953 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
955 if (!wstore(first_field.index, second_field.index)) {
958 ++overall_line_count;
959 valid = second_field.remaining;
965 template<
class Store_>
966 bool scan_matrix_coordinate_pattern(Store_ store) {
967 bool finished =
false;
968 Index current_data_line = 0;
970 if (my_nthreads == 1) {
971 finished = scan_matrix_coordinate_pattern_base(
975 check_num_lines_loop(current_data_line);
983 std::vector<char> buffer;
984 std::vector<std::tuple<Index, Index> > contents;
988 ThreadPool<Workspace> tp(
989 [&](Workspace& work) ->
bool {
990 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
992 return scan_matrix_coordinate_pattern_base(
996 work.contents.emplace_back(r, c);
1005 [&](Workspace& work) ->
bool {
1006 return configure_parallel_workspace(work);
1008 [&](Workspace& work) ->
bool {
1009 for (
const auto& con : work.contents) {
1010 check_num_lines_loop(current_data_line);
1011 if (!store(std::get<0>(con), std::get<1>(con))) {
1014 ++current_data_line;
1021 check_num_lines_final(finished, current_data_line);
1026 void check_vector_coordinate_line(
Index currow,
Index overall_line_count)
const {
1028 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1030 if (currow > my_nrows) {
1031 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1035 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1036 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1037 bool valid = input.valid();
1040 if (!skip_lines(input, overall_line_count)) {
1043 if (!chomp(input)) {
1044 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1047 auto first_field = scan_index_field<false>(input, overall_line_count);
1048 check_vector_coordinate_line(first_field.index, overall_line_count);
1051 ParseInfo<Type_> res = fparser(input, overall_line_count);
1052 if (!wstore(first_field.index, res.value)) {
1055 ++overall_line_count;
1056 valid = res.remaining;
1062 template<
typename Type_,
class FieldParser_,
class Store_>
1063 bool scan_vector_coordinate_non_pattern(Store_ store) {
1064 bool finished =
false;
1065 Index current_data_line = 0;
1067 if (my_nthreads == 1) {
1068 FieldParser_ fparser;
1069 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1073 [&](
Index r, Type_ value) ->
bool {
1074 check_num_lines_loop(current_data_line);
1075 ++current_data_line;
1076 return store(r, 1, value);
1082 std::vector<char> buffer;
1083 FieldParser_ fparser;
1084 std::vector<std::tuple<Index, Type_> > contents;
1088 ThreadPool<Workspace> tp(
1089 [&](Workspace& work) ->
bool {
1090 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1092 return scan_vector_coordinate_non_pattern_base<Type_>(
1096 [&](
Index r, Type_ value) ->
bool {
1097 work.contents.emplace_back(r, value);
1106 [&](Workspace& work) ->
bool {
1107 return configure_parallel_workspace(work);
1109 [&](Workspace& work) ->
bool {
1110 for (
const auto& con : work.contents) {
1111 check_num_lines_loop(current_data_line);
1112 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1115 ++current_data_line;
1122 check_num_lines_final(finished, current_data_line);
1127 template<
class Input2_,
class WrappedStore_>
1128 bool scan_vector_coordinate_pattern_base(Input2_& input,
Index& overall_line_count, WrappedStore_ wstore)
const {
1129 bool valid = input.valid();
1132 if (!skip_lines(input, overall_line_count)) {
1135 if (!chomp(input)) {
1136 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1139 auto first_field = scan_index_field<true>(input, overall_line_count);
1140 check_vector_coordinate_line(first_field.index, overall_line_count);
1142 if (!wstore(first_field.index)) {
1145 ++overall_line_count;
1146 valid = first_field.remaining;
1152 template<
class Store_>
1153 bool scan_vector_coordinate_pattern(Store_ store) {
1154 bool finished =
false;
1155 Index current_data_line = 0;
1157 if (my_nthreads == 1) {
1158 finished = scan_vector_coordinate_pattern_base(
1161 [&](
Index r) ->
bool {
1162 check_num_lines_loop(current_data_line);
1163 ++current_data_line;
1170 std::vector<char> buffer;
1171 std::vector<Index> contents;
1175 ThreadPool<Workspace> tp(
1176 [&](Workspace& work) ->
bool {
1177 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1179 return scan_vector_coordinate_pattern_base(
1182 [&](
Index r) ->
bool {
1183 work.contents.emplace_back(r);
1192 [&](Workspace& work) ->
bool {
1193 return configure_parallel_workspace(work);
1195 [&](Workspace& work) ->
bool {
1196 for (
const auto& r : work.contents) {
1197 check_num_lines_loop(current_data_line);
1201 ++current_data_line;
1208 check_num_lines_final(finished, current_data_line);
1213 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1214 bool scan_matrix_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1215 bool valid = input.valid();
1218 if (!skip_lines(input, overall_line_count)) {
1221 if (!chomp(input)) {
1222 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1226 ParseInfo<Type_> res = fparser(input, overall_line_count);
1227 if (!wstore(res.value)) {
1230 ++overall_line_count;
1231 valid = res.remaining;
1237 template<
typename Type_,
class FieldParser_,
class Store_>
1238 bool scan_matrix_array(Store_ store) {
1239 bool finished =
false;
1240 Index current_data_line = 0;
1242 Index currow = 1, curcol = 1;
1243 auto increment = [&]() {
1245 if (currow > my_nrows) {
1251 if (my_nthreads == 1) {
1252 FieldParser_ fparser;
1253 finished = scan_matrix_array_base<Type_>(
1257 [&](Type_ value) ->
bool {
1258 check_num_lines_loop(current_data_line);
1259 if (!store(currow, curcol, value)) {
1262 ++current_data_line;
1270 std::vector<char> buffer;
1271 FieldParser_ fparser;
1272 std::vector<Type_> contents;
1276 ThreadPool<Workspace> tp(
1277 [&](Workspace& work) ->
bool {
1278 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1280 return scan_matrix_array_base<Type_>(
1284 [&](Type_ value) ->
bool {
1285 work.contents.emplace_back(value);
1294 [&](Workspace& work) ->
bool {
1295 return configure_parallel_workspace(work);
1297 [&](Workspace& work) ->
bool {
1298 for (
const auto& val : work.contents) {
1299 check_num_lines_loop(current_data_line);
1300 if (!store(currow, curcol, val)) {
1303 ++current_data_line;
1311 check_num_lines_final(finished, current_data_line);
1316 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1317 bool scan_vector_array_base(Input2_& input,
Index& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1318 bool valid = input.valid();
1321 if (!skip_lines(input, overall_line_count)) {
1324 if (!chomp(input)) {
1325 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1329 ParseInfo<Type_> res = fparser(input, overall_line_count);
1330 if (!wstore(res.value)) {
1333 ++overall_line_count;
1334 valid = res.remaining;
1340 template<
typename Type_,
class FieldParser_,
class Store_>
1341 bool scan_vector_array(Store_ store) {
1342 bool finished =
false;
1343 Index current_data_line = 0;
1344 if (my_nthreads == 1) {
1345 FieldParser_ fparser;
1346 finished = scan_vector_array_base<Type_>(
1350 [&](Type_ value) ->
bool {
1351 check_num_lines_loop(current_data_line);
1352 ++current_data_line;
1353 return store(current_data_line, 1, value);
1359 std::vector<char> buffer;
1360 FieldParser_ fparser;
1361 std::vector<Type_> contents;
1365 ThreadPool<Workspace> tp(
1366 [&](Workspace& work) ->
bool {
1367 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1369 return scan_vector_array_base<Type_>(
1373 [&](Type_ value) ->
bool {
1374 work.contents.emplace_back(value);
1383 [&](Workspace& work) ->
bool {
1384 return configure_parallel_workspace(work);
1386 [&](Workspace& work) ->
bool {
1387 for (
const auto& val : work.contents) {
1388 check_num_lines_loop(current_data_line);
1389 ++current_data_line;
1390 if (!store(current_data_line, 1, val)) {
1399 check_num_lines_final(finished, current_data_line);
1404 void check_preamble()
const {
1405 if (!my_passed_banner || !my_passed_size) {
1406 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1410 template<
typename Type_>
1411 class IntegerFieldParser {
1413 template<
class Input2_>
1414 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1415 char firstchar = input.get();
1416 bool negative = (firstchar ==
'-');
1417 if (negative || firstchar ==
'+') {
1418 if (!(input.advance())) {
1419 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1423 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1424 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1425 constexpr Type_ upper_limit_mod = upper_limit % 10;
1426 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1427 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1428 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1433 char x = input.get();
1435 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1437 Type_ delta = x -
'0';
1441 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1442 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1447 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1448 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1456 case ' ':
case '\t':
case '\r':
1457 if (!advance_and_chomp(input)) {
1458 return ParseInfo<Type_>(val,
false);
1460 if (input.get() !=
'\n') {
1461 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1463 return ParseInfo<Type_>(val, input.advance());
1471 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1473 return ParseInfo<Type_>(val, input.advance());
1475 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1478 if (!(input.advance())) {
1483 return ParseInfo<Type_>(val,
false);
1501 template<
typename Type_ =
int,
class Store_>
1505 auto wrapped_store = [&](
Index r,
Index c, Type_ val) ->
bool {
1506 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1507 return store(r, c, val);
1514 if (my_details.
format == Format::COORDINATE) {
1515 if (my_details.
object == Object::MATRIX) {
1516 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1518 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1521 if (my_details.
object == Object::MATRIX) {
1522 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1524 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1530 template<
bool last_,
typename Type_,
typename Input2_>
1531 static typename std::conditional<last_, ParseInfo<Type_>, Type_>::type parse_special(Input2_& input,
bool negative,
bool check_inf,
Index overall_line_count) {
1532 auto what = [&]() -> std::string {
1534 return std::string(
"infinity");
1536 return std::string(
"NaN");
1540 auto check = [&](
char lower,
char upper) ->
void {
1541 if (!input.advance()) {
1542 throw std::runtime_error(
"unexpected termination of " + what() +
" on line " + std::to_string(overall_line_count + 1));
1544 char current = input.get();
1545 if (current != lower && current != upper) {
1546 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1550 bool remaining =
true;
1557 remaining = input.advance();
1559 char current = input.get();
1560 if (current !=
'\n' && current !=
' ' && current !=
'\t' && current !=
'\r') {
1561 if (current !=
'i' && current !=
'I') {
1562 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1568 remaining = input.advance();
1575 remaining = input.advance();
1580 switch(input.get()) {
1581 case ' ':
case '\t':
case '\r':
1582 if (!advance_and_chomp(input)) {
1583 if constexpr(last_) {
1587 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1589 if constexpr(last_) {
1590 if (input.get() !=
'\n') {
1591 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1593 remaining = input.advance();
1597 if constexpr(last_) {
1598 remaining = input.advance();
1601 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1603 throw std::runtime_error(
"unexpected character when parsing " + what() +
" on line " + std::to_string(overall_line_count + 1));
1606 if constexpr(!last_) {
1607 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1613 if constexpr(!std::numeric_limits<Type_>::has_infinity) {
1614 throw std::runtime_error(
"requested type does not support " + what());
1616 value = std::numeric_limits<Type_>::infinity();
1618 if constexpr(!std::numeric_limits<Type_>::has_quiet_NaN) {
1619 throw std::runtime_error(
"requested type does not support " + what());
1621 value = std::numeric_limits<Type_>::quiet_NaN();
1627 if constexpr(last_) {
1628 ParseInfo<Type_> output;
1629 output.value = value;
1630 output.remaining = remaining;
1637 template<
bool last_,
typename Type_,
typename Input2_>
1638 static typename std::conditional<last_, ParseInfo<Type_>, Type_>::type parse_real(Input2_& input,
Index overall_line_count) {
1639 char firstchar = input.get();
1640 bool negative = (firstchar ==
'-');
1641 if (negative || firstchar ==
'+') {
1642 if (!(input.advance())) {
1643 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1648 switch (input.get()) {
1650 return parse_special<last_, Type_>(input, negative,
true, overall_line_count);
1652 return parse_special<last_, Type_>(input, negative,
false, overall_line_count);
1658 bool remaining =
true;
1661 char val = input.get();
1663 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1668 case ' ':
case '\t':
case '\r':
1669 if (!advance_and_chomp(input)) {
1670 if constexpr(last_) {
1672 goto final_processing;
1674 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1676 if constexpr(last_) {
1677 if (input.get() !=
'\n') {
1678 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1680 remaining = input.advance();
1682 goto final_processing;
1684 if constexpr(last_) {
1685 remaining = input.advance();
1686 goto final_processing;
1688 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1690 if (!input.advance()) {
1691 if constexpr(last_) {
1693 goto final_processing;
1695 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1697 goto decimal_processing;
1699 if (!input.advance()) {
1700 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1702 goto exponent_processing;
1704 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1707 if (!(input.advance())) {
1708 if constexpr(last_) {
1709 remaining = input.advance();
1710 goto final_processing;
1712 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1719 Type_ multiplier = 1;
1721 char val = input.get();
1723 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1725 value += (val -
'0') / multiplier;
1728 case ' ':
case '\t':
case '\r':
1729 if (!advance_and_chomp(input)) {
1730 if constexpr(last_) {
1732 goto final_processing;
1734 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1736 if constexpr(last_) {
1737 if (input.get() !=
'\n') {
1738 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1740 remaining = input.advance();
1742 goto final_processing;
1744 if constexpr(last_) {
1745 remaining = input.advance();
1746 goto final_processing;
1748 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1750 if (!input.advance()) {
1751 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1753 goto exponent_processing;
1755 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1758 if (!(input.advance())) {
1759 if constexpr(last_) {
1760 remaining = input.advance();
1761 goto final_processing;
1763 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1771 bool expnegative = (input.get() ==
'-');
1772 if (expnegative || input.get() ==
'+') {
1773 if (!(input.advance())) {
1774 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1779 bool expfound =
false;
1781 char val = input.get();
1783 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1785 exponent += (val -
'0');
1788 case ' ':
case '\t':
case '\r':
1789 if (!advance_and_chomp(input)) {
1790 if constexpr(last_) {
1792 goto exponent_processing_finish;
1794 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1796 if constexpr(last_) {
1797 if (input.get() !=
'\n') {
1798 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1800 remaining = input.advance();
1802 goto exponent_processing_finish;
1804 if constexpr(last_) {
1805 remaining = input.advance();
1806 goto exponent_processing_finish;
1808 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1810 throw std::runtime_error(
"unrecognized character in real number on line " + std::to_string(overall_line_count + 1));
1813 if (!(input.advance())) {
1814 if constexpr(last_) {
1815 remaining = input.advance();
1816 goto exponent_processing_finish;
1818 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1822exponent_processing_finish:
1824 throw std::runtime_error(
"no digits in the decimal exponent on line " + std::to_string(overall_line_count + 1));
1829 value *= std::pow(10.0, exponent);
1834 throw std::runtime_error(
"no digits in real number on line " + std::to_string(overall_line_count + 1));
1840 if constexpr(last_) {
1841 ParseInfo<Type_> output;
1842 output.value = value;
1843 output.remaining = remaining;
1850 template<
typename Type_>
1851 class RealFieldParser {
1853 template<
class Input2_>
1854 ParseInfo<Type_> operator()(Input2_& input,
Index overall_line_count) {
1855 return parse_real<true, Type_>(input, overall_line_count);
1873 template<
typename Type_ =
double,
class Store_>
1877 auto store_real = [&](
Index r,
Index c, Type_ val) ->
bool {
1878 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, Type_>::type,
bool>::value) {
1879 return store(r, c, val);
1886 if (my_details.
format == Format::COORDINATE) {
1887 if (my_details.
object == Object::MATRIX) {
1888 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1890 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1893 if (my_details.
object == Object::MATRIX) {
1894 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1896 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1915 template<
typename Type_ =
double,
class Store_>
1921 template<
typename InnerType_>
1922 class ComplexFieldParser {
1924 template<
typename Input2_>
1925 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
Index overall_line_count) {
1926 auto first = parse_real<false, InnerType_>(input, overall_line_count);
1927 auto second = parse_real<true, InnerType_>(input, overall_line_count);
1928 ParseInfo<std::complex<InnerType_> > output;
1929 output.value.real(first);
1930 output.value.imag(second.value);
1931 output.remaining = second.remaining;
1950 template<
typename Type_ =
double,
class Store_>
1954 typedef std::complex<Type_> FullType;
1955 auto store_comp = [&](
Index r,
Index c, FullType val) ->
bool {
1956 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, FullType>::type,
bool>::value) {
1957 return store(r, c, val);
1964 if (my_details.
format == Format::COORDINATE) {
1965 if (my_details.
object == Object::MATRIX) {
1966 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1968 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1971 if (my_details.
object == Object::MATRIX) {
1972 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1974 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1994 template<
typename Type_ =
bool,
class Store_>
1997 if (my_details.
format != Format::COORDINATE) {
1998 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
2001 auto store_pat = [&](
Index r,
Index c) ->
bool {
2002 if constexpr(std::is_same<typename std::invoke_result<Store_, Index, Index, bool>::type,
bool>::value) {
2003 return store(r, c,
true);
2010 if (my_details.
object == Object::MATRIX) {
2011 return scan_matrix_coordinate_pattern(std::move(store_pat));
2013 return scan_vector_coordinate_pattern(std::move(store_pat));