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