3 * Implementation of GiNaC's ABC. */
6 * GiNaC Copyright (C) 1999-2006 Johannes Gutenberg University Mainz, Germany
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.
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.
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
25 #ifdef DO_GINAC_ASSERT
36 #include "relational.h"
37 #include "operators.h"
45 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(basic, void,
46 print_func<print_context>(&basic::do_print).
47 print_func<print_tree>(&basic::do_print_tree).
48 print_func<print_python_repr>(&basic::do_print_python_repr))
51 // default constructor, destructor, copy constructor and assignment operator
56 /** basic copy constructor: implicitly assumes that the other class is of
57 * the exact same type (as it's used by duplicate()), so it can copy the
58 * tinfo_key and the hash value. */
59 basic::basic(const basic & other) : tinfo_key(other.tinfo_key), flags(other.flags & ~status_flags::dynallocated), hashvalue(other.hashvalue)
63 /** basic assignment operator: the other object might be of a derived class. */
64 const basic & basic::operator=(const basic & other)
66 unsigned fl = other.flags & ~status_flags::dynallocated;
67 if (tinfo_key != other.tinfo_key) {
68 // The other object is of a derived class, so clear the flags as they
69 // might no longer apply (especially hash_calculated). Oh, and don't
70 // copy the tinfo_key: it is already set correctly for this object.
71 fl &= ~(status_flags::evaluated | status_flags::expanded | status_flags::hash_calculated);
73 // The objects are of the exact same class, so copy the hash value.
74 hashvalue = other.hashvalue;
95 /** Construct object from archive_node. */
96 basic::basic(const archive_node &n, lst &sym_lst) : flags(0)
98 // Reconstruct tinfo_key from class name
99 std::string class_name;
100 if (n.find_string("class", class_name))
101 tinfo_key = find_tinfo_key(class_name);
103 throw (std::runtime_error("archive node contains no class name"));
106 /** Unarchive the object. */
107 DEFAULT_UNARCHIVE(basic)
109 /** Archive the object. */
110 void basic::archive(archive_node &n) const
112 n.add_string("class", class_name());
116 // new virtual functions which can be overridden by derived classes
121 /** Output to stream. This performs double dispatch on the dynamic type of
122 * *this and the dynamic type of the supplied print context.
123 * @param c print context object that describes the output formatting
124 * @param level value that is used to identify the precedence or indentation
125 * level for placing parentheses and formatting */
126 void basic::print(const print_context & c, unsigned level) const
128 print_dispatch(get_class_info(), c, level);
131 /** Like print(), but dispatch to the specified class. Can be used by
132 * implementations of print methods to dispatch to the method of the
135 * @see basic::print */
136 void basic::print_dispatch(const registered_class_info & ri, const print_context & c, unsigned level) const
138 // Double dispatch on object type and print_context type
139 const registered_class_info * reg_info = &ri;
140 const print_context_class_info * pc_info = &c.get_class_info();
143 const std::vector<print_functor> & pdt = reg_info->options.get_print_dispatch_table();
146 unsigned id = pc_info->options.get_id();
147 if (id >= pdt.size() || !(pdt[id].is_valid())) {
149 // Method not found, try parent print_context class
150 const print_context_class_info * parent_pc_info = pc_info->get_parent();
151 if (parent_pc_info) {
152 pc_info = parent_pc_info;
156 // Method still not found, try parent class
157 const registered_class_info * parent_reg_info = reg_info->get_parent();
158 if (parent_reg_info) {
159 reg_info = parent_reg_info;
160 pc_info = &c.get_class_info();
164 // Method still not found. This shouldn't happen because basic (the
165 // base class of the algebraic hierarchy) registers a method for
166 // print_context (the base class of the print context hierarchy),
167 // so if we end up here, there's something wrong with the class
169 throw (std::runtime_error(std::string("basic::print(): method for ") + class_name() + "/" + c.class_name() + " not found"));
174 pdt[id](*this, c, level);
178 /** Default output to stream. */
179 void basic::do_print(const print_context & c, unsigned level) const
181 c.s << "[" << class_name() << " object]";
184 /** Tree output to stream. */
185 void basic::do_print_tree(const print_tree & c, unsigned level) const
187 c.s << std::string(level, ' ') << class_name() << " @" << this
188 << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec;
190 c.s << ", nops=" << nops();
192 for (size_t i=0; i<nops(); ++i)
193 op(i).print(c, level + c.delta_indent);
196 /** Python parsable output to stream. */
197 void basic::do_print_python_repr(const print_python_repr & c, unsigned level) const
199 c.s << class_name() << "()";
202 /** Little wrapper around print to be called within a debugger.
203 * This is needed because you cannot call foo.print(cout) from within the
204 * debugger because it might not know what cout is. This method can be
205 * invoked with no argument and it will simply print to stdout.
208 * @see basic::dbgprinttree */
209 void basic::dbgprint() const
211 this->print(print_dflt(std::cerr));
212 std::cerr << std::endl;
215 /** Little wrapper around printtree to be called within a debugger.
217 * @see basic::dbgprint */
218 void basic::dbgprinttree() const
220 this->print(print_tree(std::cerr));
223 /** Return relative operator precedence (for parenthezing output). */
224 unsigned basic::precedence() const
229 /** Information about the object.
231 * @see class info_flags */
232 bool basic::info(unsigned inf) const
234 // all possible properties are false for basic objects
238 /** Number of operands/members. */
239 size_t basic::nops() const
241 // iterating from 0 to nops() on atomic objects should be an empty loop,
242 // and accessing their elements is a range error. Container objects should
247 /** Return operand/member at position i. */
248 ex basic::op(size_t i) const
250 throw(std::range_error(std::string("basic::op(): ") + class_name() + std::string(" has no operands")));
253 /** Return modifyable operand/member at position i. */
254 ex & basic::let_op(size_t i)
256 ensure_if_modifiable();
257 throw(std::range_error(std::string("basic::let_op(): ") + class_name() + std::string(" has no operands")));
260 ex basic::operator[](const ex & index) const
262 if (is_exactly_a<numeric>(index))
263 return op(static_cast<size_t>(ex_to<numeric>(index).to_int()));
265 throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
268 ex basic::operator[](size_t i) const
273 ex & basic::operator[](const ex & index)
275 if (is_exactly_a<numeric>(index))
276 return let_op(ex_to<numeric>(index).to_int());
278 throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
281 ex & basic::operator[](size_t i)
286 /** Test for occurrence of a pattern. An object 'has' a pattern if it matches
287 * the pattern itself or one of the children 'has' it. As a consequence
288 * (according to the definition of children) given e=x+y+z, e.has(x) is true
289 * but e.has(x+y) is false. */
290 bool basic::has(const ex & pattern, unsigned options) const
293 if (match(pattern, repl_lst))
295 for (size_t i=0; i<nops(); i++)
296 if (op(i).has(pattern, options))
302 /** Construct new expression by applying the specified function to all
303 * sub-expressions (one level only, not recursively). */
304 ex basic::map(map_function & f) const
311 for (size_t i=0; i<num; i++) {
312 const ex & o = op(i);
314 if (!are_ex_trivially_equal(o, n)) {
322 copy->setflag(status_flags::dynallocated);
323 copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
329 /** Check whether this is a polynomial in the given variables. */
330 bool basic::is_polynomial(const ex & var) const
332 return !has(var) || is_equal(ex_to<basic>(var));
335 /** Return degree of highest power in object s. */
336 int basic::degree(const ex & s) const
338 return is_equal(ex_to<basic>(s)) ? 1 : 0;
341 /** Return degree of lowest power in object s. */
342 int basic::ldegree(const ex & s) const
344 return is_equal(ex_to<basic>(s)) ? 1 : 0;
347 /** Return coefficient of degree n in object s. */
348 ex basic::coeff(const ex & s, int n) const
350 if (is_equal(ex_to<basic>(s)))
351 return n==1 ? _ex1 : _ex0;
353 return n==0 ? *this : _ex0;
356 /** Sort expanded expression in terms of powers of some object(s).
357 * @param s object(s) to sort in
358 * @param distributed recursive or distributed form (only used when s is a list) */
359 ex basic::collect(const ex & s, bool distributed) const
364 // List of objects specified
368 return collect(s.op(0));
370 else if (distributed) {
372 // Get lower/upper degree of all symbols in list
373 size_t num = s.nops();
377 int cnt; // current degree, 'counter'
378 ex coeff; // coefficient for degree 'cnt'
380 sym_info *si = new sym_info[num];
382 for (size_t i=0; i<num; i++) {
384 si[i].ldeg = si[i].cnt = this->ldegree(si[i].sym);
385 si[i].deg = this->degree(si[i].sym);
386 c = si[i].coeff = c.coeff(si[i].sym, si[i].cnt);
391 // Calculate coeff*x1^c1*...*xn^cn
393 for (size_t i=0; i<num; i++) {
395 y *= power(si[i].sym, cnt);
397 x += y * si[num - 1].coeff;
399 // Increment counters
403 if (si[n].cnt <= si[n].deg) {
404 // Update coefficients
410 for (size_t i=n; i<num; i++)
411 c = si[i].coeff = c.coeff(si[i].sym, si[i].cnt);
416 si[n].cnt = si[n].ldeg;
427 size_t n = s.nops() - 1;
438 // Only one object specified
439 for (int n=this->ldegree(s); n<=this->degree(s); ++n)
440 x += this->coeff(s,n)*power(s,n);
443 // correct for lost fractional arguments and return
444 return x + (*this - x).expand();
447 /** Perform automatic non-interruptive term rewriting rules. */
448 ex basic::eval(int level) const
450 // There is nothing to do for basic objects:
454 /** Function object to be applied by basic::evalf(). */
455 struct evalf_map_function : public map_function {
457 evalf_map_function(int l) : level(l) {}
458 ex operator()(const ex & e) { return evalf(e, level); }
461 /** Evaluate object numerically. */
462 ex basic::evalf(int level) const
469 else if (level == -max_recursion_level)
470 throw(std::runtime_error("max recursion level reached"));
472 evalf_map_function map_evalf(level - 1);
473 return map(map_evalf);
478 /** Function object to be applied by basic::evalm(). */
479 struct evalm_map_function : public map_function {
480 ex operator()(const ex & e) { return evalm(e); }
483 /** Evaluate sums, products and integer powers of matrices. */
484 ex basic::evalm() const
489 return map(map_evalm);
492 /** Function object to be applied by basic::eval_integ(). */
493 struct eval_integ_map_function : public map_function {
494 ex operator()(const ex & e) { return eval_integ(e); }
497 /** Evaluate integrals, if result is known. */
498 ex basic::eval_integ() const
503 return map(map_eval_integ);
506 /** Perform automatic symbolic evaluations on indexed expression that
507 * contains this object as the base expression. */
508 ex basic::eval_indexed(const basic & i) const
509 // this function can't take a "const ex & i" because that would result
510 // in an infinite eval() loop
512 // There is nothing to do for basic objects
516 /** Add two indexed expressions. They are guaranteed to be of class indexed
517 * (or a subclass) and their indices are compatible. This function is used
518 * internally by simplify_indexed().
520 * @param self First indexed expression; its base object is *this
521 * @param other Second indexed expression
522 * @return sum of self and other
523 * @see ex::simplify_indexed() */
524 ex basic::add_indexed(const ex & self, const ex & other) const
529 /** Multiply an indexed expression with a scalar. This function is used
530 * internally by simplify_indexed().
532 * @param self Indexed expression; its base object is *this
533 * @param other Numeric value
534 * @return product of self and other
535 * @see ex::simplify_indexed() */
536 ex basic::scalar_mul_indexed(const ex & self, const numeric & other) const
541 /** Try to contract two indexed expressions that appear in the same product.
542 * If a contraction exists, the function overwrites one or both of the
543 * expressions and returns true. Otherwise it returns false. It is
544 * guaranteed that both expressions are of class indexed (or a subclass)
545 * and that at least one dummy index has been found. This functions is
546 * used internally by simplify_indexed().
548 * @param self Pointer to first indexed expression; its base object is *this
549 * @param other Pointer to second indexed expression
550 * @param v The complete vector of factors
551 * @return true if the contraction was successful, false otherwise
552 * @see ex::simplify_indexed() */
553 bool basic::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
559 /** Check whether the expression matches a given pattern. For every wildcard
560 * object in the pattern, an expression of the form "wildcard == matching_expression"
561 * is added to repl_lst. */
562 bool basic::match(const ex & pattern, lst & repl_lst) const
565 Sweet sweet shapes, sweet sweet shapes,
566 That's the key thing, right right.
567 Feed feed face, feed feed shapes,
568 But who is the king tonight?
569 Who is the king tonight?
570 Pattern is the thing, the key thing-a-ling,
571 But who is the king of Pattern?
572 But who is the king, the king thing-a-ling,
573 Who is the king of Pattern?
574 Bog is the king, the king thing-a-ling,
575 Bog is the king of Pattern.
576 Ba bu-bu-bu-bu bu-bu-bu-bu-bu-bu bu-bu
577 Bog is the king of Pattern.
580 if (is_exactly_a<wildcard>(pattern)) {
582 // Wildcard matches anything, but check whether we already have found
583 // a match for that wildcard first (if so, the earlier match must be
584 // the same expression)
585 for (lst::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
586 if (it->op(0).is_equal(pattern))
587 return is_equal(ex_to<basic>(it->op(1)));
589 repl_lst.append(pattern == *this);
594 // Expression must be of the same type as the pattern
595 if (tinfo() != ex_to<basic>(pattern).tinfo())
598 // Number of subexpressions must match
599 if (nops() != pattern.nops())
602 // No subexpressions? Then just compare the objects (there can't be
603 // wildcards in the pattern)
605 return is_equal_same_type(ex_to<basic>(pattern));
607 // Check whether attributes that are not subexpressions match
608 if (!match_same_type(ex_to<basic>(pattern)))
611 // Otherwise the subexpressions must match one-to-one
612 for (size_t i=0; i<nops(); i++)
613 if (!op(i).match(pattern.op(i), repl_lst))
616 // Looks similar enough, match found
621 /** Helper function for subs(). Does not recurse into subexpressions. */
622 ex basic::subs_one_level(const exmap & m, unsigned options) const
624 exmap::const_iterator it;
626 if (options & subs_options::no_pattern) {
633 for (it = m.begin(); it != m.end(); ++it) {
635 if (match(ex_to<basic>(it->first), repl_lst))
636 return it->second.subs(repl_lst, options | subs_options::no_pattern); // avoid infinite recursion when re-substituting the wildcards
643 /** Substitute a set of objects by arbitrary expressions. The ex returned
644 * will already be evaluated. */
645 ex basic::subs(const exmap & m, unsigned options) const
650 // Substitute in subexpressions
651 for (size_t i=0; i<num; i++) {
652 const ex & orig_op = op(i);
653 const ex & subsed_op = orig_op.subs(m, options);
654 if (!are_ex_trivially_equal(orig_op, subsed_op)) {
656 // Something changed, clone the object
657 basic *copy = duplicate();
658 copy->setflag(status_flags::dynallocated);
659 copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
661 // Substitute the changed operand
662 copy->let_op(i++) = subsed_op;
664 // Substitute the other operands
666 copy->let_op(i) = op(i).subs(m, options);
668 // Perform substitutions on the new object as a whole
669 return copy->subs_one_level(m, options);
674 // Nothing changed or no subexpressions
675 return subs_one_level(m, options);
678 /** Default interface of nth derivative ex::diff(s, n). It should be called
679 * instead of ::derivative(s) for first derivatives and for nth derivatives it
680 * just recurses down.
682 * @param s symbol to differentiate in
683 * @param nth order of differentiation
685 ex basic::diff(const symbol & s, unsigned nth) const
687 // trivial: zeroth derivative
691 // evaluate unevaluated *this before differentiating
692 if (!(flags & status_flags::evaluated))
693 return ex(*this).diff(s, nth);
695 ex ndiff = this->derivative(s);
696 while (!ndiff.is_zero() && // stop differentiating zeros
698 ndiff = ndiff.diff(s);
704 /** Return a vector containing the free indices of an expression. */
705 exvector basic::get_free_indices() const
707 return exvector(); // return an empty exvector
710 ex basic::conjugate() const
715 ex basic::real_part() const
717 return real_part_function(*this).hold();
720 ex basic::imag_part() const
722 return imag_part_function(*this).hold();
725 ex basic::eval_ncmul(const exvector & v) const
727 return hold_ncmul(v);
732 /** Function object to be applied by basic::derivative(). */
733 struct derivative_map_function : public map_function {
735 derivative_map_function(const symbol &sym) : s(sym) {}
736 ex operator()(const ex & e) { return diff(e, s); }
739 /** Default implementation of ex::diff(). It maps the operation on the
740 * operands (or returns 0 when the object has no operands).
743 ex basic::derivative(const symbol & s) const
748 derivative_map_function map_derivative(s);
749 return map(map_derivative);
753 /** Returns order relation between two objects of same type. This needs to be
754 * implemented by each class. It may never return anything else than 0,
755 * signalling equality, or +1 and -1 signalling inequality and determining
756 * the canonical ordering. (Perl hackers will wonder why C++ doesn't feature
757 * the spaceship operator <=> for denoting just this.) */
758 int basic::compare_same_type(const basic & other) const
760 return compare_pointers(this, &other);
763 /** Returns true if two objects of same type are equal. Normally needs
764 * not be reimplemented as long as it wasn't overwritten by some parent
765 * class, since it just calls compare_same_type(). The reason why this
766 * function exists is that sometimes it is easier to determine equality
767 * than an order relation and then it can be overridden. */
768 bool basic::is_equal_same_type(const basic & other) const
770 return compare_same_type(other)==0;
773 /** Returns true if the attributes of two objects are similar enough for
774 * a match. This function must not match subexpressions (this is already
775 * done by basic::match()). Only attributes not accessible by op() should
776 * be compared. This is also the reason why this function doesn't take the
777 * wildcard replacement list from match() as an argument: only subexpressions
778 * are subject to wildcard matches. Also, this function only needs to be
779 * implemented for container classes because is_equal_same_type() is
780 * automatically used instead of match_same_type() if nops() == 0.
782 * @see basic::match */
783 bool basic::match_same_type(const basic & other) const
785 // The default is to only consider subexpressions, but not any other
790 unsigned basic::return_type() const
792 return return_types::commutative;
795 tinfo_t basic::return_type_tinfo() const
800 /** Compute the hash value of an object and if it makes sense to store it in
801 * the objects status_flags, do so. The method inherited from class basic
802 * computes a hash value based on the type and hash values of possible
803 * members. For this reason it is well suited for container classes but
804 * atomic classes should override this implementation because otherwise they
805 * would all end up with the same hashvalue. */
806 unsigned basic::calchash() const
808 unsigned v = golden_ratio_hash((p_int)tinfo());
809 for (size_t i=0; i<nops(); i++) {
811 v ^= this->op(i).gethash();
814 // store calculated hash value only if object is already evaluated
815 if (flags & status_flags::evaluated) {
816 setflag(status_flags::hash_calculated);
823 /** Function object to be applied by basic::expand(). */
824 struct expand_map_function : public map_function {
826 expand_map_function(unsigned o) : options(o) {}
827 ex operator()(const ex & e) { return e.expand(options); }
830 /** Expand expression, i.e. multiply it out and return the result as a new
832 ex basic::expand(unsigned options) const
835 return (options == 0) ? setflag(status_flags::expanded) : *this;
837 expand_map_function map_expand(options);
838 return ex_to<basic>(map(map_expand)).setflag(options == 0 ? status_flags::expanded : 0);
844 // non-virtual functions in this class
849 /** Compare objects syntactically to establish canonical ordering.
850 * All compare functions return: -1 for *this less than other, 0 equal,
852 int basic::compare(const basic & other) const
854 #ifdef GINAC_COMPARE_STATISTICS
855 compare_statistics.total_basic_compares++;
857 const unsigned hash_this = gethash();
858 const unsigned hash_other = other.gethash();
859 if (hash_this<hash_other) return -1;
860 if (hash_this>hash_other) return 1;
861 #ifdef GINAC_COMPARE_STATISTICS
862 compare_statistics.compare_same_hashvalue++;
865 const tinfo_t typeid_this = tinfo();
866 const tinfo_t typeid_other = other.tinfo();
867 if (typeid_this==typeid_other) {
868 GINAC_ASSERT(typeid(*this)==typeid(other));
869 // int cmpval = compare_same_type(other);
871 // std::cout << "hash collision, same type: "
872 // << *this << " and " << other << std::endl;
873 // this->print(print_tree(std::cout));
874 // std::cout << " and ";
875 // other.print(print_tree(std::cout));
876 // std::cout << std::endl;
879 #ifdef GINAC_COMPARE_STATISTICS
880 compare_statistics.compare_same_type++;
882 return compare_same_type(other);
884 // std::cout << "hash collision, different types: "
885 // << *this << " and " << other << std::endl;
886 // this->print(print_tree(std::cout));
887 // std::cout << " and ";
888 // other.print(print_tree(std::cout));
889 // std::cout << std::endl;
890 return (typeid_this<typeid_other ? -1 : 1);
894 /** Test for syntactic equality.
895 * This is only a quick test, meaning objects should be in the same domain.
896 * You might have to .expand(), .normal() objects first, depending on the
897 * domain of your computation, to get a more reliable answer.
899 * @see is_equal_same_type */
900 bool basic::is_equal(const basic & other) const
902 #ifdef GINAC_COMPARE_STATISTICS
903 compare_statistics.total_basic_is_equals++;
905 if (this->gethash()!=other.gethash())
907 #ifdef GINAC_COMPARE_STATISTICS
908 compare_statistics.is_equal_same_hashvalue++;
910 if (this->tinfo()!=other.tinfo())
913 GINAC_ASSERT(typeid(*this)==typeid(other));
915 #ifdef GINAC_COMPARE_STATISTICS
916 compare_statistics.is_equal_same_type++;
918 return is_equal_same_type(other);
923 /** Stop further evaluation.
925 * @see basic::eval */
926 const basic & basic::hold() const
928 return setflag(status_flags::evaluated);
931 /** Ensure the object may be modified without hurting others, throws if this
932 * is not the case. */
933 void basic::ensure_if_modifiable() const
935 if (get_refcount() > 1)
936 throw(std::runtime_error("cannot modify multiply referenced object"));
937 clearflag(status_flags::hash_calculated | status_flags::evaluated);
944 int max_recursion_level = 1024;
947 #ifdef GINAC_COMPARE_STATISTICS
948 compare_statistics_t::~compare_statistics_t()
950 std::clog << "ex::compare() called " << total_compares << " times" << std::endl;
951 std::clog << "nontrivial compares: " << nontrivial_compares << " times" << std::endl;
952 std::clog << "basic::compare() called " << total_basic_compares << " times" << std::endl;
953 std::clog << "same hashvalue in compare(): " << compare_same_hashvalue << " times" << std::endl;
954 std::clog << "compare_same_type() called " << compare_same_type << " times" << std::endl;
955 std::clog << std::endl;
956 std::clog << "ex::is_equal() called " << total_is_equals << " times" << std::endl;
957 std::clog << "nontrivial is_equals: " << nontrivial_is_equals << " times" << std::endl;
958 std::clog << "basic::is_equal() called " << total_basic_is_equals << " times" << std::endl;
959 std::clog << "same hashvalue in is_equal(): " << is_equal_same_hashvalue << " times" << std::endl;
960 std::clog << "is_equal_same_type() called " << is_equal_same_type << " times" << std::endl;
961 std::clog << std::endl;
962 std::clog << "basic::gethash() called " << total_gethash << " times" << std::endl;
963 std::clog << "used cached hashvalue " << gethash_cached << " times" << std::endl;
966 compare_statistics_t compare_statistics;