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       {
00277         TEUCHOS_TEST_FOR_EXCEPTION(i < 1 || j < 1 || i > expectedNumRows_ || j > expectedNumCols_, 
00278          std::invalid_argument, 
00279          "Matrix is " << expectedNumRows_ << " x " << expectedNumCols_ 
00280          << ", so entry A(" << i << "," << j << ") = " 
00281          << Aij << " is out of range.");
00282         TEUCHOS_TEST_FOR_EXCEPTION(seenNumEntries_ >= expectedNumEntries_, 
00283          std::invalid_argument,
00284          "Cannot add entry A(" << i << "," << j << ") = " 
00285          << Aij << " to matrix; already have expected "
00286          "number of entries " << expectedNumEntries_ 
00287          << ".");
00288       }
00289     // i and j are 1-based indices, but we store them as 0-based.
00290     elts_.push_back (element_type (i-1, j-1, Aij));
00291 
00292     // Keep track of the rightmost column containing a matrix
00293     // entry, and the bottommost row containing a matrix entry.
00294     // This gives us a lower bound for the dimensions of the
00295     // matrix, and a check for the reported dimensions of the
00296     // matrix in the Matrix Market file.
00297     seenNumRows_ = std::max (seenNumRows_, i);
00298     seenNumCols_ = std::max (seenNumCols_, j);
00299     seenNumEntries_++;
00300   }
00301 
00311   void 
00312   print (std::ostream& out, const bool doMerge, const bool replace=false) 
00313   {
00314     if (doMerge)
00315       merge (replace);
00316     else
00317       std::sort (elts_.begin(), elts_.end());
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       {
00459         if (debug)
00460     cerr << "Attempting to open file \"" << filename 
00461          << "\" on Rank 0...";
00462         in = rcp (new std::ifstream (filename.c_str()));
00463         if (! *in)
00464     {
00465       readFile = 0;
00466       if (debug)
00467         cerr << "failed." << endl;
00468     }
00469         else
00470     {
00471       readFile = 1;
00472       if (debug)
00473         cerr << "succeeded." << endl;
00474     }
00475       }
00476     Teuchos::broadcast (comm, 0, &readFile);
00477     TEUCHOS_TEST_FOR_EXCEPTION(! readFile, std::runtime_error,
00478            "Failed to open input file \"" + filename + "\".");
00479     // Only Rank 0 will try to dereference "in".
00480     return read (comm, in, echo, tolerant, debug);
00481   }
00482 
00483 
00494   static bool
00495   read (const Comm<int>& comm,
00496         const RCP<std::istream>& in,
00497         const bool echo,
00498         const bool tolerant,
00499         const bool debug=false)
00500   {
00501     using std::cerr;
00502     using std::endl;
00503 
00504     const int myRank = Teuchos::rank (comm);
00505     std::pair<bool, std::string> result;
00506     int msgSize = 0; // Size of error message (if any)
00507     if (myRank == 0)
00508       {
00509         if (in.is_null())
00510     {
00511       result.first = false;
00512       result.second = "Input stream is null on Rank 0";
00513     }
00514         else
00515     {
00516       if (debug)
00517         cerr << "About to read from input stream on Rank 0" << endl;
00518       result = readOnRank0 (*in, echo, tolerant, debug);
00519       if (debug)
00520         {
00521           if (result.first)
00522       cerr << "Successfully read sparse matrix from "
00523         "input stream on Rank 0" << endl;
00524           else
00525       cerr << "Failed to read sparse matrix from input "
00526         "stream on Rank 0" << endl;
00527         }
00528     }
00529         if (result.first)
00530     msgSize = 0;
00531         else
00532     msgSize = result.second.size();
00533       }
00534     int success = result.first ? 1 : 0;
00535     Teuchos::broadcast (comm, 0, &success);
00536     if (! success)
00537       {
00538         if (! tolerant)
00539     {
00540       // Tell all ranks how long the error message is, so
00541       // they can make space for it in order to receive
00542       // the broadcast of the error message.
00543       Teuchos::broadcast (comm, 0, &msgSize);
00544 
00545       if (msgSize > 0)
00546         {
00547           std::string errMsg (msgSize, ' ');
00548           if (myRank == 0)
00549       std::copy (result.second.begin(), result.second.end(),
00550            errMsg.begin());
00551           Teuchos::broadcast (comm, 0, static_cast<int>(msgSize), &errMsg[0]);
00552           TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, errMsg);
00553         }
00554       else
00555         TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, 
00556                "Unknown error when reading Matrix "
00557                "Market sparse matrix file; the error "
00558                "is \"unknown\" because the error "
00559                "message has length 0.");
00560     }
00561         else if (myRank == 0)
00562     {
00563       using std::cerr;
00564       using std::endl;
00565       cerr << "The following error occurred when reading the "
00566         "sparse matrix: " << result.second << endl;
00567     }
00568       }
00569     return success;
00570   }
00571 
00572       private:
00573 
00574 
00589   static RCP<const Banner>
00590   readBanner (std::istream& in,
00591         size_t& lineNumber,
00592         const bool tolerant=false,
00593         const bool debug=false)
00594   {
00595     typedef ScalarTraits<Scalar> STS;
00596 
00597     // The pointer will be non-null on return only on MPI Rank 0.
00598     // Using a pointer lets the data persist outside the
00599     // "myRank==0" scopes.
00600     RCP<Banner> pBanner;
00601 
00602     // Keep reading lines until we get a noncomment line.
00603     std::string line;
00604     size_t numLinesRead = 0;
00605     bool commentLine = false;
00606     do {
00607       // Try to read a line from the input stream.
00608       const bool readFailed = ! getline(in, line);
00609       TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
00610              "Failed to get Matrix Market banner line "
00611              "from input, after reading " << numLinesRead
00612              << "line" << (numLinesRead != 1 ? "s." : "."));
00613       // We read a line from the input stream.
00614       lineNumber++; 
00615       numLinesRead++;
00616       size_t start, size; // Output args of checkCommentLine
00617       commentLine = checkCommentLine (line, start, size, 
00618               lineNumber, tolerant);
00619     } while (commentLine); // Loop until we find a noncomment line.
00620 
00621     // Assume that the noncomment line we found is the banner line.
00622     try {
00623       pBanner = rcp (new Banner (line, tolerant));
00624     } catch (std::exception& e) {
00625       TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, 
00626              "Matrix Market banner line contains syntax "
00627              "error(s): " << e.what());
00628     }
00629     return pBanner;
00630   }
00631 
00632 
00634   static std::pair<bool, std::string>
00635   readOnRank0 (std::istream& in,  
00636          const bool echo,
00637          const bool tolerant,
00638          const bool debug=false)
00639   {
00640     using std::cerr;
00641     using std::cout;
00642     using std::endl;
00643     typedef ScalarTraits<Scalar> STS;
00644 
00645     // This "Adder" knows how to add sparse matrix entries,
00646     // given a line of data from the file.  It also stores the
00647     // entries and can sort them.
00648     typedef Adder<Scalar, Ordinal> raw_adder_type;
00649     // SymmetrizingAdder "advices" (yes, I'm using that as a verb)
00650     // the original Adder, so that additional entries are filled
00651     // in symmetrically, if the Matrix Market banner line
00652     // specified a symmetry type other than "general".
00653     typedef SymmetrizingAdder<raw_adder_type> adder_type;
00654 
00655     // Current line number of the input stream.
00656     size_t lineNumber = 1;
00657 
00658     // Construct the "Banner" (matrix metadata, including type
00659     // and symmetry information, but not dimensions).
00660     std::ostringstream err;
00661     RCP<const Banner> pBanner;
00662     try {
00663       pBanner = readBanner (in, lineNumber, tolerant, debug);
00664     } catch (std::exception& e) {
00665       err << "Failed to read Banner: " << e.what();
00666       return std::make_pair (false, err.str());
00667     }
00668     //
00669     // Validate the metadata in the Banner.
00670     //
00671     if (pBanner->matrixType() != "coordinate")
00672       {
00673         err << "Matrix Market input file must contain a "
00674     "\"coordinate\"-format sparse matrix in "
00675     "order to create a sparse matrix object "
00676     "from it.";
00677         return std::make_pair (false, err.str());
00678       }
00679     else if (! STS::isComplex && pBanner->dataType() == "complex")
00680       {
00681         err << "The Matrix Market sparse matrix file contains complex-"
00682     "valued data, but you are try to read the data into a sparse "
00683     "matrix containing real values (your matrix's Scalar type is "
00684     "real).";
00685         return std::make_pair (false, err.str());
00686       }
00687     else if (pBanner->dataType() != "real" && 
00688        pBanner->dataType() != "complex")
00689       {
00690         err << "Only real or complex data types (no pattern or integer "
00691     "matrices) are currently supported.";
00692         return std::make_pair (false, err.str());
00693       }
00694     if (debug)
00695       cerr << "Banner line:" << endl << *pBanner << endl;
00696     
00697     // The reader will invoke the adder (see below) once for
00698     // each matrix entry it reads from the input stream.
00699     typedef CoordDataReader<adder_type, Ordinal, Scalar, 
00700       STS::isComplex> reader_type;
00701     // We will set the adder below, after calling readDimensions().
00702     reader_type reader;
00703 
00704     // Read in the dimensions of the sparse matrix: (# rows, #
00705     // columns, # matrix entries (counting duplicates as
00706     // separate entries)).  The second element of the pair tells
00707     // us whether the values were gotten successfully.
00708     std::pair<Tuple<Ordinal, 3>, bool> dims = 
00709       reader.readDimensions (in, lineNumber, tolerant);
00710     if (! dims.second)
00711       {
00712         err << "Error reading Matrix Market sparse matrix "
00713     "file: failed to read coordinate dimensions.";
00714         return std::make_pair (false, err.str());
00715       }
00716     // These are "expected" values read from the input stream's
00717     // metadata.  The actual matrix entries read from the input
00718     // stream might not conform to their constraints.  We allow
00719     // such nonconformity only in "tolerant" mode; otherwise, we
00720     // throw an exception.
00721     const Ordinal numRows = dims.first[0];
00722     const Ordinal numCols = dims.first[1];
00723     const Ordinal numEntries = dims.first[2];
00724     if (debug)
00725       {
00726         cerr << "Reported dimensions: " << numRows << " x " << numCols 
00727        << ", with " << numEntries << " entries (counting possible "
00728        << "duplicates)." << endl;
00729       }
00730 
00731     // The "raw" adder knows about the expected matrix
00732     // dimensions, but doesn't know about symmetry.
00733     RCP<raw_adder_type> rawAdder = 
00734       rcp (new raw_adder_type (numRows, numCols, numEntries, 
00735              tolerant, debug));
00736     // The symmetrizing adder knows about symmetry.
00737     RCP<adder_type> adder = 
00738       rcp (new adder_type (rawAdder, pBanner->symmType()));
00739 
00740     // Give the adder to the reader.
00741     reader.setAdder (adder);
00742 
00743     // Read the sparse matrix entries.  "results" just tells us if
00744     // and where there were any bad lines of input.  The actual
00745     // sparse matrix entries are stored in the (raw) Adder object.
00746     std::pair<bool, std::vector<size_t> > results = 
00747       reader.read (in, lineNumber, tolerant, debug);
00748     if (debug)
00749       {
00750         if (results.first)
00751     cerr << "Matrix Market file successfully read" << endl;
00752         else 
00753     cerr << "Failed to read Matrix Market file" << endl;
00754       }
00755 
00756     // Report any bad line number(s).
00757     if (! results.first)
00758       {
00759         if (! tolerant)
00760     {
00761       err << "The Matrix Market input stream had syntax error(s)."
00762         "  Here is the error report." << endl;
00763       reportBadness (err, results);
00764       err << endl;
00765       return std::make_pair (false, err.str());
00766     }
00767         else
00768     {
00769       if (debug)
00770         reportBadness (cerr, results);
00771     }
00772       }
00773     // We're done reading in the sparse matrix.  If we're in
00774     // "echo" mode, print out the matrix entries to stdout.  The
00775     // entries will have been symmetrized if applicable.
00776     if (echo)
00777       {
00778         const bool doMerge = false;
00779         const bool replace = false;
00780         rawAdder->print (cout, doMerge, replace);
00781         cout << endl;
00782       }
00783     return std::make_pair (true, err.str());
00784   }
00785 
00787   static void 
00788   reportBadness (std::ostream& out, 
00789            const std::pair<bool, std::vector<size_t> >& results) 
00790   {
00791     using std::endl;
00792     const size_t numErrors = results.second.size();
00793     const size_t maxNumErrorsToReport = 20;
00794     out << numErrors << " errors when reading Matrix Market sparse "
00795       "matrix file." << endl;
00796     if (numErrors > maxNumErrorsToReport)
00797       out << "-- We do not report individual errors when there "
00798         "are more than " << maxNumErrorsToReport << ".";
00799     else if (numErrors == 1)
00800       out << "Error on line " << results.second[0] << endl;
00801     else if (numErrors > 1)
00802       {
00803         out << "Errors on lines {";
00804         for (size_t k = 0; k < numErrors-1; ++k)
00805     out << results.second[k] << ", ";
00806         out << results.second[numErrors-1] << "}" << endl;
00807       }
00808   }
00809       };
00810 
00811     } // namespace Raw
00812   } // namespace MatrixMarket
00813 } // namespace Tpetra
00814 
00815 #endif // __MatrixMarket_raw_hpp
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines