Teuchos - Trilinos Tools Package Version of the Day
Teuchos_MatrixMarket_Banner.cpp
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 #include "Teuchos_MatrixMarket_Banner.hpp"
00043 #include "Teuchos_MatrixMarket_split.hpp"
00044 #include "Teuchos_TestForException.hpp"
00045 #include <algorithm>
00046 #include <iostream>
00047 #include <sstream>
00048 #include <stdexcept>
00049 
00050 
00051 namespace Teuchos {
00052   namespace MatrixMarket {
00053 
00054     using details::split;
00055     using details::trim_and_lowercase;
00056 
00057     std::string
00058     Banner::validateObjectType (const std::string& objectType, const bool tolerant)
00059     {
00060       // Canonical representation is lowercase
00061       std::string out = trim_and_lowercase (objectType);
00062 
00063       const char* const validValues[] = {"matrix"};
00064       const int numValidValues = 1;
00065       if (tolerant)
00066         // This is the only value currently defined for this token in
00067         // the Matrix Market format, so we just return it.
00068         return std::string (validValues[0]);
00069       else if (validValues + numValidValues ==
00070                std::find (validValues, validValues + numValidValues, out))
00071         throw std::invalid_argument("Object type \"" + out + "\" is "
00072                                     "not one of the valid values");
00073       else
00074         return out;
00075     }
00076 
00077     std::string
00078     Banner::validateMatrixType (const std::string& matrixType, const bool tolerant)
00079     {
00080       // Canonical representation is lowercase
00081       std::string out = trim_and_lowercase (matrixType);
00082 
00083       const char* const validValues[] = {"coordinate", "array"};
00084       const int numValidValues = 2;
00085       if (validValues + numValidValues == std::find (validValues, validValues + numValidValues, out))
00086         throw std::invalid_argument("Matrix type \"" + out + "\" is not one of the valid values");
00087       else
00088         return out;
00089     }
00090 
00091     std::string
00092     Banner::validateDataType (const std::string& dataType, const bool tolerant)
00093     {
00094       // Canonical representation is lowercase
00095       std::string out = trim_and_lowercase (dataType);
00096 
00097       const char* const validValues[] = {"real", "complex", "integer", "pattern"};
00098       const int numValidValues = 4;
00099       if (validValues + numValidValues == std::find (validValues, validValues + numValidValues, out))
00100         throw std::invalid_argument("Data type \"" + out + "\" is not one of the valid values");
00101       else
00102         return out;
00103     }
00104 
00105     std::string
00106     Banner::validateSymmType (const std::string& symmType, const bool tolerant)
00107     {
00108       // Canonical representation is lowercase
00109       std::string out = trim_and_lowercase (symmType);
00110 
00111       if (tolerant)
00112         {
00113           const char* const validValues[] =
00114             {"general", "nonsymmetric", "unsymmetric", "symmetric",
00115              "skew-symmetric", "skew", "hermitian"};
00116           const int numValidValues = 7;
00117           if (validValues + numValidValues == std::find (validValues, validValues + numValidValues, out))
00118             throw std::invalid_argument("Symmetry type \"" + out + "\" is not one of the valid values");
00119           else
00120             {
00121               if (out == "nonsymmetric" || out == "unsymmetric")
00122                 return std::string("general");
00123               else if (out == "skew")
00124                 return std::string("skew-symmetric");
00125               else
00126                 return out;
00127             }
00128         }
00129       else
00130         {
00131           const char* const validValues[] = {"general", "symmetric", "skew-symmetric", "hermitian"};
00132           const int numValidValues = 4;
00133           if (validValues + numValidValues == std::find (validValues, validValues + numValidValues, out))
00134             throw std::invalid_argument("Symmetry type \"" + out + "\" is not one of the valid values");
00135           else
00136             return out;
00137         }
00138     }
00139 
00140 
00141     void
00142     Banner::setDefaults (const int howMany)
00143     {
00144       if (howMany >= 4)
00145         objectType_ = "matrix";
00146       if (howMany >= 3)
00147         matrixType_ = "coordinate";
00148       if (howMany >= 2)
00149         dataType_ = "real";
00150       if (howMany >= 1)
00151         symmType_ = "general";
00152     }
00153 
00154     Banner::Banner (const std::string& line, const bool tolerant)
00155     {
00156       size_t start;
00157 
00158       if (line.empty()) {
00159         if (tolerant) {
00160           setDefaults (4);
00161           return;
00162         }
00163         else {
00164           throw std::invalid_argument ("The banner line is empty");
00165         }
00166       }
00167       start = line.find_first_not_of (" \t");
00168       if (start == std::string::npos) {
00169         if (tolerant) {
00170           setDefaults (4);
00171           return;
00172         }
00173         else {
00174           throw std::invalid_argument ("The banner line contains only "
00175                                        "whitespace characters");
00176         }
00177       }
00178       else if (start != 0 && ! tolerant) {
00179         // If tolerant, we allow the banner line to start with
00180         // whitespace characters, and keep reading.
00181         throw std::invalid_argument ("The banner line is not allowed to start "
00182                                      "with whitespace characters");
00183       }
00184 
00185       // Find "%%MatrixMarket" -- it should be the first thing in the
00186       // banner line, and all other data should come after it.
00187       // Optionally relax to allow any case, and possibly a space
00188       // between "%%" and "MatrixMarket".
00189       size_t ppStart = line.find ("%%", start);
00190       size_t tokenStart;
00191       if (ppStart == std::string::npos) {
00192         if (tolerant) {
00193           tokenStart = start; // Just ignore the missing %%
00194         }
00195         else {
00196           TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "The Matrix "
00197             "Market file's banner line should always start with \"%%\".  Here "
00198             "is the offending line: " << std::endl << line);
00199         }
00200       }
00201       else {
00202         tokenStart = ppStart + 2;
00203         if (tokenStart >= line.size()) {
00204           // There's no banner information after the %%.
00205           if (tolerant) {
00206             setDefaults (4);
00207             return;
00208           }
00209           else {
00210             TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "The Matrix "
00211               "Market file's banner line needs to contain information after the "
00212               "\"%%\" marker.  Here is the offending line: " << std::endl << line);
00213           }
00214         }
00215       }
00216       //
00217       // In tolerant mode, fill in missing tokens with their default
00218       // values.
00219       //
00220       // After extracting the %%, search for the five tokens.
00221       std::vector<std::string> tokens = split (line, " \t", 2);
00222       const int numTokens = tokens.size();
00223       if (numTokens < 1) {
00224         if (tolerant) {
00225           setDefaults (4);
00226           return;
00227         }
00228         else {
00229           TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "The Matrix "
00230             "Market file's banner line must always begin with the \"Matrix"
00231             "Market\" keyword.  Here is the offending line: " << std::endl
00232             << line);
00233         }
00234       }
00235       // In tolerant mode, just ignore the first token.
00236       if (! tolerant && tokens[0] != "MatrixMarket") {
00237         TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "The Matrix "
00238           "Market file's banner line must always begin with the \"Matrix"
00239           "Market\" keyword.  Here is the offending line: " << std::endl
00240           << line);
00241       }
00242       if (numTokens < 5) {
00243         if (tolerant) {
00244           setDefaults (5 - numTokens); // how many defaults to set
00245         }
00246         else {
00247           TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, "The Matrix "
00248             "Market file's banner line must always have 5 tokens, but yours "
00249             "only has " << numTokens << "token" << (numTokens != 1 ? "s" : "")
00250             << ".  Here is the offending line: " << std::endl << line);
00251         }
00252       }
00253       if (numTokens >= 2) {
00254         objectType_ = validateObjectType (tokens[1], tolerant);
00255       }
00256       if (numTokens >= 3) {
00257         matrixType_ = validateMatrixType (tokens[2], tolerant);
00258       }
00259       if (numTokens >= 4) {
00260         dataType_ = validateDataType (tokens[3], tolerant);
00261       }
00262       if (numTokens >= 5) {
00263         symmType_ = validateSymmType (tokens[4], tolerant);
00264       }
00265     }
00266 
00267     std::ostream&
00268     operator<< (std::ostream& out, const Banner& banner)
00269     {
00270       out << "%%MatrixMarket"
00271           << " " << banner.objectType()
00272           << " " << banner.matrixType()
00273           << " " << banner.dataType()
00274           << " " << banner.symmType();
00275       return out;
00276     }
00277   } // namespace MatrixMarket
00278 } // namespace Teuchos
00279 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines