- prepared for 1.0.13 release
[ginac.git] / ginac / archive.cpp
1 /** @file archive.cpp
2  *
3  *  Archiving of GiNaC expressions. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2003 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 "tostring.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 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 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 archive::num_expressions(void) const
117 {
118         return exprs.size();
119 }
120
121 const archive_node &archive::get_top_node(unsigned 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 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 read_unsigned(std::istream &is)
181 {
182         unsigned char b;
183         unsigned ret = 0;
184         unsigned 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 num_props = n.props.size();
200         write_unsigned(os, num_props);
201         for (unsigned 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 num_atoms = ar.atoms.size();
220         write_unsigned(os, num_atoms);
221         for (unsigned i=0; i<num_atoms; i++)
222                 os << ar.atoms[i] << std::ends;
223
224         // Write expressions
225         unsigned num_exprs = ar.exprs.size();
226         write_unsigned(os, num_exprs);
227         for (unsigned 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 num_nodes = ar.nodes.size();
234         write_unsigned(os, num_nodes);
235         for (unsigned 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 num_props = read_unsigned(is);
245         n.props.resize(num_props);
246         for (unsigned i=0; i<num_props; i++) {
247                 unsigned 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 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 num_atoms = read_unsigned(is);
269         ar.atoms.resize(num_atoms);
270         for (unsigned i=0; i<num_atoms; i++)
271                 getline(is, ar.atoms[i], '\0');
272
273         // Read expressions
274         unsigned num_exprs = read_unsigned(is);
275         ar.exprs.resize(num_exprs);
276         for (unsigned 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 num_nodes = read_unsigned(is);
284         ar.nodes.resize(num_nodes, ar);
285         for (unsigned 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 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, unsigned index) const
382 {
383         archive_atom name_atom = a.atomize(name);
384         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
385         unsigned found_index = 0;
386         while (i != iend) {
387                 if (i->type == PTYPE_BOOL && i->name == name_atom) {
388                         if (found_index == index) {
389                                 ret = i->value;
390                                 return true;
391                         }
392                         found_index++;
393                 }
394                 i++;
395         }
396         return false;
397 }
398
399 bool archive_node::find_unsigned(const std::string &name, unsigned &ret, unsigned index) const
400 {
401         archive_atom name_atom = a.atomize(name);
402         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
403         unsigned found_index = 0;
404         while (i != iend) {
405                 if (i->type == PTYPE_UNSIGNED && i->name == name_atom) {
406                         if (found_index == index) {
407                                 ret = i->value;
408                                 return true;
409                         }
410                         found_index++;
411                 }
412                 i++;
413         }
414         return false;
415 }
416
417 bool archive_node::find_string(const std::string &name, std::string &ret, unsigned index) const
418 {
419         archive_atom name_atom = a.atomize(name);
420         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
421         unsigned found_index = 0;
422         while (i != iend) {
423                 if (i->type == PTYPE_STRING && i->name == name_atom) {
424                         if (found_index == index) {
425                                 ret = a.unatomize(i->value);
426                                 return true;
427                         }
428                         found_index++;
429                 }
430                 i++;
431         }
432         return false;
433 }
434
435 bool archive_node::find_ex(const std::string &name, ex &ret, const lst &sym_lst, unsigned index) const
436 {
437         archive_atom name_atom = a.atomize(name);
438         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
439         unsigned found_index = 0;
440         while (i != iend) {
441                 if (i->type == PTYPE_NODE && i->name == name_atom) {
442                         if (found_index == index) {
443                                 ret = a.get_node(i->value).unarchive(sym_lst);
444                                 return true;
445                         }
446                         found_index++;
447                 }
448                 i++;
449         }
450         return false;
451 }
452
453 const archive_node &archive_node::find_ex_node(const std::string &name, unsigned index) const
454 {
455         archive_atom name_atom = a.atomize(name);
456         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
457         unsigned found_index = 0;
458         while (i != iend) {
459                 if (i->type == PTYPE_NODE && i->name == name_atom) {
460                         if (found_index == index)
461                                 return a.get_node(i->value);
462                         found_index++;
463                 }
464                 i++;
465         }
466         throw (std::runtime_error("property with name '" + name + "' not found in archive node"));
467 }
468
469
470 void archive_node::get_properties(propinfovector &v) const
471 {
472         v.clear();
473         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
474         while (i != iend) {
475                 property_type type = i->type;
476                 std::string name = a.unatomize(i->name);
477
478                 propinfovector::iterator a = v.begin(), aend = v.end();
479                 bool found = false;
480                 while (a != aend) {
481                         if (a->type == type && a->name == name) {
482                                 a->count++;
483                                 found = true;
484                                 break;
485                         }
486                         ++a;
487                 }
488                 if (!found)
489                         v.push_back(property_info(type, name));
490                 i++;
491         }       
492 }
493
494
495 /** Convert archive node to GiNaC expression. */
496 ex archive_node::unarchive(const lst &sym_lst) const
497 {
498         // Already unarchived? Then return cached unarchived expression.
499         if (has_expression)
500                 return e;
501
502         // Find instantiation function for class specified in node
503         std::string class_name;
504         if (!find_string("class", class_name))
505                 throw (std::runtime_error("archive node contains no class name"));
506         unarch_func f = find_unarch_func(class_name);
507
508         // Call instantiation function
509         e = f(*this, sym_lst);
510         has_expression = true;
511         return e;
512 }
513
514
515 /** Assignment operator of property_info. */
516 const archive_node::property_info &archive_node::property_info::operator=(const property_info &other)
517 {
518         if (this != &other) {
519                 type = other.type;
520                 name = other.name;
521                 count = other.count;
522         }
523         return *this;
524 }
525
526 /** Assignment operator of property. */
527 const archive_node::property &archive_node::property::operator=(const property &other)
528 {
529         if (this != &other) {
530                 type = other.type;
531                 name = other.name;
532                 value = other.value;
533         }
534         return *this;
535 }
536
537
538 void archive::clear(void)
539 {
540         atoms.clear();
541         exprs.clear();
542         nodes.clear();
543 }
544
545
546 /** Delete cached unarchived expressions in all archive_nodes (mainly for debugging). */
547 void archive::forget(void)
548 {
549         for_each(nodes.begin(), nodes.end(), std::mem_fun_ref(&archive_node::forget));
550 }
551
552 /** Delete cached unarchived expressions from node (for debugging). */
553 void archive_node::forget(void)
554 {
555         has_expression = false;
556         e = 0;
557 }
558
559
560 /** Print archive to stream in ugly raw format (for debugging). */
561 void archive::printraw(std::ostream &os) const
562 {
563         // Dump atoms
564         os << "Atoms:\n";
565         {
566                 std::vector<std::string>::const_iterator i = atoms.begin(), iend = atoms.end();
567                 archive_atom id = 0;
568                 while (i != iend) {
569                         os << " " << id << " " << *i << std::endl;
570                         i++; id++;
571                 }
572         }
573         os << std::endl;
574
575         // Dump expressions
576         os << "Expressions:\n";
577         {
578                 std::vector<archived_ex>::const_iterator i = exprs.begin(), iend = exprs.end();
579                 unsigned index = 0;
580                 while (i != iend) {
581                         os << " " << index << " \"" << unatomize(i->name) << "\" root node " << i->root << std::endl;
582                         i++; index++;
583                 }
584         }
585         os << std::endl;
586
587         // Dump nodes
588         os << "Nodes:\n";
589         {
590                 std::vector<archive_node>::const_iterator i = nodes.begin(), iend = nodes.end();
591                 archive_node_id id = 0;
592                 while (i != iend) {
593                         os << " " << id << " ";
594                         i->printraw(os);
595                         i++; id++;
596                 }
597         }
598 }
599
600 /** Output archive_node to stream in ugly raw format (for debugging). */
601 void archive_node::printraw(std::ostream &os) const
602 {
603         // Dump cached unarchived expression
604         if (has_expression)
605                 os << "(basic * " << e.bp << " = " << e << ")\n";
606         else
607                 os << "\n";
608
609         // Dump properties
610         std::vector<property>::const_iterator i = props.begin(), iend = props.end();
611         while (i != iend) {
612                 os << "  ";
613                 switch (i->type) {
614                         case PTYPE_BOOL: os << "bool"; break;
615                         case PTYPE_UNSIGNED: os << "unsigned"; break;
616                         case PTYPE_STRING: os << "string"; break;
617                         case PTYPE_NODE: os << "node"; break;
618                         default: os << "<unknown>"; break;
619                 }
620                 os << " \"" << a.unatomize(i->name) << "\" " << i->value << std::endl;
621                 i++;
622         }
623 }
624
625 /** Create a dummy archive.  The intention is to fill archive_node's default
626  *  ctor, which is currently a Cint-requirement. */
627 archive* archive_node::dummy_ar_creator(void)
628 {
629         static archive* some_ar = new archive;
630         return some_ar;
631 }
632
633
634 } // namespace GiNaC