Package sympycore :: Package basealgebra :: Module pairs
[hide private]
[frames] | no frames]

Source Code for Module sympycore.basealgebra.pairs

  1  # 
  2  # Author: Pearu Peterson 
  3  # Created: January 2008 
  4  # 
  5  """Provides CollectingField class. 
  6  """ 
  7  __docformat__ = "restructuredtext" 
  8   
  9  __all__ = ['CollectingField'] 
 10   
 11   
 12  from ..core import classes 
 13  from ..utils import str_SUM, str_PRODUCT, str_POWER, str_APPLY, str_SYMBOL, str_NUMBER 
 14  from ..utils import TERMS, FACTORS, SYMBOL, NUMBER, APPLY, POW, TUPLE, head_to_string 
 15   
 16  from .algebra import Algebra 
 17  from .ring import CommutativeRing 
 18  from .verbatim import Verbatim 
 19  from ..arithmetic.numbers import mpq, realtypes 
 20   
 21  from .pairs_ops import (add_method, sub_method, rsub_method, neg_method, 
 22                          mul_method, div_method, rdiv_method, pow_method) 
 23   
 24  from .pairs_iops import (inplace_add, inplace_add2, inplace_sub, 
 25                           return_terms, return_factors, 
 26                           inplace_mul, inplace_mul2) 
 27   
 28  from .pairs_expand import expand 
 29   
30 -class CollectingField(CommutativeRing):
31 """ Implementation of a commutative ring where sums and products 32 are represented as dictionaries of pairs. 33 """ 34 #__slots__ = ['_symbols', '_symbols_data'] 35 36 one_c = 1 # one element of coefficient algebra 37 one_e = 1 # one element of exponent algebra 38 zero_c = 0 # zero element of coefficient algebra 39 zero_e = 0 # zero element of exponent algebra 40 41 _symbols = None 42 _symbols_data = None 43 44 coefftypes = (int, long, mpq) 45 exptypes = (int, long, mpq) 46 47 _coeff_terms = (1, None) # set by MUL_VALUE_TERMS 48 __neg__ = neg_method 49 __add__ = __radd__ = add_method 50 __sub__ = sub_method 51 __rsub__ = rsub_method 52 __mul__ = __rmul__ = mul_method 53 __div__ = div_method 54 __rdiv__ = rdiv_method 55 __pow__ = pow_method 56 expand = expand 57 58 __repr__ = CommutativeRing.__repr__ 59
60 - def __nonzero__(self):
61 return self.head is not NUMBER or bool(self.data)
62
63 - def copy(self):
64 """ Return a copy of self. 65 """ 66 head, data = self.pair 67 if head is TERMS or head is FACTORS: 68 return type(self)(head, dict(data)) 69 return self
70 71 #def __repr__(self): 72 # return '%s(%r, head=%s)' % (type(self).__name__, self.data, head_to_string[self.head]) 73
74 - def as_tree(self, tab='', level=0):
75 if level: 76 r = [] 77 else: 78 r = [type(self).__name__+':'] 79 data = self.data 80 head = self.head 81 if head in [SYMBOL, NUMBER]: 82 r.append(tab + '%s[%s]' % (head_to_string[head], data)) 83 elif head in [TERMS, FACTORS]: 84 r.append(tab + '%s[' % (head_to_string[head])) 85 for t,c in data.iteritems(): 86 r.append(t.as_tree(tab=tab + (' %s:' % (str(c))), level=level+1)) 87 r.append(tab+']') 88 elif callable(head): 89 r.append(tab + '%s[%s]' % (head, data)) 90 else: 91 raise NotImplementedError(`self, head`) 92 return '\n'.join(r)
93 94 @classmethod
95 - def coefficient_to_str_data(cls, obj, sort=True):
96 tobj = type(obj) 97 if tobj is int or tobj is long: 98 if obj < 0: 99 return str_SUM, str(obj) 100 return str_NUMBER, str(obj) 101 if hasattr(obj, 'to_str_data'): 102 return obj.to_str_data(sort) 103 raise NotImplementedError('%s.coefficient_to_str_data(%r)' % (cls.__name__, obj))
104 105 @classmethod
106 - def exponent_to_str_data(cls, obj, sort=True):
107 tobj = type(obj) 108 if tobj is int or tobj is long: 109 if obj < 0: 110 return str_SUM, str(obj) 111 return str_NUMBER, str(obj) 112 if hasattr(obj, 'to_str_data'): 113 return obj.to_str_data(sort) 114 raise NotImplementedError('%s.exponent_to_str_data(%r)' % (cls.__name__, obj))
115 116 @classmethod
117 - def callable_to_str_data(cls, obj, sort=True):
118 if isinstance(obj, Algebra): 119 return obj.to_str_data(sort=True) 120 if hasattr(obj, '__name__'): 121 return str_SYMBOL, obj.__name__ 122 return str_SYMBOL, str(obj)
123
124 - def __str__(self):
125 return self.to_str_data()[1]
126
127 - def to_str_data(self, sort=True):
128 head = self.head 129 one = self.one 130 cls = type(self) 131 if head is NUMBER: 132 return cls.coefficient_to_str_data(self.data, sort) 133 if head is TERMS: 134 pos_dict = {} 135 neg_dict = {} 136 for t, c in self.data.iteritems(): 137 if isinstance(c, realtypes) and c<0: 138 d = neg_dict 139 c = -c 140 else: 141 d = pos_dict 142 h1, s1 = cls.coefficient_to_str_data(c, sort) 143 h2, s2 = t.to_str_data(sort) 144 if t is one: 145 h, s = h1, s1 146 elif c==1: 147 h, s = h2, s2 148 else: 149 if h1 > str_PRODUCT: 150 s1 = '(%s)' % (s1) 151 if h2 > str_PRODUCT: 152 s2 = '(%s)' % (s2) 153 s = '%s*%s' % (s1,s2) 154 h = str_PRODUCT 155 l = d.get(h) 156 if l is None: 157 l = d[h] = [] 158 l.append(s) 159 if sort: 160 r1 = [] 161 for k in sorted(pos_dict): 162 r1 += sorted(pos_dict[k]) 163 r2 = [] 164 for k in sorted(neg_dict): 165 r2 += sorted(neg_dict[k]) 166 else: 167 r1 = reduce(lambda x,y:x+y, pos_dict.values(),[]) 168 r2 = reduce(lambda x,y:x+y, neg_dict.values(),[]) 169 if r1: 170 if r2: 171 return str_SUM, (' + '.join(r1)) + ' - ' + (' - '.join(r2)) 172 if len(r1)>1: 173 return str_SUM, (' + '.join(r1)) 174 h,l = pos_dict.items()[0] 175 return h, l[0] 176 return str_SUM, '-' + (' - '.join(r2)) 177 if head is FACTORS: 178 d = {} 179 for t, c in self.data.iteritems(): 180 h1, s1 = t.to_str_data(sort) 181 h2, s2 = cls.exponent_to_str_data(c, sort) 182 if c==1: 183 if h1 > str_POWER: 184 s1 = '(%s)' % (s1) 185 h, s = h1, s1 186 else: 187 if h1 >= str_POWER: 188 s1 = '(%s)' % (s1) 189 if h2 >= str_POWER: 190 s2 = '(%s)' % (s2) 191 s = '%s**%s' % (s1, s2) 192 h = str_POWER 193 l = d.get(h) 194 if l is None: 195 l = d[h] = [] 196 l.append(s) 197 if sort: 198 r1 = [] 199 for k in sorted(d): 200 r1 += sorted(d[k]) 201 else: 202 r1 = reduce(lambda x,y:x+y, d.values(),[]) 203 if len(r1)>1: 204 return str_PRODUCT, '*'.join(r1) 205 h, l = d.items()[0] 206 return h, l[0] 207 if callable(head): 208 h1, s1 = cls.callable_to_str_data(head, sort) 209 if h1 > str_APPLY: 210 s1 = '(%s)' % (s1) 211 args = self.data 212 if type(args) is not tuple: 213 args = args, 214 s2 = ', '.join([a.to_str_data(sort)[1] for a in args]) 215 return str_APPLY, '%s(%s)' % (s1, s2) 216 return str_SYMBOL, str(self.data)
217 218 @property
219 - def func(self):
220 """ Return callable func such that ``self.func(**self.args) == self`` 221 """ 222 head = self.head 223 data = self.data 224 if head is SYMBOL or head is NUMBER: 225 return lambda : self 226 elif head is TERMS: 227 if len(self.data)==1: 228 return self.Mul 229 return self.Add 230 elif head is FACTORS: 231 if len(data)>1: 232 return self.Mul 233 return self.Pow 234 elif callable(head): 235 return head 236 raise NotImplementedError(`self, head`)
237 238 @property
239 - def args(self):
240 """ Return a sequence args such that ``self.func(**self.args) == self`` 241 """ 242 head = self.head 243 if head is SYMBOL or head is NUMBER: 244 return [] 245 elif head is TERMS: 246 if len(self.data)==1: 247 t, c = self.data.items()[0] 248 return [self.convert(c)] + t.as_Mul_args() 249 return self.as_Add_args() 250 elif head is FACTORS: 251 if len(self.data)>1: 252 return self.as_Mul_args() 253 return self.as_Pow_args() 254 elif callable(head): 255 data = self.data 256 if type(data) is tuple: 257 return data 258 return data, 259 raise NotImplementedError(`self, head`)
260 261 @property
262 - def is_Add(self):
263 head, data = self.pair 264 return head is TERMS and len(data) > 1
265 266 @property
267 - def is_Mul(self):
268 head, data = self.pair 269 return (head is TERMS and len(data) == 1) \ 270 or (head is FACTORS and len(data) > 1)
271 272 @property
273 - def is_Pow(self):
274 head, data = self.pair 275 return head is FACTORS and len(data) == 1
276 277 @property
278 - def is_Number(self):
279 return self.head is NUMBER
280 281 @property
282 - def is_Symbol(self):
283 return self.head is SYMBOL
284
285 - def as_Add_args(self):
286 head, data = self.pair 287 if head is TERMS: 288 if len(data)==1: 289 return [self] 290 return [t * c for t,c in data.iteritems()] 291 return [self]
292
293 - def as_Mul_args(self):
294 head, data = self.pair 295 if head is TERMS and len(data)==1: 296 t,c = data.items()[0] 297 return [self.convert(c)] + t.as_Mul_args() 298 if head is FACTORS: 299 if len(data)==1: 300 return [self] 301 l = [] 302 for t,c in data.iteritems(): 303 if c==1: 304 l.extend(t.as_Mul_args()) 305 else: 306 l.append(t**c) 307 return l 308 return [self]
309
310 - def as_Pow_args(self):
311 head, data = self.pair 312 if head is FACTORS: 313 if len(data)==1: 314 return data.items()[0] 315 return [self, self.one_e] #XXX: improve me 316 return [self, self.one_e]
317
318 - def as_Terms_args(self):
319 head, data = self.pair 320 if head is NUMBER: 321 return [(self.one, data)] 322 if head is TERMS: 323 return data.items() 324 return [(self, self.one_c)]
325
326 - def as_Factors_args(self):
327 head, data = self.pair 328 if head is FACTORS: 329 return data.items() 330 return [(self, self.one_e)]
331
332 - def as_verbatim(self):
333 """ Convert algebra element to a verbatim algebra element. 334 """ 335 head, data = self.pair 336 func = self.func 337 args = self.args 338 if func==self.Add: 339 r = Verbatim(TERMS,tuple([a.as_verbatim() for a in args])) 340 r.commutative_add = True 341 elif func==self.Mul: 342 h, d = args[0].pair 343 if h is NUMBER and d < 0: 344 if d==-1: 345 args = [a.as_verbatim() for a in args[1:]] 346 if len(args)==1: 347 return -args[0] 348 else: 349 args = [(-args[0]).as_verbatim()] + [a.as_verbatim() for a in args[1:]] 350 r = Verbatim(FACTORS, tuple(args)) 351 r.commutative_mul = True 352 r = -r 353 else: 354 r = Verbatim(FACTORS, tuple([a.as_verbatim() for a in args])) 355 r.commutative_mul = True 356 elif func==self.Pow: 357 r = Verbatim(POW, tuple(map(Verbatim.convert, args))) 358 elif head is NUMBER: 359 value = data 360 if hasattr(value, 'as_verbatim'): 361 r = value.as_verbatim() 362 elif isinstance(value, (int, long, float)) and value<0: 363 r = -Verbatim(NUMBER, -value) 364 else: 365 r = Verbatim(NUMBER, value) 366 elif head is SYMBOL: 367 if hasattr(data, 'as_verbatim'): 368 r = data.as_verbatim() 369 else: 370 r = Verbatim(SYMBOL, data) 371 elif callable(head): 372 if hasattr(data, 'as_verbatim'): 373 args = data.as_verbatim(), 374 elif isinstance(data, tuple): 375 args = tuple([a.as_verbatim() for a in args]) 376 else: 377 args = data.as_verbatim() 378 r = Verbatim(APPLY, (head,)+args) 379 else: 380 if hasattr(data, 'as_verbatim'): 381 r = data.as_verbatim() 382 else: 383 r = Verbatim(SYMBOL, (head, data)) 384 return r
385 386 @classmethod
387 - def Symbol(cls, obj):
388 """ Construct new symbol instance as an algebra element. 389 """ 390 assert isinstance(obj, str),`obj` 391 return cls(SYMBOL, obj)
392 393 @classmethod
394 - def Number(cls, obj):
395 """ Construct new number instance as an algebra number. 396 """ 397 return cls(NUMBER, obj)
398 399 @classmethod
400 - def Apply(cls, func, args):
401 return cls(func, args)
402 403 @classmethod
404 - def Add(cls, *seq):
405 """ Return canonized sum as an algebra element. 406 """ 407 d = {} 408 d_get = d.get 409 one = cls.one 410 for t in seq: 411 inplace_add(cls, t, d, d_get, one) 412 return return_terms(cls, d)
413 414 @classmethod
415 - def Sub(cls, *seq):
416 d = {} 417 d_get = d.get 418 one = cls.one 419 if seq: 420 inplace_add(cls, seq[0], d, d_get, one) 421 for t in seq[1:]: 422 inplace_sub(cls, t, d, d_get, one) 423 return return_terms(cls, d)
424 425 @classmethod
426 - def Terms(cls, *seq):
427 d = {} 428 d_get = d.get 429 one = cls.one 430 for t,c in seq: 431 inplace_add2(cls, t, c, d, d_get, one) 432 return return_terms(cls, d)
433
434 - def __iadd__(self, other):
435 head, data = self.pair 436 cls = type(self) 437 if self.is_writable and head is TERMS: 438 if type(other) is not cls: 439 t = cls.convert(other) 440 else: 441 t = other 442 h, d = t.pair 443 if h is TERMS and len(d)==1: 444 t, c = d.items()[0] 445 elif h is NUMBER: 446 c = d 447 t = cls.one 448 else: 449 c = 1 450 inplace_add2(cls, t, c, data, data.get, cls.one) 451 return self 452 return self + other
453
454 - def __imul__(self, other):
455 head, data = self.pair 456 cls = type(self) 457 if self.is_writable and head is FACTORS: 458 if type(other) is not cls: 459 t = cls.convert(other) 460 else: 461 t = other 462 h, d = t.pair 463 if h is FACTORS and len(d)==1: 464 t, c = d.items()[0] 465 elif h is NUMBER: 466 return self * t 467 elif h is TERMS and len(d)==1: 468 return self * t 469 else: 470 c = 1 471 inplace_mul2(cls, t, c, data, data.get, cls.one) 472 return self 473 return self * other
474 475 @classmethod
476 - def Mul(cls, *seq):
477 """ Return canonized product as an algebra element. 478 """ 479 d = {} 480 d_get = d.get 481 number = 1 482 for t in seq: 483 n = inplace_mul(cls, t, d, d_get) 484 if n is not 1: 485 number = number * n 486 r = return_factors(cls, d) 487 if number is 1: 488 return r 489 return r * number
490 491 @classmethod
492 - def Factors(cls, *seq):
493 d = {} 494 d_get = d.get 495 number = 1 496 for t, c in seq: 497 n = inplace_mul2(cls, t, c, d, d_get) 498 if n is not 1: 499 number = number * n 500 r = return_factors(cls, d) 501 if number is 1: 502 return r 503 return r * number
504 505 @classmethod
506 - def Pow(cls, base, exp):
507 return pow_method(base, exp)
508
509 - def __int__(self):
510 head, data = self.pair 511 assert head is NUMBER,`self` 512 return int(data)
513
514 - def __long__(self):
515 head, data = self.pair 516 assert head is NUMBER,`self` 517 return long(data)
518
519 - def __abs__(self):
520 head, data = self.pair 521 assert head is NUMBER,`self` 522 return type(self)(NUMBER, abs(data))
523
524 - def __pos__(self):
525 return self
526
527 - def __lt__(self, other):
528 if type(other) is type(self): 529 return self.data < other.data 530 return self.data < other
531 - def __le__(self, other):
532 if type(other) is type(self): 533 return self.data <= other.data 534 return self.data <= other
535 - def __gt__(self, other):
536 if type(other) is type(self): 537 return self.data > other.data 538 return self.data > other
539 - def __ge__(self, other):
540 if type(other) is type(self): 541 return self.data >= other.data 542 return self.data >= other
543 - def __ne__(self, other):
544 return not (self == other)
545
546 - def _get_symbols_data(self):
547 _symbols_data = self._symbols_data 548 if _symbols_data is None: 549 head, data = self.pair 550 if head is SYMBOL: 551 _symbols_data = set([data]) 552 elif head is NUMBER: 553 _symbols_data = set() 554 elif head is TERMS: 555 _symbols_data = set() 556 for k in data: 557 _symbols_data |= k._get_symbols_data() 558 elif head is FACTORS: 559 _symbols_data = set() 560 cls = type(self) 561 for k, c in data.iteritems(): 562 _symbols_data |= k._get_symbols_data() 563 if isinstance(c, cls): 564 _symbols_data |= c._get_symbols_data() 565 else: 566 _symbols_data = set() 567 for arg in self.args: 568 _symbols_data |= arg._get_symbols_data() 569 self._symbols_data = _symbols_data 570 return _symbols_data
571 572 @property
573 - def symbols(self):
574 """ Return as set of symbols that are contained in self. 575 """ 576 symbols = self._symbols 577 if symbols is None: 578 cls = type(self) 579 Symbol = cls.Symbol 580 symbols = self._symbols = set([Symbol(d) for d in self._get_symbols_data()]) 581 return symbols
582
583 - def has_symbol(self, symbol):
584 """ Check if self contains symbol. 585 """ 586 return symbol.data in self._get_symbols_data()
587
588 - def has(self, subexpr):
589 """ Check if self contains sub-expression. 590 """ 591 subexpr = self.convert(subexpr) 592 head, data = subexpr.pair 593 if head is SYMBOL: 594 return data in self._get_symbols_data() 595 raise NotImplementedError('%s.has(%r)' % (type(self).__name__, subexpr))
596
597 - def _subs(self, subexpr, newexpr):
598 head, data = self.pair 599 600 if head is subexpr.head: 601 if data == subexpr.data: 602 return newexpr 603 604 if head is SYMBOL or head is NUMBER: 605 return self 606 607 cls = type(self) 608 609 if head is TERMS: 610 d = {} 611 d_get = d.get 612 one = cls.one 613 for t,c in data.iteritems(): 614 r = t._subs(subexpr, newexpr) 615 if c is 1: 616 inplace_add(cls, r, d, d_get, one) 617 else: 618 inplace_add2(cls, r, c, d, d_get, one) 619 return return_terms(cls, d) 620 621 elif head is FACTORS: 622 d = {} 623 d_get = d.get 624 num = 1 625 for t,c in data.iteritems(): 626 r = t._subs(subexpr, newexpr) 627 if type(c) is cls: 628 c = c._subs(subexpr, newexpr) 629 if c is 1: 630 n = inplace_mul(cls, r, d, d_get) 631 else: 632 n = inplace_mul2(cls, r, c, d, d_get) 633 if n is not 1: 634 num = num * n 635 r = return_factors(cls, d) 636 if num is 1: 637 return r 638 return r * num 639 640 elif callable(head): 641 args = data 642 if type(args) is tuple: 643 args = [a._subs(subexpr, newexpr) for a in args] 644 return head(*args) 645 else: 646 return head(args._subs(subexpr, newexpr)) 647 648 raise NotImplementedError(`self`)
649
650 - def as_term_intcoeff(self):
651 """ Return ``(term, coeff)`` such that self=term*coeff and 652 coeff is integer. 653 """ 654 head, data = self.pair 655 if head is NUMBER: 656 td = type(data) 657 if td is int or td is long: 658 return self.one, data 659 if td is FractionTyple: 660 n,p = data 661 return self.Number(mpq((1, p))), n 662 elif head is TERMS: 663 if len(data)==1: 664 t, c = data.items()[0] 665 td = type(c) 666 if td is int or td is long: 667 return t, c 668 if td is mpq: 669 n,p = c 670 return t * FractionTyple((1,p)), n 671 return self, 1
672
673 - def to_polynomial_data(self, variables=None, fixed=False):
674 """ Convert pairs representation to ``{exponents: coeff}, 675 variables_list`` representation. 676 677 For example:: 678 679 x**2 + 3*sin(x) -> {(2,0):1, (0,1):3}, [x,sin(x)] 680 3*x*(x+y)**2 -> {(1,2):3}, [x, x+y] 681 682 Notes: 683 684 * In general, ``exponent``-s are tuples of integers. An 685 expection is when ``len(variable_list)==1``: ``exponent``-s 686 will be integers. 687 688 * When ``fixed`` is True then one must provide ``variables` list 689 that will be equal to returned ``variable_list``. Symbols 690 not in a variables list are treated as coefficients. 691 692 """ 693 head, data = self.pair 694 cls = type(self) 695 if variables is None: 696 assert not fixed 697 variables = [] 698 else: 699 variables = list(variables) 700 if head is NUMBER: 701 l = len(variables) 702 if l==1: 703 return {0 : data}, variables 704 return {(0,)*l : data}, variables 705 if head is TERMS: 706 exps_append_methods = [] 707 exponents = [] 708 coeffs = [] 709 for t, c in data.iteritems(): 710 if fixed: 711 rest = 1 712 h, d = t.pair 713 exps = [0] * len(variables) 714 exps_append_methods.append(exps.append) 715 if h is FACTORS: 716 for f, e in d.iteritems(): 717 if isinstance(e, CommutativeRing): 718 r, e = e.as_term_intcoeff() 719 f = f**r 720 else: 721 te = type(e) 722 if te is mpq: 723 n,p = e 724 f = f ** mpq((1,p)) 725 e = n 726 elif te is int or te is long: 727 pass 728 else: 729 f = cls.Factors((f,e)) 730 e = 1 731 try: 732 i = variables.index(f) 733 except ValueError: 734 if fixed: 735 # XXX: that's a bit slow 736 rest *= f**e 737 else: 738 i = len(variables) 739 variables.append(f) 740 [mth(0) for mth in exps_append_methods] 741 exps[i] += e 742 else: 743 exps[i] += e 744 elif h is NUMBER: 745 assert d==1,`t` 746 else: 747 try: 748 i = variables.index(t) 749 except ValueError: 750 if fixed: 751 rest *= t 752 else: 753 i = len(variables) 754 variables.append(t) 755 [mth(0) for mth in exps_append_methods] 756 exps[i] += 1 757 else: 758 exps[i] += 1 759 exponents.append(exps) 760 if fixed and rest is not 1: 761 c = c * rest 762 coeffs.append(c) 763 l = len(variables) 764 d = {} 765 if l==1: 766 for exps, coeff in zip(exponents, coeffs): 767 d[exps[0]] = coeff 768 else: 769 for exps, coeff in zip(exponents, coeffs): 770 d[tuple(exps)] = coeff 771 return d, variables 772 elif head is FACTORS: 773 exps = [0] * len(variables) 774 coeff = 1 775 for f, e in data.iteritems(): 776 if isinstance(e, CommutativeRing): 777 r, e = e.as_term_intcoeff() 778 f = f**r 779 else: 780 te = type(e) 781 if te is mpq: 782 n,p = e 783 f = f ** mpq((1,p)) 784 e = n 785 elif te is int or te is long: 786 pass 787 else: 788 f = cls.Factors((f,e)) 789 e = 1 790 try: 791 i = variables.index(f) 792 except ValueError: 793 if fixed: 794 coeff *= f**e 795 continue 796 i = len(variables) 797 variables.append(f) 798 exps.append(0) 799 exps[i] += e 800 l = len(variables) 801 if l==1: 802 return {exps[0]: coeff}, variables 803 return {tuple(exps): coeff}, variables 804 else: 805 coeff = 1 806 l = len(variables) 807 exps = [0]*l 808 try: 809 i = variables.index(self) 810 except ValueError: 811 if fixed: 812 coeff = self 813 exps = [0]*l 814 else: 815 i = len(variables) 816 variables.append(self) 817 exps.append(1) 818 l += 1 819 else: 820 exps[i] = 1 821 if l==1: 822 return {exps[0]: coeff}, variables 823 return {tuple(exps): coeff}, variables
824 825 classes.CollectingField = CollectingField 826 827 # initialize one and zero attributes: 828 CollectingField.one = CollectingField.Number(1) 829 CollectingField.zero = CollectingField.Number(0) 830