Tpetra Matrix/Vector Services Version of the Day
MatrixMarket_raw.hpp
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines