Merge release-5-0 into master
[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 AbstractOptionStorage *
142 BooleanOption::createStorage(const OptionManagerContainer & /*managers*/) const
143 {
144     return new BooleanOptionStorage(*this);
145 }
146
147
148 /********************************************************************
149  * IntegerOptionStorage
150  */
151
152 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
153 {
154     return formatString("%d", value);
155 }
156
157 void IntegerOptionStorage::convertValue(const std::string &value)
158 {
159     const char *ptr = value.c_str();
160     char       *endptr;
161     errno = 0;
162     long int    ival = std::strtol(ptr, &endptr, 10);
163     if (errno == ERANGE
164         || ival < std::numeric_limits<int>::min()
165         || ival > std::numeric_limits<int>::max())
166     {
167         GMX_THROW(InvalidInputError("Invalid value: '" + value
168                                     + "'; it causes an integer overflow"));
169     }
170     if (*ptr == '\0' || *endptr != '\0')
171     {
172         GMX_THROW(InvalidInputError("Invalid value: '" + value
173                                     + "'; expected an integer"));
174     }
175     addValue(ival);
176 }
177
178 void IntegerOptionStorage::processSetValues(ValueList *values)
179 {
180     if (isVector())
181     {
182         expandVector(maxValueCount(), values);
183     }
184 }
185
186 /********************************************************************
187  * IntegerOptionInfo
188  */
189
190 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
191     : OptionInfo(option)
192 {
193 }
194
195 /********************************************************************
196  * IntegerOption
197  */
198
199 AbstractOptionStorage *
200 IntegerOption::createStorage(const OptionManagerContainer & /*managers*/) const
201 {
202     return new IntegerOptionStorage(*this);
203 }
204
205
206 /********************************************************************
207  * Int64OptionStorage
208  */
209
210 std::string Int64OptionStorage::formatSingleValue(const gmx_int64_t &value) const
211 {
212     return formatString("%" GMX_PRId64, value);
213 }
214
215 void Int64OptionStorage::convertValue(const std::string &value)
216 {
217     const char       *ptr = value.c_str();
218     char             *endptr;
219     errno = 0;
220     const gmx_int64_t ival = str_to_int64_t(ptr, &endptr);
221     if (errno == ERANGE)
222     {
223         GMX_THROW(InvalidInputError("Invalid value: '" + value
224                                     + "'; it causes an integer overflow"));
225     }
226     if (*ptr == '\0' || *endptr != '\0')
227     {
228         GMX_THROW(InvalidInputError("Invalid value: '" + value
229                                     + "'; expected an integer"));
230     }
231     addValue(ival);
232 }
233
234 /********************************************************************
235  * Int64OptionInfo
236  */
237
238 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage *option)
239     : OptionInfo(option)
240 {
241 }
242
243 /********************************************************************
244  * Int64Option
245  */
246
247 AbstractOptionStorage *
248 Int64Option::createStorage(const OptionManagerContainer & /*managers*/) const
249 {
250     return new Int64OptionStorage(*this);
251 }
252
253
254 /********************************************************************
255  * DoubleOptionStorage
256  */
257
258 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
259     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
260 {
261 }
262
263 std::string DoubleOptionStorage::typeString() const
264 {
265     return isVector() ? "vector" : (isTime() ? "time" : "real");
266 }
267
268 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
269 {
270     return formatString("%g", value / factor_);
271 }
272
273 void DoubleOptionStorage::convertValue(const std::string &value)
274 {
275     const char *ptr = value.c_str();
276     char       *endptr;
277     errno = 0;
278     double      dval = std::strtod(ptr, &endptr);
279     if (errno == ERANGE)
280     {
281         GMX_THROW(InvalidInputError("Invalid value: '" + value
282                                     + "'; it causes an overflow/underflow"));
283     }
284     if (*ptr == '\0' || *endptr != '\0')
285     {
286         GMX_THROW(InvalidInputError("Invalid value: '" + value
287                                     + "'; expected a number"));
288     }
289     addValue(dval * factor_);
290 }
291
292 void DoubleOptionStorage::processSetValues(ValueList *values)
293 {
294     if (isVector())
295     {
296         expandVector(maxValueCount(), values);
297     }
298 }
299
300 void DoubleOptionStorage::setScaleFactor(double factor)
301 {
302     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
303     if (!hasFlag(efOption_HasDefaultValue))
304     {
305         double              scale = factor / factor_;
306         ValueList::iterator i;
307         for (i = values().begin(); i != values().end(); ++i)
308         {
309             (*i) *= scale;
310         }
311         refreshValues();
312     }
313     factor_ = factor;
314 }
315
316 /********************************************************************
317  * DoubleOptionInfo
318  */
319
320 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
321     : OptionInfo(option)
322 {
323 }
324
325 DoubleOptionStorage &DoubleOptionInfo::option()
326 {
327     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
328 }
329
330 const DoubleOptionStorage &DoubleOptionInfo::option() const
331 {
332     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
333 }
334
335 bool DoubleOptionInfo::isTime() const
336 {
337     return option().isTime();
338 }
339
340 void DoubleOptionInfo::setScaleFactor(double factor)
341 {
342     option().setScaleFactor(factor);
343 }
344
345 /********************************************************************
346  * DoubleOption
347  */
348
349 AbstractOptionStorage *
350 DoubleOption::createStorage(const OptionManagerContainer & /*managers*/) const
351 {
352     return new DoubleOptionStorage(*this);
353 }
354
355
356 /********************************************************************
357  * FloatOptionStorage
358  */
359
360 FloatOptionStorage::FloatOptionStorage(const FloatOption &settings)
361     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
362 {
363 }
364
365 std::string FloatOptionStorage::typeString() const
366 {
367     return isVector() ? "vector" : (isTime() ? "time" : "real");
368 }
369
370 std::string FloatOptionStorage::formatSingleValue(const float &value) const
371 {
372     return formatString("%g", value / factor_);
373 }
374
375 void FloatOptionStorage::convertValue(const std::string &value)
376 {
377     const char *ptr = value.c_str();
378     char       *endptr;
379     errno = 0;
380     double      dval = std::strtod(ptr, &endptr);
381     if (errno == ERANGE
382         || dval * factor_ < -std::numeric_limits<float>::max()
383         || dval * factor_ >  std::numeric_limits<float>::max())
384     {
385         GMX_THROW(InvalidInputError("Invalid value: '" + value
386                                     + "'; it causes an overflow/underflow"));
387     }
388     if (*ptr == '\0' || *endptr != '\0')
389     {
390         GMX_THROW(InvalidInputError("Invalid value: '" + value
391                                     + "'; expected a number"));
392     }
393     addValue(dval * factor_);
394 }
395
396 void FloatOptionStorage::processSetValues(ValueList *values)
397 {
398     if (isVector())
399     {
400         expandVector(maxValueCount(), values);
401     }
402 }
403
404 void FloatOptionStorage::setScaleFactor(double factor)
405 {
406     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
407     if (!hasFlag(efOption_HasDefaultValue))
408     {
409         double              scale = factor / factor_;
410         ValueList::iterator i;
411         for (i = values().begin(); i != values().end(); ++i)
412         {
413             (*i) *= scale;
414         }
415         refreshValues();
416     }
417     factor_ = factor;
418 }
419
420 /********************************************************************
421  * FloatOptionInfo
422  */
423
424 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage *option)
425     : OptionInfo(option)
426 {
427 }
428
429 FloatOptionStorage &FloatOptionInfo::option()
430 {
431     return static_cast<FloatOptionStorage &>(OptionInfo::option());
432 }
433
434 const FloatOptionStorage &FloatOptionInfo::option() const
435 {
436     return static_cast<const FloatOptionStorage &>(OptionInfo::option());
437 }
438
439 bool FloatOptionInfo::isTime() const
440 {
441     return option().isTime();
442 }
443
444 void FloatOptionInfo::setScaleFactor(double factor)
445 {
446     option().setScaleFactor(factor);
447 }
448
449 /********************************************************************
450  * FloatOption
451  */
452
453 AbstractOptionStorage *
454 FloatOption::createStorage(const OptionManagerContainer & /*managers*/) const
455 {
456     return new FloatOptionStorage(*this);
457 }
458
459
460 /********************************************************************
461  * StringOptionStorage
462  */
463
464 StringOptionStorage::StringOptionStorage(const StringOption &settings)
465     : MyBase(settings), info_(this), enumIndexStore_(NULL)
466 {
467     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == NULL)
468     {
469         GMX_THROW(APIError("Cannot set default enum index without enum values"));
470     }
471     if (settings.enumIndexStore_ != NULL && settings.enumValues_ == NULL)
472     {
473         GMX_THROW(APIError("Cannot set enum index store without enum values"));
474     }
475     if (settings.enumIndexStore_ != NULL && settings.maxValueCount_ < 0)
476     {
477         GMX_THROW(APIError("Cannot set enum index store with arbitrary number of values"));
478     }
479     if (settings.enumValues_ != NULL)
480     {
481         enumIndexStore_ = settings.enumIndexStore_;
482         const std::string *defaultValue = settings.defaultValue();
483         int                match        = -1;
484         int                count        = settings.enumValuesCount_;
485         if (count < 0)
486         {
487             count = 0;
488             while (settings.enumValues_[count] != NULL)
489             {
490                 ++count;
491             }
492         }
493         for (int i = 0; i < count; ++i)
494         {
495             if (settings.enumValues_[i] == NULL)
496             {
497                 GMX_THROW(APIError("Enumeration value cannot be NULL"));
498             }
499             if (defaultValue != NULL && settings.enumValues_[i] == *defaultValue)
500             {
501                 match = i;
502             }
503             allowed_.push_back(settings.enumValues_[i]);
504         }
505         if (defaultValue != NULL)
506         {
507             if (match < 0)
508             {
509                 GMX_THROW(APIError("Default value is not one of allowed values"));
510             }
511         }
512         if (settings.defaultEnumIndex_ >= 0)
513         {
514             if (settings.defaultEnumIndex_ >= static_cast<int>(allowed_.size()))
515             {
516                 GMX_THROW(APIError("Default enumeration index is out of range"));
517             }
518             if (defaultValue != NULL && *defaultValue != allowed_[settings.defaultEnumIndex_])
519             {
520                 GMX_THROW(APIError("Conflicting default values"));
521             }
522         }
523         // If there is no default value, match is still -1.
524         if (enumIndexStore_ != NULL)
525         {
526             *enumIndexStore_ = match;
527         }
528     }
529     if (settings.defaultEnumIndex_ >= 0)
530     {
531         clear();
532         addValue(allowed_[settings.defaultEnumIndex_]);
533         commitValues();
534     }
535 }
536
537 std::string StringOptionStorage::formatExtraDescription() const
538 {
539     std::string result;
540     if (!allowed_.empty())
541     {
542         result.append(": ");
543         ValueList::const_iterator i;
544         for (i = allowed_.begin(); i != allowed_.end(); ++i)
545         {
546             if (i != allowed_.begin())
547             {
548                 result.append(", ");
549             }
550             result.append(*i);
551         }
552     }
553     return result;
554 }
555
556 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
557 {
558     return value;
559 }
560
561 void StringOptionStorage::convertValue(const std::string &value)
562 {
563     if (allowed_.size() == 0)
564     {
565         addValue(value);
566     }
567     else
568     {
569         ValueList::const_iterator  i;
570         ValueList::const_iterator  match = allowed_.end();
571         for (i = allowed_.begin(); i != allowed_.end(); ++i)
572         {
573             // TODO: Case independence.
574             if (i->find(value) == 0)
575             {
576                 if (match == allowed_.end() || i->size() < match->size())
577                 {
578                     match = i;
579                 }
580             }
581         }
582         if (match == allowed_.end())
583         {
584             GMX_THROW(InvalidInputError("Invalid value: " + value));
585         }
586         addValue(*match);
587     }
588 }
589
590 void StringOptionStorage::refreshValues()
591 {
592     MyBase::refreshValues();
593     if (enumIndexStore_ != NULL)
594     {
595         for (size_t i = 0; i < values().size(); ++i)
596         {
597             ValueList::const_iterator match =
598                 std::find(allowed_.begin(), allowed_.end(), values()[i]);
599             GMX_ASSERT(match != allowed_.end(),
600                        "Enum value not found (internal error)");
601             enumIndexStore_[i] = static_cast<int>(match - allowed_.begin());
602         }
603     }
604 }
605
606 /********************************************************************
607  * StringOptionInfo
608  */
609
610 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
611     : OptionInfo(option)
612 {
613 }
614
615 StringOptionStorage &StringOptionInfo::option()
616 {
617     return static_cast<StringOptionStorage &>(OptionInfo::option());
618 }
619
620 const StringOptionStorage &StringOptionInfo::option() const
621 {
622     return static_cast<const StringOptionStorage &>(OptionInfo::option());
623 }
624
625 bool StringOptionInfo::isEnumerated() const
626 {
627     return !allowedValues().empty();
628 }
629
630 const std::vector<std::string> &StringOptionInfo::allowedValues() const
631 {
632     return option().allowedValues();
633 }
634
635 /********************************************************************
636  * StringOption
637  */
638
639 AbstractOptionStorage *
640 StringOption::createStorage(const OptionManagerContainer & /*managers*/) const
641 {
642     return new StringOptionStorage(*this);
643 }
644
645 } // namespace gmx