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