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