Remove gmx custom fixed int (e.g. gmx_int64_t) types
[alexxy/gromacs.git] / src / gromacs / utility / keyvaluetree.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2016,2017,2018, 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 #include "gmxpre.h"
36
37 #include "keyvaluetree.h"
38
39 #include <string>
40 #include <vector>
41
42 #include "gromacs/utility/compare.h"
43 #include "gromacs/utility/gmxassert.h"
44 #include "gromacs/utility/strconvert.h"
45 #include "gromacs/utility/stringutil.h"
46 #include "gromacs/utility/textwriter.h"
47
48 namespace gmx
49 {
50
51 namespace
52 {
53
54 //! Helper function to split a KeyValueTreePath to its components
55 std::vector<std::string> splitPathElements(const std::string &path)
56 {
57     GMX_ASSERT(!path.empty() && path[0] == '/',
58                "Paths to KeyValueTree should start with '/'");
59     return splitDelimitedString(path.substr(1), '/');
60 }
61
62 }   // namespace
63
64 /********************************************************************
65  * KeyValueTreePath
66  */
67
68 KeyValueTreePath::KeyValueTreePath(const char *path)
69     : path_(splitPathElements(path))
70 {
71 }
72
73 KeyValueTreePath::KeyValueTreePath(const std::string &path)
74     : path_(splitPathElements(path))
75 {
76 }
77
78 std::string KeyValueTreePath::toString() const
79 {
80     return "/" + joinStrings(path_, "/");
81 }
82
83 /********************************************************************
84  * KeyValueTreeObject
85  */
86
87 bool KeyValueTreeObject::hasDistinctProperties(const KeyValueTreeObject &obj) const
88 {
89     for (const auto &prop : obj.values_)
90     {
91         if (keyExists(prop.key()))
92         {
93             GMX_RELEASE_ASSERT(!prop.value().isArray(),
94                                "Comparison of arrays not implemented");
95             if (prop.value().isObject() && valueMap_.at(prop.key()).isObject())
96             {
97                 return valueMap_.at(prop.key()).asObject().hasDistinctProperties(prop.value().asObject());
98             }
99             return false;
100         }
101     }
102     return true;
103 }
104
105 /********************************************************************
106  * Key value tree dump
107  */
108
109 //! \cond libapi
110 void dumpKeyValueTree(TextWriter *writer, const KeyValueTreeObject &tree)
111 {
112     for (const auto &prop : tree.properties())
113     {
114         const auto &value = prop.value();
115         if (value.isObject())
116         {
117             writer->writeString(prop.key());
118             writer->writeLine(":");
119             int oldIndent = writer->wrapperSettings().indent();
120             writer->wrapperSettings().setIndent(oldIndent + 2);
121             dumpKeyValueTree(writer, value.asObject());
122             writer->wrapperSettings().setIndent(oldIndent);
123         }
124         else
125         {
126             int indent = writer->wrapperSettings().indent();
127             writer->writeString(formatString("%*s", -(33-indent), prop.key().c_str()));
128             writer->writeString(" = ");
129             if (value.isArray())
130             {
131                 writer->writeString("[");
132                 for (const auto &elem : value.asArray().values())
133                 {
134                     GMX_RELEASE_ASSERT(!elem.isObject() && !elem.isArray(),
135                                        "Arrays of objects not currently implemented");
136                     writer->writeString(" ");
137                     writer->writeString(simpleValueToString(elem));
138                 }
139                 writer->writeString(" ]");
140             }
141             else
142             {
143                 writer->writeString(simpleValueToString(value));
144             }
145             writer->writeLine();
146         }
147     }
148 }
149 //! \endcond
150
151 /********************************************************************
152  * Key value tree comparison
153  */
154
155 namespace
156 {
157
158 class CompareHelper
159 {
160     public:
161         CompareHelper(TextWriter *writer, real ftol, real abstol)
162             : writer_(writer), ftol_(ftol), abstol_(abstol)
163         {
164         }
165
166         void compareObjects(const KeyValueTreeObject &obj1,
167                             const KeyValueTreeObject &obj2)
168         {
169             for (const auto &prop1 : obj1.properties())
170             {
171                 currentPath_.append(prop1.key());
172                 if (obj2.keyExists(prop1.key()))
173                 {
174                     compareValues(prop1.value(), obj2[prop1.key()]);
175                 }
176                 else
177                 {
178                     handleMissingKeyInSecondObject(prop1.value());
179                 }
180                 currentPath_.pop_back();
181             }
182             for (const auto &prop2 : obj2.properties())
183             {
184                 currentPath_.append(prop2.key());
185                 if (!obj1.keyExists(prop2.key()))
186                 {
187                     handleMissingKeyInFirstObject(prop2.value());
188                 }
189                 currentPath_.pop_back();
190             }
191         }
192
193     private:
194         void compareValues(const KeyValueTreeValue &value1,
195                            const KeyValueTreeValue &value2)
196         {
197             if (value1.type() == value2.type())
198             {
199                 if (value1.isObject())
200                 {
201                     compareObjects(value1.asObject(), value2.asObject());
202                 }
203                 else if (value1.isArray())
204                 {
205                     GMX_RELEASE_ASSERT(false, "Array comparison not implemented");
206                 }
207                 else if (!areSimpleValuesOfSameTypeEqual(value1, value2))
208                 {
209                     writer_->writeString(currentPath_.toString());
210                     writer_->writeLine(formatString(" (%s - %s)", simpleValueToString(value1).c_str(), simpleValueToString(value2).c_str()));
211                 }
212             }
213             else if ((value1.isType<double>() && value2.isType<float>())
214                      || (value1.isType<float>() && value2.isType<double>()))
215             {
216                 const bool  firstIsDouble
217                     = value1.isType<double>();
218                 const float v1 = firstIsDouble ? value1.cast<double>() : value1.cast<float>();
219                 const float v2 = firstIsDouble ? value2.cast<float>() : value2.cast<double>();
220                 if (!equal_float(v1, v2, ftol_, abstol_))
221                 {
222                     writer_->writeString(currentPath_.toString());
223                     writer_->writeLine(formatString(" (%e - %e)", v1, v2));
224                 }
225             }
226             else
227             {
228                 handleMismatchingTypes(value1, value2);
229             }
230         }
231
232         bool areSimpleValuesOfSameTypeEqual(
233             const KeyValueTreeValue &value1,
234             const KeyValueTreeValue &value2)
235         {
236             GMX_ASSERT(value1.type() == value2.type(),
237                        "Caller should ensure that types are equal");
238             if (value1.isType<bool>())
239             {
240                 return value1.cast<bool>() == value2.cast<bool>();
241             }
242             else if (value1.isType<int>())
243             {
244                 return value1.cast<int>() == value2.cast<int>();
245             }
246             else if (value1.isType<int64_t>())
247             {
248                 return value1.cast<int64_t>() == value2.cast<int64_t>();
249             }
250             else if (value1.isType<double>())
251             {
252                 return equal_double(value1.cast<double>(), value2.cast<double>(), ftol_, abstol_);
253             }
254             else if (value1.isType<float>())
255             {
256                 return equal_float(value1.cast<float>(), value2.cast<float>(), ftol_, abstol_);
257             }
258             else if (value1.isType<std::string>())
259             {
260                 return value1.cast<std::string>() == value2.cast<std::string>();
261             }
262             else
263             {
264                 GMX_RELEASE_ASSERT(false, "Unknown value type");
265                 return false;
266             }
267         }
268
269         void handleMismatchingTypes(const KeyValueTreeValue & /* value1 */,
270                                     const KeyValueTreeValue & /* value2 */)
271         {
272             writer_->writeString(currentPath_.toString());
273             writer_->writeString(" type mismatch");
274         }
275
276         void handleMissingKeyInFirstObject(const KeyValueTreeValue &value)
277         {
278             const std::string message = formatString(
279                         "%s (missing - %s)", currentPath_.toString().c_str(),
280                         formatValueForMissingMessage(value).c_str());
281             writer_->writeLine(message);
282         }
283         void handleMissingKeyInSecondObject(const KeyValueTreeValue &value)
284         {
285             const std::string message = formatString(
286                         "%s (%s - missing)", currentPath_.toString().c_str(),
287                         formatValueForMissingMessage(value).c_str());
288             writer_->writeLine(message);
289         }
290
291         std::string formatValueForMissingMessage(const KeyValueTreeValue &value)
292         {
293             if (value.isObject() || value.isArray())
294             {
295                 return "present";
296             }
297             return simpleValueToString(value);
298         }
299
300         KeyValueTreePath  currentPath_;
301         TextWriter       *writer_;
302         real              ftol_;
303         real              abstol_;
304 };
305
306 }   // namespace
307
308 //! \cond libapi
309 void compareKeyValueTrees(TextWriter               *writer,
310                           const KeyValueTreeObject &tree1,
311                           const KeyValueTreeObject &tree2,
312                           real                      ftol,
313                           real                      abstol)
314 {
315     CompareHelper helper(writer, ftol, abstol);
316     helper.compareObjects(tree1, tree2);
317 }
318 //! \endcond
319
320 } // namespace gmx