1 #include "MaeBlock.hpp"
2 #include <cmath>
3 
4 #include "MaeParser.hpp"
5 
6 using namespace std;
7 
8 namespace schrodinger
9 {
10 namespace mae
11 {
12 
13 namespace
14 {
15 const double tolerance = 0.00001; // Tolerance to match string cutoff
16 
17 // Wrap to-string to allow it to take strings and be a no-op
local_to_string(T val)18 template <typename T> inline string local_to_string(T val)
19 {
20     return to_string(val);
21 }
22 
char_requires_escaping(char c)23 inline bool char_requires_escaping(char c)
24 {
25     return c == '"' || c == '\\';
26 }
27 
local_to_string(const string & val)28 string local_to_string(const string& val)
29 {
30     if (val.empty()) {
31         return R"("")";
32     }
33 
34     // Quotes are required if any character needs escaping, or there are
35     // spaces in the string (spaces do not require escaping)
36     bool quotes_required = false;
37     for (const char& c : val) {
38         if (char_requires_escaping(c) || c == ' ') {
39             quotes_required = true;
40             break;
41         }
42     }
43 
44     if (!quotes_required) {
45         return val;
46     }
47 
48     std::stringstream new_string;
49     new_string << '\"';
50     for (const char& c : val) {
51         if (char_requires_escaping(c)) {
52             new_string << '\\';
53         }
54         new_string << c;
55     }
56     new_string << '\"';
57 
58     return new_string.str();
59 }
60 
61 template <typename T>
output_property_names(ostream & out,const string & indentation,const map<string,T> & properties)62 inline void output_property_names(ostream& out, const string& indentation,
63                                   const map<string, T>& properties)
64 {
65     for (const auto& p : properties) {
66         out << indentation << p.first << "\n";
67     }
68 }
69 
70 template <typename T>
output_property_values(ostream & out,const string & indentation,const map<string,T> & properties)71 inline void output_property_values(ostream& out, const string& indentation,
72                                    const map<string, T>& properties)
73 {
74     for (const auto& p : properties) {
75         out << indentation << local_to_string(p.second) << "\n";
76     }
77 }
78 
79 template <typename T>
output_indexed_property_values(ostream & out,const map<string,T> & properties,unsigned int index)80 void output_indexed_property_values(ostream& out,
81                                     const map<string, T>& properties,
82                                     unsigned int index)
83 {
84     for (const auto& p : properties) {
85         const auto& property = p.second;
86         if (property->isDefined(index)) {
87             out << ' ' << local_to_string(property->at(index));
88         } else {
89             out << " <>";
90         }
91     }
92 }
93 
94 template <typename T>
maps_indexed_props_equal(const T & lmap,const T & rmap)95 bool maps_indexed_props_equal(const T& lmap, const T& rmap)
96 {
97     if (rmap.size() != lmap.size())
98         return false;
99     auto diff = std::mismatch(
100         lmap.begin(), lmap.end(), rmap.begin(),
101         [](decltype(*begin(lmap)) l, decltype(*begin(lmap)) r) {
102             return l.first == r.first && *(l.second) == *(r.second);
103         });
104     if (diff.first != lmap.end())
105         return false;
106     return true;
107 }
108 } // namespace
109 
write(ostream & out,unsigned int current_indentation) const110 void Block::write(ostream& out, unsigned int current_indentation) const
111 {
112 
113     string root_indentation = string(current_indentation, ' ');
114     current_indentation += 2;
115     string indentation = string(current_indentation, ' ');
116 
117     const bool has_data = !m_bmap.empty() || !m_rmap.empty() ||
118                           !m_imap.empty() || !m_smap.empty();
119 
120     out << root_indentation << getName() << " {\n";
121 
122     if (has_data) {
123         output_property_names(out, indentation, m_bmap);
124         output_property_names(out, indentation, m_rmap);
125         output_property_names(out, indentation, m_imap);
126         output_property_names(out, indentation, m_smap);
127 
128         out << indentation + ":::\n";
129 
130         output_property_values(out, indentation, m_bmap);
131         output_property_values(out, indentation, m_rmap);
132         output_property_values(out, indentation, m_imap);
133         output_property_values(out, indentation, m_smap);
134     }
135 
136     if (hasIndexedBlockData()) {
137         const auto block_names = m_indexed_block_map->getBlockNames();
138         for (const auto& name : block_names) {
139             const auto& indexed_block =
140                 m_indexed_block_map->getIndexedBlock(name);
141             indexed_block->write(out, current_indentation);
142         }
143     }
144 
145     for (const auto& p : m_sub_block) {
146         const auto& sub_block = p.second;
147         sub_block->write(out, current_indentation);
148     }
149 
150     out << root_indentation << "}\n\n";
151 
152     return;
153 }
154 
toString() const155 string Block::toString() const
156 {
157     ostringstream stream;
158     write(stream);
159 
160     return stream.str();
161 }
162 
getIndexedBlock(const string & name)163 shared_ptr<const IndexedBlock> Block::getIndexedBlock(const string& name)
164 {
165     if (!hasIndexedBlockData()) {
166         throw out_of_range("Indexed block not found: " + name);
167     }
168     return const_pointer_cast<const IndexedBlock>(
169         m_indexed_block_map->getIndexedBlock(name));
170 }
171 
real_map_equal(const map<string,double> & rmap1,const map<string,double> & rmap2)172 bool real_map_equal(const map<string, double>& rmap1,
173                     const map<string, double>& rmap2)
174 {
175     if (rmap1.size() != rmap2.size())
176         return false;
177     for (const auto& p : rmap1) {
178         if (rmap2.count(p.first) != 1)
179             return false;
180         if ((float) abs(p.second - rmap2.at(p.first)) > tolerance)
181             return false;
182     }
183 
184     return true;
185 }
186 
operator ==(const Block & rhs) const187 bool Block::operator==(const Block& rhs) const
188 {
189     if (m_bmap != rhs.m_bmap)
190         return false;
191     if (!real_map_equal(m_rmap, rhs.m_rmap))
192         return false;
193     if (m_imap != rhs.m_imap)
194         return false;
195     if (m_smap != rhs.m_smap)
196         return false;
197     if (m_sub_block != rhs.m_sub_block)
198         return false;
199     if (!(*m_indexed_block_map == *(rhs.m_indexed_block_map)))
200         return false;
201     return true;
202 }
203 
operator ==(const IndexedBlockMapI & rhs)204 bool IndexedBlockMapI::operator==(const IndexedBlockMapI& rhs)
205 {
206     const auto& block_names = getBlockNames();
207     for (const auto& name : block_names) {
208         if (!rhs.hasIndexedBlock(name))
209             return false;
210         const auto& block1 = rhs.getIndexedBlock(name);
211         const auto& block2 = getIndexedBlock(name);
212         if (*block1 != *block2)
213             return false;
214     }
215     return true;
216 }
217 
hasIndexedBlock(const string & name) const218 bool IndexedBlockMap::hasIndexedBlock(const string& name) const
219 {
220     return m_indexed_block.find(name) != m_indexed_block.end();
221 }
222 
223 shared_ptr<const IndexedBlock>
getIndexedBlock(const string & name) const224 IndexedBlockMap::getIndexedBlock(const string& name) const
225 {
226     map<string, shared_ptr<IndexedBlock>>::const_iterator block_iter =
227         m_indexed_block.find(name);
228     if (block_iter != m_indexed_block.end()) {
229         return const_pointer_cast<const IndexedBlock>(block_iter->second);
230     } else {
231         throw out_of_range("Indexed block not found: " + name);
232     }
233 }
234 
hasIndexedBlock(const string & name) const235 bool BufferedIndexedBlockMap::hasIndexedBlock(const string& name) const
236 {
237     if (m_indexed_buffer.find(name) != m_indexed_buffer.end()) {
238         return true;
239     } else if (m_indexed_block.find(name) != m_indexed_block.end()) {
240         return true;
241     } else {
242         return false;
243     }
244 }
245 
246 shared_ptr<const IndexedBlock>
getIndexedBlock(const string & name) const247 BufferedIndexedBlockMap::getIndexedBlock(const string& name) const
248 {
249     map<string, shared_ptr<IndexedBlock>>::const_iterator itb =
250         m_indexed_block.find(name);
251     if (itb != m_indexed_block.end()) {
252         return itb->second;
253     }
254 
255     auto itbb = m_indexed_buffer.find(name);
256     if (itbb == m_indexed_buffer.end()) {
257         throw out_of_range("Indexed block not found: " + name);
258     } else {
259         shared_ptr<const IndexedBlock> ib(itbb->second->getIndexedBlock());
260         return ib;
261     }
262 }
263 
264 template <>
265 EXPORT_MAEPARSER void
setProperty(const string & name,shared_ptr<IndexedBoolProperty> value)266 IndexedBlock::setProperty<BoolProperty>(const string& name,
267                                         shared_ptr<IndexedBoolProperty> value)
268 {
269     set_indexed_property<IndexedBoolProperty>(m_bmap, name, value);
270 }
271 
272 template <>
273 EXPORT_MAEPARSER void
setProperty(const string & name,shared_ptr<IndexedProperty<double>> value)274 IndexedBlock::setProperty<double>(const string& name,
275                                   shared_ptr<IndexedProperty<double>> value)
276 {
277     set_indexed_property<IndexedProperty<double>>(m_rmap, name, value);
278 }
279 
280 template <>
281 EXPORT_MAEPARSER void
setProperty(const string & name,shared_ptr<IndexedProperty<int>> value)282 IndexedBlock::setProperty<int>(const string& name,
283                                shared_ptr<IndexedProperty<int>> value)
284 {
285     set_indexed_property<IndexedProperty<int>>(m_imap, name, value);
286 }
287 
288 template <>
289 EXPORT_MAEPARSER void
setProperty(const string & name,shared_ptr<IndexedProperty<string>> value)290 IndexedBlock::setProperty<string>(const string& name,
291                                   shared_ptr<IndexedProperty<string>> value)
292 {
293     set_indexed_property<IndexedProperty<string>>(m_smap, name, value);
294 }
295 
size() const296 size_t IndexedBlock::size() const
297 {
298     size_t count = 0;
299     // To save memory, not all maps will have max index count for the block,
300     // so we must find the max size of all maps in the block.
301     for (const auto& p : m_bmap)
302         count = max(p.second->size(), count);
303     for (const auto& p : m_imap)
304         count = max(p.second->size(), count);
305     for (const auto& p : m_rmap)
306         count = max(p.second->size(), count);
307     for (const auto& p : m_smap)
308         count = max(p.second->size(), count);
309 
310     return count;
311 }
312 
write(ostream & out,unsigned int current_indentation) const313 void IndexedBlock::write(ostream& out, unsigned int current_indentation) const
314 {
315     string root_indentation = string(current_indentation, ' ');
316     string indentation = string(current_indentation + 2, ' ');
317 
318     const bool has_data = !m_bmap.empty() || !m_rmap.empty() ||
319                           !m_imap.empty() || !m_smap.empty();
320 
321     out << root_indentation << getName() << "[" << to_string((int) size())
322         << "] {\n";
323 
324     if (has_data) {
325         out << indentation + "# First column is Index #\n";
326 
327         output_property_names(out, indentation, m_bmap);
328         output_property_names(out, indentation, m_rmap);
329         output_property_names(out, indentation, m_imap);
330         output_property_names(out, indentation, m_smap);
331 
332         out << indentation + ":::\n";
333 
334         for (unsigned int i = 0; i < size(); ++i) {
335             out << indentation << i + 1;
336             output_indexed_property_values(out, m_bmap, i);
337             output_indexed_property_values(out, m_rmap, i);
338             output_indexed_property_values(out, m_imap, i);
339             output_indexed_property_values(out, m_smap, i);
340             out << endl;
341         }
342 
343         out << indentation + ":::\n";
344     }
345 
346     out << root_indentation << "}\n";
347 
348     return;
349 }
350 
toString() const351 string IndexedBlock::toString() const
352 {
353     ostringstream stream;
354     write(stream);
355 
356     return stream.str();
357 }
358 
359 template <typename T>
operator ==(const IndexedProperty<T> & rhs) const360 bool IndexedProperty<T>::operator==(const IndexedProperty<T>& rhs) const
361 {
362     if (m_is_null == nullptr || rhs.m_is_null == nullptr) {
363         if ((m_is_null == nullptr) != (rhs.m_is_null == nullptr))
364             return false;
365     } else if (*m_is_null != *(rhs.m_is_null)) {
366         return false;
367     }
368     if (m_data != rhs.m_data)
369         return false;
370     return true;
371 }
372 
373 // For doubles we need to implement our own comparator for the vectors to
374 // take precision into account
375 template <>
376 bool IndexedProperty<double>::
operator ==(const IndexedProperty<double> & rhs) const377 operator==(const IndexedProperty<double>& rhs) const
378 {
379     if (m_is_null == nullptr || rhs.m_is_null == nullptr) {
380         if ((m_is_null == nullptr) != (rhs.m_is_null == nullptr))
381             return false;
382     } else if (*m_is_null != *(rhs.m_is_null))
383         return false;
384 
385     for (size_t i = 0; i < m_data.size(); ++i)
386         if ((float) abs(m_data[i] - rhs.m_data[i]) > tolerance)
387             return false;
388 
389     return true;
390 }
391 
operator ==(const IndexedBlock & rhs) const392 bool IndexedBlock::operator==(const IndexedBlock& rhs) const
393 {
394     if (!maps_indexed_props_equal(m_bmap, rhs.m_bmap))
395         return false;
396     if (!maps_indexed_props_equal(m_imap, rhs.m_imap))
397         return false;
398     if (!maps_indexed_props_equal(m_rmap, rhs.m_rmap))
399         return false;
400     if (!maps_indexed_props_equal(m_smap, rhs.m_smap))
401         return false;
402 
403     return true;
404 }
405 
406 } // namespace mae
407 } // namespace schrodinger
408