]> www.ginac.de Git - ginac.git/blob - ginac/container.h
[BUGFIX] Fix crash in parser.
[ginac.git] / ginac / container.h
1 /** @file container.h
2  *
3  *  Wrapper template for making GiNaC classes out of STL containers. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2023 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #ifndef GINAC_CONTAINER_H
24 #define GINAC_CONTAINER_H
25
26 #include "ex.h"
27 #include "print.h"
28 #include "archive.h"
29 #include "assertion.h"
30 #include "compiler.h"
31
32 #include <algorithm>
33 #include <iterator>
34 #include <list>
35 #include <memory>
36 #include <stdexcept>
37 #include <vector>
38
39 namespace GiNaC {
40
41 /** Helper template for encapsulating the reserve() mechanics of STL containers. */
42 template <template <class T, class = std::allocator<T>> class C>
43 class container_storage {
44 protected:
45         typedef C<ex> STLT;
46
47         container_storage() {}
48         container_storage(size_t n, const ex & e) : seq(n, e) {}
49         container_storage(std::initializer_list<ex> il) : seq(il) {}
50
51         template <class In>
52         container_storage(In b, In e) : seq(b, e) {}
53
54         void reserve(size_t) {}
55         static void reserve(STLT &, size_t) {}
56
57         STLT seq;
58
59         // disallow destruction of container through a container_storage*
60 protected:
61         ~container_storage() {}
62 };
63
64 template <>
65 inline void container_storage<std::vector>::reserve(size_t n) { seq.reserve(n); }
66
67 template <>
68 inline void container_storage<std::vector>::reserve(std::vector<ex> & v, size_t n) { v.reserve(n); }
69
70
71 /** Wrapper template for making GiNaC classes out of STL containers. */
72 template <template <class T, class = std::allocator<T>> class C>
73 class container : public basic, public container_storage<C> {
74         GINAC_DECLARE_REGISTERED_CLASS(container, basic)
75 protected:
76         typedef typename container_storage<C>::STLT STLT;
77
78 public:
79         typedef typename STLT::const_iterator const_iterator;
80         typedef typename STLT::const_reverse_iterator const_reverse_iterator;
81
82 protected:
83         // helpers
84         static unsigned get_default_flags() { return 0; }
85         static char get_open_delim() { return '('; }
86         static char get_close_delim() { return ')'; }
87
88         // constructors
89 public:
90         container(STLT const & s)
91         {
92                 setflag(get_default_flags());
93                 this->seq = s;
94         }
95
96         explicit container(STLT && v)
97         {
98                 setflag(get_default_flags());
99                 this->seq.swap(v);
100         }
101
102         container(exvector::const_iterator b, exvector::const_iterator e)
103          : container_storage<C>(b, e)
104         {
105                 setflag(get_default_flags());
106         }
107
108         container(std::initializer_list<ex> il)
109          : container_storage<C>(il)
110         {
111                 setflag(get_default_flags());
112         }
113
114         // functions overriding virtual functions from base classes
115 public:
116         bool info(unsigned inf) const override { return inherited::info(inf); }
117         unsigned precedence() const override { return 10; }
118         size_t nops() const override { return this->seq.size(); }
119         ex op(size_t i) const override;
120         ex & let_op(size_t i) override;
121         ex subs(const exmap & m, unsigned options = 0) const override;
122
123         void read_archive(const archive_node &n, lst &sym_lst) override
124         {
125                 inherited::read_archive(n, sym_lst);
126                 setflag(get_default_flags());
127
128                 auto range =  n.find_property_range("seq", "seq");
129                 this->reserve(this->seq, range.end - range.begin);
130                 for (archive_node::archive_node_cit i=range.begin; i<range.end; ++i) {
131                         ex e;
132                         n.find_ex_by_loc(i, e, sym_lst);
133                         this->seq.emplace_back(e);
134                 }
135         }
136
137         /** Archive the object. */
138         void archive(archive_node &n) const override
139         {
140                 inherited::archive(n);
141                 for (auto & i : this->seq) {
142                         n.add_ex("seq", i);
143                 }
144         }
145
146 protected:
147         ex conjugate() const override
148         {
149                 STLT *newcont = nullptr;
150                 for (const_iterator i=this->seq.begin(); i!=this->seq.end(); ++i) {
151                         if (newcont) {
152                                 newcont->push_back(i->conjugate());
153                                 continue;
154                         }
155                         ex x = i->conjugate();
156                         if (are_ex_trivially_equal(x, *i)) {
157                                 continue;
158                         }
159                         newcont = new STLT;
160                         this->reserve(*newcont, this->seq.size());
161                         for (const_iterator j=this->seq.begin(); j!=i; ++j) {
162                                 newcont->push_back(*j);
163                         }
164                         newcont->push_back(x);
165                 }
166                 if (newcont) {
167                         ex result = thiscontainer(*newcont);
168                         delete newcont;
169                         return result;
170                 }
171                 return *this;
172         }
173
174         ex real_part() const override
175         {
176                 STLT cont;
177                 this->reserve(cont, nops());
178                 const_iterator b = begin();
179                 const_iterator e = end();
180                 for(const_iterator i=b; i!=e; ++i)
181                         cont.push_back(i->real_part());
182                 return thiscontainer(cont);
183         }
184
185         ex imag_part() const override
186         {
187                 STLT cont;
188                 this->reserve(cont, nops());
189                 const_iterator b = begin();
190                 const_iterator e = end();
191                 for(const_iterator i=b; i!=e; ++i)
192                         cont.push_back(i->imag_part());
193                 return thiscontainer(cont);
194         }
195
196         bool is_equal_same_type(const basic & other) const override;
197
198         // new virtual functions which can be overridden by derived classes
199 protected:
200         /** Similar to duplicate(), but with a preset sequence. Must be
201          *  overridden by derived classes. */
202         virtual ex thiscontainer(const STLT & v) const { return container(v); }
203
204         /** Similar to duplicate(), but with a preset sequence (which gets
205          *  pilfered). Must be overridden by derived classes. */
206         virtual ex thiscontainer(STLT && v) const { return container(std::move(v)); }
207
208         virtual void printseq(const print_context & c, char openbracket, char delim,
209                               char closebracket, unsigned this_precedence,
210                               unsigned upper_precedence = 0) const;
211
212         // non-virtual functions in this class
213 private:
214         void sort_(std::random_access_iterator_tag)
215         {
216                 std::sort(this->seq.begin(), this->seq.end(), ex_is_less());
217         }
218
219         void sort_(std::input_iterator_tag)
220         {
221                 this->seq.sort(ex_is_less());
222         }
223
224         void unique_()
225         {
226                 typename STLT::iterator p = std::unique(this->seq.begin(), this->seq.end(), ex_is_equal());
227                 this->seq.erase(p, this->seq.end());
228         }
229
230 public:
231         container & prepend(const ex & b);
232         container & append(const ex & b);
233         container & remove_first();
234         container & remove_last();
235         container & remove_all();
236         container & sort();
237         container & unique();
238
239         const_iterator begin() const {return this->seq.begin();}
240         const_iterator end() const {return this->seq.end();}
241         const_reverse_iterator rbegin() const {return this->seq.rbegin();}
242         const_reverse_iterator rend() const {return this->seq.rend();}
243
244 protected:
245         void do_print(const print_context & c, unsigned level) const;
246         void do_print_tree(const print_tree & c, unsigned level) const;
247         void do_print_python(const print_python & c, unsigned level) const;
248         void do_print_python_repr(const print_python_repr & c, unsigned level) const;
249         STLT subschildren(const exmap & m, unsigned options = 0) const;
250 };
251
252 /** Default constructor */
253 template <template <class T, class = std::allocator<T>> class C>
254 container<C>::container()
255 {
256         setflag(get_default_flags());
257 }
258
259 template <template <class T, class = std::allocator<T>> class C>
260 void container<C>::do_print(const print_context & c, unsigned level) const
261 {
262         // always print brackets around seq, ignore upper_precedence
263         printseq(c, get_open_delim(), ',', get_close_delim(), precedence(), precedence()+1);
264 }
265
266 template <template <class T, class = std::allocator<T>> class C>
267 void container<C>::do_print_tree(const print_tree & c, unsigned level) const
268 {
269         c.s << std::string(level, ' ') << class_name() << " @" << this
270             << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
271             << ", nops=" << nops()
272             << std::endl;
273         const_iterator i = this->seq.begin(), end = this->seq.end();
274         while (i != end) {
275                 i->print(c, level + c.delta_indent);
276                 ++i;
277         }
278         c.s << std::string(level + c.delta_indent,' ') << "=====" << std::endl;
279 }
280
281 template <template <class T, class = std::allocator<T>> class C>
282 void container<C>::do_print_python(const print_python & c, unsigned level) const
283 {
284         printseq(c, '[', ',', ']', precedence(), precedence()+1);
285 }
286
287 template <template <class T, class = std::allocator<T>> class C>
288 void container<C>::do_print_python_repr(const print_python_repr & c, unsigned level) const
289 {
290         c.s << class_name();
291         printseq(c, '(', ',', ')', precedence(), precedence()+1);
292 }
293
294 template <template <class T, class = std::allocator<T>> class C>
295 ex container<C>::op(size_t i) const
296 {
297         GINAC_ASSERT(i < nops());
298
299         const_iterator it = this->seq.begin();
300         advance(it, i);
301         return *it;
302 }
303
304 template <template <class T, class = std::allocator<T>> class C>
305 ex & container<C>::let_op(size_t i)
306 {
307         GINAC_ASSERT(i < nops());
308
309         ensure_if_modifiable();
310         typename STLT::iterator it = this->seq.begin();
311         advance(it, i);
312         return *it;
313 }
314
315 template <template <class T, class = std::allocator<T>> class C>
316 ex container<C>::subs(const exmap & m, unsigned options) const
317 {
318         // After having subs'ed all children, this method subs'es one final
319         // level, but only if the intermediate result is a container! This is
320         // because if the intermediate result has eval'ed to a non-container a
321         // last level substitution would be wrong, as this example involving a
322         // function f and its inverse f^-1 shows:
323         // f(x).subs(x==f^-1(x))
324         //   -> f(f^-1(x))  [subschildren]
325         //   -> x           [eval]   /* must not subs(x==f^-1(x))! */
326         STLT subsed = subschildren(m, options);
327         if (!subsed.empty()) {
328                 ex result(thiscontainer(subsed));
329                 if (is_a<container<C>>(result))
330                         return ex_to<basic>(result).subs_one_level(m, options);
331                 else
332                         return result;
333         } else {
334                 if (is_a<container<C>>(*this))
335                         return subs_one_level(m, options);
336                 else
337                         return *this;
338         }
339 }
340
341 /** Compare two containers of the same type. */
342 template <template <class T, class = std::allocator<T>> class C>
343 int container<C>::compare_same_type(const basic & other) const
344 {
345         GINAC_ASSERT(is_a<container>(other));
346         const container & o = static_cast<const container &>(other);
347
348         const_iterator it1 = this->seq.begin(), it1end = this->seq.end(),
349                        it2 = o.seq.begin(), it2end = o.seq.end();
350
351         while (it1 != it1end && it2 != it2end) {
352                 int cmpval = it1->compare(*it2);
353                 if (cmpval)
354                         return cmpval;
355                 ++it1; ++it2;
356         }
357
358         return (it1 == it1end) ? (it2 == it2end ? 0 : -1) : 1;
359 }
360
361 template <template <class T, class = std::allocator<T>> class C>
362 bool container<C>::is_equal_same_type(const basic & other) const
363 {
364         GINAC_ASSERT(is_a<container>(other));
365         const container & o = static_cast<const container &>(other);
366
367         if (this->seq.size() != o.seq.size())
368                 return false;
369
370         const_iterator it1 = this->seq.begin(), it1end = this->seq.end(), it2 = o.seq.begin();
371         while (it1 != it1end) {
372                 if (!it1->is_equal(*it2))
373                         return false;
374                 ++it1; ++it2;
375         }
376
377         return true;
378 }
379
380 /** Add element at front. */
381 template <template <class T, class = std::allocator<T>> class C>
382 container<C> & container<C>::prepend(const ex & b)
383 {
384         ensure_if_modifiable();
385         this->seq.push_front(b);
386         return *this;
387 }
388
389 /** Add element at back. */
390 template <template <class T, class = std::allocator<T>> class C>
391 container<C> & container<C>::append(const ex & b)
392 {
393         ensure_if_modifiable();
394         this->seq.push_back(b);
395         return *this;
396 }
397
398 /** Remove first element. */
399 template <template <class T, class = std::allocator<T>> class C>
400 container<C> & container<C>::remove_first()
401 {
402         ensure_if_modifiable();
403         this->seq.pop_front();
404         return *this;
405 }
406
407 /** Remove last element. */
408 template <template <class T, class = std::allocator<T>> class C>
409 container<C> & container<C>::remove_last()
410 {
411         ensure_if_modifiable();
412         this->seq.pop_back();
413         return *this;
414 }
415
416 /** Remove all elements. */
417 template <template <class T, class = std::allocator<T>> class C>
418 container<C> & container<C>::remove_all()
419 {
420         ensure_if_modifiable();
421         this->seq.clear();
422         return *this;
423 }
424
425 /** Sort elements. */
426 template <template <class T, class = std::allocator<T>> class C>
427 container<C> & container<C>::sort()
428 {
429         ensure_if_modifiable();
430         sort_(typename std::iterator_traits<typename STLT::iterator>::iterator_category());
431         return *this;
432 }
433
434 /** Specialization of container::unique_() for std::list. */
435 template<> inline void container<std::list>::unique_()
436 {
437         this->seq.unique(ex_is_equal());
438 }
439
440 /** Remove adjacent duplicate elements. */
441 template <template <class T, class = std::allocator<T>> class C>
442 container<C> & container<C>::unique()
443 {
444         ensure_if_modifiable();
445         unique_();
446         return *this;
447 }
448
449 /** Print sequence of contained elements. */
450 template <template <class T, class = std::allocator<T>> class C>
451 void container<C>::printseq(const print_context & c, char openbracket, char delim,
452                             char closebracket, unsigned this_precedence,
453                             unsigned upper_precedence) const
454 {
455         if (this_precedence <= upper_precedence)
456                 c.s << openbracket;
457
458         if (!this->seq.empty()) {
459                 const_iterator it = this->seq.begin(), itend = this->seq.end();
460                 --itend;
461                 while (it != itend) {
462                         it->print(c, this_precedence);
463                         c.s << delim;
464                         ++it;
465                 }
466                 it->print(c, this_precedence);
467         }
468
469         if (this_precedence <= upper_precedence)
470                 c.s << closebracket;
471 }
472
473 template <template <class T, class = std::allocator<T>> class C>
474 typename container<C>::STLT container<C>::subschildren(const exmap & m, unsigned options) const
475 {
476         // returns an empty container if nothing had to be substituted
477         // returns a STLT with substituted elements otherwise
478
479         const_iterator cit = this->seq.begin(), end = this->seq.end();
480         while (cit != end) {
481                 const ex & subsed_ex = cit->subs(m, options);
482                 if (!are_ex_trivially_equal(*cit, subsed_ex)) {
483
484                         // copy first part of seq which hasn't changed
485                         STLT s(this->seq.begin(), cit);
486                         this->reserve(s, this->seq.size());
487
488                         // insert changed element
489                         s.push_back(subsed_ex);
490                         ++cit;
491
492                         // copy rest
493                         while (cit != end) {
494                                 s.push_back(cit->subs(m, options));
495                                 ++cit;
496                         }
497
498                         return s;
499                 }
500
501                 ++cit;
502         }
503         
504         return STLT(); // nothing has changed
505 }
506
507 } // namespace GiNaC
508
509 #endif // ndef GINAC_CONTAINER_H