passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.45.dist-info/METADATA +115 -0
- passagemath_objects-10.6.45.dist-info/RECORD +280 -0
- passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
- passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
- passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2106 -0
- sage/categories/morphism.cpython-313-x86_64-linux-musl.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3290 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-313-x86_64-linux-musl.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-313-x86_64-linux-musl.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +275 -0
- sage/misc/sage_timeit_class.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-313-x86_64-linux-musl.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-313-x86_64-linux-musl.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-313-x86_64-linux-musl.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
|
@@ -0,0 +1,1422 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
Factorizations
|
|
4
|
+
|
|
5
|
+
The :class:`Factorization` class provides a structure for holding quite
|
|
6
|
+
general lists of objects with integer multiplicities. These may hold
|
|
7
|
+
the results of an arithmetic or algebraic factorization, where the
|
|
8
|
+
objects may be primes or irreducible polynomials and the
|
|
9
|
+
multiplicities are the (nonzero) exponents in the factorization. For
|
|
10
|
+
other types of examples, see below.
|
|
11
|
+
|
|
12
|
+
:class:`Factorization` class objects contain a ``list``, so can be
|
|
13
|
+
printed nicely and be manipulated like a list of prime-exponent pairs,
|
|
14
|
+
or easily turned into a plain list. For example, we factor the
|
|
15
|
+
integer `-45`::
|
|
16
|
+
|
|
17
|
+
sage: F = factor(-45)
|
|
18
|
+
|
|
19
|
+
This returns an object of type :class:`Factorization`::
|
|
20
|
+
|
|
21
|
+
sage: type(F)
|
|
22
|
+
<class 'sage.structure.factorization_integer.IntegerFactorization'>
|
|
23
|
+
|
|
24
|
+
It prints in a nice factored form::
|
|
25
|
+
|
|
26
|
+
sage: F
|
|
27
|
+
-1 * 3^2 * 5
|
|
28
|
+
|
|
29
|
+
There is an underlying list representation, which ignores the 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: # needs sage.libs.pari
|
|
71
|
+
sage: x = QQ['x'].0
|
|
72
|
+
sage: f = -5*(x-2)*(x-3)
|
|
73
|
+
sage: f
|
|
74
|
+
-5*x^2 + 25*x - 30
|
|
75
|
+
sage: F = f.factor(); F
|
|
76
|
+
(-5) * (x - 3) * (x - 2)
|
|
77
|
+
sage: F.unit()
|
|
78
|
+
-5
|
|
79
|
+
sage: F.value()
|
|
80
|
+
-5*x^2 + 25*x - 30
|
|
81
|
+
|
|
82
|
+
The underlying list is the list of pairs `(p_i, e_i)`, where each
|
|
83
|
+
`p_i` is a 'prime' and each `e_i` is an integer. The unit part
|
|
84
|
+
is discarded by the list::
|
|
85
|
+
|
|
86
|
+
sage: # needs sage.libs.pari
|
|
87
|
+
sage: list(F)
|
|
88
|
+
[(x - 3, 1), (x - 2, 1)]
|
|
89
|
+
sage: len(F)
|
|
90
|
+
2
|
|
91
|
+
sage: F[1]
|
|
92
|
+
(x - 2, 1)
|
|
93
|
+
|
|
94
|
+
In the ring `\ZZ[x]`, the integer `-5` is not a unit, so the
|
|
95
|
+
factorization has three factors::
|
|
96
|
+
|
|
97
|
+
sage: # needs sage.libs.pari
|
|
98
|
+
sage: x = ZZ['x'].0
|
|
99
|
+
sage: f = -5*(x-2)*(x-3)
|
|
100
|
+
sage: f
|
|
101
|
+
-5*x^2 + 25*x - 30
|
|
102
|
+
sage: F = f.factor(); F
|
|
103
|
+
(-1) * 5 * (x - 3) * (x - 2)
|
|
104
|
+
sage: F.universe()
|
|
105
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
106
|
+
sage: F.unit()
|
|
107
|
+
-1
|
|
108
|
+
sage: list(F)
|
|
109
|
+
[(5, 1), (x - 3, 1), (x - 2, 1)]
|
|
110
|
+
sage: F.value()
|
|
111
|
+
-5*x^2 + 25*x - 30
|
|
112
|
+
sage: len(F)
|
|
113
|
+
3
|
|
114
|
+
|
|
115
|
+
On the other hand, -1 is a unit in `\ZZ`, so it is included in the unit::
|
|
116
|
+
|
|
117
|
+
sage: # needs sage.libs.pari
|
|
118
|
+
sage: x = ZZ['x'].0
|
|
119
|
+
sage: f = -1 * (x-2) * (x-3)
|
|
120
|
+
sage: F = f.factor(); F
|
|
121
|
+
(-1) * (x - 3) * (x - 2)
|
|
122
|
+
sage: F.unit()
|
|
123
|
+
-1
|
|
124
|
+
sage: list(F)
|
|
125
|
+
[(x - 3, 1), (x - 2, 1)]
|
|
126
|
+
|
|
127
|
+
Factorizations can involve fairly abstract mathematical objects::
|
|
128
|
+
|
|
129
|
+
sage: # needs sage.modular
|
|
130
|
+
sage: F = ModularSymbols(11,4).factorization(); F
|
|
131
|
+
(Modular Symbols subspace of dimension 2 of Modular Symbols space
|
|
132
|
+
of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field) *
|
|
133
|
+
(Modular Symbols subspace of dimension 2 of Modular Symbols space
|
|
134
|
+
of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field) *
|
|
135
|
+
(Modular Symbols subspace of dimension 2 of Modular Symbols space
|
|
136
|
+
of dimension 6 for Gamma_0(11) of weight 4 with sign 0 over Rational Field)
|
|
137
|
+
sage: type(F)
|
|
138
|
+
<class 'sage.structure.factorization.Factorization'>
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
sage: # needs sage.rings.number_field
|
|
142
|
+
sage: x = ZZ['x'].0
|
|
143
|
+
sage: K.<a> = NumberField(x^2 + 3); K
|
|
144
|
+
Number Field in a with defining polynomial x^2 + 3
|
|
145
|
+
sage: f = K.factor(15); f
|
|
146
|
+
(Fractional ideal (-a))^2 * (Fractional ideal (5))
|
|
147
|
+
sage: f.universe()
|
|
148
|
+
Monoid of ideals of Number Field in a with defining polynomial x^2 + 3
|
|
149
|
+
sage: f.unit()
|
|
150
|
+
Fractional ideal (1)
|
|
151
|
+
sage: g = K.factor(9); g
|
|
152
|
+
(Fractional ideal (-a))^4
|
|
153
|
+
sage: f.lcm(g)
|
|
154
|
+
(Fractional ideal (-a))^4 * (Fractional ideal (5))
|
|
155
|
+
sage: f.gcd(g)
|
|
156
|
+
(Fractional ideal (-a))^2
|
|
157
|
+
sage: f.is_integral()
|
|
158
|
+
True
|
|
159
|
+
|
|
160
|
+
TESTS::
|
|
161
|
+
|
|
162
|
+
sage: F = factor(-20); F
|
|
163
|
+
-1 * 2^2 * 5
|
|
164
|
+
sage: G = loads(dumps(F)); G
|
|
165
|
+
-1 * 2^2 * 5
|
|
166
|
+
sage: G == F
|
|
167
|
+
True
|
|
168
|
+
sage: G is F
|
|
169
|
+
False
|
|
170
|
+
|
|
171
|
+
AUTHORS:
|
|
172
|
+
|
|
173
|
+
- William Stein (2006-01-22): added unit part as suggested by David Kohel.
|
|
174
|
+
|
|
175
|
+
- William Stein (2008-01-17): wrote much of the documentation and
|
|
176
|
+
fixed a couple of bugs.
|
|
177
|
+
|
|
178
|
+
- Nick Alexander (2008-01-19): added support for non-commuting factors.
|
|
179
|
+
|
|
180
|
+
- John Cremona (2008-08-22): added division, lcm, gcd, is_integral and
|
|
181
|
+
universe functions
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
# ****************************************************************************
|
|
185
|
+
# Copyright (C) 2005 William Stein <wstein@gmail.com>
|
|
186
|
+
#
|
|
187
|
+
# This program is free software: you can redistribute it and/or modify
|
|
188
|
+
# it under the terms of the GNU General Public License as published by
|
|
189
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
190
|
+
# (at your option) any later version.
|
|
191
|
+
# https://www.gnu.org/licenses/
|
|
192
|
+
# ****************************************************************************
|
|
193
|
+
|
|
194
|
+
from sage.structure.sage_object import SageObject
|
|
195
|
+
from sage.structure.element import Element
|
|
196
|
+
from sage.structure.sequence import Sequence
|
|
197
|
+
from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal
|
|
198
|
+
from sage.misc.cachefunc import cached_method
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@richcmp_method
|
|
202
|
+
class Factorization(SageObject):
|
|
203
|
+
"""
|
|
204
|
+
A formal factorization of an object.
|
|
205
|
+
|
|
206
|
+
EXAMPLES::
|
|
207
|
+
|
|
208
|
+
sage: N = 2006
|
|
209
|
+
sage: F = N.factor(); F
|
|
210
|
+
2 * 17 * 59
|
|
211
|
+
sage: F.unit()
|
|
212
|
+
1
|
|
213
|
+
sage: F = factor(-2006); F
|
|
214
|
+
-1 * 2 * 17 * 59
|
|
215
|
+
sage: F.unit()
|
|
216
|
+
-1
|
|
217
|
+
sage: loads(F.dumps()) == F
|
|
218
|
+
True
|
|
219
|
+
sage: F = Factorization([(x, 1/3)]) # needs sage.symbolic
|
|
220
|
+
Traceback (most recent call last):
|
|
221
|
+
...
|
|
222
|
+
TypeError: no conversion of this rational to integer
|
|
223
|
+
"""
|
|
224
|
+
def __init__(self, x, unit=None, cr=False, sort=True, simplify=True):
|
|
225
|
+
"""
|
|
226
|
+
Create a :class:`Factorization` object.
|
|
227
|
+
|
|
228
|
+
INPUT:
|
|
229
|
+
|
|
230
|
+
- ``x`` -- list of pairs (p, e) with e an integer
|
|
231
|
+
otherwise a :exc:`TypeError` is raised
|
|
232
|
+
|
|
233
|
+
- ``unit`` -- (default: 1) the unit part of the factorization
|
|
234
|
+
|
|
235
|
+
- ``cr`` -- (default: ``False``) if ``True``, print the factorization
|
|
236
|
+
with carriage returns between factors
|
|
237
|
+
|
|
238
|
+
- ``sort`` -- (default: ``True``) if ``True``, sort the factors by
|
|
239
|
+
calling the sort function ``self.sort()`` after creating
|
|
240
|
+
the factorization
|
|
241
|
+
|
|
242
|
+
- ``simplify`` -- (default: ``True``) if ``True``, remove duplicate
|
|
243
|
+
factors from the factorization. See the documentation for
|
|
244
|
+
self.simplify.
|
|
245
|
+
|
|
246
|
+
OUTPUT: a Factorization object
|
|
247
|
+
|
|
248
|
+
EXAMPLES:
|
|
249
|
+
|
|
250
|
+
We create a factorization with all the default options::
|
|
251
|
+
|
|
252
|
+
sage: Factorization([(2,3), (5, 1)])
|
|
253
|
+
2^3 * 5
|
|
254
|
+
|
|
255
|
+
We create a factorization with a specified unit part::
|
|
256
|
+
|
|
257
|
+
sage: Factorization([(2,3), (5, 1)], unit=-1)
|
|
258
|
+
-1 * 2^3 * 5
|
|
259
|
+
|
|
260
|
+
We try to create a factorization but with a string an exponent, which
|
|
261
|
+
results in a TypeError::
|
|
262
|
+
|
|
263
|
+
sage: Factorization([(2,3), (5, 'x')])
|
|
264
|
+
Traceback (most recent call last):
|
|
265
|
+
...
|
|
266
|
+
TypeError: unable to convert 'x' to an integer
|
|
267
|
+
|
|
268
|
+
We create a factorization that puts newlines after each multiply sign
|
|
269
|
+
when printing. This is mainly useful when the primes are large::
|
|
270
|
+
|
|
271
|
+
sage: Factorization([(2,3), (5, 2)], cr=True)
|
|
272
|
+
2^3 *
|
|
273
|
+
5^2
|
|
274
|
+
|
|
275
|
+
Another factorization with newlines and nontrivial unit part, which
|
|
276
|
+
appears on a line by itself::
|
|
277
|
+
|
|
278
|
+
sage: Factorization([(2,3), (5, 2)], cr=True, unit=-2)
|
|
279
|
+
-2 *
|
|
280
|
+
2^3 *
|
|
281
|
+
5^2
|
|
282
|
+
|
|
283
|
+
A factorization, but where we do not sort the factors::
|
|
284
|
+
|
|
285
|
+
sage: Factorization([(5,3), (2, 3)], sort=False)
|
|
286
|
+
5^3 * 2^3
|
|
287
|
+
|
|
288
|
+
By default, in the commutative case, factorizations are sorted by the
|
|
289
|
+
prime base::
|
|
290
|
+
|
|
291
|
+
sage: Factorization([(2, 7), (5,2), (2, 5)])
|
|
292
|
+
2^12 * 5^2
|
|
293
|
+
sage: R.<a,b> = FreeAlgebra(QQ, 2) # needs sage.combinat sage.modules
|
|
294
|
+
sage: Factorization([(a,1), (b,1), (a,2)]) # needs sage.combinat sage.modules
|
|
295
|
+
a * b * a^2
|
|
296
|
+
|
|
297
|
+
Autosorting (the default) swaps around the factors below::
|
|
298
|
+
|
|
299
|
+
sage: F = Factorization([(ZZ^3, 2), (ZZ^2, 5)], cr=True); F # needs sage.modules
|
|
300
|
+
(Ambient free module of rank 2 over the principal ideal domain Integer Ring)^5 *
|
|
301
|
+
(Ambient free module of rank 3 over the principal ideal domain Integer Ring)^2
|
|
302
|
+
"""
|
|
303
|
+
from sage.rings.integer import Integer
|
|
304
|
+
x = [(p, Integer(e)) for (p, e) in x]
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
self.__universe = Sequence(t[0] for t in x).universe()
|
|
308
|
+
except TypeError:
|
|
309
|
+
self.__universe = None
|
|
310
|
+
|
|
311
|
+
self.__x = [(t[0], int(t[1])) for t in x]
|
|
312
|
+
if unit is None:
|
|
313
|
+
if x:
|
|
314
|
+
try:
|
|
315
|
+
unit = self.__universe(1)
|
|
316
|
+
except (AttributeError, TypeError):
|
|
317
|
+
unit = Integer(1)
|
|
318
|
+
else:
|
|
319
|
+
unit = Integer(1)
|
|
320
|
+
self.__unit = unit
|
|
321
|
+
self.__cr = cr
|
|
322
|
+
if sort and self.is_commutative():
|
|
323
|
+
self.sort()
|
|
324
|
+
if simplify:
|
|
325
|
+
self.simplify()
|
|
326
|
+
|
|
327
|
+
def __getitem__(self, i):
|
|
328
|
+
"""
|
|
329
|
+
Return `i`-th factor of ``self``.
|
|
330
|
+
|
|
331
|
+
EXAMPLES::
|
|
332
|
+
|
|
333
|
+
sage: a = factor(-75); a
|
|
334
|
+
-1 * 3 * 5^2
|
|
335
|
+
sage: a[0]
|
|
336
|
+
(3, 1)
|
|
337
|
+
sage: a[1]
|
|
338
|
+
(5, 2)
|
|
339
|
+
sage: a[-1]
|
|
340
|
+
(5, 2)
|
|
341
|
+
sage: a[5]
|
|
342
|
+
Traceback (most recent call last):
|
|
343
|
+
...
|
|
344
|
+
IndexError: list index out of range
|
|
345
|
+
"""
|
|
346
|
+
return self.__x[i]
|
|
347
|
+
|
|
348
|
+
def __setitem__(self, i, v):
|
|
349
|
+
"""
|
|
350
|
+
Set the `i`-th factor of ``self``.
|
|
351
|
+
|
|
352
|
+
.. warning::
|
|
353
|
+
|
|
354
|
+
NOT ALLOWED -- Factorizations are immutable.
|
|
355
|
+
|
|
356
|
+
EXAMPLES::
|
|
357
|
+
|
|
358
|
+
sage: a = factor(-75); a
|
|
359
|
+
-1 * 3 * 5^2
|
|
360
|
+
sage: a[0] = (2,3)
|
|
361
|
+
Traceback (most recent call last):
|
|
362
|
+
...
|
|
363
|
+
TypeError: 'Factorization' object does not support item assignment
|
|
364
|
+
"""
|
|
365
|
+
raise TypeError("'Factorization' object does not support item assignment")
|
|
366
|
+
|
|
367
|
+
def __len__(self):
|
|
368
|
+
"""
|
|
369
|
+
Return the number of prime factors of ``self``, not counting
|
|
370
|
+
the unit part.
|
|
371
|
+
|
|
372
|
+
EXAMPLES::
|
|
373
|
+
|
|
374
|
+
sage: len(factor(15))
|
|
375
|
+
2
|
|
376
|
+
|
|
377
|
+
Note that the unit part is not included in the count::
|
|
378
|
+
|
|
379
|
+
sage: a = factor(-75); a
|
|
380
|
+
-1 * 3 * 5^2
|
|
381
|
+
sage: len(a)
|
|
382
|
+
2
|
|
383
|
+
sage: list(a)
|
|
384
|
+
[(3, 1), (5, 2)]
|
|
385
|
+
sage: len(list(a))
|
|
386
|
+
2
|
|
387
|
+
"""
|
|
388
|
+
return len(self.__x)
|
|
389
|
+
|
|
390
|
+
def __richcmp__(self, other, op):
|
|
391
|
+
"""
|
|
392
|
+
Compare ``self`` and ``other``.
|
|
393
|
+
|
|
394
|
+
This first compares the values.
|
|
395
|
+
|
|
396
|
+
If values are equal, this compares the units.
|
|
397
|
+
|
|
398
|
+
If units are equal, this compares the underlying lists of
|
|
399
|
+
``self`` and ``other``.
|
|
400
|
+
|
|
401
|
+
EXAMPLES:
|
|
402
|
+
|
|
403
|
+
We compare two contrived formal factorizations::
|
|
404
|
+
|
|
405
|
+
sage: a = Factorization([(2, 7), (5,2), (2, 5)])
|
|
406
|
+
sage: b = Factorization([(2, 7), (5,10), (7, 3)])
|
|
407
|
+
sage: a
|
|
408
|
+
2^12 * 5^2
|
|
409
|
+
sage: b
|
|
410
|
+
2^7 * 5^10 * 7^3
|
|
411
|
+
sage: a < b
|
|
412
|
+
True
|
|
413
|
+
sage: b < a
|
|
414
|
+
False
|
|
415
|
+
sage: a.value()
|
|
416
|
+
102400
|
|
417
|
+
sage: b.value()
|
|
418
|
+
428750000000
|
|
419
|
+
|
|
420
|
+
We compare factorizations of some polynomials::
|
|
421
|
+
|
|
422
|
+
sage: x = polygen(QQ)
|
|
423
|
+
sage: x^2 - 1 > x^2 - 4
|
|
424
|
+
True
|
|
425
|
+
sage: factor(x^2 - 1) > factor(x^2 - 4) # needs sage.libs.pari
|
|
426
|
+
True
|
|
427
|
+
"""
|
|
428
|
+
if not isinstance(other, Factorization):
|
|
429
|
+
return NotImplemented
|
|
430
|
+
|
|
431
|
+
lx = self.value()
|
|
432
|
+
rx = other.value()
|
|
433
|
+
if lx != rx:
|
|
434
|
+
return richcmp_not_equal(lx, rx, op)
|
|
435
|
+
|
|
436
|
+
lx = self.__unit
|
|
437
|
+
rx = other.__unit
|
|
438
|
+
if lx != rx:
|
|
439
|
+
return richcmp_not_equal(lx, rx, op)
|
|
440
|
+
|
|
441
|
+
return richcmp(self.__x, other.__x, op)
|
|
442
|
+
|
|
443
|
+
def __copy__(self):
|
|
444
|
+
r"""
|
|
445
|
+
Return a copy of ``self``.
|
|
446
|
+
|
|
447
|
+
This is *not* a deepcopy -- only references to the factors are
|
|
448
|
+
returned, not copies of them. Use ``deepcopy(self)`` if you need
|
|
449
|
+
a deep copy of ``self``.
|
|
450
|
+
|
|
451
|
+
EXAMPLES:
|
|
452
|
+
|
|
453
|
+
We create a factorization that has mutable primes::
|
|
454
|
+
|
|
455
|
+
sage: F = Factorization([([1,2], 5), ([5,6], 10)]); F
|
|
456
|
+
([1, 2])^5 * ([5, 6])^10
|
|
457
|
+
|
|
458
|
+
We make a copy of it::
|
|
459
|
+
|
|
460
|
+
sage: G = copy(F); G
|
|
461
|
+
([1, 2])^5 * ([5, 6])^10
|
|
462
|
+
sage: G is F
|
|
463
|
+
False
|
|
464
|
+
|
|
465
|
+
Note that if we change one of the mutable "primes" of F, this does
|
|
466
|
+
change G::
|
|
467
|
+
|
|
468
|
+
sage: F[1][0][0] = 'hello'
|
|
469
|
+
sage: G
|
|
470
|
+
([1, 2])^5 * (['hello', 6])^10
|
|
471
|
+
"""
|
|
472
|
+
# No need to sort, since the factorization is already sorted
|
|
473
|
+
# in whatever order is desired.
|
|
474
|
+
return Factorization(self.__x, unit=self.__unit, cr=self.__cr,
|
|
475
|
+
sort=False, simplify=False)
|
|
476
|
+
|
|
477
|
+
def __deepcopy__(self, memo):
|
|
478
|
+
r"""
|
|
479
|
+
Return a deep copy of ``self``.
|
|
480
|
+
|
|
481
|
+
EXAMPLES:
|
|
482
|
+
|
|
483
|
+
We make a factorization that has mutable entries::
|
|
484
|
+
|
|
485
|
+
sage: F = Factorization([([1,2], 5), ([5,6], 10)]); F
|
|
486
|
+
([1, 2])^5 * ([5, 6])^10
|
|
487
|
+
|
|
488
|
+
Now we make a copy of it and a deep copy::
|
|
489
|
+
|
|
490
|
+
sage: K = copy(F)
|
|
491
|
+
sage: G = deepcopy(F); G
|
|
492
|
+
([1, 2])^5 * ([5, 6])^10
|
|
493
|
+
|
|
494
|
+
We change one of the mutable entries of F::
|
|
495
|
+
|
|
496
|
+
sage: F[0][0][0] = 10
|
|
497
|
+
|
|
498
|
+
This of course changes F::
|
|
499
|
+
|
|
500
|
+
sage: F
|
|
501
|
+
([10, 2])^5 * ([5, 6])^10
|
|
502
|
+
|
|
503
|
+
It also changes the copy K of F::
|
|
504
|
+
|
|
505
|
+
sage: K
|
|
506
|
+
([10, 2])^5 * ([5, 6])^10
|
|
507
|
+
|
|
508
|
+
It does *not* change the deep copy G::
|
|
509
|
+
|
|
510
|
+
sage: G
|
|
511
|
+
([1, 2])^5 * ([5, 6])^10
|
|
512
|
+
"""
|
|
513
|
+
import copy
|
|
514
|
+
return Factorization(copy.deepcopy(list(self), memo),
|
|
515
|
+
cr=self.__cr, sort=False, simplify=False)
|
|
516
|
+
|
|
517
|
+
def universe(self):
|
|
518
|
+
r"""
|
|
519
|
+
Return the parent structure of my factors.
|
|
520
|
+
|
|
521
|
+
.. NOTE::
|
|
522
|
+
|
|
523
|
+
This used to be called ``base_ring``, but the universe
|
|
524
|
+
of a factorization need not be a ring.
|
|
525
|
+
|
|
526
|
+
EXAMPLES::
|
|
527
|
+
|
|
528
|
+
sage: F = factor(2006)
|
|
529
|
+
sage: F.universe()
|
|
530
|
+
Integer Ring
|
|
531
|
+
|
|
532
|
+
sage: R.<x,y,z> = FreeAlgebra(QQ, 3) # needs sage.combinat sage.modules
|
|
533
|
+
sage: F = Factorization([(z, 2)], 3) # needs sage.combinat sage.modules
|
|
534
|
+
sage: (F*F^-1).universe() # needs sage.combinat sage.modules
|
|
535
|
+
Free Algebra on 3 generators (x, y, z) over Rational Field
|
|
536
|
+
|
|
537
|
+
sage: F = ModularSymbols(11,4).factorization() # needs sage.modular
|
|
538
|
+
sage: F.universe() # needs sage.modular
|
|
539
|
+
"""
|
|
540
|
+
try:
|
|
541
|
+
return self.__universe
|
|
542
|
+
except AttributeError:
|
|
543
|
+
return None
|
|
544
|
+
|
|
545
|
+
def base_change(self, U):
|
|
546
|
+
"""
|
|
547
|
+
Return the factorization ``self``, with its factors (including the
|
|
548
|
+
unit part) coerced into the universe `U`.
|
|
549
|
+
|
|
550
|
+
EXAMPLES::
|
|
551
|
+
|
|
552
|
+
sage: F = factor(2006)
|
|
553
|
+
sage: F.universe()
|
|
554
|
+
Integer Ring
|
|
555
|
+
sage: P.<x> = ZZ[]
|
|
556
|
+
sage: F.base_change(P).universe()
|
|
557
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
558
|
+
|
|
559
|
+
This method will return a :exc:`TypeError` if the coercion is not
|
|
560
|
+
possible::
|
|
561
|
+
|
|
562
|
+
sage: g = x^2 - 1
|
|
563
|
+
sage: F = factor(g); F # needs sage.libs.pari
|
|
564
|
+
(x - 1) * (x + 1)
|
|
565
|
+
sage: F.universe() # needs sage.libs.pari
|
|
566
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
567
|
+
sage: F.base_change(ZZ) # needs sage.libs.pari
|
|
568
|
+
Traceback (most recent call last):
|
|
569
|
+
...
|
|
570
|
+
TypeError: Impossible to coerce the factors of (x - 1) * (x + 1) into Integer Ring
|
|
571
|
+
"""
|
|
572
|
+
if len(self) == 0:
|
|
573
|
+
return self
|
|
574
|
+
try:
|
|
575
|
+
return Factorization([(U(f[0]), f[1]) for f in list(self)], unit=U(self.unit()))
|
|
576
|
+
except TypeError:
|
|
577
|
+
raise TypeError("Impossible to coerce the factors of %s into %s" % (self, U))
|
|
578
|
+
|
|
579
|
+
def is_commutative(self) -> bool:
|
|
580
|
+
"""
|
|
581
|
+
Return whether the factors commute.
|
|
582
|
+
|
|
583
|
+
EXAMPLES::
|
|
584
|
+
|
|
585
|
+
sage: F = factor(2006)
|
|
586
|
+
sage: F.is_commutative()
|
|
587
|
+
True
|
|
588
|
+
|
|
589
|
+
sage: # needs sage.rings.number_field
|
|
590
|
+
sage: K = QuadraticField(23, 'a')
|
|
591
|
+
sage: F = K.factor(13)
|
|
592
|
+
sage: F.is_commutative()
|
|
593
|
+
True
|
|
594
|
+
|
|
595
|
+
sage: # needs sage.combinat sage.modules
|
|
596
|
+
sage: R.<x,y,z> = FreeAlgebra(QQ, 3)
|
|
597
|
+
sage: F = Factorization([(z, 2)], 3)
|
|
598
|
+
sage: F.is_commutative()
|
|
599
|
+
False
|
|
600
|
+
sage: (F*F^-1).is_commutative()
|
|
601
|
+
False
|
|
602
|
+
"""
|
|
603
|
+
try:
|
|
604
|
+
return self.universe().is_commutative()
|
|
605
|
+
except Exception:
|
|
606
|
+
# This is not the mathematically correct default, but agrees with
|
|
607
|
+
# history -- we've always assumed factored things commute
|
|
608
|
+
return True
|
|
609
|
+
|
|
610
|
+
def _set_cr(self, cr):
|
|
611
|
+
"""
|
|
612
|
+
Change whether or not the factorization is printed with
|
|
613
|
+
carriage returns after each factor.
|
|
614
|
+
|
|
615
|
+
EXAMPLES::
|
|
616
|
+
|
|
617
|
+
sage: x = polygen(QQ,'x')
|
|
618
|
+
sage: F = factor(x^6 - 1); F # needs sage.libs.pari
|
|
619
|
+
(x - 1) * (x + 1) * (x^2 - x + 1) * (x^2 + x + 1)
|
|
620
|
+
sage: F._set_cr(True); F # needs sage.libs.pari
|
|
621
|
+
(x - 1) *
|
|
622
|
+
(x + 1) *
|
|
623
|
+
(x^2 - x + 1) *
|
|
624
|
+
(x^2 + x + 1)
|
|
625
|
+
sage: F._set_cr(False); F # needs sage.libs.pari
|
|
626
|
+
(x - 1) * (x + 1) * (x^2 - x + 1) * (x^2 + x + 1)
|
|
627
|
+
"""
|
|
628
|
+
self.__cr = bool(cr)
|
|
629
|
+
|
|
630
|
+
def simplify(self):
|
|
631
|
+
"""
|
|
632
|
+
Combine adjacent products as much as possible.
|
|
633
|
+
|
|
634
|
+
TESTS::
|
|
635
|
+
|
|
636
|
+
sage: # needs sage.combinat sage.modules
|
|
637
|
+
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
|
|
638
|
+
sage: F = Factorization([(x,3), (y, 2), (y,2)], simplify=False); F
|
|
639
|
+
x^3 * y^2 * y^2
|
|
640
|
+
sage: F.simplify(); F
|
|
641
|
+
x^3 * y^4
|
|
642
|
+
sage: F * Factorization([(y, -2)], 2)
|
|
643
|
+
(2) * x^3 * y^2
|
|
644
|
+
"""
|
|
645
|
+
repeat = False
|
|
646
|
+
simp = []
|
|
647
|
+
import itertools
|
|
648
|
+
for obj, agroup in itertools.groupby(list(self), lambda x: x[0]):
|
|
649
|
+
xs = list(agroup)
|
|
650
|
+
if len(xs) > 1:
|
|
651
|
+
repeat = True
|
|
652
|
+
n = sum([x[1] for x in xs])
|
|
653
|
+
if n != 0:
|
|
654
|
+
simp.append((obj, n))
|
|
655
|
+
self.__x[0:] = simp
|
|
656
|
+
if repeat:
|
|
657
|
+
self.simplify()
|
|
658
|
+
|
|
659
|
+
def sort(self, key=None):
|
|
660
|
+
r"""
|
|
661
|
+
Sort the factors in this factorization.
|
|
662
|
+
|
|
663
|
+
INPUT:
|
|
664
|
+
|
|
665
|
+
- ``key`` -- (default: ``None``) comparison key
|
|
666
|
+
|
|
667
|
+
OUTPUT: changes this factorization to be sorted (inplace)
|
|
668
|
+
|
|
669
|
+
If ``key`` is ``None``, we determine the comparison key as
|
|
670
|
+
follows:
|
|
671
|
+
|
|
672
|
+
If the prime in the first factor has a dimension
|
|
673
|
+
method, then we sort based first on *dimension* then on
|
|
674
|
+
the exponent.
|
|
675
|
+
|
|
676
|
+
If there is no dimension method, we next
|
|
677
|
+
attempt to sort based on a degree method, in which case, we
|
|
678
|
+
sort based first on *degree*, then exponent to break ties
|
|
679
|
+
when two factors have the same degree, and if those match
|
|
680
|
+
break ties based on the actual prime itself.
|
|
681
|
+
|
|
682
|
+
Otherwise, we sort according to the prime itself.
|
|
683
|
+
|
|
684
|
+
EXAMPLES:
|
|
685
|
+
|
|
686
|
+
We create a factored polynomial::
|
|
687
|
+
|
|
688
|
+
sage: x = polygen(QQ, 'x')
|
|
689
|
+
sage: F = factor(x^3 + 1); F # needs sage.libs.pari
|
|
690
|
+
(x + 1) * (x^2 - x + 1)
|
|
691
|
+
|
|
692
|
+
We sort it by decreasing degree::
|
|
693
|
+
|
|
694
|
+
sage: F.sort(key=lambda x: (-x[0].degree(), x)) # needs sage.libs.pari
|
|
695
|
+
sage: F # needs sage.libs.pari
|
|
696
|
+
(x^2 - x + 1) * (x + 1)
|
|
697
|
+
"""
|
|
698
|
+
if len(self) == 0:
|
|
699
|
+
return
|
|
700
|
+
|
|
701
|
+
if key is not None:
|
|
702
|
+
self.__x.sort(key=key)
|
|
703
|
+
return
|
|
704
|
+
|
|
705
|
+
a = self.__x[0][0]
|
|
706
|
+
sort_key = None
|
|
707
|
+
if hasattr(a, 'dimension'):
|
|
708
|
+
try:
|
|
709
|
+
a.dimension()
|
|
710
|
+
|
|
711
|
+
def sort_key(f):
|
|
712
|
+
return (f[0].dimension(), f[1], f[0])
|
|
713
|
+
except (AttributeError, NotImplementedError, TypeError):
|
|
714
|
+
pass
|
|
715
|
+
elif hasattr(a, 'degree'):
|
|
716
|
+
try:
|
|
717
|
+
a.degree()
|
|
718
|
+
|
|
719
|
+
def sort_key(f):
|
|
720
|
+
return (f[0].degree(), f[1], f[0])
|
|
721
|
+
except (AttributeError, NotImplementedError, TypeError):
|
|
722
|
+
pass
|
|
723
|
+
|
|
724
|
+
if sort_key is None:
|
|
725
|
+
|
|
726
|
+
def sort_key(f):
|
|
727
|
+
return f[0]
|
|
728
|
+
|
|
729
|
+
self.__x.sort(key=sort_key)
|
|
730
|
+
|
|
731
|
+
def unit(self):
|
|
732
|
+
r"""
|
|
733
|
+
Return the unit part of this factorization.
|
|
734
|
+
|
|
735
|
+
EXAMPLES:
|
|
736
|
+
|
|
737
|
+
We create a polynomial over the real double field and factor it::
|
|
738
|
+
|
|
739
|
+
sage: x = polygen(RDF, 'x')
|
|
740
|
+
sage: F = factor(-2*x^2 - 1); F # needs numpy
|
|
741
|
+
(-2.0) * (x^2 + 0.5000000000000001)
|
|
742
|
+
|
|
743
|
+
Note that the unit part of the factorization is `-2.0`::
|
|
744
|
+
|
|
745
|
+
sage: F.unit() # needs numpy
|
|
746
|
+
-2.0
|
|
747
|
+
|
|
748
|
+
sage: F = factor(-2006); F
|
|
749
|
+
-1 * 2 * 17 * 59
|
|
750
|
+
sage: F.unit()
|
|
751
|
+
-1
|
|
752
|
+
"""
|
|
753
|
+
return self.__unit
|
|
754
|
+
|
|
755
|
+
def _cr(self):
|
|
756
|
+
"""
|
|
757
|
+
Return whether or not factorizations are printed with carriage
|
|
758
|
+
returns between factors.
|
|
759
|
+
|
|
760
|
+
EXAMPLES:
|
|
761
|
+
|
|
762
|
+
Our first example involves factoring an integer::
|
|
763
|
+
|
|
764
|
+
sage: F = factor(-93930); F
|
|
765
|
+
-1 * 2 * 3 * 5 * 31 * 101
|
|
766
|
+
sage: F._cr()
|
|
767
|
+
False
|
|
768
|
+
sage: F._set_cr(True)
|
|
769
|
+
sage: F._cr()
|
|
770
|
+
True
|
|
771
|
+
|
|
772
|
+
This of course looks funny::
|
|
773
|
+
|
|
774
|
+
sage: F
|
|
775
|
+
-1 *
|
|
776
|
+
2 *
|
|
777
|
+
3 *
|
|
778
|
+
5 *
|
|
779
|
+
31 *
|
|
780
|
+
101
|
|
781
|
+
|
|
782
|
+
Next we factor a modular symbols space::
|
|
783
|
+
|
|
784
|
+
sage: F = ModularSymbols(11).factor(); F # needs sage.modular
|
|
785
|
+
(Modular Symbols subspace of dimension 1 of ...) *
|
|
786
|
+
(Modular Symbols subspace of dimension 1 of ...) *
|
|
787
|
+
(Modular Symbols subspace of dimension 1 of ...)
|
|
788
|
+
"""
|
|
789
|
+
try:
|
|
790
|
+
return self.__cr
|
|
791
|
+
except AttributeError:
|
|
792
|
+
self.__cr = False
|
|
793
|
+
return False
|
|
794
|
+
|
|
795
|
+
def _repr_(self):
|
|
796
|
+
"""
|
|
797
|
+
Return the string representation of this factorization.
|
|
798
|
+
|
|
799
|
+
EXAMPLES::
|
|
800
|
+
|
|
801
|
+
sage: f = factor(-100); f
|
|
802
|
+
-1 * 2^2 * 5^2
|
|
803
|
+
sage: f._repr_()
|
|
804
|
+
'-1 * 2^2 * 5^2'
|
|
805
|
+
|
|
806
|
+
Note that the default printing of a factorization can be overloaded
|
|
807
|
+
using the rename method::
|
|
808
|
+
|
|
809
|
+
sage: f.rename('factorization of -100')
|
|
810
|
+
sage: f
|
|
811
|
+
factorization of -100
|
|
812
|
+
|
|
813
|
+
However ``_repr_`` always prints normally::
|
|
814
|
+
|
|
815
|
+
sage: f._repr_()
|
|
816
|
+
'-1 * 2^2 * 5^2'
|
|
817
|
+
|
|
818
|
+
EXAMPLES::
|
|
819
|
+
|
|
820
|
+
sage: x = polygen(QQ)
|
|
821
|
+
sage: Factorization([(x-1,1), (x-2,2)])
|
|
822
|
+
(x - 1) * (x - 2)^2
|
|
823
|
+
sage: Factorization([(x + 1, -3)])
|
|
824
|
+
(x + 1)^-3
|
|
825
|
+
"""
|
|
826
|
+
cr = self._cr()
|
|
827
|
+
if len(self) == 0:
|
|
828
|
+
return repr(self.__unit)
|
|
829
|
+
s = ''
|
|
830
|
+
mul = ' * '
|
|
831
|
+
if cr:
|
|
832
|
+
mul += '\n'
|
|
833
|
+
x = self.__x[0][0]
|
|
834
|
+
try:
|
|
835
|
+
atomic = (isinstance(x, int) or
|
|
836
|
+
self.universe()._repr_option('element_is_atomic'))
|
|
837
|
+
except AttributeError:
|
|
838
|
+
atomic = False
|
|
839
|
+
|
|
840
|
+
if isinstance(x, Element):
|
|
841
|
+
one = x.parent()(1)
|
|
842
|
+
else:
|
|
843
|
+
one = 1
|
|
844
|
+
|
|
845
|
+
for i in range(len(self)):
|
|
846
|
+
t = repr(self.__x[i][0])
|
|
847
|
+
n = self.__x[i][1]
|
|
848
|
+
if not atomic and (n != 1 or len(self) > 1 or self.__unit != one):
|
|
849
|
+
if '+' in t or '-' in t or ' ' in t:
|
|
850
|
+
t = '(%s)' % t
|
|
851
|
+
if n != 1:
|
|
852
|
+
t += '^%s' % n
|
|
853
|
+
s += t
|
|
854
|
+
if i < len(self) - 1:
|
|
855
|
+
s += mul
|
|
856
|
+
if self.__unit != one:
|
|
857
|
+
if atomic:
|
|
858
|
+
u = repr(self.__unit)
|
|
859
|
+
else:
|
|
860
|
+
u = '(%s)' % self.__unit
|
|
861
|
+
s = u + mul + s
|
|
862
|
+
return s
|
|
863
|
+
|
|
864
|
+
def _latex_(self):
|
|
865
|
+
r"""
|
|
866
|
+
Return the LaTeX representation of this factorization.
|
|
867
|
+
|
|
868
|
+
EXAMPLES::
|
|
869
|
+
|
|
870
|
+
sage: f = factor(-100); f
|
|
871
|
+
-1 * 2^2 * 5^2
|
|
872
|
+
sage: latex(f)
|
|
873
|
+
-1 \cdot 2^{2} \cdot 5^{2}
|
|
874
|
+
sage: f._latex_()
|
|
875
|
+
'-1 \\cdot 2^{2} \\cdot 5^{2}'
|
|
876
|
+
sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_() # Issue #12178 # needs sage.rings.number_field
|
|
877
|
+
'(x^{2} + x + 1.000000000000000?)'
|
|
878
|
+
"""
|
|
879
|
+
if len(self) == 0:
|
|
880
|
+
return self.__unit._latex_()
|
|
881
|
+
try:
|
|
882
|
+
atomic = (isinstance(self.__x[0][0], int) or
|
|
883
|
+
self.universe()._repr_option('element_is_atomic'))
|
|
884
|
+
except AttributeError:
|
|
885
|
+
atomic = False
|
|
886
|
+
s = ''
|
|
887
|
+
for i in range(len(self)):
|
|
888
|
+
t = self.__x[i][0]._latex_()
|
|
889
|
+
if not atomic and ('+' in t or '-' in t or ' ' in t):
|
|
890
|
+
t = '(%s)' % t
|
|
891
|
+
n = self.__x[i][1]
|
|
892
|
+
if n != 1:
|
|
893
|
+
t += '^{%s}' % n
|
|
894
|
+
s += t
|
|
895
|
+
if i < len(self) - 1:
|
|
896
|
+
s += ' \\cdot '
|
|
897
|
+
if self.__unit != 1:
|
|
898
|
+
if atomic:
|
|
899
|
+
u = self.__unit._latex_()
|
|
900
|
+
else:
|
|
901
|
+
u = '\\left(%s\\right)' % self.__unit._latex_()
|
|
902
|
+
s = u + ' \\cdot ' + s
|
|
903
|
+
return s
|
|
904
|
+
|
|
905
|
+
@cached_method
|
|
906
|
+
def __pari__(self):
|
|
907
|
+
"""
|
|
908
|
+
Return the PARI factorization matrix corresponding to ``self``.
|
|
909
|
+
|
|
910
|
+
EXAMPLES::
|
|
911
|
+
|
|
912
|
+
sage: f = factor(-24)
|
|
913
|
+
sage: pari(f) # needs sage.libs.pari
|
|
914
|
+
[-1, 1; 2, 3; 3, 1]
|
|
915
|
+
|
|
916
|
+
sage: R.<x> = QQ[]
|
|
917
|
+
sage: g = factor(x^10 - 1) # needs sage.libs.pari
|
|
918
|
+
sage: pari(g) # needs sage.libs.pari
|
|
919
|
+
[x - 1, 1; x + 1, 1; x^4 - x^3 + x^2 - x + 1, 1; x^4 + x^3 + x^2 + x + 1, 1]
|
|
920
|
+
"""
|
|
921
|
+
from sage.libs.pari import pari
|
|
922
|
+
from itertools import chain
|
|
923
|
+
|
|
924
|
+
n = len(self)
|
|
925
|
+
if self.__unit == 1:
|
|
926
|
+
init = ()
|
|
927
|
+
else:
|
|
928
|
+
init = (self.__unit, 1)
|
|
929
|
+
n += 1
|
|
930
|
+
# concatenate (p, e) tuples
|
|
931
|
+
entries = init + tuple(chain.from_iterable(self))
|
|
932
|
+
return pari.matrix(n, 2, entries)
|
|
933
|
+
|
|
934
|
+
def __add__(self, other):
|
|
935
|
+
"""
|
|
936
|
+
Return the (unfactored) sum of ``self`` and ``other``.
|
|
937
|
+
|
|
938
|
+
EXAMPLES::
|
|
939
|
+
|
|
940
|
+
sage: factor(-10) + 16
|
|
941
|
+
6
|
|
942
|
+
sage: factor(10) - 16
|
|
943
|
+
-6
|
|
944
|
+
sage: factor(100) + factor(19)
|
|
945
|
+
119
|
|
946
|
+
"""
|
|
947
|
+
if isinstance(other, Factorization):
|
|
948
|
+
other = other.value()
|
|
949
|
+
return self.value() + other
|
|
950
|
+
|
|
951
|
+
def __sub__(self, other):
|
|
952
|
+
"""
|
|
953
|
+
Return the (unfactored) difference of ``self`` and ``other``.
|
|
954
|
+
|
|
955
|
+
EXAMPLES::
|
|
956
|
+
|
|
957
|
+
sage: factor(-10) + 16
|
|
958
|
+
6
|
|
959
|
+
sage: factor(10) - 16
|
|
960
|
+
-6
|
|
961
|
+
"""
|
|
962
|
+
if isinstance(other, Factorization):
|
|
963
|
+
other = other.value()
|
|
964
|
+
return self.value() - other
|
|
965
|
+
|
|
966
|
+
def __radd__(self, left):
|
|
967
|
+
"""
|
|
968
|
+
Return the (unfactored) sum of ``self`` and ``left``.
|
|
969
|
+
|
|
970
|
+
EXAMPLES::
|
|
971
|
+
|
|
972
|
+
sage: 16 + factor(-10)
|
|
973
|
+
6
|
|
974
|
+
"""
|
|
975
|
+
return self.value() + left
|
|
976
|
+
|
|
977
|
+
def __rsub__(self, left):
|
|
978
|
+
"""
|
|
979
|
+
Return the (unfactored) difference of ``left`` and ``self``.
|
|
980
|
+
|
|
981
|
+
EXAMPLES::
|
|
982
|
+
|
|
983
|
+
sage: 16 - factor(10)
|
|
984
|
+
6
|
|
985
|
+
"""
|
|
986
|
+
return left - self.value()
|
|
987
|
+
|
|
988
|
+
def __neg__(self):
|
|
989
|
+
"""
|
|
990
|
+
Return negative of this factorization.
|
|
991
|
+
|
|
992
|
+
EXAMPLES::
|
|
993
|
+
|
|
994
|
+
sage: a = factor(-75); a
|
|
995
|
+
-1 * 3 * 5^2
|
|
996
|
+
sage: -a
|
|
997
|
+
3 * 5^2
|
|
998
|
+
sage: (-a).unit()
|
|
999
|
+
1
|
|
1000
|
+
"""
|
|
1001
|
+
unit = -self.__unit
|
|
1002
|
+
return Factorization(list(self), unit, self.__cr,
|
|
1003
|
+
sort=False, simplify=False)
|
|
1004
|
+
|
|
1005
|
+
def __rmul__(self, left):
|
|
1006
|
+
"""
|
|
1007
|
+
Return the product ``left * self``, where ``left`` is not a Factorization.
|
|
1008
|
+
|
|
1009
|
+
EXAMPLES::
|
|
1010
|
+
|
|
1011
|
+
sage: a = factor(15); a
|
|
1012
|
+
3 * 5
|
|
1013
|
+
sage: -2 * a
|
|
1014
|
+
-2 * 3 * 5
|
|
1015
|
+
sage: a * -2
|
|
1016
|
+
-2 * 3 * 5
|
|
1017
|
+
sage: R.<x,y> = FreeAlgebra(QQ, 2) # needs sage.combinat sage.modules
|
|
1018
|
+
sage: f = Factorization([(x,2), (y,3)]); f # needs sage.combinat sage.modules
|
|
1019
|
+
x^2 * y^3
|
|
1020
|
+
sage: x * f # needs sage.combinat sage.modules
|
|
1021
|
+
x^3 * y^3
|
|
1022
|
+
sage: f * x # needs sage.combinat sage.modules
|
|
1023
|
+
x^2 * y^3 * x
|
|
1024
|
+
|
|
1025
|
+
Note that this does not automatically factor ``left``::
|
|
1026
|
+
|
|
1027
|
+
sage: F = Factorization([(5,3), (2,3)])
|
|
1028
|
+
sage: 46 * F
|
|
1029
|
+
2^3 * 5^3 * 46
|
|
1030
|
+
"""
|
|
1031
|
+
return Factorization([(left, 1)]) * self
|
|
1032
|
+
|
|
1033
|
+
def __mul__(self, other):
|
|
1034
|
+
r"""
|
|
1035
|
+
Return the product of two factorizations, which is obtained by
|
|
1036
|
+
combining together like factors.
|
|
1037
|
+
|
|
1038
|
+
If the two factorizations have different universes, this
|
|
1039
|
+
method will attempt to find a common universe for the
|
|
1040
|
+
product. A :exc:`TypeError` is raised if this is impossible.
|
|
1041
|
+
|
|
1042
|
+
EXAMPLES::
|
|
1043
|
+
|
|
1044
|
+
sage: factor(-10) * factor(-16)
|
|
1045
|
+
2^5 * 5
|
|
1046
|
+
sage: factor(-10) * factor(16)
|
|
1047
|
+
-1 * 2^5 * 5
|
|
1048
|
+
|
|
1049
|
+
sage: # needs sage.combinat sage.modules
|
|
1050
|
+
sage: R.<x,y> = FreeAlgebra(ZZ, 2)
|
|
1051
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
|
|
1052
|
+
x^3 * y^2 * x
|
|
1053
|
+
sage: F*F
|
|
1054
|
+
x^3 * y^2 * x^4 * y^2 * x
|
|
1055
|
+
sage: -1 * F
|
|
1056
|
+
(-1) * x^3 * y^2 * x
|
|
1057
|
+
|
|
1058
|
+
sage: P.<x> = ZZ[]
|
|
1059
|
+
sage: f = 2*x + 2
|
|
1060
|
+
sage: c = f.content(); g = f//c
|
|
1061
|
+
sage: Fc = factor(c); Fc.universe()
|
|
1062
|
+
Integer Ring
|
|
1063
|
+
sage: Fg = factor(g); Fg.universe()
|
|
1064
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1065
|
+
sage: F = Fc * Fg; F.universe()
|
|
1066
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1067
|
+
sage: [type(a[0]) for a in F]
|
|
1068
|
+
[<... 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>,
|
|
1069
|
+
<... 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>]
|
|
1070
|
+
"""
|
|
1071
|
+
if not isinstance(other, Factorization):
|
|
1072
|
+
return self * Factorization([(other, 1)])
|
|
1073
|
+
|
|
1074
|
+
if len(self) and len(other):
|
|
1075
|
+
try:
|
|
1076
|
+
# since self is a factorization, all its factors
|
|
1077
|
+
# are in the same universe.
|
|
1078
|
+
# the same is true for the factorization other.
|
|
1079
|
+
# so if we want to put the factorizations together we just
|
|
1080
|
+
# need to find a common universe for the first factor of
|
|
1081
|
+
# self and the first factor of other
|
|
1082
|
+
U = Sequence([self[0][0], other[0][0]]).universe()
|
|
1083
|
+
self = self.base_change(U)
|
|
1084
|
+
other = other.base_change(U)
|
|
1085
|
+
except TypeError:
|
|
1086
|
+
raise TypeError("Cannot multiply %s and %s because they cannot be coerced into a common universe" % (self, other))
|
|
1087
|
+
|
|
1088
|
+
if self.is_commutative() and other.is_commutative():
|
|
1089
|
+
d1 = dict(self)
|
|
1090
|
+
d2 = dict(other)
|
|
1091
|
+
s = {}
|
|
1092
|
+
for a in set(d1).union(set(d2)):
|
|
1093
|
+
s[a] = d1.get(a, 0) + d2.get(a, 0)
|
|
1094
|
+
return Factorization(list(s.items()), unit=self.unit() * other.unit())
|
|
1095
|
+
else:
|
|
1096
|
+
return Factorization(list(self) + list(other), unit=self.unit() * other.unit())
|
|
1097
|
+
|
|
1098
|
+
def __pow__(self, n):
|
|
1099
|
+
"""
|
|
1100
|
+
Return the `n`-th power of a factorization, which is got by
|
|
1101
|
+
combining together like factors.
|
|
1102
|
+
|
|
1103
|
+
EXAMPLES::
|
|
1104
|
+
|
|
1105
|
+
sage: f = factor(-100); f
|
|
1106
|
+
-1 * 2^2 * 5^2
|
|
1107
|
+
sage: f^3
|
|
1108
|
+
-1 * 2^6 * 5^6
|
|
1109
|
+
sage: f^4
|
|
1110
|
+
2^8 * 5^8
|
|
1111
|
+
|
|
1112
|
+
sage: x = polygen(ZZ, 'x')
|
|
1113
|
+
sage: K.<a> = NumberField(x^3 - 39*x - 91) # needs sage.rings.number_field
|
|
1114
|
+
sage: F = K.factor(7); F # needs sage.rings.number_field
|
|
1115
|
+
(Fractional ideal (7, a)) * (Fractional ideal (7, a + 2)) * (Fractional ideal (7, a - 2))
|
|
1116
|
+
sage: F^9 # needs sage.rings.number_field
|
|
1117
|
+
(Fractional ideal (7, a))^9 * (Fractional ideal (7, a + 2))^9 * (Fractional ideal (7, a - 2))^9
|
|
1118
|
+
|
|
1119
|
+
sage: R.<x,y> = FreeAlgebra(ZZ, 2) # needs sage.combinat sage.modules
|
|
1120
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F # needs sage.combinat sage.modules
|
|
1121
|
+
x^3 * y^2 * x
|
|
1122
|
+
sage: F**2 # needs sage.combinat sage.modules
|
|
1123
|
+
x^3 * y^2 * x^4 * y^2 * x
|
|
1124
|
+
"""
|
|
1125
|
+
from sage.rings.integer import Integer
|
|
1126
|
+
if not isinstance(n, Integer):
|
|
1127
|
+
try:
|
|
1128
|
+
n = Integer(n)
|
|
1129
|
+
except TypeError:
|
|
1130
|
+
raise TypeError("Exponent n (= %s) must be an integer." % n)
|
|
1131
|
+
if n == 1:
|
|
1132
|
+
return self
|
|
1133
|
+
if n == 0:
|
|
1134
|
+
return Factorization([])
|
|
1135
|
+
if self.is_commutative():
|
|
1136
|
+
return Factorization([(p, n * e) for p, e in self], unit=self.unit()**n,
|
|
1137
|
+
cr=self.__cr, sort=False, simplify=False)
|
|
1138
|
+
if n < 0:
|
|
1139
|
+
self = ~self
|
|
1140
|
+
n = -n
|
|
1141
|
+
from sage.arith.power import generic_power
|
|
1142
|
+
return generic_power(self, n)
|
|
1143
|
+
|
|
1144
|
+
def __invert__(self):
|
|
1145
|
+
r"""
|
|
1146
|
+
Return the formal inverse of the factors in the factorization.
|
|
1147
|
+
|
|
1148
|
+
EXAMPLES::
|
|
1149
|
+
|
|
1150
|
+
sage: F = factor(2006); F
|
|
1151
|
+
2 * 17 * 59
|
|
1152
|
+
sage: F^-1
|
|
1153
|
+
2^-1 * 17^-1 * 59^-1
|
|
1154
|
+
|
|
1155
|
+
sage: R.<x,y> = FreeAlgebra(QQ, 2) # needs sage.combinat sage.modules
|
|
1156
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)], 2); F # needs sage.combinat sage.modules
|
|
1157
|
+
(2) * x^3 * y^2 * x
|
|
1158
|
+
sage: F^-1 # needs sage.combinat sage.modules
|
|
1159
|
+
(1/2) * x^-1 * y^-2 * x^-3
|
|
1160
|
+
"""
|
|
1161
|
+
return Factorization([(p, -e) for p, e in reversed(self)],
|
|
1162
|
+
cr=self._cr(), unit=self.unit()**(-1))
|
|
1163
|
+
|
|
1164
|
+
def __truediv__(self, other):
|
|
1165
|
+
r"""
|
|
1166
|
+
Return the quotient of two factorizations, which is obtained by
|
|
1167
|
+
multiplying the first by the inverse of the second.
|
|
1168
|
+
|
|
1169
|
+
EXAMPLES::
|
|
1170
|
+
|
|
1171
|
+
sage: factor(-10) / factor(-16)
|
|
1172
|
+
2^-3 * 5
|
|
1173
|
+
sage: factor(-10) / factor(16)
|
|
1174
|
+
-1 * 2^-3 * 5
|
|
1175
|
+
|
|
1176
|
+
sage: # needs sage.combinat sage.modules
|
|
1177
|
+
sage: R.<x,y> = FreeAlgebra(QQ, 2)
|
|
1178
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F
|
|
1179
|
+
x^3 * y^2 * x
|
|
1180
|
+
sage: G = Factorization([(y, 1), (x,1)],1); G
|
|
1181
|
+
y * x
|
|
1182
|
+
sage: F / G
|
|
1183
|
+
x^3 * y
|
|
1184
|
+
"""
|
|
1185
|
+
if not isinstance(other, Factorization):
|
|
1186
|
+
return self / Factorization([(other, 1)])
|
|
1187
|
+
return self * other**-1
|
|
1188
|
+
|
|
1189
|
+
def __call__(self, *args, **kwds):
|
|
1190
|
+
"""
|
|
1191
|
+
Implement the substitution.
|
|
1192
|
+
|
|
1193
|
+
This is assuming that each term can be substituted.
|
|
1194
|
+
|
|
1195
|
+
There is another mechanism for substitution
|
|
1196
|
+
in symbolic products.
|
|
1197
|
+
|
|
1198
|
+
EXAMPLES::
|
|
1199
|
+
|
|
1200
|
+
sage: # needs sage.combinat sage.modules
|
|
1201
|
+
sage: R.<x,y> = FreeAlgebra(QQ, 2)
|
|
1202
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)])
|
|
1203
|
+
sage: F(x=4)
|
|
1204
|
+
4^3 * y^2 * 4
|
|
1205
|
+
sage: F.subs({y:2})
|
|
1206
|
+
x^3 * 2^2 * x
|
|
1207
|
+
|
|
1208
|
+
sage: R.<x,y> = PolynomialRing(QQ, 2)
|
|
1209
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)])
|
|
1210
|
+
sage: F(x=4)
|
|
1211
|
+
4 * 4^3 * y^2
|
|
1212
|
+
sage: F.subs({y:x})
|
|
1213
|
+
x * x^2 * x^3
|
|
1214
|
+
sage: F(x=y+x)
|
|
1215
|
+
(x + y) * y^2 * (x + y)^3
|
|
1216
|
+
|
|
1217
|
+
TESTS::
|
|
1218
|
+
|
|
1219
|
+
sage: R.<x,y> = PolynomialRing(QQ, 2)
|
|
1220
|
+
sage: F = Factorization([(x-2,3), (y+3, 2)])
|
|
1221
|
+
sage: F(x=2)
|
|
1222
|
+
0
|
|
1223
|
+
|
|
1224
|
+
sage: QQt = QQ['t'].fraction_field()
|
|
1225
|
+
sage: t = QQt.gen()
|
|
1226
|
+
sage: R.<x> = PolynomialRing(QQt, 1)
|
|
1227
|
+
sage: F = Factorization([(x,3), (x+t, 2)], unit=QQt.gen())
|
|
1228
|
+
sage: F(t=0)
|
|
1229
|
+
0
|
|
1230
|
+
|
|
1231
|
+
sage: # needs sage.libs.pari sage.modules
|
|
1232
|
+
sage: R.<x> = LaurentPolynomialRing(QQ, 1)
|
|
1233
|
+
sage: F = ((x+2)/x**3).factor()
|
|
1234
|
+
sage: F(x=4)
|
|
1235
|
+
1/64 * 6
|
|
1236
|
+
"""
|
|
1237
|
+
unit = self.__unit.subs(*args, **kwds)
|
|
1238
|
+
if unit == 0:
|
|
1239
|
+
return self.universe().zero()
|
|
1240
|
+
data = [(p.subs(*args, **kwds), e) for p, e in self.__x]
|
|
1241
|
+
if any(p == 0 for p, _ in data):
|
|
1242
|
+
return self.universe().zero()
|
|
1243
|
+
return Factorization(data, unit=unit, simplify=False)
|
|
1244
|
+
|
|
1245
|
+
subs = __call__
|
|
1246
|
+
|
|
1247
|
+
def value(self):
|
|
1248
|
+
"""
|
|
1249
|
+
Return the product of the factors in the factorization, multiplied out.
|
|
1250
|
+
|
|
1251
|
+
EXAMPLES::
|
|
1252
|
+
|
|
1253
|
+
sage: F = factor(-2006); F
|
|
1254
|
+
-1 * 2 * 17 * 59
|
|
1255
|
+
sage: F.value()
|
|
1256
|
+
-2006
|
|
1257
|
+
|
|
1258
|
+
sage: R.<x,y> = FreeAlgebra(ZZ, 2) # needs sage.combinat sage.modules
|
|
1259
|
+
sage: F = Factorization([(x,3), (y, 2), (x,1)]); F # needs sage.combinat sage.modules
|
|
1260
|
+
x^3 * y^2 * x
|
|
1261
|
+
sage: F.value() # needs sage.combinat sage.modules
|
|
1262
|
+
x^3*y^2*x
|
|
1263
|
+
"""
|
|
1264
|
+
from sage.misc.misc_c import prod
|
|
1265
|
+
return prod([p**e for p, e in self.__x], self.__unit)
|
|
1266
|
+
|
|
1267
|
+
# Two aliases for ``value(self)``.
|
|
1268
|
+
expand = value
|
|
1269
|
+
prod = value
|
|
1270
|
+
|
|
1271
|
+
def gcd(self, other):
|
|
1272
|
+
r"""
|
|
1273
|
+
Return the gcd of two factorizations.
|
|
1274
|
+
|
|
1275
|
+
If the two factorizations have different universes, this
|
|
1276
|
+
method will attempt to find a common universe for the
|
|
1277
|
+
gcd. A :exc:`TypeError` is raised if this is impossible.
|
|
1278
|
+
|
|
1279
|
+
EXAMPLES::
|
|
1280
|
+
|
|
1281
|
+
sage: factor(-30).gcd(factor(-160))
|
|
1282
|
+
2 * 5
|
|
1283
|
+
sage: factor(gcd(-30,160))
|
|
1284
|
+
2 * 5
|
|
1285
|
+
|
|
1286
|
+
sage: R.<x> = ZZ[]
|
|
1287
|
+
sage: (factor(-20).gcd(factor(5*x+10))).universe() # needs sage.libs.pari
|
|
1288
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1289
|
+
"""
|
|
1290
|
+
if not isinstance(other, Factorization):
|
|
1291
|
+
raise NotImplementedError("can't take gcd of factorization and non-factorization")
|
|
1292
|
+
|
|
1293
|
+
if len(self) and len(other):
|
|
1294
|
+
try:
|
|
1295
|
+
# first get the two factorizations to have the same
|
|
1296
|
+
# universe
|
|
1297
|
+
U = Sequence([self[0][0], other[0][0]]).universe()
|
|
1298
|
+
self = self.base_change(U)
|
|
1299
|
+
other = other.base_change(U)
|
|
1300
|
+
except TypeError:
|
|
1301
|
+
raise TypeError("Cannot take the gcd of %s and %s because they cannot be coerced into a common universe" % (self, other))
|
|
1302
|
+
|
|
1303
|
+
if self.is_commutative() and other.is_commutative():
|
|
1304
|
+
d1 = dict(self)
|
|
1305
|
+
d2 = dict(other)
|
|
1306
|
+
s = {}
|
|
1307
|
+
for a in set(d1).intersection(set(d2)):
|
|
1308
|
+
s[a] = min(d1[a], d2[a])
|
|
1309
|
+
return Factorization(list(s.items()))
|
|
1310
|
+
else:
|
|
1311
|
+
raise NotImplementedError("gcd is not implemented for non-commutative factorizations")
|
|
1312
|
+
|
|
1313
|
+
def lcm(self, other):
|
|
1314
|
+
r"""
|
|
1315
|
+
Return the lcm of two factorizations.
|
|
1316
|
+
|
|
1317
|
+
If the two factorizations have different universes, this
|
|
1318
|
+
method will attempt to find a common universe for the
|
|
1319
|
+
lcm. A :exc:`TypeError` is raised if this is impossible.
|
|
1320
|
+
|
|
1321
|
+
EXAMPLES::
|
|
1322
|
+
|
|
1323
|
+
sage: factor(-10).lcm(factor(-16))
|
|
1324
|
+
2^4 * 5
|
|
1325
|
+
sage: factor(lcm(-10,16))
|
|
1326
|
+
2^4 * 5
|
|
1327
|
+
|
|
1328
|
+
sage: R.<x> = ZZ[]
|
|
1329
|
+
sage: (factor(-20).lcm(factor(5*x + 10))).universe() # needs sage.libs.pari
|
|
1330
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1331
|
+
"""
|
|
1332
|
+
if not isinstance(other, Factorization):
|
|
1333
|
+
raise NotImplementedError("can't take lcm of factorization and non-factorization")
|
|
1334
|
+
|
|
1335
|
+
if len(self) and len(other):
|
|
1336
|
+
try:
|
|
1337
|
+
# first get the two factorizations to have the same
|
|
1338
|
+
# universe
|
|
1339
|
+
U = Sequence([self[0][0], other[0][0]]).universe()
|
|
1340
|
+
self = self.base_change(U)
|
|
1341
|
+
other = other.base_change(U)
|
|
1342
|
+
except TypeError:
|
|
1343
|
+
raise TypeError("Cannot take the lcm of %s and %s because they cannot be coerced into a common universe" % (self, other))
|
|
1344
|
+
|
|
1345
|
+
if self.is_commutative() and other.is_commutative():
|
|
1346
|
+
d1 = dict(self)
|
|
1347
|
+
d2 = dict(other)
|
|
1348
|
+
s = {}
|
|
1349
|
+
for a in set(d1).union(set(d2)):
|
|
1350
|
+
s[a] = max(d1.get(a, 0), d2.get(a, 0))
|
|
1351
|
+
return Factorization(list(s.items()))
|
|
1352
|
+
else:
|
|
1353
|
+
raise NotImplementedError("lcm is not implemented for non-commutative factorizations")
|
|
1354
|
+
|
|
1355
|
+
def is_integral(self) -> bool:
|
|
1356
|
+
r"""
|
|
1357
|
+
Return whether all exponents of this Factorization are nonnegative.
|
|
1358
|
+
|
|
1359
|
+
EXAMPLES::
|
|
1360
|
+
|
|
1361
|
+
sage: F = factor(-10); F
|
|
1362
|
+
-1 * 2 * 5
|
|
1363
|
+
sage: F.is_integral()
|
|
1364
|
+
True
|
|
1365
|
+
|
|
1366
|
+
sage: F = factor(-10) / factor(16); F
|
|
1367
|
+
-1 * 2^-3 * 5
|
|
1368
|
+
sage: F.is_integral()
|
|
1369
|
+
False
|
|
1370
|
+
"""
|
|
1371
|
+
return all(e >= 0 for p, e in self.__x)
|
|
1372
|
+
|
|
1373
|
+
def radical(self):
|
|
1374
|
+
"""
|
|
1375
|
+
Return the factorization of the radical of the value of ``self``.
|
|
1376
|
+
|
|
1377
|
+
First, check that all exponents in the factorization are
|
|
1378
|
+
positive, raise :exc:`ValueError` otherwise. If all exponents are
|
|
1379
|
+
positive, return ``self`` with all exponents set to 1 and with the
|
|
1380
|
+
unit set to 1.
|
|
1381
|
+
|
|
1382
|
+
EXAMPLES::
|
|
1383
|
+
|
|
1384
|
+
sage: F = factor(-100); F
|
|
1385
|
+
-1 * 2^2 * 5^2
|
|
1386
|
+
sage: F.radical()
|
|
1387
|
+
2 * 5
|
|
1388
|
+
sage: factor(1/2).radical()
|
|
1389
|
+
Traceback (most recent call last):
|
|
1390
|
+
...
|
|
1391
|
+
ValueError: all exponents in the factorization must be positive
|
|
1392
|
+
"""
|
|
1393
|
+
if not all(e > 0 for _, e in self.__x):
|
|
1394
|
+
raise ValueError("all exponents in the factorization must be positive")
|
|
1395
|
+
return Factorization([(p, 1) for p, _ in self.__x], unit=self.unit().parent()(1),
|
|
1396
|
+
cr=self.__cr, sort=False, simplify=False)
|
|
1397
|
+
|
|
1398
|
+
def radical_value(self):
|
|
1399
|
+
"""
|
|
1400
|
+
Return the product of the prime factors in ``self``.
|
|
1401
|
+
|
|
1402
|
+
First, check that all exponents in the factorization are
|
|
1403
|
+
positive, raise :exc:`ValueError` otherwise. If all exponents are
|
|
1404
|
+
positive, return the product of the prime factors in ``self``.
|
|
1405
|
+
This should be functionally equivalent to
|
|
1406
|
+
``self.radical().value()``.
|
|
1407
|
+
|
|
1408
|
+
EXAMPLES::
|
|
1409
|
+
|
|
1410
|
+
sage: F = factor(-100); F
|
|
1411
|
+
-1 * 2^2 * 5^2
|
|
1412
|
+
sage: F.radical_value()
|
|
1413
|
+
10
|
|
1414
|
+
sage: factor(1/2).radical_value()
|
|
1415
|
+
Traceback (most recent call last):
|
|
1416
|
+
...
|
|
1417
|
+
ValueError: all exponents in the factorization must be positive
|
|
1418
|
+
"""
|
|
1419
|
+
if not all(e > 0 for _, e in self.__x):
|
|
1420
|
+
raise ValueError("all exponents in the factorization must be positive")
|
|
1421
|
+
from sage.misc.misc_c import prod
|
|
1422
|
+
return prod([p for p, _ in self.__x])
|