/* Cadabra: a field-theory motivated computer algebra system. Copyright (C) 2001-2014 Kasper Peeters This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // Classes handling storage of property information. Actual property // implementations are in the properties directory in separate files. #pragma once #include #include #include #include "Storage.hh" namespace cadabra { class Properties; class Kernel; class Accent; class LaTeXForm; class Ex_comparator; class pattern { public: pattern(); pattern(const Ex&); /// Match a pattern to an expression. If ignore_parent_rel is /// true, this will match regardless of the parent_rel of the /// top node. If ignore_properties is true, property /// information will not be used to match symbols anywhere /// (in which case A_{a} and A_{b} will no longer match even /// when 'a' and 'b' have the same Indices property, for /// example). The latter feature is mostly used to do pattern /// matching when the property for which we need it cannot /// rely on such child node properties, e.g. Accent; see the /// specialisation in get below. bool match(const Properties&, const Ex::iterator&, bool ignore_parent_rel=false, bool ignore_properties=false) const; bool children_wildcard() const; /// As `match`, but using a comparator object which is /// externally provided, so that the caller can use /// the found pattern map. bool match_ext(const Properties&, const Ex::iterator&, Ex_comparator& comp, bool ignore_parent_rel=false, bool ignore_properties=false) const; Ex obj; }; /// Arguments to properties get parsed into a keyval_t structure. class keyval_t { public: typedef std::pair kvpair_t; typedef std::list kvlist_t; typedef kvlist_t::const_iterator const_iterator; typedef kvlist_t::iterator iterator; typedef kvpair_t value_type; const_iterator find(const std::string&) const; iterator find(const std::string&); const_iterator begin() const; const_iterator end() const; void push_back(const kvpair_t&); void erase(iterator); private: kvlist_t keyvals; }; /// \ingroup core /// /// Base class for all properties, handling argument parsing and /// defining the interface. /// /// Properties can have arguments. Parsing of these is done in the /// properties object itself, with the use of some helper functions. /// Parsing is done by implementing the virtual function /// property::parse(const Kernel&, keyval_t&). The argument is a /// container class which represents the arguments passed to the /// property as key/value pairs keyval_t type. /// /// Properties will be asked to check that they can be associated to a /// given pattern through the virtual property::validate(const /// Properties&, const Ex&) function. The // default implementation returns true for any pattern. /// /// FIXME: the above two need to be merged, because parse may need access /// to the actual pattern tree, and once we are there, we may as well /// do checking. /// HOWEVER: in TableauSymmetry.cc: /// FIXME: we get the wrong pattern in case of a list! We should have /// been fed each individual item in the list, not the list itself. /// /// This suggests that we should be calling once for every pattern, but /// that is wasteful in case we are just parsing arguments. Can we really /// avoid calling parse for every pattern? /// /// {A_{m n}, A_{m n p}, A_{m n p q}}::TableauSymmetry(shape={2,1}, indices={0,1,2}). /// /// leads to a problem, because the property needs to setup its internal /// structures but also verify that these can match all objects in the /// same way. /// /// /// Make all identical properties point to the same property object, so /// that normal and list properties become pretty much identical. class property { public: property(bool hidden=false); virtual ~property() {}; // Parse the argument tree into key-value pairs. Returns false on error. bool parse_to_keyvals(const Ex&, keyval_t&); // Use the pre-parsed arguments in key/value form to set parameters. // Parses universal arguments by default. Will be called once for // every property; assigning a non-list property to multiple patterns // still calls this only once. // FIXME: failure to call // into superclass may lead to problems for labelled properties. virtual bool parse(Kernel&, keyval_t& keyvals); // New entry point, which also passes the Ex of the pattern, so that // the property itself can inject other properties automatically (e.g. // declare an InverseMetric if a Metric is declared). virtual bool parse(Kernel&, std::shared_ptr, keyval_t& keyvals); // Check whether the property can be associated with the pattern. // Throw an error if validation fails. Needs access to all other // declared properties so that it can understand what the pattern // means (which objects are indices etc.). virtual void validate(const Kernel&, const Ex&) const; /// Display the property on the stream // virtual void display(std::ostream&) const; /// Generate a LaTeX representation of the property, assuming LaTeX /// is in text mode (so it needs dollar symbols to switch to maths). virtual void latex(std::ostream&) const; virtual std::string name() const=0; virtual std::string unnamed_argument() const; // To compare properties we sometimes need to compare their variables, not only // their type. The following function needs to be overridden in all properties // for which comparison by type is not sufficient to establish equality. // // id_match: only one of these properties can be registered, but their data is not the same // exact_match: these properties are exactly identical enum match_t { no_match, id_match, exact_match }; virtual match_t equals(const property *) const; /// Properties can be hidden because they only make sense to the /// system; they will not be printed when the user asks for a list /// of properties. void hidden(bool h); bool hidden(void) const; private: bool parse_one_argument(Ex::iterator arg, keyval_t& keyvals); bool hidden_; }; class labelled_property : virtual public property { public: virtual bool parse(Kernel&, std::shared_ptr, keyval_t&) override; std::string label; }; /// Something cannot be both a list property and a normal property at /// the same time, so we can safely inherit without virtual. class list_property : public property { public: }; /// If a property X derives from Inherit, and get is called on /// an object which has an X property (but no Y property), then the /// get will look at the non-index child of the object to see if /// that has a Y property. FIXME: need to decided what to do if there /// are more non-index children. template class Inherit : virtual public property { public: virtual ~Inherit() {}; virtual std::string name() const { return std::string("Stay Away"); }; }; /// PropertyInherit is like Inherit for all properties. This is very /// generic and almost never really useful. class PropertyInherit : virtual public property { public: virtual std::string name() const { return std::string("PropertyInherit"); }; }; /// \ingroup core /// /// Class holding a collection of properties attached to expressions. /// Symbols and expressions do not have a default meaning in /// Cadabra. They get their meaning by attaching properties to /// them. When the core manipulator calls an algorithm object, it /// passes an instance of the Properties class along with the /// expression tree on which to act, so that the algorithm can figure /// out what the symbols in the expression tree mean. class Properties { public: // Registering property types. class registered_property_map_t { public: ~registered_property_map_t(); typedef std::map internal_property_map_t; typedef internal_property_map_t::iterator iterator; internal_property_map_t store; }; /// Registering properties. When inserting a property or /// list_property, ownership of the property gets transferred to /// this class. void register_property(property* (*)(), const std::string& name); registered_property_map_t registered_properties; typedef std::pair pat_prop_pair_t; /// We keep two multi-maps: one from the pattern to the property (roughly) and /// one from the property to the pattern. These are both multi-maps because /// one pattern can have multiple properties assigned to it, and one property can /// be assigned to multiple properties. /// /// When we delete properties, we check the pats map to see if the reference count /// for that property has dropped to zero. typedef std::multimap property_map_t; typedef std::multimap pattern_map_t; /// Register a property for the indicated Ex. Takes both normal and list /// properties and works out which insert calls to make. The property ownership /// is transferred to us on using this call. std::string master_insert(Ex proptree, const property *thepropbase); void clear(); /// The following two maps own the pointers to the properties and patterns stored /// in them; use clear() to clean up. Note that pointers can sit in in more than one /// entry in this map (when they are pointing to list_property objects, which are /// shared between patterns). property_map_t props; // pattern -> property pattern_map_t pats; // property -> pattern; for list properties, patterns are stored here in order /// Normal search: given a pattern, get its property if any. template const T* get(Ex::iterator, bool ignore_parent_rel=false) const; template const T* get(Ex::iterator, int& serialnum, bool doserial=true, bool ignore_parent_rel=false) const; /// Ditto for labelled properties template const T* get(Ex::iterator, const std::string& label) const; template const T* get(Ex::iterator, int& serialnum, const std::string& label, bool doserial=true) const; /// For list properties: given two patterns, get a common property. template const T* get(Ex::iterator, Ex::iterator, bool ignore_parent_rel=false) const; template const T* get(Ex::iterator, Ex::iterator, int&, int&, bool ignore_parent_rel=false) const; /// General property finder, which will return not only the property but also /// the pattern which matched the given node. All 'get' functions above call /// this function; all functionality is contained in here. template std::pair get_with_pattern(Ex::iterator, int& serialnum, const std::string& label, bool doserial=true, bool ignore_parent_rel=false) const; template std::pair get_with_pattern_ext(Ex::iterator, Ex_comparator&, int& serialnum, const std::string& label, bool doserial=true, bool ignore_parent_rel=false) const; // Get the outermost node which has the given property attached, i.e. go down through // all (if any) nodes which have just inherited the property. template Ex::iterator head(Ex::iterator, bool ignore_parent_rel=false) const; // Inverse search: given a property type, get a pattern which has this property. // When given an iterator, it starts to search in the property // map from this particular point. Note: this searches on property type, not exact property. // template // property_map_t::iterator get_pattern(property_map_t::iterator=props.begin()); // Equivalent search: given a node, get a pattern of equivalents. // property_map_t::iterator get_equivalent(Ex::iterator, // property_map_t::iterator=props.begin()); private: // Insert a property. Do not use this directly, use the public // interface `master_insert` instead. void insert_prop(const Ex&, const property *); void insert_list_prop(const std::vector&, const list_property *); bool check_label(const property *, const std::string&) const; bool check_label(const labelled_property *, const std::string&) const; // Search through pointers bool has(const property *, Ex::iterator); // Find serial number of a pattern in a given list property int serial_number(const property *, const pattern *) const; Ex_comparator *create_comparator() const; void destroy_comparator(Ex_comparator *) const; }; template const T* Properties::get(Ex::iterator it, bool ignore_parent_rel) const { int tmp; return get(it, tmp, false, ignore_parent_rel); } template const T* Properties::get(Ex::iterator it, int& serialnum, bool doserial, bool ignore_parent_rel) const { auto ret = get_with_pattern(it, serialnum, "", doserial, ignore_parent_rel); return ret.first; } template std::pair Properties::get_with_pattern(Ex::iterator it, int& serialnum, const std::string& label, bool doserial, bool ignore_parent_rel) const { Ex_comparator *compptr = create_comparator(); // FIXME: catch and rethrow all exceptions so we do not leak memory auto ret = get_with_pattern_ext(it, *compptr, serialnum, label, doserial, ignore_parent_rel); destroy_comparator(compptr); return ret; } template std::pair Properties::get_with_pattern_ext(Ex::iterator it, Ex_comparator& comp, int& serialnum, const std::string& label, bool doserial, bool ignore_parent_rel) const { std::pair ret; ret.first=0; ret.second=0; bool inherits=false; //std::cerr << *it->name_only() << std::endl; // std::cerr << props.size() << std::endl; std::pair pit=props.equal_range(it->name_only()); // First look for properties of the node itself. Go through the loop twice: // once looking for patterns which do not have wildcards, and then looking // for wildcard patterns. bool wildcards=false; // For some properties, we cannot lookup properties lower down the // tree, because it would lead to an endless recursion (and it would // not make sense anyway). At the moment, this is only for Accent. bool ignore_properties=false; if(std::is_same::value) ignore_properties=true; for(;;) { property_map_t::const_iterator walk=pit.first; while(walk!=pit.second) { if(wildcards==(*walk).second.first->children_wildcard()) { // First check property type; a dynamic cast is much faster than a pattern match. ret.first=dynamic_cast((*walk).second.second); if(ret.first) { if((*walk).second.first->match_ext(*this, it, comp, ignore_parent_rel, ignore_properties)) { ret.second=(*walk).second.first; if(!check_label(ret.first, label)) ret.first=0; else { if(doserial) serialnum=serial_number( (*walk).second.second, (*walk).second.first ); break; } } } ret.first=0; if(dynamic_cast((*walk).second.second)) inherits=true; else if(dynamic_cast *>((*walk).second.second)) inherits=true; } ++walk; } if(!wildcards && !ret.first) { // std::cerr << "not yet found, switching to wildcards" << std::endl; wildcards=true; } else break; } // Do not walk down the tree if the property cannot be passed up the tree. // FIXME: see issue/259. if(std::is_same::value) inherits=false; // If no property was found, figure out whether a property is inherited from a child node. if(!ret.first && inherits) { // std::cout << "no match but perhaps inheritance?" << std::endl; Ex::sibling_iterator sib=it.begin(); while(sib!=it.end()) { std::pair tmp=get_with_pattern((Ex::iterator)(sib), serialnum, label, doserial); if(tmp.first) { ret=tmp; break; } ++sib; } } // std::cout << ret << std::endl; return ret; } template const T* Properties::get(Ex::iterator it, const std::string& label) const { int tmp; return get(it, tmp, label, false); } template const T* Properties::get(Ex::iterator it, int& serialnum, const std::string& label, bool doserial) const { auto ret=get_with_pattern(it, serialnum, label, doserial, false); return ret.first; } template const T* Properties::get(Ex::iterator it1, Ex::iterator it2, bool ignore_parent_rel) const { int tmp1, tmp2; return get(it1,it2,tmp1,tmp2, ignore_parent_rel); } template const T* Properties::get(Ex::iterator it1, Ex::iterator it2, int& serialnum1, int& serialnum2, bool ignore_parent_rel) const { const T* ret1=0; const T* ret2=0; bool found=false; bool inherits1=false, inherits2=false; std::pair pit1=props.equal_range(it1->name_only()); std::pair pit2=props.equal_range(it2->name_only()); property_map_t::const_iterator walk1=pit1.first; while(walk1!=pit1.second) { if((*walk1).second.first->match(*this, it1, ignore_parent_rel)) { // match for object 1 found ret1=dynamic_cast((*walk1).second.second); if(ret1) { // property of the right type found for object 1 property_map_t::const_iterator walk2=pit2.first; while(walk2!=pit2.second) { if((*walk2).second.first->match(*this, it2, ignore_parent_rel)) { // match for object 2 found ret2=dynamic_cast((*walk2).second.second); if(ret2) { // property of the right type found for object 2 if(ret1==ret2 && walk1!=walk2) { // accept if properties are the same and patterns are not serialnum1=serial_number( (*walk1).second.second, (*walk1).second.first ); serialnum2=serial_number( (*walk2).second.second, (*walk2).second.first ); found=true; goto done; } } } if(dynamic_cast((*walk2).second.second)) inherits2=true; ++walk2; } } if(dynamic_cast((*walk1).second.second)) inherits1=true; } ++walk1; } // If no property was found, figure out whether a property is inherited from a child node. if(!found && (inherits1 || inherits2)) { Ex::sibling_iterator sib1, sib2; if(inherits1) sib1=it1.begin(); else sib1=it1; bool keepgoing1=true; do { // 1 bool keepgoing2=true; if(inherits2) sib2=it2.begin(); else sib2=it2; do { // 2 const T* tmp=get((Ex::iterator)(sib1), (Ex::iterator)(sib2), serialnum1, serialnum2, ignore_parent_rel); if(tmp) { ret1=tmp; found=true; goto done; } if(!inherits2 || ++sib2==it2.end()) keepgoing2=false; } while(keepgoing2); if(!inherits1 || ++sib1==it1.end()) keepgoing1=false; } while(keepgoing1); } done: if(!found) ret1=0; return ret1; } template Ex::iterator Properties::head(Ex::iterator it, bool ignore_parent_rel) const { Ex::iterator dn=it; for(;;) { if(get(dn, ignore_parent_rel)) { dn=dn.begin(); } else { assert(get(dn)); break; } } return dn; } }