Merge "Merge branch release-4-6 into release-5-0" into release-5-0
[alexxy/gromacs.git] / src / gromacs / options / basicoptions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes in basicoptions.h and basicoptionstorage.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_options
41  */
42 #include "basicoptions.h"
43 #include "basicoptionstorage.h"
44
45 #include <cerrno>
46 #include <cstdio>
47 #include <cstdlib>
48
49 #include <limits>
50 #include <string>
51 #include <vector>
52
53 #include "gromacs/utility/cstringutil.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/stringutil.h"
56
57 namespace
58 {
59
60 /*! \brief
61  * Expands a single value to a vector by copying the value.
62  *
63  * \tparam        ValueType  Type of values to process.
64  * \param[in]     length     Length of the resulting vector.
65  * \param[in,out] values     Values to process.
66  * \throws   std::bad_alloc    if out of memory.
67  * \throws   InvalidInputError if \p values has an invalid number of values.
68  *
69  * \p values should have 0, 1, or \p length values.
70  * If \p values has 1 value, it is expanded such that it has \p length
71  * identical values.  In other valid cases, nothing is done.
72  */
73 template <typename ValueType>
74 void expandVector(size_t length, std::vector<ValueType> *values)
75 {
76     if (length > 0 && !values->empty() && values->size() != length)
77     {
78         if (values->size() != 1)
79         {
80             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
81                                                      "Expected 1 or %d values, got %d", length, values->size())));
82         }
83         const ValueType &value = (*values)[0];
84         values->resize(length, value);
85     }
86 }
87
88 } // namespace
89
90 namespace gmx
91 {
92
93 /********************************************************************
94  * BooleanOptionStorage
95  */
96
97 std::string BooleanOptionStorage::formatSingleValue(const bool &value) const
98 {
99     return value ? "yes" : "no";
100 }
101
102 void BooleanOptionStorage::convertValue(const std::string &value)
103 {
104     // TODO: Case-independence
105     if (value == "1" || value == "yes" || value == "true")
106     {
107         addValue(true);
108         return;
109     }
110     else if (value == "0" || value == "no" || value == "false")
111     {
112         addValue(false);
113         return;
114     }
115     GMX_THROW(InvalidInputError("Invalid value: '" + value + "'; supported values are: 1, 0, yes, no, true, false"));
116 }
117
118 /********************************************************************
119  * BooleanOptionInfo
120  */
121
122 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage *option)
123     : OptionInfo(option)
124 {
125 }
126
127 const BooleanOptionStorage &BooleanOptionInfo::option() const
128 {
129     return static_cast<const BooleanOptionStorage &>(OptionInfo::option());
130 }
131
132 bool BooleanOptionInfo::defaultValue() const
133 {
134     return option().defaultValue();
135 }
136
137 /********************************************************************
138  * BooleanOption
139  */
140
141 AbstractOptionStoragePointer BooleanOption::createStorage() const
142 {
143     return AbstractOptionStoragePointer(new BooleanOptionStorage(*this));
144 }
145
146
147 /********************************************************************
148  * IntegerOptionStorage
149  */
150
151 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
152 {
153     return formatString("%d", value);
154 }
155
156 void IntegerOptionStorage::convertValue(const std::string &value)
157 {
158     const char *ptr = value.c_str();
159     char       *endptr;
160     errno = 0;
161     long int    ival = std::strtol(ptr, &endptr, 10);
162     if (errno == ERANGE
163         || ival < std::numeric_limits<int>::min()
164         || ival > std::numeric_limits<int>::max())
165     {
166         GMX_THROW(InvalidInputError("Invalid value: '" + value
167                                     + "'; it causes an integer overflow"));
168     }
169     if (*ptr == '\0' || *endptr != '\0')
170     {
171         GMX_THROW(InvalidInputError("Invalid value: '" + value
172                                     + "'; expected an integer"));
173     }
174     addValue(ival);
175 }
176
177 void IntegerOptionStorage::processSetValues(ValueList *values)
178 {
179     if (isVector())
180     {
181         expandVector(maxValueCount(), values);
182     }
183 }
184
185 /********************************************************************
186  * IntegerOptionInfo
187  */
188
189 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
190     : OptionInfo(option)
191 {
192 }
193
194 /********************************************************************
195  * IntegerOption
196  */
197
198 AbstractOptionStoragePointer IntegerOption::createStorage() const
199 {
200     return AbstractOptionStoragePointer(new IntegerOptionStorage(*this));
201 }
202
203
204 /********************************************************************
205  * Int64OptionStorage
206  */
207
208 std::string Int64OptionStorage::formatSingleValue(const gmx_int64_t &value) const
209 {
210     return formatString("%" GMX_PRId64, value);
211 }
212
213 void Int64OptionStorage::convertValue(const std::string &value)
214 {
215     const char       *ptr = value.c_str();
216     char             *endptr;
217     errno = 0;
218     const gmx_int64_t ival = str_to_int64_t(ptr, &endptr);
219     if (errno == ERANGE)
220     {
221         GMX_THROW(InvalidInputError("Invalid value: '" + value
222                                     + "'; it causes an integer overflow"));
223     }
224     if (*ptr == '\0' || *endptr != '\0')
225     {
226         GMX_THROW(InvalidInputError("Invalid value: '" + value
227                                     + "'; expected an integer"));
228     }
229     addValue(ival);
230 }
231
232 /********************************************************************
233  * Int64OptionInfo
234  */
235
236 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage *option)
237     : OptionInfo(option)
238 {
239 }
240
241 /********************************************************************
242  * Int64Option
243  */
244
245 AbstractOptionStoragePointer Int64Option::createStorage() const
246 {
247     return AbstractOptionStoragePointer(new Int64OptionStorage(*this));
248 }
249
250
251 /********************************************************************
252  * DoubleOptionStorage
253  */
254
255 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
256     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
257 {
258 }
259
260 std::string DoubleOptionStorage::typeString() const
261 {
262     return isVector() ? "vector" : (isTime() ? "time" : "real");
263 }
264
265 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
266 {
267     return formatString("%g", value / factor_);
268 }
269
270 void DoubleOptionStorage::convertValue(const std::string &value)
271 {
272     const char *ptr = value.c_str();
273     char       *endptr;
274     errno = 0;
275     double      dval = std::strtod(ptr, &endptr);
276     if (errno == ERANGE)
277     {
278         GMX_THROW(InvalidInputError("Invalid value: '" + value
279                                     + "'; it causes an overflow/underflow"));
280     }
281     if (*ptr == '\0' || *endptr != '\0')
282     {
283         GMX_THROW(InvalidInputError("Invalid value: '" + value
284                                     + "'; expected a number"));
285     }
286     addValue(dval * factor_);
287 }
288
289 void DoubleOptionStorage::processSetValues(ValueList *values)
290 {
291     if (isVector())
292     {
293         expandVector(maxValueCount(), values);
294     }
295 }
296
297 void DoubleOptionStorage::setScaleFactor(double factor)
298 {
299     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
300     if (!hasFlag(efOption_HasDefaultValue))
301     {
302         double              scale = factor / factor_;
303         ValueList::iterator i;
304         for (i = values().begin(); i != values().end(); ++i)
305         {
306             (*i) *= scale;
307         }
308         refreshValues();
309     }
310     factor_ = factor;
311 }
312
313 /********************************************************************
314  * DoubleOptionInfo
315  */
316
317 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
318     : OptionInfo(option)
319 {
320 }
321
322 DoubleOptionStorage &DoubleOptionInfo::option()
323 {
324     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
325 }
326
327 const DoubleOptionStorage &DoubleOptionInfo::option() const
328 {
329     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
330 }
331
332 bool DoubleOptionInfo::isTime() const
333 {
334     return option().isTime();
335 }
336
337 void DoubleOptionInfo::setScaleFactor(double factor)
338 {
339     option().setScaleFactor(factor);
340 }
341
342 /********************************************************************
343  * DoubleOption
344  */
345
346 AbstractOptionStoragePointer DoubleOption::createStorage() const
347 {
348     return AbstractOptionStoragePointer(new DoubleOptionStorage(*this));
349 }
350
351
352 /********************************************************************
353  * FloatOptionStorage
354  */
355
356 FloatOptionStorage::FloatOptionStorage(const FloatOption &settings)
357     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
358 {
359 }
360
361 std::string FloatOptionStorage::typeString() const
362 {
363     return isVector() ? "vector" : (isTime() ? "time" : "real");
364 }
365
366 std::string FloatOptionStorage::formatSingleValue(const float &value) const
367 {
368     return formatString("%g", value / factor_);
369 }
370
371 void FloatOptionStorage::convertValue(const std::string &value)
372 {
373     const char *ptr = value.c_str();
374     char       *endptr;
375     errno = 0;
376     double      dval = std::strtod(ptr, &endptr);
377     if (errno == ERANGE
378         || dval * factor_ < -std::numeric_limits<float>::max()
379         || dval * factor_ >  std::numeric_limits<float>::max())
380     {
381         GMX_THROW(InvalidInputError("Invalid value: '" + value
382                                     + "'; it causes an overflow/underflow"));
383     }
384     if (*ptr == '\0' || *endptr != '\0')
385     {
386         GMX_THROW(InvalidInputError("Invalid value: '" + value
387                                     + "'; expected a number"));
388     }
389     addValue(dval * factor_);
390 }
391
392 void FloatOptionStorage::processSetValues(ValueList *values)
393 {
394     if (isVector())
395     {
396         expandVector(maxValueCount(), values);
397     }
398 }
399
400 void FloatOptionStorage::setScaleFactor(double factor)
401 {
402     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
403     if (!hasFlag(efOption_HasDefaultValue))
404     {
405         double              scale = factor / factor_;
406         ValueList::iterator i;
407         for (i = values().begin(); i != values().end(); ++i)
408         {
409             (*i) *= scale;
410         }
411         refreshValues();
412     }
413     factor_ = factor;
414 }
415
416 /********************************************************************
417  * FloatOptionInfo
418  */
419
420 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage *option)
421     : OptionInfo(option)
422 {
423 }
424
425 FloatOptionStorage &FloatOptionInfo::option()
426 {
427     return static_cast<FloatOptionStorage &>(OptionInfo::option());
428 }
429
430 const FloatOptionStorage &FloatOptionInfo::option() const
431 {
432     return static_cast<const FloatOptionStorage &>(OptionInfo::option());
433 }
434
435 bool FloatOptionInfo::isTime() const
436 {
437     return option().isTime();
438 }
439
440 void FloatOptionInfo::setScaleFactor(double factor)
441 {
442     option().setScaleFactor(factor);
443 }
444
445 /********************************************************************
446  * FloatOption
447  */
448
449 AbstractOptionStoragePointer FloatOption::createStorage() const
450 {
451     return AbstractOptionStoragePointer(new FloatOptionStorage(*this));
452 }
453
454
455 /********************************************************************
456  * StringOptionStorage
457  */
458
459 StringOptionStorage::StringOptionStorage(const StringOption &settings)
460     : MyBase(settings), info_(this), enumIndexStore_(NULL)
461 {
462     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == NULL)
463     {
464         GMX_THROW(APIError("Cannot set default enum index without enum values"));
465     }
466     if (settings.enumIndexStore_ != NULL && settings.enumValues_ == NULL)
467     {
468         GMX_THROW(APIError("Cannot set enum index store without enum values"));
469     }
470     if (settings.enumIndexStore_ != NULL && settings.maxValueCount_ < 0)
471     {
472         GMX_THROW(APIError("Cannot set enum index store with arbitrary number of values"));
473     }
474     if (settings.enumValues_ != NULL)
475     {
476         enumIndexStore_ = settings.enumIndexStore_;
477         const std::string *defaultValue = settings.defaultValue();
478         int                match        = -1;
479         int                count        = settings.enumValuesCount_;
480         if (count < 0)
481         {
482             count = 0;
483             while (settings.enumValues_[count] != NULL)
484             {
485                 ++count;
486             }
487         }
488         for (int i = 0; i < count; ++i)
489         {
490             if (settings.enumValues_[i] == NULL)
491             {
492                 GMX_THROW(APIError("Enumeration value cannot be NULL"));
493             }
494             if (defaultValue != NULL && settings.enumValues_[i] == *defaultValue)
495             {
496                 match = i;
497             }
498             allowed_.push_back(settings.enumValues_[i]);
499         }
500         if (defaultValue != NULL)
501         {
502             if (match < 0)
503             {
504                 GMX_THROW(APIError("Default value is not one of allowed values"));
505             }
506         }
507         if (settings.defaultEnumIndex_ >= 0)
508         {
509             if (settings.defaultEnumIndex_ >= static_cast<int>(allowed_.size()))
510             {
511                 GMX_THROW(APIError("Default enumeration index is out of range"));
512             }
513             if (defaultValue != NULL && *defaultValue != allowed_[settings.defaultEnumIndex_])
514             {
515                 GMX_THROW(APIError("Conflicting default values"));
516             }
517         }
518         // If there is no default value, match is still -1.
519         if (enumIndexStore_ != NULL)
520         {
521             *enumIndexStore_ = match;
522         }
523     }
524     if (settings.defaultEnumIndex_ >= 0)
525     {
526         clear();
527         addValue(allowed_[settings.defaultEnumIndex_]);
528         commitValues();
529     }
530 }
531
532 std::string StringOptionStorage::formatExtraDescription() const
533 {
534     std::string result;
535     if (!allowed_.empty())
536     {
537         result.append(": ");
538         ValueList::const_iterator i;
539         for (i = allowed_.begin(); i != allowed_.end(); ++i)
540         {
541             if (i != allowed_.begin())
542             {
543                 result.append(", ");
544             }
545             result.append(*i);
546         }
547     }
548     return result;
549 }
550
551 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
552 {
553     return value;
554 }
555
556 void StringOptionStorage::convertValue(const std::string &value)
557 {
558     if (allowed_.size() == 0)
559     {
560         addValue(value);
561     }
562     else
563     {
564         ValueList::const_iterator  i;
565         ValueList::const_iterator  match = allowed_.end();
566         for (i = allowed_.begin(); i != allowed_.end(); ++i)
567         {
568             // TODO: Case independence.
569             if (i->find(value) == 0)
570             {
571                 if (match == allowed_.end() || i->size() < match->size())
572                 {
573                     match = i;
574                 }
575             }
576         }
577         if (match == allowed_.end())
578         {
579             GMX_THROW(InvalidInputError("Invalid value: " + value));
580         }
581         addValue(*match);
582     }
583 }
584
585 void StringOptionStorage::refreshValues()
586 {
587     MyBase::refreshValues();
588     if (enumIndexStore_ != NULL)
589     {
590         for (size_t i = 0; i < values().size(); ++i)
591         {
592             ValueList::const_iterator match =
593                 std::find(allowed_.begin(), allowed_.end(), values()[i]);
594             GMX_ASSERT(match != allowed_.end(),
595                        "Enum value not found (internal error)");
596             enumIndexStore_[i] = static_cast<int>(match - allowed_.begin());
597         }
598     }
599 }
600
601 /********************************************************************
602  * StringOptionInfo
603  */
604
605 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
606     : OptionInfo(option)
607 {
608 }
609
610 StringOptionStorage &StringOptionInfo::option()
611 {
612     return static_cast<StringOptionStorage &>(OptionInfo::option());
613 }
614
615 const StringOptionStorage &StringOptionInfo::option() const
616 {
617     return static_cast<const StringOptionStorage &>(OptionInfo::option());
618 }
619
620 bool StringOptionInfo::isEnumerated() const
621 {
622     return !allowedValues().empty();
623 }
624
625 const std::vector<std::string> &StringOptionInfo::allowedValues() const
626 {
627     return option().allowedValues();
628 }
629
630 /********************************************************************
631  * StringOption
632  */
633
634 AbstractOptionStoragePointer StringOption::createStorage() const
635 {
636     return AbstractOptionStoragePointer(new StringOptionStorage(*this));
637 }
638
639 } // namespace gmx