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