4 The polynomials are immutable data types, under the public API. If 5 one tries to manipulate its underlying data attributes, immutability 6 will be able to be broken. 9 from __future__
import division
17 Base class for all univariate polynomials. 21 For any descendants of PolynomialInterface, `coefficients' 22 have to be given. Other arguments, if necessary, should be 23 passed as keyword arguments. Note that in this __init__ 24 method, no actual initialization happens. 26 formalsum.FormalSumContainerInterface.__init__(self)
33 if not isinstance(other, PolynomialInterface):
34 warnings.warn(
"comparison falls back to that of formal sum.")
35 return formalsum.FormalSumContainerInterface.__eq__(self, other)
36 sterms = [t
for t
in self.
iterterms()
if t[1]]
38 oterms = [t
for t
in other.iterterms()
if t[1]]
40 return sterms == oterms
43 val = sum([hash(t)
for t
in self.
iterterms()
if t[1]])
50 raise NotImplementedError(
"should be overridden")
54 Multiplication of two polynomials in the same ring. 60 if term_degree
in mul_coeff:
61 mul_coeff[term_degree] += cs*co
63 mul_coeff[term_degree] = cs*co
68 Return the result of scalar multiplication. 74 Return the result of multiplication with the given term. 75 The term can be given as a tuple (degree, coeff) or as a 78 if isinstance(term, PolynomialInterface):
79 degree, coeff = iter(term).next()
86 Return square of this polynomial. 88 raise NotImplementedError(
"should be overridden")
92 Return the formal differentiation of self. 104 Return the polynomial obtained by shifting upward all terms 105 with degrees of 'slide'. 107 f.upshift_degree(slide) is equivalent to 108 f.term_mul((slide, 1)). 110 return self.
bases_map(
lambda x: x + slide)
114 Return the polynomial obtained by shifting downward all terms 115 with degrees of 'slide'. 117 Be careful that if the least degree term has the degree less 118 than 'slide' then the result is not mathematically a 119 polynomial. Even in such a case, the method does not raise an 122 f.downshift_degree(slide) is equivalent to 123 f.upshift_degree(-slide). 125 return self.
bases_map(
lambda x: x - slide)
129 Create a new formal sum container by applying func to each 130 term. func must be a function taking 2 arguments. 141 Create a new univariate polynomial of the same class with 142 self, with given only the terms and use copy of self's data if 145 return self.__class__(terms, **self._init_kwds)
150 Basic polynomial data type ignoring a variable name and the ring. 154 BasicPolynomial(coefficients) 156 coefficients can be any dict initial values. 158 PolynomialInterface.__init__(self, coefficients, **kwds)
166 sum_coeff = dict(iter(self))
167 for term, coeff
in other:
168 if term
in sum_coeff:
169 sum_coeff[term] += coeff
171 sum_coeff[term] = coeff
178 dif_coeff = dict(iter(self))
179 for term, coeff
in other:
180 if term
in dif_coeff:
181 dif_coeff[term] -= coeff
183 dif_coeff[term] = -coeff
190 If type of other is Polynomial, do multiplication in ring. 191 Otherwise, do scalar multiplication. 193 if isinstance(other, PolynomialInterface):
199 return NotImplemented
205 If type of other does not support multiplication with self 206 from left, this method is called. In the context, it is only 207 posiible that other be a scalar. 212 return NotImplemented
228 Return the square of self. 239 (d1, c1), (d2, c2) = self.
terms()
254 mid = fst.ring_mul(snd.scalar_mul(2))
255 return fst.square() + mid + snd.square()
263 raise ValueError(
"negative index is not allowed.")
267 one = _ring.getRing(c).one
286 power_product *= power_of_2
289 power_of_2 = power_of_2.square()
307 iterator for (degree, coefficient) pairs. 308 The iterator is equivalent to 309 zip(self.iterbases(), self.itercoefficients()) 315 iterator for coefficients. 321 iterator for degrees. 327 Return the coefficient of specified degree. 328 If there is no term of degree, return 0. 334 Return True if there is a term of specified degree. 341 Return the number of data entries. 351 return PolynomialInterface.__eq__(self, other)
354 return PolynomialInterface.__hash__(self)
357 return "%s(%s)" % (self.__class__.__name__, repr(self.
_coefficients))
362 SortedPolynomial stores terms of a polynomial in sorted manner. 363 All methods and operations keep it sorted. 365 def __init__(self, coefficients, _sorted=False, **kwds):
367 SortedPolynomial(coefficients) 369 'coefficients' can be any dict initial values. 370 Optionally '_sorted' can be True if the coefficients is 373 PolynomialInterface.__init__(self, coefficients, **kwds)
375 if not _sorted
or isinstance(coefficients, dict):
376 for t
in dict(coefficients).iteritems():
379 self.
sorted = list(coefficients)
385 Insert the term into self.seorted list, and keep it sorted. 387 - This method is destructive. 388 - This method is not in a part of the API. 394 Return the index where to insert term of degree. 396 - This method is not in a part of the API. 397 - The code is just adapting bisect.bisect_right to the 400 lo, hi = 0, len(self.
sorted)
403 if degree < self.
sorted[mid][0]:
426 iter_self = iter(self.
sorted)
427 self_term = iter_self.next()
431 iter_other = iter(other.sorted)
432 other_term = iter_other.next()
436 while self_term
and other_term:
437 compared =
cmp(self_term[0], other_term[0])
439 sorted.append(self_term)
441 self_term = iter_self.next()
442 except StopIteration:
446 c = self_term[1] + other_term[1]
448 sorted.append((self_term[0], c))
450 self_term = iter_self.next()
451 except StopIteration:
454 other_term = iter_other.next()
455 except StopIteration:
457 if self_term
is None or other_term
is None:
460 sorted.append(other_term)
462 other_term = iter_other.next()
463 except StopIteration:
466 if other_term
is None and self_term:
467 sorted.append(self_term)
468 for term
in iter_self:
470 elif self_term
is None and other_term:
471 sorted.append(other_term)
472 for term
in iter_other:
480 It is something like merge sort. 483 iter_self = iter(self.
sorted)
484 self_term = iter_self.next()
488 iter_other = iter(other.sorted)
489 other_term = iter_other.next()
493 while self_term
and other_term:
494 compared =
cmp(self_term[0], other_term[0])
496 sorted.append(self_term)
498 self_term = iter_self.next()
499 except StopIteration:
503 c = self_term[1] - other_term[1]
505 sorted.append((self_term[0], c))
507 self_term = iter_self.next()
508 except StopIteration:
511 other_term = iter_other.next()
512 except StopIteration:
514 if self_term
is None or other_term
is None:
517 sorted.append((other_term[0], -other_term[1]))
519 other_term = iter_other.next()
520 except StopIteration:
523 if other_term
is None and self_term:
524 sorted.append(self_term)
525 for term
in iter_self:
527 elif self_term
is None and other_term:
528 sorted.append((other_term[0], -other_term[1]))
529 for term
in iter_other:
530 sorted.append((term[0], -term[1]))
537 If type of other is SortedPolynomial, do multiplication 538 in ring. Otherwise, do scalar multiplication. 540 if isinstance(other, PolynomialInterface):
546 return NotImplemented
552 If type of other does not support multiplication with self 553 from left, this method is called. In the context, it is only 554 posiible that other be a scalar. 559 return NotImplemented
563 Multiplication of two polynomials in the same ring. 565 Computation is carried out by Karatsuba method. 569 if not self
or not other:
573 return other.term_mul(self)
574 if len(other.sorted) == 1:
578 p, q = [other.term_mul(term)
for term
in self]
580 if len(other.sorted) == 2:
581 p, q = [self.
term_mul(term)
for term
in other]
584 black_left_degree, black_right_degree = self.
sorted[0][0], self.
sorted[-1][0]
585 red_left_degree, red_right_degree = other.sorted[0][0], other.sorted[-1][0]
586 left_degree = min(black_left_degree, red_left_degree)
587 right_degree = max(black_right_degree, red_right_degree)
589 assert left_degree < right_degree
590 half_degree = (left_degree + right_degree) >> 1
591 black_half_index = self.
_bisect(half_degree)
592 red_half_index = other._bisect(half_degree)
593 if not black_half_index:
595 if not red_half_index:
597 club =
polynomial([(d - left_degree, c)
for (d, c)
in self.
sorted[:black_half_index]])
598 spade =
polynomial([(d - half_degree, c)
for (d, c)
in self.
sorted[black_half_index:]])
599 dia =
polynomial([(d - left_degree, c)
for (d, c)
in other.sorted[:red_half_index]])
600 heart =
polynomial([(d - half_degree, c)
for (d, c)
in other.sorted[red_half_index:]])
601 weaker = club.ring_mul_karatsuba(dia)
602 stronger = spade.ring_mul_karatsuba(heart)
605 return (weaker.upshift_degree(left_degree * 2) +
606 karatsuba.upshift_degree(left_degree + half_degree) +
607 stronger.upshift_degree(half_degree * 2))
610 karatsuba.upshift_degree(half_degree) +
611 stronger.upshift_degree(half_degree * 2))
615 Return the result of scalar multiplication. 624 if keep_ring
and scaled
not in coeffring:
625 coeffring = coeffring.getCommonSuperring(_ring.getRing(scaled))
626 new_coeff.append((d, scaled))
629 new_coeff = [(d, c * scale)
for (d, c)
in self
if c]
634 Return the square of self. 641 data_length = len(self.
sorted)
651 (d1, c1), (d2, c2) = [(d, c)
for (d, c)
in self]
652 return polynomial({d1*2:c1**2, d1+d2:c1*c2*2, d2*2:c2**2})
654 right_degree = self.
sorted[-1][0]
655 left_degree = self.
sorted[0][0]
656 half_degree = (right_degree + left_degree) >> 1
657 half_index = self.
_bisect(half_degree)
658 fst =
polynomial([(d - left_degree, c)
for (d, c)
in self.
sorted[:half_index]])
659 snd =
polynomial([(d - half_degree, c)
for (d, c)
in self.
sorted[half_index:]])
660 fst_squared = fst.square()
661 snd_squared = snd.square()
662 karatsuba = (fst + snd).
square() - fst_squared - snd_squared
664 return (fst_squared.upshift_degree(left_degree * 2) +
665 karatsuba.upshift_degree(left_degree + half_degree) +
666 snd_squared.upshift_degree(half_degree * 2))
668 return (fst_squared +
669 karatsuba.upshift_degree(half_degree) +
670 snd_squared.upshift_degree(half_degree * 2))
676 No ternary powering is defined here, because there is no 677 modulus operator defined. 681 raise ValueError(
"negative index is not allowed.")
685 one = _ring.getRing(c).one
697 elif len(self.
sorted) == 1:
704 power_product *= power_of_2
707 power_of_2 = power_of_2.square()
712 Return the degree of self 715 for d, c
in self.
sorted[::-1]:
722 Return the leading coefficient 725 for d, c
in self.
sorted[::-1]:
732 Return the leading term as a tuple (degree, coefficient). 735 for d, c
in self.
sorted[::-1]:
742 iterator for (base, coefficient) pairs. 743 The iterator is equivalent to 744 zip(self.iterbases(), self.itercoefficients()) 757 iterator for coefficients. 764 Return the coefficient of specified degree. 765 If there is no term of degree, return 0. 769 if self.
sorted[rindex - 1][0] == degree:
770 return self.
sorted[rindex - 1][1]
775 Return True if there is a term of specified degree. 779 if self.
sorted[rindex - 1][0] == degree:
785 Return the number of data entries. 795 if not isinstance(other, SortedPolynomial):
796 return PolynomialInterface.__eq__(self, other)
797 if [t
for t
in self
if t[1]] == [t
for t
in other
if t[1]]:
802 val = sum([hash(t)
for t
in self
if t[1]])
815 while len(items) > 0:
817 result = result * val_pow.setdefault(d - e, val ** (d - e)) + f
820 return result * val_pow.get(d, val ** d)