8dff27360da2a41ccdeabc470018b69d93cf0a56
[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 "gmxpre.h"
43
44 #include "basicoptions.h"
45 #include "basicoptionstorage.h"
46
47 #include <cerrno>
48 #include <cstdio>
49 #include <cstdlib>
50
51 #include <limits>
52 #include <string>
53 #include <vector>
54
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/stringutil.h"
58
59 namespace
60 {
61
62 /*! \brief
63  * Expands a single value to a vector by copying the value.
64  *
65  * \tparam        ValueType  Type of values to process.
66  * \param[in]     length     Length of the resulting vector.
67  * \param[in,out] values     Values to process.
68  * \throws   std::bad_alloc    if out of memory.
69  * \throws   InvalidInputError if \p values has an invalid number of values.
70  *
71  * \p values should have 0, 1, or \p length values.
72  * If \p values has 1 value, it is expanded such that it has \p length
73  * identical values.  In other valid cases, nothing is done.
74  */
75 template <typename ValueType>
76 void expandVector(size_t length, std::vector<ValueType> *values)
77 {
78     if (length > 0 && !values->empty() && values->size() != length)
79     {
80         if (values->size() != 1)
81         {
82             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
83                                                      "Expected 1 or %d values, got %d", length, values->size())));
84         }
85         const ValueType &value = (*values)[0];
86         values->resize(length, value);
87     }
88 }
89
90 } // namespace
91
92 namespace gmx
93 {
94
95 /********************************************************************
96  * BooleanOptionStorage
97  */
98
99 std::string BooleanOptionStorage::formatSingleValue(const bool &value) const
100 {
101     return value ? "yes" : "no";
102 }
103
104 void BooleanOptionStorage::convertValue(const std::string &value)
105 {
106     // TODO: Case-independence
107     if (value == "1" || value == "yes" || value == "true")
108     {
109         addValue(true);
110         return;
111     }
112     else if (value == "0" || value == "no" || value == "false")
113     {
114         addValue(false);
115         return;
116     }
117     GMX_THROW(InvalidInputError("Invalid value: '" + value + "'; supported values are: 1, 0, yes, no, true, false"));
118 }
119
120 /********************************************************************
121  * BooleanOptionInfo
122  */
123
124 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage *option)
125     : OptionInfo(option)
126 {
127 }
128
129 const BooleanOptionStorage &BooleanOptionInfo::option() const
130 {
131     return static_cast<const BooleanOptionStorage &>(OptionInfo::option());
132 }
133
134 bool BooleanOptionInfo::defaultValue() const
135 {
136     return option().defaultValue();
137 }
138
139 /********************************************************************
140  * BooleanOption
141  */
142
143 AbstractOptionStorage *
144 BooleanOption::createStorage(const OptionManagerContainer & /*managers*/) const
145 {
146     return new BooleanOptionStorage(*this);
147 }
148
149
150 /********************************************************************
151  * IntegerOptionStorage
152  */
153
154 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
155 {
156     return formatString("%d", value);
157 }
158
159 void IntegerOptionStorage::convertValue(const std::string &value)
160 {
161     const char *ptr = value.c_str();
162     char       *endptr;
163     errno = 0;
164     long int    ival = std::strtol(ptr, &endptr, 10);
165     if (errno == ERANGE
166         || ival < std::numeric_limits<int>::min()
167         || ival > std::numeric_limits<int>::max())
168     {
169         GMX_THROW(InvalidInputError("Invalid value: '" + value
170                                     + "'; it causes an integer overflow"));
171     }
172     if (*ptr == '\0' || *endptr != '\0')
173     {
174         GMX_THROW(InvalidInputError("Invalid value: '" + value
175                                     + "'; expected an integer"));
176     }
177     addValue(ival);
178 }
179
180 void IntegerOptionStorage::processSetValues(ValueList *values)
181 {
182     if (isVector())
183     {
184         expandVector(maxValueCount(), values);
185     }
186 }
187
188 /********************************************************************
189  * IntegerOptionInfo
190  */
191
192 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
193     : OptionInfo(option)
194 {
195 }
196
197 /********************************************************************
198  * IntegerOption
199  */
200
201 AbstractOptionStorage *
202 IntegerOption::createStorage(const OptionManagerContainer & /*managers*/) const
203 {
204     return new IntegerOptionStorage(*this);
205 }
206
207
208 /********************************************************************
209  * Int64OptionStorage
210  */
211
212 std::string Int64OptionStorage::formatSingleValue(const gmx_int64_t &value) const
213 {
214     return formatString("%" GMX_PRId64, value);
215 }
216
217 void Int64OptionStorage::convertValue(const std::string &value)
218 {
219     const char       *ptr = value.c_str();
220     char             *endptr;
221     errno = 0;
222     const gmx_int64_t ival = str_to_int64_t(ptr, &endptr);
223     if (errno == ERANGE)
224     {
225         GMX_THROW(InvalidInputError("Invalid value: '" + value
226                                     + "'; it causes an integer overflow"));
227     }
228     if (*ptr == '\0' || *endptr != '\0')
229     {
230         GMX_THROW(InvalidInputError("Invalid value: '" + value
231                                     + "'; expected an integer"));
232     }
233     addValue(ival);
234 }
235
236 /********************************************************************
237  * Int64OptionInfo
238  */
239
240 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage *option)
241     : OptionInfo(option)
242 {
243 }
244
245 /********************************************************************
246  * Int64Option
247  */
248
249 AbstractOptionStorage *
250 Int64Option::createStorage(const OptionManagerContainer & /*managers*/) const
251 {
252     return new Int64OptionStorage(*this);
253 }
254
255
256 /********************************************************************
257  * DoubleOptionStorage
258  */
259
260 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
261     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
262 {
263 }
264
265 std::string DoubleOptionStorage::typeString() const
266 {
267     return isVector() ? "vector" : (isTime() ? "time" : "real");
268 }
269
270 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
271 {
272     return formatString("%g", value / factor_);
273 }
274
275 void DoubleOptionStorage::convertValue(const std::string &value)
276 {
277     const char *ptr = value.c_str();
278     char       *endptr;
279     errno = 0;
280     double      dval = std::strtod(ptr, &endptr);
281     if (errno == ERANGE)
282     {
283         GMX_THROW(InvalidInputError("Invalid value: '" + value
284                                     + "'; it causes an overflow/underflow"));
285     }
286     if (*ptr == '\0' || *endptr != '\0')
287     {
288         GMX_THROW(InvalidInputError("Invalid value: '" + value
289                                     + "'; expected a number"));
290     }
291     addValue(dval * factor_);
292 }
293
294 void DoubleOptionStorage::processSetValues(ValueList *values)
295 {
296     if (isVector())
297     {
298         expandVector(maxValueCount(), values);
299     }
300 }
301
302 void DoubleOptionStorage::setScaleFactor(double factor)
303 {
304     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
305     if (!hasFlag(efOption_HasDefaultValue))
306     {
307         double              scale = factor / factor_;
308         ValueList::iterator i;
309         for (i = values().begin(); i != values().end(); ++i)
310         {
311             (*i) *= scale;
312         }
313         refreshValues();
314     }
315     factor_ = factor;
316 }
317
318 /********************************************************************
319  * DoubleOptionInfo
320  */
321
322 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
323     : OptionInfo(option)
324 {
325 }
326
327 DoubleOptionStorage &DoubleOptionInfo::option()
328 {
329     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
330 }
331
332 const DoubleOptionStorage &DoubleOptionInfo::option() const
333 {
334     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
335 }
336
337 bool DoubleOptionInfo::isTime() const
338 {
339     return option().isTime();
340 }
341
342 void DoubleOptionInfo::setScaleFactor(double factor)
343 {
344     option().setScaleFactor(factor);
345 }
346
347 /********************************************************************
348  * DoubleOption
349  */
350
351 AbstractOptionStorage *
352 DoubleOption::createStorage(const OptionManagerContainer & /*managers*/) const
353 {
354     return new DoubleOptionStorage(*this);
355 }
356
357
358 /********************************************************************
359  * FloatOptionStorage
360  */
361
362 FloatOptionStorage::FloatOptionStorage(const FloatOption &settings)
363     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
364 {
365 }
366
367 std::string FloatOptionStorage::typeString() const
368 {
369     return isVector() ? "vector" : (isTime() ? "time" : "real");
370 }
371
372 std::string FloatOptionStorage::formatSingleValue(const float &value) const
373 {
374     return formatString("%g", value / factor_);
375 }
376
377 void FloatOptionStorage::convertValue(const std::string &value)
378 {
379     const char *ptr = value.c_str();
380     char       *endptr;
381     errno = 0;
382     double      dval = std::strtod(ptr, &endptr);
383     if (errno == ERANGE
384         || dval * factor_ < -std::numeric_limits<float>::max()
385         || dval * factor_ >  std::numeric_limits<float>::max())
386     {
387         GMX_THROW(InvalidInputError("Invalid value: '" + value
388                                     + "'; it causes an overflow/underflow"));
389     }
390     if (*ptr == '\0' || *endptr != '\0')
391     {
392         GMX_THROW(InvalidInputError("Invalid value: '" + value
393                                     + "'; expected a number"));
394     }
395     addValue(dval * factor_);
396 }
397
398 void FloatOptionStorage::processSetValues(ValueList *values)
399 {
400     if (isVector())
401     {
402         expandVector(maxValueCount(), values);
403     }
404 }
405
406 void FloatOptionStorage::setScaleFactor(double factor)
407 {
408     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
409     if (!hasFlag(efOption_HasDefaultValue))
410     {
411         double              scale = factor / factor_;
412         ValueList::iterator i;
413         for (i = values().begin(); i != values().end(); ++i)
414         {
415             (*i) *= scale;
416         }
417         refreshValues();
418     }
419     factor_ = factor;
420 }
421
422 /********************************************************************
423  * FloatOptionInfo
424  */
425
426 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage *option)
427     : OptionInfo(option)
428 {
429 }
430
431 FloatOptionStorage &FloatOptionInfo::option()
432 {
433     return static_cast<FloatOptionStorage &>(OptionInfo::option());
434 }
435
436 const FloatOptionStorage &FloatOptionInfo::option() const
437 {
438     return static_cast<const FloatOptionStorage &>(OptionInfo::option());
439 }
440
441 bool FloatOptionInfo::isTime() const
442 {
443     return option().isTime();
444 }
445
446 void FloatOptionInfo::setScaleFactor(double factor)
447 {
448     option().setScaleFactor(factor);
449 }
450
451 /********************************************************************
452  * FloatOption
453  */
454
455 AbstractOptionStorage *
456 FloatOption::createStorage(const OptionManagerContainer & /*managers*/) const
457 {
458     return new FloatOptionStorage(*this);
459 }
460
461
462 /********************************************************************
463  * StringOptionStorage
464  */
465
466 StringOptionStorage::StringOptionStorage(const StringOption &settings)
467     : MyBase(settings), info_(this), enumIndexStore_(NULL)
468 {
469     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == NULL)
470     {
471         GMX_THROW(APIError("Cannot set default enum index without enum values"));
472     }
473     if (settings.enumIndexStore_ != NULL && settings.enumValues_ == NULL)
474     {
475         GMX_THROW(APIError("Cannot set enum index store without enum values"));
476     }
477     if (settings.enumIndexStore_ != NULL && settings.maxValueCount_ < 0)
478     {
479         GMX_THROW(APIError("Cannot set enum index store with arbitrary number of values"));
480     }
481     if (settings.enumValues_ != NULL)
482     {
483         enumIndexStore_ = settings.enumIndexStore_;
484         const std::string *defaultValue = settings.defaultValue();
485         int                match        = -1;
486         int                count        = settings.enumValuesCount_;
487         if (count < 0)
488         {
489             count = 0;
490             while (settings.enumValues_[count] != NULL)
491             {
492                 ++count;
493             }
494         }
495         for (int i = 0; i < count; ++i)
496         {
497             if (settings.enumValues_[i] == NULL)
498             {
499                 GMX_THROW(APIError("Enumeration value cannot be NULL"));
500             }
501             if (defaultValue != NULL && settings.enumValues_[i] == *defaultValue)
502             {
503                 match = i;
504             }
505             allowed_.push_back(settings.enumValues_[i]);
506         }
507         if (defaultValue != NULL)
508         {
509             if (match < 0)
510             {
511                 GMX_THROW(APIError("Default value is not one of allowed values"));
512             }
513         }
514         if (settings.defaultEnumIndex_ >= 0)
515         {
516             if (settings.defaultEnumIndex_ >= static_cast<int>(allowed_.size()))
517             {
518                 GMX_THROW(APIError("Default enumeration index is out of range"));
519             }
520             if (defaultValue != NULL && *defaultValue != allowed_[settings.defaultEnumIndex_])
521             {
522                 GMX_THROW(APIError("Conflicting default values"));
523             }
524         }
525     }
526     if (settings.defaultEnumIndex_ >= 0)
527     {
528         clear();
529         addValue(allowed_[settings.defaultEnumIndex_]);
530         commitValues();
531     }
532     // Somewhat subtly, this does not update the stored enum index if the
533     // caller has not provided store() or storeVector(), because values()
534     // will be empty in such a case.  This leads to (desired) behavior of
535     // preserving the existing value in the enum index store variable in such
536     // cases.
537     refreshEnumIndexStore();
538 }
539
540 std::string StringOptionStorage::formatExtraDescription() const
541 {
542     std::string result;
543     if (!allowed_.empty())
544     {
545         result.append(": ");
546         ValueList::const_iterator i;
547         for (i = allowed_.begin(); i != allowed_.end(); ++i)
548         {
549             if (i != allowed_.begin())
550             {
551                 result.append(", ");
552             }
553             result.append(*i);
554         }
555     }
556     return result;
557 }
558
559 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
560 {
561     return value;
562 }
563
564 void StringOptionStorage::convertValue(const std::string &value)
565 {
566     if (allowed_.size() == 0)
567     {
568         addValue(value);
569     }
570     else
571     {
572         ValueList::const_iterator  i;
573         ValueList::const_iterator  match = allowed_.end();
574         for (i = allowed_.begin(); i != allowed_.end(); ++i)
575         {
576             // TODO: Case independence.
577             if (i->find(value) == 0)
578             {
579                 if (match == allowed_.end() || i->size() < match->size())
580                 {
581                     match = i;
582                 }
583             }
584         }
585         if (match == allowed_.end())
586         {
587             GMX_THROW(InvalidInputError("Invalid value: " + value));
588         }
589         addValue(*match);
590     }
591 }
592
593 void StringOptionStorage::refreshValues()
594 {
595     MyBase::refreshValues();
596     refreshEnumIndexStore();
597 }
598
599 void StringOptionStorage::refreshEnumIndexStore()
600 {
601     if (enumIndexStore_ != NULL)
602     {
603         for (size_t i = 0; i < values().size(); ++i)
604         {
605             if (values()[i].empty())
606             {
607                 enumIndexStore_[i] = -1;
608             }
609             else
610             {
611                 ValueList::const_iterator match =
612                     std::find(allowed_.begin(), allowed_.end(), values()[i]);
613                 GMX_ASSERT(match != allowed_.end(),
614                            "Enum value not found (internal error)");
615                 enumIndexStore_[i] = static_cast<int>(match - allowed_.begin());
616             }
617         }
618     }
619 }
620
621 /********************************************************************
622  * StringOptionInfo
623  */
624
625 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
626     : OptionInfo(option)
627 {
628 }
629
630 StringOptionStorage &StringOptionInfo::option()
631 {
632     return static_cast<StringOptionStorage &>(OptionInfo::option());
633 }
634
635 const StringOptionStorage &StringOptionInfo::option() const
636 {
637     return static_cast<const StringOptionStorage &>(OptionInfo::option());
638 }
639
640 bool StringOptionInfo::isEnumerated() const
641 {
642     return !allowedValues().empty();
643 }
644
645 const std::vector<std::string> &StringOptionInfo::allowedValues() const
646 {
647     return option().allowedValues();
648 }
649
650 /********************************************************************
651  * StringOption
652  */
653
654 AbstractOptionStorage *
655 StringOption::createStorage(const OptionManagerContainer & /*managers*/) const
656 {
657     return new StringOptionStorage(*this);
658 }
659
660 } // namespace gmx