1 /*
2   This file is part of MADNESS.
3 
4   Copyright (C) 2007,2010 Oak Ridge National Laboratory
5 
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 
20   For more information please contact:
21 
22   Robert J. Harrison
23   Oak Ridge National Laboratory
24   One Bethel Valley Road
25   P.O. Box 2008, MS-6367
26 
27   email: harrisonrj@ornl.gov
28   tel:   865-241-3937
29   fax:   865-572-0680
30 */
31 
32 #include <iostream>
33 using std::cout;
34 using std::endl;
35 
36 #include <cmath>
37 
38 /// \file test.cc
39 /// \brief Tests serialization by some of the archives
40 
41 #define ARCHIVE_REGISTER_TYPE_INSTANTIATE_HERE
42 
43 #include <madness/world/text_fstream_archive.h>
44 using madness::archive::TextFstreamInputArchive;
45 using madness::archive::TextFstreamOutputArchive;
46 
47 #include <madness/world/binary_fstream_archive.h>
48 using madness::archive::BinaryFstreamInputArchive;
49 using madness::archive::BinaryFstreamOutputArchive;
50 
51 #include <madness/world/vector_archive.h>
52 using madness::archive::VectorInputArchive;
53 using madness::archive::VectorOutputArchive;
54 
55 #include <madness/world/buffer_archive.h>
56 using madness::archive::BufferInputArchive;
57 using madness::archive::BufferOutputArchive;
58 
59 
60 // A is a class that provides a symmetric serialize method
61 class A {
62 public:
63     float a;
A(float a=0.0)64     A(float a = 0.0) : a(a) {};
65 
66     template <class Archive>
serialize(const Archive & ar)67     inline void serialize(const Archive& ar) {
68         ar & a;
69     }
70 };
71 
72 // B is a class without a serialize method but with symmetric serialization.
73 class B {
74 public:
75     bool b;
B(bool b=false)76     B(bool b = false) : b(b) {};
77 };
78 
79 namespace madness {
80     namespace archive {
81 
82         template <class Archive>
83         struct ArchiveSerializeImpl<Archive,B> {
serializemadness::archive::ArchiveSerializeImpl84             static inline void serialize(const Archive& ar, B& b) {
85                 ar & b.b;
86             };
87         };
88     }
89 }
90 
91 // C is a class with asymmetric load/store.
92 class C {
93 public:
94     long c;
C(long c=0)95     C(long c = 0) : c(c) {};
96 };
97 
98 namespace madness {
99     namespace archive {
100         template <class Archive>
101         struct ArchiveLoadImpl<Archive,C> {
loadmadness::archive::ArchiveLoadImpl102             static inline void load(const Archive& ar, C& c) {
103                 ar >> c.c;
104             };
105         };
106 
107         template <class Archive>
108         struct ArchiveStoreImpl<Archive,C> {
storemadness::archive::ArchiveStoreImpl109             static inline void store(const Archive& ar, const C& c) {
110                 ar << c.c;
111             };
112         };
113     }
114 }
115 
116 // POD can be serialized to most (except text stream) archives without any effort
117 struct D {
118   int32_t i;
119   int64_t l;
120 };
121 
122 // to serialize a POD to a text stream just overload stream redirection operators
123 struct F {
124   int32_t i;
125   int64_t l;
126 };
operator <<(std::ostream & os,const F & data)127 std::ostream& operator<<(std::ostream& os, const F& data) {
128   os << data.i << " " << data.l;
129   return os;
130 }
operator >>(std::istream & os,F & data)131 std::istream& operator>>(std::istream& os, F& data) {
132   os >> data.i >> data.l;
133   return os;
134 }
135 
136 static_assert(!madness::is_ostreammable<A>::value, "A is not ostreammable");
137 static_assert(madness::is_ostreammable<F>::value, "F is ostreammable");
138 static_assert(madness::is_ostreammable<bool>::value, "bool is ostreammable");
139 static_assert(madness::is_ostreammable<int>::value, "int is ostreammable");
140 static_assert(!madness::is_istreammable<A>::value, "A is not istreammable");
141 static_assert(madness::is_istreammable<F>::value, "F is istreammable");
142 static_assert(madness::is_istreammable<bool>::value, "bool is istreammable");
143 static_assert(madness::is_istreammable<int>::value, "int is istreammable");
144 
145 // A better example of a class with asym load/store
146 class linked_list {
147     int value;
148     linked_list *next;
149 public:
linked_list(int value=0)150     linked_list(int value = 0) : value(value), next(0) {};
151 
append(int value)152     void append(int value) {
153         if (next) next->append(value);
154         else next = new linked_list(value);
155     };
156 
set_value(int val)157     void set_value(int val) {
158         value = val;
159     };
160 
get_value() const161     int get_value() const {
162         return value;
163     };
164 
get_next() const165     linked_list* get_next() const {
166         return next;
167     };
168 
print()169     void print() {
170         if (next) {
171             cout << value << " --> ";
172             next->print();
173         }
174         else cout << value << endl;
175     };
176 };
177 
178 namespace madness {
179     namespace archive {
180         template <class Archive>
181         struct ArchiveStoreImpl<Archive,linked_list> {
storemadness::archive::ArchiveStoreImpl182             static void store(const Archive& ar, const linked_list& c) {
183                 ar & c.get_value() & bool(c.get_next());
184                 if (c.get_next()) ar & *c.get_next();
185             };
186         };
187 
188         template <class Archive>
189         struct ArchiveLoadImpl<Archive,linked_list> {
loadmadness::archive::ArchiveLoadImpl190             static void load(const Archive& ar, linked_list& c) {
191                 int value = 0;
192                 bool flag = false;
193                 ar & value & flag;
194                 c.set_value(value);
195                 if (flag) {
196                     c.append(0);
197                     ar & *c.get_next();
198                 }
199             };
200         };
201     }
202 }
203 
204 namespace madness {
205     namespace archive {
206         typedef std::map< short,std::complex<double> > map_short_complex_double;
207         typedef std::pair< short,std::complex<double> > pair_short_complex_double;
208         typedef std::pair<int,double> pair_int_double;
209         ARCHIVE_REGISTER_TYPE_AND_PTR(A,128);
210         ARCHIVE_REGISTER_TYPE_AND_PTR(B,129);
211         ARCHIVE_REGISTER_TYPE_AND_PTR(C,130);
212         ARCHIVE_REGISTER_TYPE_AND_PTR(linked_list,131);
213         ARCHIVE_REGISTER_TYPE_AND_PTR(pair_int_double,132);
214         ARCHIVE_REGISTER_TYPE_AND_PTR(map_short_complex_double,133);
215         ARCHIVE_REGISTER_TYPE_AND_PTR(pair_short_complex_double, 134);
216     }
217 }
218 
219 using namespace std;
220 
221 using madness::archive::wrap;
222 
223 typedef std::complex<double> double_complex;
224 typedef std::tuple<int,double,std::complex<float>> tuple_int_double_complexfloat;
225 
226 template <typename Archive, typename POD, typename Disabler = std::enable_if_t<std::is_same<std::decay_t<Archive>,TextFstreamOutputArchive>::value>>
pod_serialize_dispatch(Archive && ar,const POD & pod)227 void pod_serialize_dispatch(Archive&& ar, const POD& pod) {
228 }
229 template <typename Archive, typename POD>
pod_serialize_dispatch(Archive && ar,const POD & pod,std::enable_if_t<!std::is_same<std::decay_t<Archive>,TextFstreamOutputArchive>::value> * =nullptr)230 void pod_serialize_dispatch(Archive&& ar, const POD& pod, std::enable_if_t<!std::is_same<std::decay_t<Archive>,TextFstreamOutputArchive>::value>* = nullptr) {
231   ar & pod;
232   ar << pod;
233 }
234 
235 template <typename Archive, typename POD, typename Disabler = std::enable_if_t<std::is_same<std::decay_t<Archive>,TextFstreamInputArchive>::value>>
pod_deserialize_dispatch(Archive && ar,POD && pod)236 void pod_deserialize_dispatch(Archive&& ar, POD&& pod) {
237 }
238 template <typename Archive, typename POD>
pod_deserialize_dispatch(Archive && ar,POD && pod,std::enable_if_t<!std::is_same<std::decay_t<Archive>,TextFstreamInputArchive>::value> * =nullptr)239 void pod_deserialize_dispatch(Archive&& ar, POD&& pod, std::enable_if_t<!std::is_same<std::decay_t<Archive>,TextFstreamInputArchive>::value>* = nullptr) {
240   ar & pod;
241   ar >> pod;
242 }
243 
244 template <class OutputArchive>
test_out(const OutputArchive & oar)245 void test_out(const OutputArchive& oar) {
246     const int n = 3;
247     A a, an[n];
248     B b, bn[n];
249     C c, cn[n];
250     D d, dn[n];
251     F f, fn[n];
252     int i, in[n];
253     double *p = new double[n];
254     A *q = new A[n];
255     vector<int> v(n);
256     vector<vector<int>> vv(n);
257     pair<int,double> pp(33,99.0);
258     map<short,double_complex> m;
259     const char* teststr = "hello \n dude !";
260     string str(teststr);
261     linked_list list(0);
262     double pi = atan(1.0)*4.0;
263     double e = exp(1.0);
264     tuple_int_double_complexfloat t = std::make_tuple(1,2.0,std::complex<float>(3.0f,4.0f));
265 
266     // Initialize data
267     a.a = b.b = c.c = i = 1;
268     d.i = 1;  d.l = 2;
269     f.i = 1;  f.l = 2;
270     for (int k=0; k<n; ++k) {
271         p[k] = q[k].a = an[k].a = v[k] = cn[k].c = in[k] = k;
272         dn[k].i = k+1;
273         dn[k].l = k+2;
274         fn[k].i = k+3;
275         fn[k].l = k+4;
276         vv[k] = {k+1, k+2, k+3, k+4};
277         bn[k].b = k&1;
278         m[k] = double_complex(k,k);
279         list.append(k+1);
280     }
281 
282     // test example list code
283     list.print();
284     oar & list;
285 
286     oar & pi & e;
287 
288     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " constant double value" << std::endl);
289     oar & 1.0;
290     oar << 1.0;
291     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " int" << std::endl);
292     oar & i;
293     oar << i;
294     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A" << std::endl);
295     oar & a;
296     oar << a;
297     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " B" << std::endl);
298     oar & b;
299     oar << b;
300     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " C" << std::endl);
301     oar & c;
302     oar << c;
303     // all but text archives should work with POD
304     if (!std::is_same<OutputArchive, TextFstreamOutputArchive>::value) {
305       MAD_ARCHIVE_DEBUG(std::cout << std::endl << " D" << std::endl);
306       pod_serialize_dispatch(oar, d);
307     }
308     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " F" << std::endl);
309     oar & f;
310     oar << f;
311     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " int[]" << std::endl);
312     oar & in;
313     oar << in;
314     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A[]" << std::endl);
315     oar & an;
316     oar << an;
317     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " B[]" << std::endl);
318     oar << bn;
319     oar & bn;
320     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " C[]" << std::endl);
321     oar << cn;
322     oar & cn;
323     if (!std::is_same<OutputArchive, TextFstreamOutputArchive>::value) {
324       MAD_ARCHIVE_DEBUG(std::cout << std::endl << " D[]" << std::endl);
325       pod_serialize_dispatch(oar, dn);
326     }
327     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " F[]" << std::endl);
328     oar << fn;
329     oar & fn;
330     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " double *p wrapped" << std::endl);
331     oar << wrap(p,n);
332     oar & wrap(p,n);
333     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A *q wrapped" << std::endl);
334     oar << wrap(q,n);
335     oar & wrap(q,n);
336     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " vector<int>" << std::endl);
337     oar << v;
338     oar & v;
339     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " vector<vector<int>>" << std::endl);
340     oar << vv;
341     oar & vv;
342     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " pair<int,double>" << std::endl);
343     oar << pp;
344     oar & pp;
345     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " map<short,complex<double>>" << std::endl);
346     oar << m;
347     oar & m;
348     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " tuple<int,double,complex<float>>" << std::endl);
349     oar << t;
350     oar & t;
351     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " string" << std::endl);
352     oar << str;
353     oar & str;
354 
355     oar & 1.0 & i & a & b & c & f & in & an & bn & cn & fn & wrap(p,n) & wrap(q,n) & pp & m & t & str;
356     if (!std::is_same<OutputArchive, TextFstreamOutputArchive>::value) {
357       pod_serialize_dispatch(oar, d);
358       pod_serialize_dispatch(oar, dn);
359     }
360 }
361 
362 template <class InputArchive>
test_in(const InputArchive & iar)363 void test_in(const InputArchive& iar) {
364     const int n = 3;
365     A a, an[n];
366     B b, bn[n];
367     C c, cn[n];
368     D d, dn[n];
369     F f, fn[n];
370     int i, in[n];
371     double *p = new double[n];
372     A *q = new A[n];
373     vector<int> v(n);
374     vector<vector<int>> vv(n);
375     pair<int,double> pp(33,99.0);
376     map<short,double_complex> m;
377     const char* teststr = "hello \n dude !";
378     string str(teststr);
379     linked_list list;
380     double pi = 0.0, e = 0.0;
381     tuple_int_double_complexfloat t;
382 
383     // Destroy in-core data
384     a.a = b.b = c.c = i = 0;
385     d.i = -1;  d.l = -1;
386     f.i = -1;  f.l = -1;
387     for (int k=0; k<n; ++k) {
388         p[k] = q[k].a = an[k].a = v[k] = cn[k].c = in[k] = -1;
389         dn[k].i = -1;
390         dn[k].l = -1;
391         fn[k].i = -1;
392         fn[k].l = -1;
393         vv[k] = {};
394         bn[k].b = (k+1)&1;
395         m[k] = double_complex(0,0);
396     }
397     pp = pair<int,double>(0,0.0);
398     str = string("");
399 
400     iar & list;
401     list.print();
402 
403     iar & pi & e;
404     cout.setf(std::ios::scientific);
405     cout << "error in pi " << (pi - 4.0*atan(1.0)) << endl;
406     cout << "error in e " << (e - exp(1.0)) << endl;
407 
408     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " constant double value" << std::endl);
409     double val;
410     iar & val;
411     iar >> val;
412     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " int" << std::endl);
413     iar & i;
414     iar >> i;
415     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A" << std::endl);
416     iar & a;
417     iar >> a;
418     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " B" << std::endl);
419     iar & b;
420     iar >> b;
421     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " C" << std::endl);
422     iar & c;
423     iar >> c;
424     if (!std::is_same<InputArchive, TextFstreamInputArchive>::value) {
425       MAD_ARCHIVE_DEBUG(std::cout << std::endl << " D" << std::endl);
426       pod_deserialize_dispatch(iar, d);
427     }
428     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " F" << std::endl);
429     iar & f;
430     iar >> f;
431     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " int[]" << std::endl);
432     iar & in;
433     iar >> in;
434     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A[]" << std::endl);
435     iar & an;
436     iar >> an;
437     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " B[]" << std::endl);
438     iar & bn;
439     iar >> bn;
440     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " C[]" << std::endl);
441     iar & cn;
442     iar >> cn;
443     if (!std::is_same<InputArchive, TextFstreamInputArchive>::value) {
444       MAD_ARCHIVE_DEBUG(std::cout << std::endl << " D[]" << std::endl);
445       pod_deserialize_dispatch(iar, dn);
446     }
447     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " F[]" << std::endl);
448     iar & fn;
449     iar >> fn;
450     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " double *p wrapped" << std::endl);
451     iar & wrap(p,n);
452     iar >> wrap(p,n);
453     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " A *q wrapped" << std::endl);
454     iar & wrap(q,n);
455     iar >> wrap(q,n);
456     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " vector<int>" << std::endl);
457     iar & v;
458     iar >> v;
459     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " vector<vector<int>>" << std::endl);
460     iar & vv;
461     iar >> vv;
462     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " pair<int,double>" << std::endl);
463     iar & pp;
464     iar >> pp;
465     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " map<short,complex<double>>" << std::endl);
466     iar & m;
467     iar >> m;
468     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " map<int,double,complex<float>>" << std::endl);
469     iar & t;
470     iar >> t;
471     MAD_ARCHIVE_DEBUG(std::cout << std::endl << " string" << std::endl);
472     iar & str;
473     iar >> str;
474 
475     iar & 1.0 & i & a & b & c & f & in & an & bn & cn & fn & wrap(p,n) & wrap(q,n) & pp & m & t & str;
476     if (!std::is_same<InputArchive, TextFstreamInputArchive>::value) {
477       pod_deserialize_dispatch(iar, d);
478       pod_deserialize_dispatch(iar, dn);
479     }
480     // Test data
481     bool status = true;
482 
483 #define TEST(cond) status &= cond;  \
484                    if (!(cond)) std::cout << #cond << " failed" << std::endl
485     TEST(a.a == 1);
486     TEST(b.b == 1);
487     TEST(c.c == 1);
488     if (!std::is_same<InputArchive, TextFstreamInputArchive>::value) {
489       TEST(d.i == 1);
490       TEST(d.l == 2);
491     }
492     TEST(f.i == 1);
493     TEST(f.l == 2);
494     TEST(i == 1);
495     for (int k=0; k<n; ++k) {
496         TEST(an[k].a == k);
497         TEST(bn[k].b == (k&1));
498         TEST(cn[k].c == k);
499         if (!std::is_same<InputArchive, TextFstreamInputArchive>::value) {
500           TEST(dn[k].i == k+1);
501           TEST(dn[k].l == k+2);
502         }
503         TEST(fn[k].i == k+3);
504         TEST(fn[k].l == k+4);
505         TEST(in[k] == k);
506         TEST(p[k] == k);
507         TEST(q[k].a == k);
508         TEST(v[k] == k);
509         TEST(vv[k].size() == 4);
510         TEST(vv[k][0] == k+1);
511         TEST(vv[k][1] == k+2);
512         TEST(vv[k][2] == k+3);
513         TEST(vv[k][3] == k+4);
514         TEST(m[k] == double_complex(k,k));
515     }
516     TEST(pp.first==33 && pp.second==99.0);
517     TEST(str == string(teststr));
518     TEST(t == std::make_tuple(1,2.0,std::complex<float>(3.0f,4.0f)));
519 
520 #undef TEST
521 
522     if (status)
523         std::cout << "Serialization appears to work." << std::endl;
524     else
525         std::cout << "Sorry, back to the drawing board.\n";
526 }
527 
main()528 int main() {
529     madness::archive::archive_initialize_type_names();
530     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(A);
531     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(B);
532     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(C);
533     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(linked_list);
534     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(madness::archive::pair_int_double);
535     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(madness::archive::pair_short_complex_double);
536     ARCHIVE_REGISTER_TYPE_AND_PTR_NAMES(madness::archive::map_short_complex_double);
537 
538     {
539         const char* f = "test.dat";
540         cout << endl << "testing binary fstream archive" << endl;
541         BinaryFstreamOutputArchive oar(f);
542         test_out(oar);
543         oar.close();
544 
545         BinaryFstreamInputArchive iar(f);
546         test_in(iar);
547         iar.close();
548     }
549 
550     {
551         cout << endl << "testing vector archive" << endl;
552         std::vector<unsigned char> f;
553         VectorOutputArchive oar(f);
554         test_out(oar);
555         oar.close();
556 
557         VectorInputArchive iar(f);
558         test_in(iar);
559         iar.close();
560     }
561 
562     {
563         const char* f = "test.dat";
564         cout << endl << "testing text fstream archive" << endl;
565         TextFstreamOutputArchive oar(f);
566         test_out(oar);
567         oar.close();
568 
569         TextFstreamInputArchive iar(f);
570         test_in(iar);
571         iar.close();
572     }
573 
574     {
575         cout << endl << "testing buffer archive" << endl;
576         unsigned char buf[32768];
577         BufferOutputArchive oar(buf,sizeof(buf));
578         test_out(oar);
579         std::size_t nbyte = oar.size();
580         oar.close();
581 
582         BufferInputArchive iar(buf,nbyte);
583         test_in(iar);
584         iar.close();
585     }
586 
587     return 0;
588 }
589