Zoltan2
Zoltan2_IntegerRangeList.hpp
Go to the documentation of this file.
00001 // @HEADER
00002 //
00003 // ***********************************************************************
00004 //
00005 //   Zoltan2: A package of combinatorial algorithms for scientific computing
00006 //                  Copyright 2012 Sandia Corporation
00007 //
00008 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
00009 // the U.S. Government retains certain rights in this software.
00010 //
00011 // Redistribution and use in source and binary forms, with or without
00012 // modification, are permitted provided that the following conditions are
00013 // met:
00014 //
00015 // 1. Redistributions of source code must retain the above copyright
00016 // notice, this list of conditions and the following disclaimer.
00017 //
00018 // 2. Redistributions in binary form must reproduce the above copyright
00019 // notice, this list of conditions and the following disclaimer in the
00020 // documentation and/or other materials provided with the distribution.
00021 //
00022 // 3. Neither the name of the Corporation nor the names of the
00023 // contributors may be used to endorse or promote products derived from
00024 // this software without specific prior written permission.
00025 //
00026 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00027 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00028 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00029 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00030 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00031 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00032 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00033 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00034 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00035 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00036 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037 //
00038 // Questions? Contact Karen Devine      (kddevin@sandia.gov)
00039 //                    Erik Boman        (egboman@sandia.gov)
00040 //                    Siva Rajamanickam (srajama@sandia.gov)
00041 //
00042 // ***********************************************************************
00043 //
00044 // @HEADER
00045 
00050 #ifndef _ZOLTAN2_INTEGERRANGELIST_HPP_
00051 #define _ZOLTAN2_INTEGERRANGELIST_HPP_
00052 
00053 #include <Zoltan2_config.h>
00054 
00055 #include <Teuchos_ParameterEntryValidator.hpp>
00056 #include <Teuchos_ValidatorXMLConverter.hpp>
00057 #include <Teuchos_XMLObject.hpp>
00058 #include <Teuchos_ValidatorMaps.hpp>
00059 #include <Teuchos_DummyObjectGetter.hpp>
00060 #include <Teuchos_StrUtils.hpp>
00061 
00062 #ifdef _MSC_VER
00063 // for isspace(int), else one gets isspace(_Elem _Ch, const locale& _Loc) from <locale>
00064 #include <cctype>
00065 #endif
00066 
00067 // Had to redefine this type from Teuchos_ParameterEntryValidator.hpp.
00068 // Compiler stumbled on it.
00069 typedef Teuchos::RCP<const Teuchos::Array<std::string> > ValidStringsList;
00070 
00071 namespace Zoltan2{
00072 
00076 enum RangeType {
00077   RANGE_INCLUDES_ALL,    
00078   RANGE_IS_EMPTY,        
00079   RANGE_IS_LISTED,       
00080   NUM_RANGE_TYPES
00081 };
00082 
00083 template <typename T> class IntegerRangeListValidator;
00084 
00086 // Helper functions for interpreting integer range list arrays.
00088 
00095 template <typename Integral>
00096   bool validIntegralRangeList(const Teuchos::Array<Integral> &vals)
00097 {
00098   typedef typename Teuchos::Array<Integral>::size_type arraySize_t;
00099   arraySize_t len = vals.size();
00100   if (len==0)
00101     return false;
00102 
00103   Integral flag = vals[len-1];
00104   if ((flag != RANGE_INCLUDES_ALL) && (flag != RANGE_IS_EMPTY) &&
00105       (flag != RANGE_IS_LISTED))
00106     return false;
00107 
00108   return true;
00109 }
00110 
00125 template <typename Integral>
00126   bool allValuesAreInRangeList(const Teuchos::Array<Integral> &vals)
00127 {
00128   if (!validIntegralRangeList(vals))
00129     throw std::runtime_error("list is not a valid range list");
00130   return vals[vals.size()-1] == RANGE_INCLUDES_ALL;
00131 }
00132 
00147 template <typename Integral>
00148   bool allValuesAreInRangeList(const Teuchos::ParameterEntry &e)
00149 {
00150   if (!e.isType<Teuchos::Array<Integral> >())
00151     throw std::runtime_error("Should not call until modified");
00152 
00153   Teuchos::Array<Integral> *valPtr=NULL;
00154   Teuchos::Array<Integral> &vals = e.getValue(valPtr);
00155   return allValuesAreInRangeList(vals);
00156 }
00157 
00172 template <typename Integral>
00173   bool noValuesAreInRangeList(const Teuchos::Array<Integral> &vals)
00174 {
00175   if (!validIntegralRangeList(vals))
00176     throw std::runtime_error("list is not a valid range list");
00177   return vals[vals.size()-1] == RANGE_IS_EMPTY;
00178 }
00179 
00194 template <typename Integral>
00195   bool noValuesAreInRangeList(const Teuchos::ParameterEntry &e)
00196 {
00197   if (!e.isType<Teuchos::Array<Integral> >())
00198     throw std::runtime_error("Should not call until modified");
00199 
00200   Teuchos::Array<Integral> *valPtr=NULL;
00201   Teuchos::Array<Integral> &vals = e.getValue(valPtr);
00202   return noValuesAreInRangeList(vals);
00203 }
00204 
00205 // TODO :
00206 // inList(std::vector<Integral> &val, std::vector<bool> &result)
00207 
00219 template <typename Integral>
00220   bool IsInRangeList(const Integral val, const Teuchos::Array<Integral> &valList, bool sorted=true)
00221 {
00222   if (allValuesAreInRangeList(valList))
00223     return true;
00224   else if (noValuesAreInRangeList(valList))
00225     return false;
00226 
00227   if (sorted){
00228     typename Teuchos::Array<Integral>::const_iterator flag = valList.end();
00229     --flag;
00230     if (std::binary_search(valList.begin(), flag, val))
00231       return true;
00232     else
00233       return false;
00234   }
00235   else{
00236     for (typename Teuchos::Array<Integral>::size_type i=0; i < valList.size()-1; i++){
00237       if (valList[i] == val)
00238         return true;
00239     }
00240     return false;
00241   }
00242 }
00243 
00254 template <typename Integral>
00255   bool IsInRangeList(const Integral val, const Teuchos::ParameterEntry &e)
00256 {
00257   if (!e.isType<Teuchos::Array<Integral> >())
00258     throw std::runtime_error("Should not call until modified");
00259 
00260   Teuchos::Array<Integral> *valPtr=NULL;
00261   Teuchos::Array<Integral> &valList = e.getValue(valPtr);
00262 
00263   typedef IntegerRangeListValidator<Integral> irl_t;
00264   RCP<const irl_t> irl;
00265   bool fail = false;
00266  
00267   RCP<const Teuchos::ParameterEntryValidator> valtor = e.validator();
00268   if (!valtor.is_null()){
00269     try{
00270       irl = rcp_dynamic_cast<const irl_t>(valtor);
00271     }
00272     catch (...){
00273       fail = true;
00274     }
00275   }
00276   else{
00277     fail = true;
00278   }
00279 
00280   if (fail)
00281     throw std::runtime_error("wrong type of parameter entry");
00282 
00283   bool sorted = irl->inputListWillBeSorted();
00284 
00285   return IsInRangeList(val, valList, sorted);
00286 } 
00287 
00296 template <typename Integral>
00297   Teuchos::ArrayView<Integral> getList(Teuchos::Array<Integral> &irl)
00298 {
00299   Teuchos::ArrayView<Integral> av;  // av.size() == 0
00300 
00301   if (!Zoltan2::allValuesAreInRangeList(irl) &&
00302       !Zoltan2::noValuesAreInRangeList(irl))
00303     av = irl.view(0, irl.size()-1); // skip last value, it's a flag
00304 
00305   return av;
00306 }
00307 
00316 template <typename Integral>
00317   void printIntegralRangeList(std::ostream &os, Teuchos::Array<Integral> &irl)
00318 {
00319   if (Zoltan2::allValuesAreInRangeList(irl))
00320     os << "all";
00321   else if (Zoltan2::noValuesAreInRangeList(irl))
00322     os << "empty";
00323   else{
00324     Teuchos::ArrayView<const Integral> view = 
00325       irl.view(0, irl.size()-1); // skip last value, it's a flag
00326     os << view;
00327   }    
00328 }
00329 
00331 // Validator class
00333 
00393 template <typename Integral>
00394   class IntegerRangeListValidator : public Teuchos::ParameterEntryValidator
00395 {
00396 private:
00397 
00398 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00399 
00400   Integral min_;
00401   Integral max_;
00402   bool unsorted_;
00403 
00404   static const std::string listDelim_;
00405   static const std::string rangeDelim_;
00406   static const std::string allText_;
00407 
00408   static void checkValid(char c); 
00409   static bool listSaysAll(std::string &l);
00410   static int breakRange(std::string &range, std::string &from, std::string &to);
00411 
00412 #endif
00413 
00414 public:
00420   IntegerRangeListValidator(bool unsorted=false);
00421 
00431   IntegerRangeListValidator(Integral validMin, Integral validMax,
00432     bool unsorted=false); 
00433 
00434   // Implementation of ParameterEntryValidator interface
00435 
00436   const std::string getXMLTypeName() const; 
00437 
00438   void printDoc(std::string const& docString, std::ostream &out) const;
00439 
00440   ValidStringsList validStringValues() const ;
00441 
00442   void validate( Teuchos::ParameterEntry  const& entry,
00443     std::string const& paramName, std::string const& sublistName
00444     ) const;
00445 
00446   void validateAndModify( std::string const& paramName,
00447     std::string const& sublistName, Teuchos::ParameterEntry * entry
00448     ) const;
00449 
00450   // IntegerRangeListValidator methods
00451 
00457   Integral getAllowedMinimum() const { return min_;}
00458 
00464   Integral getAllowedMaximum() const { return max_;}
00465 
00472   bool inputListWillBeSorted() const { return !unsorted_;}
00473 
00474 }; // end class
00475 
00477 // Class definitions
00479 
00480 template <typename Integral>
00481 const std::string IntegerRangeListValidator<Integral>::listDelim_(",");
00482 
00483 template <typename Integral>
00484 const std::string IntegerRangeListValidator<Integral>::rangeDelim_("-");
00485 
00486 template <typename Integral>
00487 const std::string IntegerRangeListValidator<Integral>::allText_("all");
00488 
00489 template <typename Integral>
00490   void IntegerRangeListValidator<Integral>::checkValid(char c)
00491 {
00492   if (std::isspace(c) || std::isdigit(c) || (c == ',') || (c == '-'))
00493     return;
00494   else
00495     throw std::runtime_error("invalid integer range list");
00496 }
00497 
00498 template <typename Integral>
00499   bool IntegerRangeListValidator<Integral>::listSaysAll(std::string &l)
00500 {
00501   std::transform(l.begin(), l.end(), l.begin(), (int(*)(int)) tolower);
00502   if (l.find(allText_) != std::string::npos)
00503     return true;  // "all" is in the string
00504   else
00505     return false;
00506 }
00507 
00508 template <typename Integral>
00509   int IntegerRangeListValidator<Integral>::breakRange(
00510     std::string &range, std::string &from, std::string &to)
00511 {
00512   from.clear();
00513   to.clear();
00514   std::string::size_type loc = range.find(rangeDelim_);
00515   if (loc == std::string::npos){
00516     from = range;
00517   }
00518   else{
00519     from = range.substr(0, loc);
00520     to = range.substr(loc+1, range.size());
00521   }
00522   long a, b;
00523   std::istringstream iss1(from);
00524   iss1 >> a;
00525   b = a;
00526   if (to.size() > 0){
00527     std::istringstream iss2(to);
00528     iss2 >> b;
00529     if (b < a)
00530       std::swap(from,to);
00531   }
00532   return (b != a) ? 2 : 1;
00533 }
00534 
00535 
00536 template <typename Integral>
00537   IntegerRangeListValidator<Integral>::IntegerRangeListValidator(
00538     bool unsorted): min_(1), max_(0), unsorted_(unsorted)
00539 {
00540 }
00541 
00542 template <typename Integral>
00543   IntegerRangeListValidator<Integral>::IntegerRangeListValidator(
00544     Integral validMin, Integral validMax, bool unsorted) :
00545       min_(validMin), max_(validMax), unsorted_(unsorted)
00546 {
00547   if (min_ < max_) std::swap(min_,max_);
00548 }
00549 
00550   // Implementation of ParameterEntryValidator interface
00551 
00552 template <typename Integral>
00553   const std::string 
00554     IntegerRangeListValidator<Integral>::getXMLTypeName() const 
00555 {
00556   std::string className("IntegerRangeListValidator");
00557   std::string classType("("+Teuchos::TypeNameTraits<Integral>::name()+")");
00558   return std::string(className + classType);
00559 }
00560 
00561 template <typename Integral>
00562   void IntegerRangeListValidator<Integral>::printDoc(
00563     std::string const& docString, std::ostream &out) const  
00564 {
00565   Teuchos::StrUtils::printLines(out,"# ",docString);
00566   out << "#\tAn integer range list is a string which can contain:\n";
00567   out << "#\t\tthe text \"all\", which indicates all values\n";
00568   out << "#\t\ta list of integer ranges separated by commas.\n";
00569   out << "#\tA range is one value, or two values separated by a dash.\n";
00570   out << "#\tExample: \"all\" or \"1-10\" or \"3, 10-12\" or \"25\"\n";
00571   if (max_ >= min_){
00572     out << "#\tThe range of valid integers is [";
00573     out << min_ << "," << max_ << "]\n";
00574   }
00575 }
00576 
00577 template <typename Integral>
00578   ValidStringsList IntegerRangeListValidator<Integral>::validStringValues() const 
00579 { 
00580   return Teuchos::null; 
00581 }
00582 
00583 template <typename Integral>
00584   void IntegerRangeListValidator<Integral>::validate( 
00585     Teuchos::ParameterEntry  const& entry,
00586     std::string const& paramName, std::string const& sublistName) const
00587 {
00588   if (!entry.isType<std::string>()){
00589     return;  // already converted to an an array
00590   }
00591   std::string *sptr=NULL;
00592   std::string &rangeList = entry.getValue(sptr);
00593   std::string inValue(rangeList);
00594 
00595   if (listSaysAll(inValue))
00596     return;  // "all" is in the string
00597 
00598   // throw error if invalid integer range list
00599   std::for_each(inValue.begin(), inValue.end(), checkValid);
00600 
00601   if (max_ >= min_){
00602     std::string::const_iterator rangeBegin = inValue.begin();
00603     std::string::const_iterator valueEnd = inValue.end();
00604 
00605     while (rangeBegin != valueEnd){
00606       std::string::const_iterator rangeEnd = std::search(
00607         rangeBegin, valueEnd, listDelim_.begin(), listDelim_.end());
00608       std::string range(rangeBegin, rangeEnd);
00609       std::string aHalf, bHalf;
00610       int count = breakRange(range, aHalf, bHalf);
00611 
00612       Integral a, b;
00613       std::istringstream iss1(aHalf);
00614       iss1 >> a;
00615       if (count > 1){
00616         std::istringstream iss2(bHalf);
00617         iss2 >> b;
00618       }
00619       else
00620         b = a;
00621 
00622       if ((a < min_) || (b > max_)){
00623         std::ostringstream oss;
00624         oss << "input range [" << a << "," << b << "] ";
00625         oss << "exceeds valid range [" << min_ << "," << max_ << "] ";
00626         throw std::runtime_error(oss.str());
00627       }
00628       if (rangeEnd == valueEnd)
00629         rangeBegin = rangeEnd;
00630       else
00631         rangeBegin = ++rangeEnd;
00632     }
00633   }
00634 }
00635 
00636 template <typename Integral>
00637   void IntegerRangeListValidator<Integral>::validateAndModify( 
00638     std::string const& paramName, std::string const& sublistName, 
00639     Teuchos::ParameterEntry * entry) const
00640 {
00641   typedef typename Teuchos::Array<Integral>::size_type arraySize_t;
00642   if (!entry->isType<std::string>()){
00643     return;
00644   }
00645   
00646   std::string *sptr=NULL;
00647   std::string &rangeList = entry->getValue(sptr);
00648   Teuchos::Array<Integral> valueList;
00649 
00650   std::string inValue(rangeList);
00651 
00652   if (listSaysAll(inValue)){
00653     valueList.push_back(RANGE_INCLUDES_ALL);
00654   }
00655   else{
00656     // throw error if invalid integer range list
00657     std::for_each(inValue.begin(), inValue.end(), checkValid);
00658 
00659     std::string::const_iterator rangeBegin = inValue.begin();
00660     std::string::const_iterator valueEnd = inValue.end();
00661 
00662     while (rangeBegin != valueEnd){
00663       std::string::const_iterator rangeEnd = std::search(rangeBegin,
00664         valueEnd, listDelim_.begin(), listDelim_.end());
00665       std::string range(rangeBegin, rangeEnd);
00666       std::string aHalf, bHalf;
00667       int count = breakRange(range, aHalf, bHalf);
00668 
00669       Integral a, b;
00670       std::istringstream iss1(aHalf);
00671       iss1 >> a;
00672       if (count > 1){
00673         std::istringstream iss2(bHalf);
00674         iss2 >> b;
00675       }
00676       else
00677         b = a;
00678 
00679       if ((max_ >= min_) && ((a < min_) || (b > max_))){
00680         std::ostringstream oss;
00681         oss << "input range [" << a << "," << b << "] ";
00682         oss << "exceeds valid range [" << min_ << "," << max_ << "] ";
00683         throw std::runtime_error(oss.str());
00684       }
00685       for (Integral i=a; i <=b; i++)
00686         valueList.push_back(i);
00687       if (rangeEnd == valueEnd)
00688        rangeBegin = rangeEnd;
00689       else
00690         rangeBegin = ++rangeEnd;
00691     }
00692     if (!unsorted_ && valueList.size() > 1){  // sort & remove duplicates
00693       std::sort(valueList.begin(), valueList.end());
00694       arraySize_t listEnd = 0;
00695       arraySize_t length = valueList.size();
00696       for (arraySize_t i=1; i < length; i++){
00697         if (valueList[i] > valueList[listEnd]){
00698           listEnd++;
00699           if (listEnd != i)
00700             valueList[listEnd] = valueList[i];
00701         }
00702       }
00703       if (++listEnd < length)
00704         valueList.resize(listEnd);
00705     }
00706 
00707     Integral flag = RANGE_IS_LISTED;
00708     if (valueList.size() == 0){
00709       flag = RANGE_IS_EMPTY;
00710     }
00711     else if (max_ >= min_){
00712       Integral allSize = max_ - min_ + 1;
00713       if (valueList.size() == allSize){
00714         flag = RANGE_INCLUDES_ALL;
00715         valueList.clear();
00716       }
00717     }
00718     valueList.push_back(flag);
00719   }
00720   entry->setValue(valueList);
00721 }
00722 
00724 // Parameter entry validator <-> XML conversion
00726 
00739 template <typename Integral>
00740 class IntegerRangeListValidatorXMLConverter : public Teuchos::ValidatorXMLConverter
00741 {
00742 
00743 public:
00744 
00745   RCP<Teuchos::ParameterEntryValidator> convertXML(
00746     const Teuchos::XMLObject& xmlObj,
00747     const Teuchos::IDtoValidatorMap& validatorIDsMap) const;
00748 
00749   void convertValidator(
00750     const RCP<const Teuchos::ParameterEntryValidator> validator,
00751     Teuchos::XMLObject& xmlObj,
00752     const Teuchos::ValidatortoIDMap& validatorIDsMap) const;
00753 
00754 #ifdef HAVE_TEUCHOS_DEBUG
00755 
00756   RCP<const Teuchos::ParameterEntryValidator> getDummyValidator() const{
00757     return Teuchos::DummyObjectGetter<IntegerRangeListValidator<Integral> >::getDummyObject();
00758   }
00759 #endif
00760 };
00761 
00762 template <typename Integral>
00763   RCP<Teuchos::ParameterEntryValidator>
00764    IntegerRangeListValidatorXMLConverter<Integral>::convertXML(
00765      const Teuchos::XMLObject& xmlObj, 
00766      const Teuchos::IDtoValidatorMap& /*validatorIDsMap*/) const
00767 {
00768   Integral minValue=0, maxValue=0;
00769   bool unsorted=false, hasMin=false, hasMax=false;
00770 
00771   if (xmlObj.hasAttribute(std::string("min"))) {
00772     minValue = xmlObj.getRequired<Integral>(std::string("min"));
00773     hasMin = true;
00774   }
00775 
00776   if (xmlObj.hasAttribute(std::string("max"))) {
00777     maxValue = xmlObj.getRequired<Integral>(std::string("max"));
00778     hasMax = true;
00779   }
00780 
00781   if (xmlObj.hasAttribute(std::string("unsorted"))) 
00782     unsorted = xmlObj.getRequired<bool>(std::string("unsorted"));
00783 
00784   RCP<Teuchos::ParameterEntryValidator> toReturn;
00785 
00786   if (hasMin && hasMax)
00787     toReturn = rcp(new IntegerRangeListValidator<Integral>(minValue, maxValue, unsorted));
00788   else if (!hasMin && !hasMax)
00789     toReturn = rcp(new IntegerRangeListValidator<Integral>(unsorted));
00790   else
00791     throw std::runtime_error("invalid XML representation");
00792 
00793   return toReturn;
00794 }
00795 
00796 template<typename Integral>
00797 void IntegerRangeListValidatorXMLConverter<Integral>::convertValidator(
00798   const RCP<const Teuchos::ParameterEntryValidator > validator,
00799   Teuchos::XMLObject& xmlObj,
00800   const Teuchos::ValidatortoIDMap& /*validatorIDsMap*/) const
00801 {
00802   RCP<const IntegerRangeListValidator<Integral> > castedValidator =
00803     rcp_dynamic_cast<const IntegerRangeListValidator<Integral> >(
00804       validator, true);
00805 
00806   Integral minValue = castedValidator->getAllowedMinimum();
00807   Integral maxValue = castedValidator->getAllowedMaximum();
00808   bool unsorted = castedValidator->inputListWillBeSorted();
00809 
00810   if (minValue < maxValue){
00811     xmlObj.addAttribute<Integral>(std::string("min"), minValue);
00812     xmlObj.addAttribute<Integral>(std::string("max"), maxValue);
00813   }
00814 
00815   xmlObj.addAttribute<bool>(std::string("unsorted"), unsorted);
00816 }
00817 
00818 }
00819  // end of namespace Zoltan2
00820 
00821 #endif