Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/structure/factorization.py
4036 views
1
r"""
2
Factorizations
3
4
The :class:`Factorization` class provides a structure for holding quite
5
general lists of objects with integer multiplicities. These may hold
6
the results of an arithmetic or algebraic factorization, where the
7
objects may be primes or irreducible polynomials and the
8
multiplicities are the (non-zero) exponents in the factorization. For
9
other types of examples, see below.
10
11
:class:`Factorization` class objects contain a ``list``, so can be
12
printed nicely and be manipulated like a list of prime-exponent pairs,
13
or easily turned into a plain list. For example, we factor the
14
integer `-45`::
15
16
sage: F = factor(-45)
17
18
This returns an object of type :class:`Factorization`::
19
20
sage: type(F)
21
<class 'sage.structure.factorization_integer.IntegerFactorization'>
22
23
It prints in a nice factored form::
24
25
sage: F
26
-1 * 3^2 * 5
27
28
There is an underlying list representation, \emph{which ignores the
29
unit part}::
30
31
sage: list(F)
32
[(3, 2), (5, 1)]
33
34
A :class:`Factorization` is not actually a list::
35
36
sage: isinstance(F, list)
37
False
38
39
However, we can access the :class:`Factorization` F itself as if it were a list::
40
41
sage: F[0]
42
(3, 2)
43
sage: F[1]
44
(5, 1)
45
46
To get at the unit part, use the :meth:`Factorization.unit` function::
47
48
sage: F.unit()
49
-1
50
51
All factorizations are immutable, up to ordering with ``sort()`` and
52
simplifying with ``simplify()``. Thus if you write a function that
53
returns a cached version of a factorization, you do not have to return
54
a copy.
55
56
::
57
58
sage: F = factor(-12); F
59
-1 * 2^2 * 3
60
sage: F[0] = (5,4)
61
Traceback (most recent call last):
62
...
63
TypeError: 'Factorization' object does not support item assignment
64
65
EXAMPLES:
66
67
This more complicated example involving polynomials also illustrates
68
that the unit part is not discarded from factorizations::
69
70
sage: x = QQ['x'].0
71
sage: f = -5*(x-2)*(x-3)
72
sage: f
73
-5*x^2 + 25*x - 30
74
sage: F = f.factor(); F
75
(-5) * (x - 3) * (x - 2)
76
sage: F.unit()
77
-5
78
sage: F.value()
79
-5*x^2 + 25*x - 30
80
81
The underlying list is the list of pairs `(p_i, e_i)`, where each
82
`p_i` is a 'prime' and each `e_i` is an integer. The unit part
83
is discarded by the list::
84
85
sage: list(F)
86
[(x - 3, 1), (x - 2, 1)]
87
sage: len(F)
88
2
89
sage: F[1]
90
(x - 2, 1)
91
92
In the ring `\ZZ[x]`, the integer `-5` is not a unit, so the
93
factorization has three factors::
94
95
sage: x = ZZ['x'].0
96
sage: f = -5*(x-2)*(x-3)
97
sage: f
98
-5*x^2 + 25*x - 30
99
sage: F = f.factor(); F
100
(-1) * 5 * (x - 3) * (x - 2)
101
sage: F.universe()
102
Univariate Polynomial Ring in x over Integer Ring
103
sage: F.unit()
104
-1
105
sage: list(F)
106
[(5, 1), (x - 3, 1), (x - 2, 1)]
107
sage: F.value()
108
-5*x^2 + 25*x - 30
109
sage: len(F)
110
3
111
112
On the other hand, -1 is a unit in `\ZZ`, so it is included in the unit::
113
114
sage: x = ZZ['x'].0
115
sage: f = -1*(x-2)*(x-3)
116
sage: F = f.factor(); F
117
(-1) * (x - 3) * (x - 2)
118
sage: F.unit()
119
-1
120
sage: list(F)
121
[(x - 3, 1), (x - 2, 1)]
122
123
Factorizations can involve fairly abstract mathematical objects::
124
125
sage: F = ModularSymbols(11,4).factorization()
126
sage: F
127
(Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field) *
128
(Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field) *
129
(Modular Symbols subspace of dimension 2 of Modular Symbols space of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field)
130
sage: type(F)
131
<class 'sage.structure.factorization.Factorization'>
132
133
134
sage: K.<a> = NumberField(x^2 + 3); K
135
Number Field in a with defining polynomial x^2 + 3
136
sage: f = K.factor(15); f
137
(Fractional ideal (-a))^2 * (Fractional ideal (5))
138
sage: f.universe()
139
Monoid of ideals of Number Field in a with defining polynomial x^2 + 3
140
sage: f.unit()
141
Fractional ideal (1)
142
sage: g=K.factor(9); g
143
(Fractional ideal (-a))^4
144
sage: f.lcm(g)
145
(Fractional ideal (-a))^4 * (Fractional ideal (5))
146
sage: f.gcd(g)
147
(Fractional ideal (-a))^2
148
sage: f.is_integral()
149
True
150
151
TESTS::
152
153
sage: F = factor(-20); F
154
-1 * 2^2 * 5
155
sage: G = loads(dumps(F)); G
156
-1 * 2^2 * 5
157
sage: G == F
158
True
159
sage: G is F
160
False
161
162
AUTHORS:
163
164
- William Stein (2006-01-22): added unit part as suggested by David Kohel.
165
166
- William Stein (2008-01-17): wrote much of the documentation and
167
fixed a couple of bugs.
168
169
- Nick Alexander (2008-01-19): added support for non-commuting factors.
170
171
- John Cremona (2008-08-22): added division, lcm, gcd, is_integral and
172
universe functions
173
"""
174
175
#*****************************************************************************
176
#
177
# Sage: System for Algebra and Geometry Experimentation
178
#
179
# Copyright (C) 2005 William Stein <[email protected]>
180
#
181
# Distributed under the terms of the GNU General Public License (GPL)
182
# http://www.gnu.org/licenses/
183
#*****************************************************************************
184
185
from sage.structure.sage_object import SageObject
186
from sage.structure.sequence import Sequence
187
from sage.rings.integer import Integer
188
from sage.misc.all import prod
189
190
class Factorization(SageObject):
191
"""
192
A formal factorization of an object.
193
194
EXAMPLES::
195
196
sage: N = 2006
197
sage: F = N.factor(); F
198
2 * 17 * 59
199
sage: F.unit()
200
1
201
sage: F = factor(-2006); F
202
-1 * 2 * 17 * 59
203
sage: F.unit()
204
-1
205
sage: loads(F.dumps()) == F
206
True
207
sage: F = Factorization([(x,1/3)])
208
Traceback (most recent call last):
209
...
210
TypeError: exponents of factors must be integers
211
"""
212
def __init__(self, x, unit=None, cr=False, sort=True, simplify=True):
213
"""
214
Create a :class:`Factorization` object.
215
216
INPUT:
217
218
- ``x`` - a list of pairs (p, e) with e an integer;
219
otherwise a TypeError is raised
220
221
- ``unit`` - (default: 1) the unit part of the factorization.
222
223
- ``cr`` - (default: False) if True, print the factorization with
224
carriage returns between factors.
225
226
- ``sort`` - (default: True) if True, sort the factors by calling
227
the sort function ``self.sort()`` after creating the factorization
228
229
- ``simplify`` - (default: True) if True, remove duplicate
230
factors from the factorization. See the documentation for
231
self.simplify.
232
233
OUTPUT:
234
235
- a Factorization object
236
237
EXAMPLES:
238
We create a factorization with all the default options::
239
240
sage: Factorization([(2,3), (5, 1)])
241
2^3 * 5
242
243
We create a factorization with a specified unit part::
244
245
sage: Factorization([(2,3), (5, 1)], unit=-1)
246
-1 * 2^3 * 5
247
248
We try to create a factorization but with a string an exponent, which
249
results in a TypeError::
250
251
sage: Factorization([(2,3), (5, 'x')])
252
Traceback (most recent call last):
253
...
254
TypeError: exponents of factors must be integers
255
256
We create a factorization that puts newlines after each multiply sign
257
when printing. This is mainly useful when the primes are large::
258
259
sage: Factorization([(2,3), (5, 2)], cr=True)
260
2^3 *
261
5^2
262
263
Another factorization with newlines and nontrivial unit part, which
264
appears on a line by itself::
265
266
sage: Factorization([(2,3), (5, 2)], cr=True, unit=-2)
267
-2 *
268
2^3 *
269
5^2
270
271
A factorization, but where we do not sort the factors::
272
273
sage: Factorization([(5,3), (2, 3)], sort=False)
274
5^3 * 2^3
275
276
By default, in the commutative case, factorizations are sorted by the
277
prime base::
278
279
sage: Factorization([(2, 7), (5,2), (2, 5)])
280
2^12 * 5^2
281
sage: R.<a,b> = FreeAlgebra(QQ,2)
282
sage: Factorization([(a,1),(b,1),(a,2)])
283
a * b * a^2
284
285
Autosorting (the default) swaps around the factors below::
286
287
sage: F = Factorization([(ZZ^3, 2), (ZZ^2, 5)], cr=True); F
288
(Ambient free module of rank 2 over the principal ideal domain Integer Ring)^5 *
289
(Ambient free module of rank 3 over the principal ideal domain Integer Ring)^2
290
"""
291
if not isinstance(x, list):
292
raise TypeError, "x must be a list"
293
for i in xrange(len(x)):
294
t=x[i]
295
if not (isinstance(t, tuple) and len(t) == 2):
296
raise TypeError, "x must be a list of pairs (p, e) with e an integer"
297
if not isinstance(t[1],(int, long, Integer)):
298
try:
299
x[i]= (t[0], Integer(t[1]))
300
except TypeError:
301
raise TypeError, "exponents of factors must be integers"
302
303
try:
304
self.__universe = Sequence(t[0] for t in x).universe()
305
except TypeError:
306
self.__universe = None
307
308
self.__x = [ (t[0],int(t[1])) for t in x]
309
if unit is None:
310
if len(x) > 0:
311
try:
312
unit = self.__universe(1)
313
except (AttributeError, TypeError):
314
unit = Integer(1)
315
else:
316
unit = Integer(1)
317
self.__unit = unit
318
self.__cr = cr
319
if sort and self.is_commutative():
320
self.sort()
321
if simplify:
322
self.simplify()
323
324
def __getitem__(self, i):
325
"""
326
Return `i^{th}` factor of self.
327
328
EXAMPLES::
329
330
sage: a = factor(-75); a
331
-1 * 3 * 5^2
332
sage: a[0]
333
(3, 1)
334
sage: a[1]
335
(5, 2)
336
sage: a[-1]
337
(5, 2)
338
sage: a[5]
339
Traceback (most recent call last):
340
...
341
IndexError: list index out of range
342
"""
343
return self.__x.__getitem__(i)
344
345
def __setitem__(self, i, v):
346
"""
347
Set the `i^{th}` factor of self.
348
349
.. warning::
350
351
NOT ALLOWED -- Factorizations are immutable.
352
353
EXAMPLES::
354
355
sage: a = factor(-75); a
356
-1 * 3 * 5^2
357
sage: a[0] = (2,3)
358
Traceback (most recent call last):
359
...
360
TypeError: 'Factorization' object does not support item assignment
361
"""
362
raise TypeError, "'Factorization' object does not support item assignment"
363
364
def __len__(self):
365
"""
366
Return the number of prime factors of self, not counting
367
the unit part.
368
369
EXAMPLES::
370
371
sage: len(factor(15))
372
2
373
374
Note that the unit part is not included in the count::
375
376
sage: a = factor(-75); a
377
-1 * 3 * 5^2
378
sage: len(a)
379
2
380
sage: list(a)
381
[(3, 1), (5, 2)]
382
sage: len(list(a))
383
2
384
"""
385
return len(self.__x)
386
387
def __cmp__(self, other):
388
"""
389
Compare self and other. This compares the underlying
390
lists of self and other, ignoring the unit!
391
392
EXAMPLES:
393
394
We compare two contrived formal factorizations::
395
396
sage: a = Factorization([(2, 7), (5,2), (2, 5)])
397
sage: b = Factorization([(2, 7), (5,10), (7, 3)])
398
sage: a
399
2^12 * 5^2
400
sage: b
401
2^7 * 5^10 * 7^3
402
sage: a < b
403
True
404
sage: b < a
405
False
406
sage: a.value()
407
102400
408
sage: b.value()
409
428750000000
410
411
We compare factorizations of some polynomials::
412
413
sage: x = polygen(QQ)
414
sage: x^2 - 1 > x^2 - 4
415
True
416
sage: factor(x^2 - 1) > factor(x^2 - 4)
417
True
418
"""
419
if not isinstance(other, Factorization):
420
return cmp(type(self), type(other))
421
try:
422
return cmp(self.value(), other.value())
423
except:
424
c = cmp(self.__unit, other.__unit)
425
if c: return c
426
return list.__cmp__(self, other)
427
428
def __copy__(self):
429
r"""
430
Return a copy of self.
431
432
This is *not* a deepcopy -- only references to the factors are
433
returned, not copies of them. Use ``deepcopy(self)`` if you need
434
a deep copy of self.
435
436
EXAMPLES:
437
438
We create a factorization that has mutable primes::
439
440
sage: F = Factorization([([1,2], 5), ([5,6], 10)]); F
441
([1, 2])^5 * ([5, 6])^10
442
443
We make a copy of it::
444
445
sage: G = copy(F); G
446
([1, 2])^5 * ([5, 6])^10
447
sage: G is F
448
False
449
450
Note that if we change one of the mutable "primes" of F, this does
451
change G::
452
453
sage: F[1][0][0] = 'hello'
454
sage: G
455
([1, 2])^5 * (['hello', 6])^10
456
"""
457
# No need to sort, since the factorization is already sorted
458
# in whatever order is desired.
459
return Factorization(self.__x, unit=self.__unit, cr=self.__cr,
460
sort=False, simplify=False)
461
462
def __deepcopy__(self, memo):
463
r"""
464
Return a deep copy of self.
465
466
EXAMPLES:
467
468
We make a factorization that has mutable entries::
469
470
sage: F = Factorization([([1,2], 5), ([5,6], 10)]); F
471
([1, 2])^5 * ([5, 6])^10
472
473
Now we make a copy of it and a deep copy::
474
475
sage: K = copy(F)
476
sage: G = deepcopy(F); G
477
([1, 2])^5 * ([5, 6])^10
478
479
We change one of the mutable entries of F::
480
481
sage: F[0][0][0] = 10
482
483
This of course changes F::
484
485
sage: F
486
([10, 2])^5 * ([5, 6])^10
487
488
It also changes the copy K of F::
489
490
sage: K
491
([10, 2])^5 * ([5, 6])^10
492
493
It does *not* change the deep copy G::
494
495
sage: G
496
([1, 2])^5 * ([5, 6])^10
497
"""
498
import copy
499
return Factorization(copy.deepcopy(list(self), memo),
500
cr=self.__cr, sort=False, simplify=False)
501
502
def universe(self):
503
r"""
504
Return the parent structure of my factors.
505
506
.. note::
507
508
This used to be called ``base_ring``, but the universe
509
of a factorization need not be a ring.
510
511
EXAMPLES::
512
513
sage: F = factor(2006)
514
sage: F.universe()
515
Integer Ring
516
517
sage: R.<x,y,z> = FreeAlgebra(QQ, 3)
518
sage: F = Factorization([(z, 2)], 3)
519
sage: (F*F^-1).universe()
520
Free Algebra on 3 generators (x, y, z) over Rational Field
521
522
sage: F = ModularSymbols(11,4).factorization()
523
sage: F.universe()
524
"""
525
try:
526
return self.__universe
527
except AttributeError:
528
return None
529
530
def base_change(self, U):
531
"""
532
Return the factorization self, with its factors (including the
533
unit part) coerced into the universe `U`.
534
535
EXAMPLES::
536
537
sage: F = factor(2006)
538
sage: F.universe()
539
Integer Ring
540
sage: P.<x> = ZZ[]
541
sage: F.base_change(P).universe()
542
Univariate Polynomial Ring in x over Integer Ring
543
544
This method will return a TypeError if the coercion is not
545
possible::
546
547
sage: g = x^2 - 1
548
sage: F = factor(g); F
549
(x - 1) * (x + 1)
550
sage: F.universe()
551
Univariate Polynomial Ring in x over Integer Ring
552
sage: F.base_change(ZZ)
553
Traceback (most recent call last):
554
...
555
TypeError: Impossible to coerce the factors of (x - 1) * (x + 1) into Integer Ring
556
"""
557
if len(self) == 0:
558
return self
559
try:
560
return Factorization([(U(f[0]), f[1]) for f in list(self)], unit=U(self.unit()))
561
except TypeError:
562
raise TypeError, "Impossible to coerce the factors of %s into %s"%(self, U)
563
564
def is_commutative(self):
565
"""
566
Return True if my factors commute.
567
568
EXAMPLES::
569
570
sage: F = factor(2006)
571
sage: F.is_commutative()
572
True
573
sage: K = QuadraticField(23, 'a')
574
sage: F = K.factor(13)
575
sage: F.is_commutative()
576
True
577
sage: R.<x,y,z> = FreeAlgebra(QQ, 3)
578
sage: F = Factorization([(z, 2)], 3)
579
sage: F.is_commutative()
580
False
581
sage: (F*F^-1).is_commutative()
582
False
583
"""
584
try:
585
return self.universe().is_commutative()
586
except:
587
# This is not the mathematically correct default, but agrees with
588
# history -- we've always assumed factored things commute
589
return True
590
591
def _set_cr(self, cr):
592
"""
593
Change whether or not the factorization is printed with
594
carriage returns after each factor.
595
596
EXAMPLES::
597
598
sage: x = polygen(QQ,'x')
599
sage: F = factor(x^6 - 1); F
600
(x - 1) * (x + 1) * (x^2 - x + 1) * (x^2 + x + 1)
601
sage: F._set_cr(True); F
602
(x - 1) *
603
(x + 1) *
604
(x^2 - x + 1) *
605
(x^2 + x + 1)
606
sage: F._set_cr(False); F
607
(x - 1) * (x + 1) * (x^2 - x + 1) * (x^2 + x + 1)
608
"""
609
self.__cr = bool(cr)
610
611
def simplify(self):
612
"""
613
Combine adjacent products as much as possible.
614
615
TESTS::
616
617
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
618
sage: F = Factorization([(x,3), (y, 2), (y,2)], simplify=False); F
619
x^3 * y^2 * y^2
620
sage: F.simplify(); F
621
x^3 * y^4
622
sage: F * Factorization([(y, -2)], 2)
623
(2) * x^3 * y^2
624
"""
625
repeat = False
626
simp = []
627
import itertools
628
for obj, agroup in itertools.groupby(list(self), lambda x: x[0]):
629
xs = list(agroup)
630
if len(xs) > 1:
631
repeat = True
632
n = sum([x[1] for x in xs])
633
if n != 0:
634
simp.append((obj, n))
635
self.__x[0:] = simp
636
if repeat:
637
self.simplify()
638
639
def sort(self, _cmp=None):
640
r"""
641
Sort the factors in this factorization.
642
643
INPUT:
644
645
- ``_cmp`` - (default: None) comparison function
646
647
OUTPUT:
648
649
- changes this factorization to be sorted
650
651
If _cmp is None, we determine the comparison function as
652
follows: If the prime in the first factor has a dimension
653
method, then we sort based first on *dimension* then on
654
the exponent. If there is no dimension method, we next
655
attempt to sort based on a degree method, in which case, we
656
sort based first on *degree*, then exponent to break ties
657
when two factors have the same degree, and if those match
658
break ties based on the actual prime itself. If there is no
659
degree method, we sort based on dimension.
660
661
EXAMPLES:
662
663
We create a factored polynomial::
664
665
sage: x = polygen(QQ,'x')
666
sage: F = factor(x^3 + 1); F
667
(x + 1) * (x^2 - x + 1)
668
669
Then we sort it but using the negated version of the standard
670
Python cmp function::
671
672
sage: F.sort(_cmp = lambda x,y: -cmp(x,y))
673
sage: F
674
(x^2 - x + 1) * (x + 1)
675
"""
676
if len(self) == 0:
677
return
678
if _cmp is None:
679
try:
680
a = self.__x[0][0].dimension()
681
def _cmp(f,g):
682
"""
683
This is used internally for comparing. (indirect doctest)
684
685
EXAMPLES::
686
687
sage: factor(6)
688
2 * 3
689
"""
690
try:
691
return cmp((f[0].dimension(), f[1]), (g[0].dimension(),g[1]))
692
except (AttributeError, NotImplementedError):
693
return cmp((f[0],f[1]), (g[0], g[1]))
694
except (AttributeError, NotImplementedError):
695
try:
696
a = self.__x[0][0].degree()
697
def _cmp(f,g):
698
"""
699
This is used internally for comparing. (indirect doctest)
700
701
EXAMPLES::
702
703
sage: factor(6)
704
2 * 3
705
"""
706
try:
707
return cmp((f[0].degree(),f[1],f[0]), (g[0].degree(),g[1],g[0]))
708
except (AttributeError, NotImplementedError):
709
return cmp(f[0], g[0])
710
except (AttributeError, NotImplementedError, TypeError): # TypeError in case degree must take an argument, e.g., for symbolic expressions it has to.
711
self.__x.sort()
712
return
713
714
self.__x.sort(_cmp)
715
716
def unit(self):
717
r"""
718
Return the unit part of this factorization.
719
720
EXAMPLES:
721
722
We create a polynomial over the real double field and factor it::
723
724
sage: x = polygen(RDF, 'x')
725
sage: F = factor(-2*x^2 - 1); F
726
(-2.0) * (x^2 + 0.5)
727
728
Note that the unit part of the factorization is `-2.0`::
729
730
sage: F.unit()
731
-2.0
732
733
sage: F = factor(-2006); F
734
-1 * 2 * 17 * 59
735
sage: F.unit()
736
-1
737
"""
738
return self.__unit
739
740
def _cr(self):
741
"""
742
Return whether or not factorizations are printed with carriage
743
returns between factors.
744
745
EXAMPLES:
746
747
Our first example involves factoring an integer::
748
749
sage: F = factor(-93930); F
750
-1 * 2 * 3 * 5 * 31 * 101
751
sage: F._cr()
752
False
753
sage: F._set_cr(True)
754
sage: F._cr()
755
True
756
757
This of course looks funny::
758
759
sage: F
760
-1 *
761
2 *
762
3 *
763
5 *
764
31 *
765
101
766
767
Next we factor a modular symbols space::
768
769
sage: F = ModularSymbols(11).factor(); F
770
(Modular Symbols subspace of dimension 1 of ...) *
771
(Modular Symbols subspace of dimension 1 of ...) *
772
(Modular Symbols subspace of dimension 1 of ...)
773
"""
774
try:
775
return self.__cr
776
except AttributeError:
777
self.__cr = False
778
return False
779
780
def _repr_(self):
781
"""
782
Return the string representation of this factorization.
783
784
EXAMPLES::
785
786
sage: f = factor(-100); f
787
-1 * 2^2 * 5^2
788
sage: f._repr_()
789
'-1 * 2^2 * 5^2'
790
791
Note that the default printing of a factorization can be overloaded
792
using the rename method::
793
794
sage: f.rename('factorization of -100')
795
sage: f
796
factorization of -100
797
798
However _repr_ always prints normally::
799
800
sage: f._repr_()
801
'-1 * 2^2 * 5^2'
802
803
EXAMPLES::
804
805
sage: x = polygen(QQ)
806
sage: Factorization([(x-1,1), (x-2,2)])
807
(x - 1) * (x - 2)^2
808
sage: Factorization([(x + 1, -3)])
809
(x + 1)^-3
810
"""
811
cr = self._cr()
812
if len(self) == 0:
813
return repr(self.__unit)
814
try:
815
atomic = ((isinstance(self.__x[0][0], (int, long)) or \
816
self.universe().is_atomic_repr()))
817
except AttributeError:
818
atomic = False
819
s = ''
820
mul = ' * '
821
if cr:
822
mul += '\n'
823
x = self.__x[0][0]
824
if hasattr(x, 'parent'):
825
one = x.parent()(1)
826
else:
827
one = 1
828
for i in range(len(self)):
829
t = repr(self.__x[i][0])
830
n = self.__x[i][1]
831
if (n != 1 or len(self) > 1 or self.__unit != one) and not atomic \
832
and ('+' in t or '-' in t or ' ' in t):
833
t = '(%s)'%t
834
if n != 1:
835
t += '^%s'%n
836
s += t
837
if i < len(self)-1:
838
s += mul
839
if self.__unit != one:
840
if atomic:
841
u = repr(self.__unit)
842
else:
843
u = '(%s)'%self.__unit
844
s = u + mul + s
845
return s
846
847
def _latex_(self):
848
r"""
849
Return the LaTeX representation of this factorization.
850
851
EXAMPLES::
852
853
sage: f = factor(-100); f
854
-1 * 2^2 * 5^2
855
sage: latex(f)
856
-1 \cdot 2^{2} \cdot 5^{2}
857
sage: f._latex_()
858
'-1 \\cdot 2^{2} \\cdot 5^{2}'
859
sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_() # trac 12178
860
'(x^{2} + x + 1.000000000000000?)'
861
"""
862
if len(self) == 0:
863
return self.__unit._latex_()
864
try:
865
atomic = ((isinstance(self.__x[0][0], (int, long)) or \
866
self.universe().is_atomic_repr()))
867
except AttributeError:
868
atomic = False
869
s = ''
870
for i in range(len(self)):
871
t = self.__x[i][0]._latex_()
872
if not atomic and ('+' in t or '-' in t or ' ' in t):
873
t = '(%s)'%t
874
n = self.__x[i][1]
875
if n != 1:
876
t += '^{%s}'%n
877
s += t
878
if i < len(self)-1:
879
s += ' \\cdot '
880
if self.__unit != 1:
881
if atomic:
882
u = self.__unit._latex_()
883
else:
884
u = '\\left(%s\\right)'%self.__unit._latex_()
885
s = u + ' \\cdot ' + s
886
return s
887
888
def __add__(self, other):
889
"""
890
Return the (unfactored) sum of self and other.
891
892
EXAMPLES::
893
894
sage: factor(-10) + 16
895
6
896
sage: factor(10) - 16
897
-6
898
sage: factor(100) + factor(19)
899
119
900
"""
901
if isinstance(other, Factorization):
902
other = other.value()
903
return self.value() + other
904
905
def __sub__(self, other):
906
"""
907
Return the (unfactored) difference of self and other.
908
909
EXAMPLES::
910
911
sage: factor(-10) + 16
912
6
913
sage: factor(10) - 16
914
-6
915
"""
916
if isinstance(other, Factorization):
917
other = other.value()
918
return self.value() - other
919
920
def __radd__(self, left):
921
"""
922
Return the (unfactored) sum of self and left.
923
924
EXAMPLES::
925
926
sage: 16 + factor(-10)
927
6
928
"""
929
return self.value() + left
930
931
932
def __rsub__(self, left):
933
"""
934
Return the (unfactored) difference of left and self.
935
936
EXAMPLES::
937
938
sage: 16 - factor(10)
939
6
940
"""
941
return left - self.value()
942
943
def __neg__(self):
944
"""
945
Return negative of this factorization.
946
947
EXAMPLES::
948
949
sage: a = factor(-75); a
950
-1 * 3 * 5^2
951
sage: -a
952
3 * 5^2
953
sage: (-a).unit()
954
1
955
"""
956
unit = -self.__unit
957
return Factorization(list(self), unit, self.__cr,
958
sort=False, simplify=False)
959
960
def __rmul__(self, left):
961
"""
962
Return the product left * self, where left is not a Factorization.
963
964
EXAMPLES::
965
966
sage: a = factor(15); a
967
3 * 5
968
sage: -2 * a
969
-2 * 3 * 5
970
sage: a * -2
971
-2 * 3 * 5
972
sage: R.<x,y> = FreeAlgebra(QQ,2)
973
sage: f = Factorization([(x,2),(y,3)]); f
974
x^2 * y^3
975
sage: x * f
976
x^3 * y^3
977
sage: f * x
978
x^2 * y^3 * x
979
980
Note that this does not automatically factor ``left``::
981
982
sage: F = Factorization([(5,3), (2,3)])
983
sage: 46 * F
984
2^3 * 5^3 * 46
985
"""
986
return Factorization([(left, 1)]) * self
987
988
def __mul__(self, other):
989
r"""
990
Return the product of two factorizations, which is obtained by
991
combining together like factors.
992
993
If the two factorizations have different universes, this
994
method will attempt to find a common universe for the
995
product. A TypeError is raised if this is impossible.
996
997
EXAMPLES::
998
999
sage: factor(-10) * factor(-16)
1000
2^5 * 5
1001
sage: factor(-10) * factor(16)
1002
-1 * 2^5 * 5
1003
1004
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
1005
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
1006
x^3 * y^2 * x
1007
sage: F*F
1008
x^3 * y^2 * x^4 * y^2 * x
1009
sage: -1 * F
1010
(-1) * x^3 * y^2 * x
1011
1012
sage: P.<x> = ZZ[]
1013
sage: f = 2*x + 2
1014
sage: c = f.content(); g = f//c
1015
sage: Fc = factor(c); Fc.universe()
1016
Integer Ring
1017
sage: Fg = factor(g); Fg.universe()
1018
Univariate Polynomial Ring in x over Integer Ring
1019
sage: F = Fc * Fg; F.universe()
1020
Univariate Polynomial Ring in x over Integer Ring
1021
sage: [type(a[0]) for a in F]
1022
[<type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>,
1023
<type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>]
1024
"""
1025
if not isinstance(other, Factorization):
1026
return self * Factorization([(other, 1)])
1027
1028
if len(self) and len(other):
1029
try:
1030
# since self is a factorization, all its factors
1031
# are in the same universe.
1032
# the same is true for the factorization other.
1033
# so if we want to put the factorizations together we just
1034
# need to find a common universe for the first factor of
1035
# self and the first factor of other
1036
U = Sequence([self[0][0], other[0][0]]).universe()
1037
self = self.base_change(U)
1038
other = other.base_change(U)
1039
except TypeError:
1040
raise TypeError, "Cannot multiply %s and %s because they cannot be coerced into a common universe"%(self,other)
1041
1042
if self.is_commutative() and other.is_commutative():
1043
d1 = dict(self)
1044
d2 = dict(other)
1045
s = {}
1046
for a in set(d1.keys()).union(set(d2.keys())):
1047
s[a] = d1.get(a,0) + d2.get(a,0)
1048
return Factorization(list(s.iteritems()), unit=self.unit()*other.unit())
1049
else:
1050
return Factorization(list(self) + list(other), unit=self.unit()*other.unit())
1051
1052
def __pow__(self, n):
1053
"""
1054
Return the `n^{th}` power of a factorization, which is got by
1055
combining together like factors.
1056
1057
EXAMPLES::
1058
1059
sage: f = factor(-100); f
1060
-1 * 2^2 * 5^2
1061
sage: f^3
1062
-1 * 2^6 * 5^6
1063
sage: f^4
1064
2^8 * 5^8
1065
1066
sage: K.<a> = NumberField(x^3 - 39*x - 91)
1067
sage: F = K.factor(7); F
1068
(Fractional ideal (7, a)) * (Fractional ideal (7, a + 2)) * (Fractional ideal (7, a - 2))
1069
sage: F^9
1070
(Fractional ideal (7, a))^9 * (Fractional ideal (7, a + 2))^9 * (Fractional ideal (7, a - 2))^9
1071
1072
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
1073
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
1074
x^3 * y^2 * x
1075
sage: F**2
1076
x^3 * y^2 * x^4 * y^2 * x
1077
"""
1078
if not isinstance(n, Integer):
1079
try:
1080
n = Integer(n)
1081
except:
1082
raise TypeError, "Exponent n (= %s) must be an integer." % n
1083
if n == 1:
1084
return self
1085
if n == 0:
1086
return Factorization([])
1087
if self.is_commutative():
1088
return Factorization([(p, n*e) for p, e in self], unit=self.unit()**n, cr=self.__cr, sort=False, simplify=False)
1089
from sage.groups.generic import power
1090
return power(self, n, Factorization([]))
1091
1092
def __invert__(self):
1093
r"""
1094
Return the formal inverse of the factors in the factorization.
1095
1096
EXAMPLES::
1097
1098
sage: F = factor(2006); F
1099
2 * 17 * 59
1100
sage: F^-1
1101
2^-1 * 17^-1 * 59^-1
1102
1103
sage: R.<x,y> = FreeAlgebra(QQ, 2)
1104
sage: F = Factorization([(x,3), (y, 2), (x,1)], 2); F
1105
(2) * x^3 * y^2 * x
1106
sage: F^-1
1107
(1/2) * x^-1 * y^-2 * x^-3
1108
"""
1109
return Factorization([(p,-e) for p,e in reversed(self)],
1110
cr=self._cr(), unit=self.unit()**(-1))
1111
1112
def __div__(self, other):
1113
r"""
1114
Return the quotient of two factorizations, which is obtained by
1115
multiplying the first by the inverse of the second.
1116
1117
EXAMPLES::
1118
1119
sage: factor(-10) / factor(-16)
1120
2^-3 * 5
1121
sage: factor(-10) / factor(16)
1122
-1 * 2^-3 * 5
1123
1124
sage: R.<x,y> = FreeAlgebra(QQ, 2)
1125
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
1126
x^3 * y^2 * x
1127
sage: G = Factorization([(y, 1), (x,1)],1); G
1128
y * x
1129
sage: F / G
1130
x^3 * y
1131
"""
1132
if not isinstance(other, Factorization):
1133
return self / Factorization([(other, 1)])
1134
return self * other**-1
1135
1136
def value(self):
1137
"""
1138
Return the product of the factors in the factorization, multiplied out.
1139
1140
EXAMPLES::
1141
1142
sage: F = factor(-2006); F
1143
-1 * 2 * 17 * 59
1144
sage: F.value()
1145
-2006
1146
1147
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
1148
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
1149
x^3 * y^2 * x
1150
sage: F.value()
1151
x^3*y^2*x
1152
"""
1153
return prod([p**e for p,e in self.__x], self.__unit)
1154
1155
# Two aliases for ``value(self)``.
1156
expand = value
1157
prod = value
1158
1159
def gcd(self, other):
1160
r"""
1161
Return the gcd of two factorizations.
1162
1163
If the two factorizations have different universes, this
1164
method will attempt to find a common universe for the
1165
gcd. A TypeError is raised if this is impossible.
1166
1167
EXAMPLES::
1168
1169
sage: factor(-30).gcd(factor(-160))
1170
2 * 5
1171
sage: factor(gcd(-30,160))
1172
2 * 5
1173
1174
sage: R.<x> = ZZ[]
1175
sage: (factor(-20).gcd(factor(5*x+10))).universe()
1176
Univariate Polynomial Ring in x over Integer Ring
1177
"""
1178
if not isinstance(other, Factorization):
1179
raise NotImplementedError, "can't take gcd of factorization and non-factorization"
1180
1181
if len(self) and len(other):
1182
try:
1183
# first get the two factorizations to have the same
1184
# universe
1185
U = Sequence([self[0][0], other[0][0]]).universe()
1186
self = self.base_change(U)
1187
other = other.base_change(U)
1188
except TypeError:
1189
raise TypeError, "Cannot take the gcd of %s and %s because they cannot be coerced into a common universe"%(self,other)
1190
1191
if self.is_commutative() and other.is_commutative():
1192
d1 = dict(self)
1193
d2 = dict(other)
1194
s = {}
1195
for a in set(d1.keys()).intersection(set(d2.keys())):
1196
s[a] = min(d1[a],d2[a])
1197
return Factorization(list(s.iteritems()))
1198
else:
1199
raise NotImplementedError, "gcd is not implemented for non-commutative factorizations"
1200
1201
def lcm(self, other):
1202
r"""
1203
Return the lcm of two factorizations.
1204
1205
If the two factorizations have different universes, this
1206
method will attempt to find a common universe for the
1207
lcm. A TypeError is raised if this is impossible.
1208
1209
EXAMPLES::
1210
1211
sage: factor(-10).lcm(factor(-16))
1212
2^4 * 5
1213
sage: factor(lcm(-10,16))
1214
2^4 * 5
1215
1216
sage: R.<x> = ZZ[]
1217
sage: (factor(-20).lcm(factor(5*x+10))).universe()
1218
Univariate Polynomial Ring in x over Integer Ring
1219
"""
1220
if not isinstance(other, Factorization):
1221
raise NotImplementedError, "can't take lcm of factorization and non-factorization"
1222
1223
if len(self) and len(other):
1224
try:
1225
# first get the two factorizations to have the same
1226
# universe
1227
U = Sequence([self[0][0], other[0][0]]).universe()
1228
self = self.base_change(U)
1229
other = other.base_change(U)
1230
except TypeError:
1231
raise TypeError, "Cannot take the lcm of %s and %s because they cannot be coerced into a common universe"%(self,other)
1232
1233
if self.is_commutative() and other.is_commutative():
1234
d1 = dict(self)
1235
d2 = dict(other)
1236
s = {}
1237
for a in set(d1.keys()).union(set(d2.keys())):
1238
s[a] = max(d1.get(a,0),d2.get(a,0))
1239
return Factorization(list(s.iteritems()))
1240
else:
1241
raise NotImplementedError, "lcm is not implemented for non-commutative factorizations"
1242
1243
def is_integral(self):
1244
r"""
1245
Return True iff all exponents of this Factorization are non-negative.
1246
1247
EXAMPLES::
1248
1249
sage: F = factor(-10); F
1250
-1 * 2 * 5
1251
sage: F.is_integral()
1252
True
1253
1254
sage: F = factor(-10) / factor(16); F
1255
-1 * 2^-3 * 5
1256
sage: F.is_integral()
1257
False
1258
1259
"""
1260
return all([e >=0 for p,e in self.__x])
1261
1262
def radical(self):
1263
"""
1264
Return the factorization of the radical of the value of self.
1265
1266
First, check that all exponents in the factorization are
1267
positive, raise ValueError otherwise. If all exponents are
1268
positive, return self with all exponents set to 1 and with the
1269
unit set to 1.
1270
1271
EXAMPLES::
1272
1273
sage: F = factor(-100); F
1274
-1 * 2^2 * 5^2
1275
sage: F.radical()
1276
2 * 5
1277
sage: factor(1/2).radical()
1278
Traceback (most recent call last):
1279
...
1280
ValueError: All exponents in the factorization must be positive.
1281
"""
1282
if not all([e > 0 for p,e in self.__x]):
1283
raise ValueError, "All exponents in the factorization must be positive."
1284
return Factorization([(p,1) for p,e in self.__x], unit=self.unit().parent()(1), cr=self.__cr, sort=False, simplify=False)
1285
1286
def radical_value(self):
1287
"""
1288
Return the product of the prime factors in self.
1289
1290
First, check that all exponents in the factorization are
1291
positive, raise ValueError otherwise. If all exponents are
1292
positive, return the product of the prime factors in self.
1293
This should be functionally equivalent to
1294
self.radical().value()
1295
1296
EXAMPLES::
1297
1298
sage: F = factor(-100); F
1299
-1 * 2^2 * 5^2
1300
sage: F.radical_value()
1301
10
1302
sage: factor(1/2).radical_value()
1303
Traceback (most recent call last):
1304
...
1305
ValueError: All exponents in the factorization must be positive.
1306
"""
1307
if not all([e > 0 for p,e in self.__x]):
1308
raise ValueError, "All exponents in the factorization must be positive."
1309
return prod([p for p,e in self.__x])
1310
1311
1312