|
Tpetra Matrix/Vector Services Version of the Day
|
00001 // @HEADER 00002 // *********************************************************************** 00003 // 00004 // Tpetra: Templated Linear Algebra Services Package 00005 // Copyright (2008) Sandia Corporation 00006 // 00007 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00008 // the U.S. Government retains certain rights in this software. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // ************************************************************************ 00040 // @HEADER 00041 00042 #ifndef __MatrixMarket_raw_hpp 00043 #define __MatrixMarket_raw_hpp 00044 00045 #include "MatrixMarket_Banner.hpp" 00046 #include "MatrixMarket_CoordDataReader.hpp" 00047 #include "MatrixMarket_util.hpp" 00048 #include "Tpetra_ConfigDefs.hpp" 00049 00050 #include <algorithm> 00051 #include <fstream> 00052 #include <iostream> 00053 #include <iterator> 00054 #include <vector> 00055 #include <stdexcept> 00056 00057 namespace Tpetra { 00058 namespace MatrixMarket { 00059 namespace Raw { 00060 00077 template<class Scalar, class Ordinal> 00078 class Element { 00079 public: 00081 Element () : rowIndex_ (-1), colIndex_ (-1), value_ (0) {} 00082 00084 Element (const Ordinal i, const Ordinal j, const Scalar& Aij) : 00085 rowIndex_ (i), colIndex_ (j), value_ (Aij) {} 00086 00088 bool operator== (const Element& rhs) { 00089 return rowIndex_ == rhs.rowIndex_ && colIndex_ == rhs.colIndex_; 00090 } 00091 00093 bool operator!= (const Element& rhs) { 00094 return ! (*this == rhs); 00095 } 00096 00098 bool operator< (const Element& rhs) const { 00099 if (rowIndex_ < rhs.rowIndex_) 00100 return true; 00101 else if (rowIndex_ > rhs.rowIndex_) 00102 return false; 00103 else { // equal 00104 return colIndex_ < rhs.colIndex_; 00105 } 00106 } 00107 00113 void merge (const Element& rhs, const bool replace=false) { 00114 if (rowIndex() != rhs.rowIndex() || colIndex() != rhs.colIndex()) 00115 throw std::logic_error("Can only merge elements at the same " 00116 "location in the sparse matrix"); 00117 else if (replace) 00118 value_ = rhs.value_; 00119 else 00120 value_ += rhs.value_; 00121 } 00122 00124 Ordinal rowIndex() const { return rowIndex_; } 00125 00127 Ordinal colIndex() const { return colIndex_; } 00128 00130 Scalar value() const { return value_; } 00131 00132 private: 00133 Ordinal rowIndex_, colIndex_; 00134 Scalar value_; 00135 }; 00136 00147 template<class Scalar, class Ordinal> 00148 std::ostream& 00149 operator<< (std::ostream& out, const Element<Scalar, Ordinal>& elt) 00150 { 00151 typedef ScalarTraits<Scalar> STS; 00152 // Non-Ordinal types are floating-point types. In order not to 00153 // lose information when we print a floating-point type, we have 00154 // to set the number of digits to print. C++ standard behavior 00155 // in the default locale seems to be to print only five decimal 00156 // digits after the decimal point; this does not suffice for 00157 // double precision. We solve the problem of how many digits to 00158 // print more generally below. It's a rough solution so please 00159 // feel free to audit and revise it. 00160 // 00161 // FIXME (mfh 01 Feb 2011) 00162 // This really calls for the following approach: 00163 // 00164 // Guy L. Steele and Jon L. White, "How to print floating-point 00165 // numbers accurately", 20 Years of the ACM/SIGPLAN Conference 00166 // on Programming Language Design and Implementation 00167 // (1979-1999): A Selection, 2003. 00168 if (! STS::isOrdinal) 00169 { 00170 // std::scientific, std::fixed, and default are the three 00171 // output states for floating-point numbers. A reasonable 00172 // user-defined floating-point type should respect these 00173 // flags; hopefully it does. 00174 out << std::scientific; 00175 00176 // Decimal output is standard for Matrix Market format. 00177 out << std::setbase (10); 00178 00179 // Compute the number of decimal digits required for expressing 00180 // a Scalar, by comparing with IEEE 754 double precision (16 00181 // decimal digits, 53 binary digits). This would be easier if 00182 // Teuchos exposed std::numeric_limits<T>::digits10, alas. 00183 const double numDigitsAsDouble = 00184 16 * ((double) STS::t() / (double) ScalarTraits<double>::t()); 00185 // Adding 0.5 and truncating is a portable "floor". 00186 const int numDigits = static_cast<int> (numDigitsAsDouble + 0.5); 00187 00188 // Precision to which a Scalar should be written. 00189 out << std::setprecision (numDigits); 00190 } 00191 out << elt.rowIndex() << " " << elt.colIndex() << " "; 00192 if (STS::isComplex) 00193 out << STS::real(elt.value()) << " " << STS::imag(elt.value()); 00194 else 00195 out << elt.value(); 00196 return out; 00197 } 00198 00199 template<class Scalar, class Ordinal> 00200 class Adder { 00201 public: 00202 typedef Ordinal index_type; 00203 typedef Scalar value_type; 00204 typedef Element<Scalar, Ordinal> element_type; 00205 typedef typename std::vector<element_type>::size_type size_type; 00206 00217 Adder () : 00218 expectedNumRows_(0), 00219 expectedNumCols_(0), 00220 expectedNumEntries_(0), 00221 seenNumRows_(0), 00222 seenNumCols_(0), 00223 seenNumEntries_(0), 00224 tolerant_ (true), 00225 debug_ (false) 00226 {} 00227 00245 Adder (const Ordinal expectedNumRows, 00246 const Ordinal expectedNumCols, 00247 const Ordinal expectedNumEntries, 00248 const bool tolerant=false, 00249 const bool debug=false) : 00250 expectedNumRows_(expectedNumRows), 00251 expectedNumCols_(expectedNumCols), 00252 expectedNumEntries_(expectedNumEntries), 00253 seenNumRows_(0), 00254 seenNumCols_(0), 00255 seenNumEntries_(0), 00256 tolerant_ (tolerant), 00257 debug_ (debug) 00258 {} 00259 00272 void 00273 operator() (const Ordinal i, const Ordinal j, const Scalar& Aij) 00274 { 00275 if (! tolerant_) { 00276 const bool indexPairOutOfRange = i < 1 || j < 1 || 00277 i > expectedNumRows_ || j > expectedNumCols_; 00278 00279 TEUCHOS_TEST_FOR_EXCEPTION(indexPairOutOfRange, 00280 std::invalid_argument, "Matrix is " << expectedNumRows_ << " x " 00281 << expectedNumCols_ << ", so entry A(" << i << "," << j << ") = " 00282 << Aij << " is out of range."); 00283 TEUCHOS_TEST_FOR_EXCEPTION(seenNumEntries_ >= expectedNumEntries_, 00284 std::invalid_argument, "Cannot add entry A(" << i << "," << j 00285 << ") = " << Aij << " to matrix; already have expected number " 00286 "of entries " << expectedNumEntries_ << "."); 00287 } 00288 // i and j are 1-based indices, but we store them as 0-based. 00289 elts_.push_back (element_type (i-1, j-1, Aij)); 00290 00291 // Keep track of the rightmost column containing a matrix 00292 // entry, and the bottommost row containing a matrix entry. 00293 // This gives us a lower bound for the dimensions of the 00294 // matrix, and a check for the reported dimensions of the 00295 // matrix in the Matrix Market file. 00296 seenNumRows_ = std::max (seenNumRows_, i); 00297 seenNumCols_ = std::max (seenNumCols_, j); 00298 seenNumEntries_++; 00299 } 00300 00310 void 00311 print (std::ostream& out, const bool doMerge, const bool replace=false) 00312 { 00313 if (doMerge) { 00314 merge (replace); 00315 } else { 00316 std::sort (elts_.begin(), elts_.end()); 00317 } 00318 // Print out the results, delimited by newlines. 00319 typedef std::ostream_iterator<element_type> iter_type; 00320 std::copy (elts_.begin(), elts_.end(), iter_type (out, "\n")); 00321 } 00322 00345 std::pair<size_type, size_type> 00346 merge (const bool replace=false) 00347 { 00348 typedef typename std::vector<element_type>::iterator iter_type; 00349 00350 // Start with a sorted container. Element objects sort in 00351 // lexicographic order of their (row, column) indices, for 00352 // easy conversion to CSR format. If you expect that the 00353 // elements will usually be sorted in the desired order, you 00354 // can check first whether they are already sorted. We have 00355 // no such expectation, so we don't even bother to spend the 00356 // extra O(# entries) operations to check. 00357 std::sort (elts_.begin(), elts_.end()); 00358 00359 // Walk through the array of elements in place, merging 00360 // duplicates and pushing unique elements up to the front of 00361 // the array. We can't use std::unique for this because it 00362 // doesn't let us merge duplicate elements; it only removes 00363 // them from the sequence. 00364 size_type numUnique = 0; 00365 iter_type cur = elts_.begin(); 00366 if (cur == elts_.end()) 00367 // There are no elements to merge 00368 return std::make_pair (numUnique, size_type(0)); 00369 else { 00370 iter_type next = cur; 00371 ++next; // There is one unique element 00372 ++numUnique; 00373 while (next != elts_.end()) { 00374 if (*cur == *next) { 00375 // Merge in the duplicated element *next 00376 cur->merge (*next, replace); 00377 } else { 00378 // *cur is already a unique element. Move over one to 00379 // *make space for the new unique element. 00380 ++cur; 00381 *cur = *next; // Add the new unique element 00382 ++numUnique; 00383 } 00384 // Look at the "next" not-yet-considered element 00385 ++next; 00386 } 00387 // Remember how many elements we removed before resizing. 00388 const size_type numRemoved = elts_.size() - numUnique; 00389 elts_.resize (numUnique); 00390 return std::make_pair (numUnique, numRemoved); 00391 } 00392 } 00393 00395 const std::vector<element_type>& getEntries() const { 00396 return elts_; 00397 } 00398 00400 void clear() { 00401 seenNumRows_ = 0; 00402 seenNumCols_ = 0; 00403 seenNumEntries_ = 0; 00404 elts_.resize (0); 00405 } 00406 00408 const Ordinal numRows() const { return seenNumRows_; } 00409 00411 const Ordinal numCols() const { return seenNumCols_; } 00412 00413 private: 00414 Ordinal expectedNumRows_, expectedNumCols_, expectedNumEntries_; 00415 Ordinal seenNumRows_, seenNumCols_, seenNumEntries_; 00416 bool tolerant_; 00417 bool debug_; 00418 std::vector<element_type> elts_; 00419 }; 00420 00428 template<class Scalar, class Ordinal> 00429 class Reader { 00430 public: 00441 static bool 00442 readFile (const Comm<int>& comm, 00443 const std::string& filename, 00444 const bool echo, 00445 const bool tolerant, 00446 const bool debug=false) 00447 { 00448 using std::cerr; 00449 using std::endl; 00450 00451 const int myRank = Teuchos::rank (comm); 00452 // Teuchos::broadcast doesn't accept a bool; we use an int 00453 // instead, with the usual 1->true, 0->false Boolean 00454 // interpretation. 00455 int readFile = 0; 00456 RCP<std::ifstream> in; // only valid on Rank 0 00457 if (myRank == 0) { 00458 if (debug) { 00459 cerr << "Attempting to open file \"" << filename 00460 << "\" on Rank 0..."; 00461 } 00462 in = rcp (new std::ifstream (filename.c_str())); 00463 if (! *in) { 00464 readFile = 0; 00465 if (debug) 00466 cerr << "failed." << endl; 00467 } 00468 else { 00469 readFile = 1; 00470 if (debug) 00471 cerr << "succeeded." << endl; 00472 } 00473 } 00474 Teuchos::broadcast (comm, 0, &readFile); 00475 TEUCHOS_TEST_FOR_EXCEPTION(! readFile, std::runtime_error, 00476 "Failed to open input file \"" + filename + "\"."); 00477 // Only Rank 0 will try to dereference "in". 00478 return read (comm, in, echo, tolerant, debug); 00479 } 00480 00481 00492 static bool 00493 read (const Comm<int>& comm, 00494 const RCP<std::istream>& in, 00495 const bool echo, 00496 const bool tolerant, 00497 const bool debug=false) 00498 { 00499 using std::cerr; 00500 using std::endl; 00501 00502 const int myRank = Teuchos::rank (comm); 00503 std::pair<bool, std::string> result; 00504 int msgSize = 0; // Size of error message (if any) 00505 if (myRank == 0) { 00506 if (in.is_null()) { 00507 result.first = false; 00508 result.second = "Input stream is null on Rank 0"; 00509 } 00510 else { 00511 if (debug) { 00512 cerr << "About to read from input stream on Rank 0" << endl; 00513 } 00514 result = readOnRank0 (*in, echo, tolerant, debug); 00515 if (debug) { 00516 if (result.first) { 00517 cerr << "Successfully read sparse matrix from " 00518 "input stream on Rank 0" << endl; 00519 } 00520 else { 00521 cerr << "Failed to read sparse matrix from input " 00522 "stream on Rank 0" << endl; 00523 } 00524 } 00525 } 00526 if (result.first) { 00527 msgSize = 0; 00528 } 00529 else { 00530 msgSize = result.second.size(); 00531 } 00532 } 00533 int success = result.first ? 1 : 0; 00534 Teuchos::broadcast (comm, 0, &success); 00535 if (! success) { 00536 if (! tolerant) { 00537 // Tell all ranks how long the error message is, so 00538 // they can make space for it in order to receive 00539 // the broadcast of the error message. 00540 Teuchos::broadcast (comm, 0, &msgSize); 00541 00542 if (msgSize > 0) { 00543 std::string errMsg (msgSize, ' '); 00544 if (myRank == 0) { 00545 std::copy (result.second.begin(), result.second.end(), 00546 errMsg.begin()); 00547 } 00548 Teuchos::broadcast (comm, 0, static_cast<int>(msgSize), &errMsg[0]); 00549 TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, errMsg); 00550 } 00551 else { 00552 TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, 00553 "Unknown error when reading Matrix " 00554 "Market sparse matrix file; the error " 00555 "is \"unknown\" because the error " 00556 "message has length 0."); 00557 } 00558 } 00559 else if (myRank == 0) { 00560 using std::cerr; 00561 using std::endl; 00562 cerr << "The following error occurred when reading the " 00563 "sparse matrix: " << result.second << endl; 00564 } 00565 } 00566 return success; 00567 } 00568 00569 private: 00570 00571 00586 static RCP<const Banner> 00587 readBanner (std::istream& in, 00588 size_t& lineNumber, 00589 const bool tolerant=false, 00590 const bool debug=false) 00591 { 00592 typedef ScalarTraits<Scalar> STS; 00593 00594 // The pointer will be non-null on return only on MPI Rank 0. 00595 // Using a pointer lets the data persist outside the 00596 // "myRank==0" scopes. 00597 RCP<Banner> pBanner; 00598 00599 // Keep reading lines until we get a noncomment line. 00600 std::string line; 00601 size_t numLinesRead = 0; 00602 bool commentLine = false; 00603 do { 00604 // Try to read a line from the input stream. 00605 const bool readFailed = ! getline(in, line); 00606 TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument, 00607 "Failed to get Matrix Market banner line " 00608 "from input, after reading " << numLinesRead 00609 << "line" << (numLinesRead != 1 ? "s." : ".")); 00610 // We read a line from the input stream. 00611 lineNumber++; 00612 numLinesRead++; 00613 size_t start, size; // Output args of checkCommentLine 00614 commentLine = checkCommentLine (line, start, size, 00615 lineNumber, tolerant); 00616 } while (commentLine); // Loop until we find a noncomment line. 00617 00618 // Assume that the noncomment line we found is the banner line. 00619 try { 00620 pBanner = rcp (new Banner (line, tolerant)); 00621 } catch (std::exception& e) { 00622 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, 00623 "Matrix Market banner line contains syntax " 00624 "error(s): " << e.what()); 00625 } 00626 return pBanner; 00627 } 00628 00629 00631 static std::pair<bool, std::string> 00632 readOnRank0 (std::istream& in, 00633 const bool echo, 00634 const bool tolerant, 00635 const bool debug=false) 00636 { 00637 using std::cerr; 00638 using std::cout; 00639 using std::endl; 00640 typedef ScalarTraits<Scalar> STS; 00641 00642 // This "Adder" knows how to add sparse matrix entries, 00643 // given a line of data from the file. It also stores the 00644 // entries and can sort them. 00645 typedef Adder<Scalar, Ordinal> raw_adder_type; 00646 // SymmetrizingAdder "advices" (yes, I'm using that as a verb) 00647 // the original Adder, so that additional entries are filled 00648 // in symmetrically, if the Matrix Market banner line 00649 // specified a symmetry type other than "general". 00650 typedef SymmetrizingAdder<raw_adder_type> adder_type; 00651 00652 // Current line number of the input stream. 00653 size_t lineNumber = 1; 00654 00655 // Construct the "Banner" (matrix metadata, including type 00656 // and symmetry information, but not dimensions). 00657 std::ostringstream err; 00658 RCP<const Banner> pBanner; 00659 try { 00660 pBanner = readBanner (in, lineNumber, tolerant, debug); 00661 } 00662 catch (std::exception& e) { 00663 err << "Failed to read Banner: " << e.what(); 00664 return std::make_pair (false, err.str()); 00665 } 00666 // 00667 // Validate the metadata in the Banner. 00668 // 00669 if (pBanner->matrixType() != "coordinate") { 00670 err << "Matrix Market input file must contain a " 00671 "\"coordinate\"-format sparse matrix in " 00672 "order to create a sparse matrix object " 00673 "from it."; 00674 return std::make_pair (false, err.str()); 00675 } 00676 else if (! STS::isComplex && pBanner->dataType() == "complex") { 00677 err << "The Matrix Market sparse matrix file contains complex-" 00678 "valued data, but you are try to read the data into a sparse " 00679 "matrix containing real values (your matrix's Scalar type is " 00680 "real)."; 00681 return std::make_pair (false, err.str()); 00682 } 00683 else if (pBanner->dataType() != "real" && 00684 pBanner->dataType() != "complex") { 00685 err << "Only real or complex data types (no pattern or integer " 00686 "matrices) are currently supported."; 00687 return std::make_pair (false, err.str()); 00688 } 00689 if (debug) { 00690 cerr << "Banner line:" << endl << *pBanner << endl; 00691 } 00692 00693 // The reader will invoke the adder (see below) once for 00694 // each matrix entry it reads from the input stream. 00695 typedef CoordDataReader<adder_type, Ordinal, Scalar, 00696 STS::isComplex> reader_type; 00697 // We will set the adder below, after calling readDimensions(). 00698 reader_type reader; 00699 00700 // Read in the dimensions of the sparse matrix: (# rows, # 00701 // columns, # matrix entries (counting duplicates as 00702 // separate entries)). The second element of the pair tells 00703 // us whether the values were gotten successfully. 00704 std::pair<Tuple<Ordinal, 3>, bool> dims = 00705 reader.readDimensions (in, lineNumber, tolerant); 00706 if (! dims.second) { 00707 err << "Error reading Matrix Market sparse matrix " 00708 "file: failed to read coordinate dimensions."; 00709 return std::make_pair (false, err.str()); 00710 } 00711 // These are "expected" values read from the input stream's 00712 // metadata. The actual matrix entries read from the input 00713 // stream might not conform to their constraints. We allow 00714 // such nonconformity only in "tolerant" mode; otherwise, we 00715 // throw an exception. 00716 const Ordinal numRows = dims.first[0]; 00717 const Ordinal numCols = dims.first[1]; 00718 const Ordinal numEntries = dims.first[2]; 00719 if (debug) { 00720 cerr << "Reported dimensions: " << numRows << " x " << numCols 00721 << ", with " << numEntries << " entries (counting possible " 00722 << "duplicates)." << endl; 00723 } 00724 00725 // The "raw" adder knows about the expected matrix 00726 // dimensions, but doesn't know about symmetry. 00727 RCP<raw_adder_type> rawAdder = 00728 rcp (new raw_adder_type (numRows, numCols, numEntries, 00729 tolerant, debug)); 00730 // The symmetrizing adder knows about symmetry. 00731 RCP<adder_type> adder = 00732 rcp (new adder_type (rawAdder, pBanner->symmType())); 00733 00734 // Give the adder to the reader. 00735 reader.setAdder (adder); 00736 00737 // Read the sparse matrix entries. "results" just tells us if 00738 // and where there were any bad lines of input. The actual 00739 // sparse matrix entries are stored in the (raw) Adder object. 00740 std::pair<bool, std::vector<size_t> > results = 00741 reader.read (in, lineNumber, tolerant, debug); 00742 if (debug) { 00743 if (results.first) { 00744 cerr << "Matrix Market file successfully read" << endl; 00745 } 00746 else { 00747 cerr << "Failed to read Matrix Market file" << endl; 00748 } 00749 } 00750 00751 // Report any bad line number(s). 00752 if (! results.first) { 00753 if (! tolerant) { 00754 err << "The Matrix Market input stream had syntax error(s)." 00755 " Here is the error report." << endl; 00756 reportBadness (err, results); 00757 err << endl; 00758 return std::make_pair (false, err.str()); 00759 } 00760 else { 00761 if (debug) { 00762 reportBadness (cerr, results); 00763 } 00764 } 00765 } 00766 // We're done reading in the sparse matrix. If we're in 00767 // "echo" mode, print out the matrix entries to stdout. The 00768 // entries will have been symmetrized if applicable. 00769 if (echo) { 00770 const bool doMerge = false; 00771 const bool replace = false; 00772 rawAdder->print (cout, doMerge, replace); 00773 cout << endl; 00774 } 00775 return std::make_pair (true, err.str()); 00776 } 00777 00779 static void 00780 reportBadness (std::ostream& out, 00781 const std::pair<bool, std::vector<size_t> >& results) 00782 { 00783 using std::endl; 00784 const size_t numErrors = results.second.size(); 00785 const size_t maxNumErrorsToReport = 20; 00786 out << numErrors << " errors when reading Matrix Market sparse " 00787 "matrix file." << endl; 00788 if (numErrors > maxNumErrorsToReport) { 00789 out << "-- We do not report individual errors when there " 00790 "are more than " << maxNumErrorsToReport << "."; 00791 } 00792 else if (numErrors == 1) { 00793 out << "Error on line " << results.second[0] << endl; 00794 } 00795 else if (numErrors > 1) { 00796 out << "Errors on lines {"; 00797 for (size_t k = 0; k < numErrors-1; ++k) { 00798 out << results.second[k] << ", "; 00799 } 00800 out << results.second[numErrors-1] << "}" << endl; 00801 } 00802 } 00803 }; 00804 00805 } // namespace Raw 00806 } // namespace MatrixMarket 00807 } // namespace Tpetra 00808 00809 #endif // __MatrixMarket_raw_hpp
1.7.4