* introduced typedef propinfovector in order to please Cint.
[ginac.git] / ginac / archive.cpp
1 /** @file archive.cpp
2  *
3  *  Archiving of GiNaC expressions. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2001 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <iostream>
24 #include <stdexcept>
25
26 #include "archive.h"
27 #include "registrar.h"
28 #include "ex.h"
29 #include "config.h"
30 #include "utils.h"
31
32 namespace GiNaC {
33
34
35 void archive::archive_ex(const ex &e, const char *name)
36 {
37         // Create root node (which recursively archives the whole expression tree)
38         // and add it to the archive
39         archive_node_id id = add_node(archive_node(*this, e));
40
41         // Add root node ID to list of archived expressions
42         archived_ex ae = archived_ex(atomize(name), id);
43         exprs.push_back(ae);
44 }
45
46
47 /** Add archive_node to archive if the corresponding expression is
48  *  not already archived.
49  *  @return ID of archived node */
50 archive_node_id archive::add_node(const archive_node &n)
51 {
52         // Search for node in nodes vector
53         std::vector<archive_node>::const_iterator i = nodes.begin(), iend = nodes.end();
54         archive_node_id id = 0;
55         while (i != iend) {
56                 if (i->has_same_ex_as(n))
57                         return id;
58                 i++; id++;
59         }
60
61         // Not found, add archive_node to nodes vector
62         nodes.push_back(n);
63         return id;
64 }
65
66
67 /** Retrieve archive_node by ID. */
68 archive_node &archive::get_node(archive_node_id id)
69 {
70         if (id >= nodes.size())
71                 throw (std::range_error("archive::get_node(): archive node ID out of range"));
72
73         return nodes[id];
74 }
75
76
77 ex archive::unarchive_ex(const lst &sym_lst, const char *name) const
78 {
79         // Find root node
80         std::string name_string = name;
81         archive_atom id = atomize(name_string);
82         std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
83         while (i != iend) {
84                 if (i->name == id)
85                         goto found;
86                 i++;
87         }
88         throw (std::runtime_error("expression with name '" + name_string + "' not found in archive"));
89
90 found:
91         // Recursively unarchive all nodes, starting at the root node
92         return nodes[i->root].unarchive(sym_lst);
93 }
94
95 ex archive::unarchive_ex(const lst &sym_lst, unsigned int index) const
96 {
97         if (index >= exprs.size())
98                 throw (std::range_error("index of archived expression out of range"));
99
100         // Recursively unarchive all nodes, starting at the root node
101         return nodes[exprs[index].root].unarchive(sym_lst);
102 }
103
104 ex archive::unarchive_ex(const lst &sym_lst, std::string &name, unsigned int index) const
105 {
106         if (index >= exprs.size())
107                 throw (std::range_error("index of archived expression out of range"));
108
109         // Return expression name
110         name = unatomize(exprs[index].name);
111
112         // Recursively unarchive all nodes, starting at the root node
113         return nodes[exprs[index].root].unarchive(sym_lst);
114 }
115
116 unsigned int archive::num_expressions(void) const
117 {
118         return exprs.size();
119 }
120
121 const archive_node &archive::get_top_node(unsigned int index) const
122 {
123         if (index >= exprs.size())
124                 throw (std::range_error("index of archived expression out of range"));
125
126         return nodes[exprs[index].root];
127 }
128
129
130 /*
131  *  Archive file format
132  *
133  *   - 4 bytes signature 'GARC'
134  *   - unsigned version number
135  *   - unsigned number of atoms
136  *      - atom strings (each zero-terminated)
137  *   - unsigned number of expressions
138  *      - unsigned name atom
139  *      - unsigned root node ID
140  *   - unsigned number of nodes
141  *      - unsigned number of properties
142  *        - unsigned containing type (PTYPE_*) in its lower 3 bits and
143  *          name atom in the upper bits
144  *        - unsigned property value
145  *
146  *  Unsigned quantities are stored in a compressed format:
147  *   - numbers in the range 0x00..0x7f are stored verbatim (1 byte)
148  *   - numbers larger than 0x7f are stored in 7-bit packets (1 byte per
149  *     packet), starting with the LSBs; all bytes except the last one have
150  *     their upper bit set
151  *
152  *  Examples:
153  *   0x00           =   0x00
154  *    ..                 ..
155  *   0x7f           =   0x7f
156  *   0x80 0x01      =   0x80
157  *    ..   ..            ..
158  *   0xff 0x01      =   0xff
159  *   0x80 0x02      =  0x100
160  *    ..   ..            ..
161  *   0xff 0x02      =  0x17f
162  *   0x80 0x03      =  0x180
163  *    ..   ..            ..
164  *   0xff 0x7f      = 0x3fff
165  *   0x80 0x80 0x01 = 0x4000
166  *    ..   ..   ..       ..
167  */
168
169 /** Write unsigned integer quantity to stream. */
170 static void write_unsigned(std::ostream &os, unsigned int val)
171 {
172         while (val >= 0x80) {
173                 os.put((val & 0x7f) | 0x80);
174                 val >>= 7;
175         }
176         os.put(val);
177 }
178
179 /** Read unsigned integer quantity from stream. */
180 static unsigned int read_unsigned(std::istream &is)
181 {
182         unsigned char b;
183         unsigned int ret = 0;
184         unsigned int shift = 0;
185         do {
186         char b2;
187                 is.get(b2);
188         b = b2;
189                 ret |= (b & 0x7f) << shift;
190                 shift += 7;
191         } while (b & 0x80);
192         return ret;
193 }
194
195 /** Write archive_node to binary data stream. */
196 std::ostream &operator<<(std::ostream &os, const archive_node &n)
197 {
198         // Write properties
199         unsigned int num_props = n.props.size();
200         write_unsigned(os, num_props);
201         for (unsigned int i=0; i<num_props; i++) {
202                 write_unsigned(os, n.props[i].type | (n.props[i].name << 3));
203                 write_unsigned(os, n.props[i].value);
204         }
205         return os;
206 }
207
208 /** Write archive to binary data stream. */
209 std::ostream &operator<<(std::ostream &os, const archive &ar)
210 {
211         // Write header
212         os.put('G');    // Signature
213         os.put('A');
214         os.put('R');
215         os.put('C');
216         write_unsigned(os, ARCHIVE_VERSION);
217
218         // Write atoms
219         unsigned int num_atoms = ar.atoms.size();
220         write_unsigned(os, num_atoms);
221         for (unsigned int i=0; i<num_atoms; i++)
222                 os << ar.atoms[i] << std::ends;
223
224         // Write expressions
225         unsigned int num_exprs = ar.exprs.size();
226         write_unsigned(os, num_exprs);
227         for (unsigned int i=0; i<num_exprs; i++) {
228                 write_unsigned(os, ar.exprs[i].name);
229                 write_unsigned(os, ar.exprs[i].root);
230         }
231
232         // Write nodes
233         unsigned int num_nodes = ar.nodes.size();
234         write_unsigned(os, num_nodes);
235         for (unsigned int i=0; i<num_nodes; i++)
236                 os << ar.nodes[i];
237         return os;
238 }
239
240 /** Read archive_node from binary data stream. */
241 std::istream &operator>>(std::istream &is, archive_node &n)
242 {
243         // Read properties
244         unsigned int num_props = read_unsigned(is);
245         n.props.resize(num_props);
246         for (unsigned int i=0; i<num_props; i++) {
247                 unsigned int name_type = read_unsigned(is);
248                 n.props[i].type = (archive_node::property_type)(name_type & 7);
249                 n.props[i].name = name_type >> 3;
250                 n.props[i].value = read_unsigned(is);
251         }
252         return is;
253 }
254
255 /** Read archive from binary data stream. */
256 std::istream &operator>>(std::istream &is, archive &ar)
257 {
258         // Read header
259         char c1, c2, c3, c4;
260         is.get(c1); is.get(c2); is.get(c3); is.get(c4);
261         if (c1 != 'G' || c2 != 'A' || c3 != 'R' || c4 != 'C')
262                 throw (std::runtime_error("not a GiNaC archive (signature not found)"));
263         unsigned int version = read_unsigned(is);
264         if (version > ARCHIVE_VERSION || version < ARCHIVE_VERSION - ARCHIVE_AGE)
265                 throw (std::runtime_error("archive version " + ToString(version) + " cannot be read by this GiNaC library (which supports versions " + ToString(ARCHIVE_VERSION-ARCHIVE_AGE) + " thru " + ToString(ARCHIVE_VERSION)));
266
267         // Read atoms
268         unsigned int num_atoms = read_unsigned(is);
269         ar.atoms.resize(num_atoms);
270         for (unsigned int i=0; i<num_atoms; i++)
271                 getline(is, ar.atoms[i], '\0');
272
273         // Read expressions
274         unsigned int num_exprs = read_unsigned(is);
275         ar.exprs.resize(num_exprs);
276         for (unsigned int i=0; i<num_exprs; i++) {
277                 archive_atom name = read_unsigned(is);
278                 archive_node_id root = read_unsigned(is);
279                 ar.exprs[i] = archive::archived_ex(name, root);
280         }
281
282         // Read nodes
283         unsigned int num_nodes = read_unsigned(is);
284         ar.nodes.resize(num_nodes, ar);
285         for (unsigned int i=0; i<num_nodes; i++)
286                 is >> ar.nodes[i];
287         return is;
288 }
289
290
291 /** Atomize a string (i.e. convert it into an ID number that uniquely
292  *  represents the string). */
293 archive_atom archive::atomize(const std::string &s) const
294 {
295         // Search for string in atoms vector
296         std::vector<std::string>::const_iterator i = atoms.begin(), iend = atoms.end();
297         archive_atom id = 0;
298         while (i != iend) {
299                 if (*i == s)
300                         return id;
301                 i++; id++;
302         }
303
304         // Not found, add to atoms vector
305         atoms.push_back(s);
306         return id;
307 }
308
309 /** Unatomize a string (i.e. convert the ID number back to the string). */
310 const std::string &archive::unatomize(archive_atom id) const
311 {
312         if (id >= atoms.size())
313                 throw (std::range_error("archive::unatomizee(): atom ID out of range"));
314
315         return atoms[id];
316 }
317
318
319 /** Copy constructor of archive_node. */
320 archive_node::archive_node(const archive_node &other)
321   : a(other.a), props(other.props), has_expression(other.has_expression), e(other.e)
322 {
323 }
324
325
326 /** Assignment operator of archive_node. */
327 const archive_node &archive_node::operator=(const archive_node &other)
328 {
329         if (this != &other) {
330                 a = other.a;
331                 props = other.props;
332                 has_expression = other.has_expression;
333                 e = other.e;
334         }
335         return *this;
336 }
337
338
339 /** Recursively construct archive node from expression. */
340 archive_node::archive_node(archive &ar, const ex &expr)
341   : a(ar), has_expression(true), e(expr)
342 {
343         expr.bp->archive(*this);
344 }
345
346
347 /** Check if the archive_node stores the same expression as another
348  *  archive_node.
349  *  @return "true" if expressions are the same */
350 bool archive_node::has_same_ex_as(const archive_node &other) const
351 {
352         if (!has_expression || !other.has_expression)
353                 return false;
354         return e.bp == other.e.bp;
355 }
356
357
358 void archive_node::add_bool(const std::string &name, bool value)
359 {
360         props.push_back(property(a.atomize(name), PTYPE_BOOL, value));
361 }
362
363 void archive_node::add_unsigned(const std::string &name, unsigned int value)
364 {
365         props.push_back(property(a.atomize(name), PTYPE_UNSIGNED, value));
366 }
367
368 void archive_node::add_string(const std::string &name, const std::string &value)
369 {
370         props.push_back(property(a.atomize(name), PTYPE_STRING, a.atomize(value)));
371 }
372
373 void archive_node::add_ex(const std::string &name, const ex &value)
374 {
375         // Recursively create an archive_node and add its ID to the properties of this node
376         archive_node_id id = a.add_node(archive_node(a, value));
377         props.push_back(property(a.atomize(name), PTYPE_NODE, id));
378 }
379
380
381 bool archive_node::find_bool(const std::string &name, bool &ret) const
382 {
383         archive_atom name_atom = a.atomize(name);
384         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
385         while (i != iend) {
386                 if (i->type == PTYPE_BOOL && i->name == name_atom) {
387                         ret = i->value;
388                         return true;
389                 }
390                 i++;
391         }
392         return false;
393 }
394
395 bool archive_node::find_unsigned(const std::string &name, unsigned int &ret) const
396 {
397         archive_atom name_atom = a.atomize(name);
398         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
399         while (i != iend) {
400                 if (i->type == PTYPE_UNSIGNED && i->name == name_atom) {
401                         ret = i->value;
402                         return true;
403                 }
404                 i++;
405         }
406         return false;
407 }
408
409 bool archive_node::find_string(const std::string &name, std::string &ret) const
410 {
411         archive_atom name_atom = a.atomize(name);
412         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
413         while (i != iend) {
414                 if (i->type == PTYPE_STRING && i->name == name_atom) {
415                         ret = a.unatomize(i->value);
416                         return true;
417                 }
418                 i++;
419         }
420         return false;
421 }
422
423 bool archive_node::find_ex(const std::string &name, ex &ret, const lst &sym_lst, unsigned int index) const
424 {
425         archive_atom name_atom = a.atomize(name);
426         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
427         unsigned int found_index = 0;
428         while (i != iend) {
429                 if (i->type == PTYPE_NODE && i->name == name_atom) {
430                         if (found_index == index)
431                                 goto found;
432                         found_index++;
433                 }
434                 i++;
435         }
436         return false;
437
438 found:
439         ret = a.get_node(i->value).unarchive(sym_lst);
440         return true;
441 }
442
443 const archive_node &archive_node::find_ex_node(const std::string &name, unsigned int index) const
444 {
445         archive_atom name_atom = a.atomize(name);
446         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
447         unsigned int found_index = 0;
448         while (i != iend) {
449                 if (i->type == PTYPE_NODE && i->name == name_atom) {
450                         if (found_index == index)
451                                 return a.get_node(i->value);
452                         found_index++;
453                 }
454                 i++;
455         }
456         throw (std::runtime_error("property with name '" + name + "' not found in archive node"));
457 }
458
459
460 void archive_node::get_properties(propinfovector &v) const
461 {
462         v.clear();
463         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
464         while (i != iend) {
465                 property_type type = i->type;
466                 string name = a.unatomize(i->name);
467
468                 propinfovector::iterator a = v.begin(), aend = v.end();
469                 bool found = false;
470                 while (a != aend) {
471                         if (a->type == type && a->name == name) {
472                                 a->count++;
473                                 found = true;
474                                 break;
475                         }
476                         a++;
477                 }
478                 if (!found)
479                         v.push_back(property_info(type, name));
480                 i++;
481         }       
482 }
483
484
485 /** Convert archive node to GiNaC expression. */
486 ex archive_node::unarchive(const lst &sym_lst) const
487 {
488         // Already unarchived? Then return cached unarchived expression.
489         if (has_expression)
490                 return e;
491
492         // Find instantiation function for class specified in node
493         std::string class_name;
494         if (!find_string("class", class_name))
495                 throw (std::runtime_error("archive node contains no class name"));
496         unarch_func f = find_unarch_func(class_name);
497
498         // Call instantiation function
499         e = f(*this, sym_lst);
500         has_expression = true;
501         return e;
502 }
503
504
505 /** Assignment operator of property_info. */
506 const archive_node::property_info &archive_node::property_info::operator=(const property_info &other)
507 {
508         if (this != &other) {
509                 type = other.type;
510                 name = other.name;
511                 count = other.count;
512         }
513         return *this;
514 }
515
516 /** Assignment operator of property. */
517 const archive_node::property &archive_node::property::operator=(const property &other)
518 {
519         if (this != &other) {
520                 type = other.type;
521                 name = other.name;
522                 value = other.value;
523         }
524         return *this;
525 }
526
527
528 void archive::clear(void)
529 {
530         atoms.clear();
531         exprs.clear();
532         nodes.clear();
533 }
534
535
536 /** Delete cached unarchived expressions in all archive_nodes (mainly for debugging). */
537 void archive::forget(void)
538 {
539         std::vector<archive_node>::iterator i = nodes.begin(), iend = nodes.end();
540         while (i != iend) {
541                 i->forget();
542                 i++;
543         }
544 }
545
546 /** Delete cached unarchived expressions from node (for debugging). */
547 void archive_node::forget(void)
548 {
549         has_expression = false;
550         e = 0;
551 }
552
553
554 /** Print archive to stream in ugly raw format (for debugging). */
555 void archive::printraw(std::ostream &os) const
556 {
557         // Dump atoms
558         os << "Atoms:\n";
559         {
560                 std::vector<std::string>::const_iterator i = atoms.begin(), iend = atoms.end();
561                 archive_atom id = 0;
562                 while (i != iend) {
563                         os << " " << id << " " << *i << std::endl;
564                         i++; id++;
565                 }
566         }
567         os << std::endl;
568
569         // Dump expressions
570         os << "Expressions:\n";
571         {
572                 std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
573                 unsigned int index = 0;
574                 while (i != iend) {
575                         os << " " << index << " \"" << unatomize(i->name) << "\" root node " << i->root << std::endl;
576                         i++; index++;
577                 }
578         }
579         os << std::endl;
580
581         // Dump nodes
582         os << "Nodes:\n";
583         {
584                 std::vector<archive_node>::const_iterator i = nodes.begin(), iend = nodes.end();
585                 archive_node_id id = 0;
586                 while (i != iend) {
587                         os << " " << id << " ";
588                         i->printraw(os);
589                         i++; id++;
590                 }
591         }
592 }
593
594 /** Output archive_node to stream in ugly raw format (for debugging). */
595 void archive_node::printraw(std::ostream &os) const
596 {
597         // Dump cached unarchived expression
598         if (has_expression)
599                 os << "(basic * " << e.bp << " = " << e << ")\n";
600         else
601                 os << "\n";
602
603         // Dump properties
604         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
605         while (i != iend) {
606                 os << "  ";
607                 switch (i->type) {
608                         case PTYPE_BOOL: os << "bool"; break;
609                         case PTYPE_UNSIGNED: os << "unsigned"; break;
610                         case PTYPE_STRING: os << "string"; break;
611                         case PTYPE_NODE: os << "node"; break;
612                         default: os << "<unknown>"; break;
613                 }
614                 os << " \"" << a.unatomize(i->name) << "\" " << i->value << std::endl;
615                 i++;
616         }
617 }
618
619 /** Create a dummy archive.  The intention is to fill archive_node's default
620  *  ctor, which is currently a Cint-requirement. */
621 archive* archive_node::dummy_ar_creator(void)
622 {
623         static archive* some_ar = new archive;
624         return some_ar;
625 }
626
627
628 } // namespace GiNaC