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