MTerryJack commited on
Commit
26f49c8
·
verified ·
1 Parent(s): 831d7c0

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.13/site-packages/sympy/concrete/tests/__init__.py +0 -0
  2. .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py +499 -0
  3. .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py +204 -0
  4. .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py +82 -0
  5. .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py +410 -0
  6. .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py +1676 -0
  7. .venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py +0 -0
  8. .venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py +851 -0
  9. .venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py +41 -0
  10. .venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py +57 -0
  11. .venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py +638 -0
  12. .venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py +15 -0
  13. .venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py +198 -0
  14. .venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py +52 -0
  15. .venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py +1382 -0
  16. .venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py +38 -0
  17. .venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py +278 -0
  18. .venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py +57 -0
  19. .venv/lib/python3.13/site-packages/sympy/polys/domains/field.py +118 -0
  20. .venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py +368 -0
  21. .venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py +181 -0
  22. .venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py +706 -0
  23. .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py +16 -0
  24. .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py +105 -0
  25. .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py +100 -0
  26. .venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py +99 -0
  27. .venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py +276 -0
  28. .venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py +237 -0
  29. .venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py +181 -0
  30. .venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py +188 -0
  31. .venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py +490 -0
  32. .venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py +203 -0
  33. .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py +16 -0
  34. .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py +98 -0
  35. .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py +22 -0
  36. .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py +73 -0
  37. .venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py +202 -0
  38. .venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py +200 -0
  39. .venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py +220 -0
  40. .venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py +118 -0
  41. .venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py +15 -0
  42. .venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py +0 -0
  43. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py +91 -0
  44. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py +236 -0
  45. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py +1007 -0
  46. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py +730 -0
  47. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py +714 -0
  48. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py +95 -0
  49. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py +208 -0
  50. .venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py +712 -0
.venv/lib/python3.13/site-packages/sympy/concrete/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.concrete import Sum
2
+ from sympy.concrete.delta import deltaproduct as dp, deltasummation as ds, _extract_delta
3
+ from sympy.core import Eq, S, symbols, oo
4
+ from sympy.functions import KroneckerDelta as KD, Piecewise, piecewise_fold
5
+ from sympy.logic import And
6
+ from sympy.testing.pytest import raises
7
+
8
+ i, j, k, l, m = symbols("i j k l m", integer=True, finite=True)
9
+ x, y = symbols("x y", commutative=False)
10
+
11
+
12
+ def test_deltaproduct_trivial():
13
+ assert dp(x, (j, 1, 0)) == 1
14
+ assert dp(x, (j, 1, 3)) == x**3
15
+ assert dp(x + y, (j, 1, 3)) == (x + y)**3
16
+ assert dp(x*y, (j, 1, 3)) == (x*y)**3
17
+ assert dp(KD(i, j), (k, 1, 3)) == KD(i, j)
18
+ assert dp(x*KD(i, j), (k, 1, 3)) == x**3*KD(i, j)
19
+ assert dp(x*y*KD(i, j), (k, 1, 3)) == (x*y)**3*KD(i, j)
20
+
21
+
22
+ def test_deltaproduct_basic():
23
+ assert dp(KD(i, j), (j, 1, 3)) == 0
24
+ assert dp(KD(i, j), (j, 1, 1)) == KD(i, 1)
25
+ assert dp(KD(i, j), (j, 2, 2)) == KD(i, 2)
26
+ assert dp(KD(i, j), (j, 3, 3)) == KD(i, 3)
27
+ assert dp(KD(i, j), (j, 1, k)) == KD(i, 1)*KD(k, 1) + KD(k, 0)
28
+ assert dp(KD(i, j), (j, k, 3)) == KD(i, 3)*KD(k, 3) + KD(k, 4)
29
+ assert dp(KD(i, j), (j, k, l)) == KD(i, l)*KD(k, l) + KD(k, l + 1)
30
+
31
+
32
+ def test_deltaproduct_mul_x_kd():
33
+ assert dp(x*KD(i, j), (j, 1, 3)) == 0
34
+ assert dp(x*KD(i, j), (j, 1, 1)) == x*KD(i, 1)
35
+ assert dp(x*KD(i, j), (j, 2, 2)) == x*KD(i, 2)
36
+ assert dp(x*KD(i, j), (j, 3, 3)) == x*KD(i, 3)
37
+ assert dp(x*KD(i, j), (j, 1, k)) == x*KD(i, 1)*KD(k, 1) + KD(k, 0)
38
+ assert dp(x*KD(i, j), (j, k, 3)) == x*KD(i, 3)*KD(k, 3) + KD(k, 4)
39
+ assert dp(x*KD(i, j), (j, k, l)) == x*KD(i, l)*KD(k, l) + KD(k, l + 1)
40
+
41
+
42
+ def test_deltaproduct_mul_add_x_y_kd():
43
+ assert dp((x + y)*KD(i, j), (j, 1, 3)) == 0
44
+ assert dp((x + y)*KD(i, j), (j, 1, 1)) == (x + y)*KD(i, 1)
45
+ assert dp((x + y)*KD(i, j), (j, 2, 2)) == (x + y)*KD(i, 2)
46
+ assert dp((x + y)*KD(i, j), (j, 3, 3)) == (x + y)*KD(i, 3)
47
+ assert dp((x + y)*KD(i, j), (j, 1, k)) == \
48
+ (x + y)*KD(i, 1)*KD(k, 1) + KD(k, 0)
49
+ assert dp((x + y)*KD(i, j), (j, k, 3)) == \
50
+ (x + y)*KD(i, 3)*KD(k, 3) + KD(k, 4)
51
+ assert dp((x + y)*KD(i, j), (j, k, l)) == \
52
+ (x + y)*KD(i, l)*KD(k, l) + KD(k, l + 1)
53
+
54
+
55
+ def test_deltaproduct_add_kd_kd():
56
+ assert dp(KD(i, k) + KD(j, k), (k, 1, 3)) == 0
57
+ assert dp(KD(i, k) + KD(j, k), (k, 1, 1)) == KD(i, 1) + KD(j, 1)
58
+ assert dp(KD(i, k) + KD(j, k), (k, 2, 2)) == KD(i, 2) + KD(j, 2)
59
+ assert dp(KD(i, k) + KD(j, k), (k, 3, 3)) == KD(i, 3) + KD(j, 3)
60
+ assert dp(KD(i, k) + KD(j, k), (k, 1, l)) == KD(l, 0) + \
61
+ KD(i, 1)*KD(l, 1) + KD(j, 1)*KD(l, 1) + \
62
+ KD(i, 1)*KD(j, 2)*KD(l, 2) + KD(j, 1)*KD(i, 2)*KD(l, 2)
63
+ assert dp(KD(i, k) + KD(j, k), (k, l, 3)) == KD(l, 4) + \
64
+ KD(i, 3)*KD(l, 3) + KD(j, 3)*KD(l, 3) + \
65
+ KD(i, 2)*KD(j, 3)*KD(l, 2) + KD(i, 3)*KD(j, 2)*KD(l, 2)
66
+ assert dp(KD(i, k) + KD(j, k), (k, l, m)) == KD(l, m + 1) + \
67
+ KD(i, m)*KD(l, m) + KD(j, m)*KD(l, m) + \
68
+ KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + KD(i, m - 1)*KD(j, m)*KD(l, m - 1)
69
+
70
+
71
+ def test_deltaproduct_mul_x_add_kd_kd():
72
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0
73
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == x*(KD(i, 1) + KD(j, 1))
74
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == x*(KD(i, 2) + KD(j, 2))
75
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == x*(KD(i, 3) + KD(j, 3))
76
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \
77
+ x*KD(i, 1)*KD(l, 1) + x*KD(j, 1)*KD(l, 1) + \
78
+ x**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + x**2*KD(j, 1)*KD(i, 2)*KD(l, 2)
79
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \
80
+ x*KD(i, 3)*KD(l, 3) + x*KD(j, 3)*KD(l, 3) + \
81
+ x**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + x**2*KD(i, 3)*KD(j, 2)*KD(l, 2)
82
+ assert dp(x*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \
83
+ x*KD(i, m)*KD(l, m) + x*KD(j, m)*KD(l, m) + \
84
+ x**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \
85
+ x**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1)
86
+
87
+
88
+ def test_deltaproduct_mul_add_x_y_add_kd_kd():
89
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0
90
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == \
91
+ (x + y)*(KD(i, 1) + KD(j, 1))
92
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == \
93
+ (x + y)*(KD(i, 2) + KD(j, 2))
94
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == \
95
+ (x + y)*(KD(i, 3) + KD(j, 3))
96
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \
97
+ (x + y)*KD(i, 1)*KD(l, 1) + (x + y)*KD(j, 1)*KD(l, 1) + \
98
+ (x + y)**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + \
99
+ (x + y)**2*KD(j, 1)*KD(i, 2)*KD(l, 2)
100
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \
101
+ (x + y)*KD(i, 3)*KD(l, 3) + (x + y)*KD(j, 3)*KD(l, 3) + \
102
+ (x + y)**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + \
103
+ (x + y)**2*KD(i, 3)*KD(j, 2)*KD(l, 2)
104
+ assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \
105
+ (x + y)*KD(i, m)*KD(l, m) + (x + y)*KD(j, m)*KD(l, m) + \
106
+ (x + y)**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \
107
+ (x + y)**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1)
108
+
109
+
110
+ def test_deltaproduct_add_mul_x_y_mul_x_kd():
111
+ assert dp(x*y + x*KD(i, j), (j, 1, 3)) == (x*y)**3 + \
112
+ x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3)
113
+ assert dp(x*y + x*KD(i, j), (j, 1, 1)) == x*y + x*KD(i, 1)
114
+ assert dp(x*y + x*KD(i, j), (j, 2, 2)) == x*y + x*KD(i, 2)
115
+ assert dp(x*y + x*KD(i, j), (j, 3, 3)) == x*y + x*KD(i, 3)
116
+ assert dp(x*y + x*KD(i, j), (j, 1, k)) == \
117
+ (x*y)**k + Piecewise(
118
+ ((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
119
+ (0, True)
120
+ )
121
+ assert dp(x*y + x*KD(i, j), (j, k, 3)) == \
122
+ (x*y)**(-k + 4) + Piecewise(
123
+ ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
124
+ (0, True)
125
+ )
126
+ assert dp(x*y + x*KD(i, j), (j, k, l)) == \
127
+ (x*y)**(-k + l + 1) + Piecewise(
128
+ ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
129
+ (0, True)
130
+ )
131
+
132
+
133
+ def test_deltaproduct_mul_x_add_y_kd():
134
+ assert dp(x*(y + KD(i, j)), (j, 1, 3)) == (x*y)**3 + \
135
+ x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3)
136
+ assert dp(x*(y + KD(i, j)), (j, 1, 1)) == x*(y + KD(i, 1))
137
+ assert dp(x*(y + KD(i, j)), (j, 2, 2)) == x*(y + KD(i, 2))
138
+ assert dp(x*(y + KD(i, j)), (j, 3, 3)) == x*(y + KD(i, 3))
139
+ assert dp(x*(y + KD(i, j)), (j, 1, k)) == \
140
+ (x*y)**k + Piecewise(
141
+ ((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
142
+ (0, True)
143
+ ).expand()
144
+ assert dp(x*(y + KD(i, j)), (j, k, 3)) == \
145
+ ((x*y)**(-k + 4) + Piecewise(
146
+ ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
147
+ (0, True)
148
+ )).expand()
149
+ assert dp(x*(y + KD(i, j)), (j, k, l)) == \
150
+ ((x*y)**(-k + l + 1) + Piecewise(
151
+ ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
152
+ (0, True)
153
+ )).expand()
154
+
155
+
156
+ def test_deltaproduct_mul_x_add_y_twokd():
157
+ assert dp(x*(y + 2*KD(i, j)), (j, 1, 3)) == (x*y)**3 + \
158
+ 2*x*(x*y)**2*KD(i, 1) + 2*x*y*x*x*y*KD(i, 2) + 2*(x*y)**2*x*KD(i, 3)
159
+ assert dp(x*(y + 2*KD(i, j)), (j, 1, 1)) == x*(y + 2*KD(i, 1))
160
+ assert dp(x*(y + 2*KD(i, j)), (j, 2, 2)) == x*(y + 2*KD(i, 2))
161
+ assert dp(x*(y + 2*KD(i, j)), (j, 3, 3)) == x*(y + 2*KD(i, 3))
162
+ assert dp(x*(y + 2*KD(i, j)), (j, 1, k)) == \
163
+ (x*y)**k + Piecewise(
164
+ (2*(x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
165
+ (0, True)
166
+ ).expand()
167
+ assert dp(x*(y + 2*KD(i, j)), (j, k, 3)) == \
168
+ ((x*y)**(-k + 4) + Piecewise(
169
+ (2*(x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
170
+ (0, True)
171
+ )).expand()
172
+ assert dp(x*(y + 2*KD(i, j)), (j, k, l)) == \
173
+ ((x*y)**(-k + l + 1) + Piecewise(
174
+ (2*(x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
175
+ (0, True)
176
+ )).expand()
177
+
178
+
179
+ def test_deltaproduct_mul_add_x_y_add_y_kd():
180
+ assert dp((x + y)*(y + KD(i, j)), (j, 1, 3)) == ((x + y)*y)**3 + \
181
+ (x + y)*((x + y)*y)**2*KD(i, 1) + \
182
+ (x + y)*y*(x + y)**2*y*KD(i, 2) + \
183
+ ((x + y)*y)**2*(x + y)*KD(i, 3)
184
+ assert dp((x + y)*(y + KD(i, j)), (j, 1, 1)) == (x + y)*(y + KD(i, 1))
185
+ assert dp((x + y)*(y + KD(i, j)), (j, 2, 2)) == (x + y)*(y + KD(i, 2))
186
+ assert dp((x + y)*(y + KD(i, j)), (j, 3, 3)) == (x + y)*(y + KD(i, 3))
187
+ assert dp((x + y)*(y + KD(i, j)), (j, 1, k)) == \
188
+ ((x + y)*y)**k + Piecewise(
189
+ (((x + y)*y)**(-1)*((x + y)*y)**i*(x + y)*((x + y)*y
190
+ )**k*((x + y)*y)**(-i), (i >= 1) & (i <= k)), (0, True))
191
+ assert dp((x + y)*(y + KD(i, j)), (j, k, 3)) == (
192
+ (x + y)*y)**4*((x + y)*y)**(-k) + Piecewise((((x + y)*y)**i*(
193
+ (x + y)*y)**(-k)*(x + y)*((x + y)*y)**3*((x + y)*y)**(-i),
194
+ (i >= k) & (i <= 3)), (0, True))
195
+ assert dp((x + y)*(y + KD(i, j)), (j, k, l)) == \
196
+ (x + y)*y*((x + y)*y)**l*((x + y)*y)**(-k) + Piecewise(
197
+ (((x + y)*y)**i*((x + y)*y)**(-k)*(x + y)*((x + y)*y
198
+ )**l*((x + y)*y)**(-i), (i >= k) & (i <= l)), (0, True))
199
+
200
+
201
+ def test_deltaproduct_mul_add_x_kd_add_y_kd():
202
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == \
203
+ KD(i, 1)*(KD(i, k) + x)*((KD(i, k) + x)*y)**2 + \
204
+ KD(i, 2)*(KD(i, k) + x)*y*(KD(i, k) + x)**2*y + \
205
+ KD(i, 3)*((KD(i, k) + x)*y)**2*(KD(i, k) + x) + \
206
+ ((KD(i, k) + x)*y)**3
207
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == \
208
+ (x + KD(i, k))*(y + KD(i, 1))
209
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == \
210
+ (x + KD(i, k))*(y + KD(i, 2))
211
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == \
212
+ (x + KD(i, k))*(y + KD(i, 3))
213
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == \
214
+ ((KD(i, k) + x)*y)**k + Piecewise(
215
+ (((KD(i, k) + x)*y)**(-1)*((KD(i, k) + x)*y)**i*(KD(i, k) + x
216
+ )*((KD(i, k) + x)*y)**k*((KD(i, k) + x)*y)**(-i), (i >= 1
217
+ ) & (i <= k)), (0, True))
218
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == (
219
+ (KD(i, k) + x)*y)**4*((KD(i, k) + x)*y)**(-k) + Piecewise(
220
+ (((KD(i, k) + x)*y)**i*((KD(i, k) + x)*y)**(-k)*(KD(i, k)
221
+ + x)*((KD(i, k) + x)*y)**3*((KD(i, k) + x)*y)**(-i),
222
+ (i >= k) & (i <= 3)), (0, True))
223
+ assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == (
224
+ KD(i, k) + x)*y*((KD(i, k) + x)*y)**l*((KD(i, k) + x)*y
225
+ )**(-k) + Piecewise((((KD(i, k) + x)*y)**i*((KD(i, k) + x
226
+ )*y)**(-k)*(KD(i, k) + x)*((KD(i, k) + x)*y)**l*((KD(i, k) + x
227
+ )*y)**(-i), (i >= k) & (i <= l)), (0, True))
228
+
229
+
230
+ def test_deltasummation_trivial():
231
+ assert ds(x, (j, 1, 0)) == 0
232
+ assert ds(x, (j, 1, 3)) == 3*x
233
+ assert ds(x + y, (j, 1, 3)) == 3*(x + y)
234
+ assert ds(x*y, (j, 1, 3)) == 3*x*y
235
+ assert ds(KD(i, j), (k, 1, 3)) == 3*KD(i, j)
236
+ assert ds(x*KD(i, j), (k, 1, 3)) == 3*x*KD(i, j)
237
+ assert ds(x*y*KD(i, j), (k, 1, 3)) == 3*x*y*KD(i, j)
238
+
239
+
240
+ def test_deltasummation_basic_numerical():
241
+ n = symbols('n', integer=True, nonzero=True)
242
+ assert ds(KD(n, 0), (n, 1, 3)) == 0
243
+
244
+ # return unevaluated, until it gets implemented
245
+ assert ds(KD(i**2, j**2), (j, -oo, oo)) == \
246
+ Sum(KD(i**2, j**2), (j, -oo, oo))
247
+
248
+ assert Piecewise((KD(i, k), And(1 <= i, i <= 3)), (0, True)) == \
249
+ ds(KD(i, j)*KD(j, k), (j, 1, 3)) == \
250
+ ds(KD(j, k)*KD(i, j), (j, 1, 3))
251
+
252
+ assert ds(KD(i, k), (k, -oo, oo)) == 1
253
+ assert ds(KD(i, k), (k, 0, oo)) == Piecewise((1, S.Zero <= i), (0, True))
254
+ assert ds(KD(i, k), (k, 1, 3)) == \
255
+ Piecewise((1, And(1 <= i, i <= 3)), (0, True))
256
+ assert ds(k*KD(i, j)*KD(j, k), (k, -oo, oo)) == j*KD(i, j)
257
+ assert ds(j*KD(i, j), (j, -oo, oo)) == i
258
+ assert ds(i*KD(i, j), (i, -oo, oo)) == j
259
+ assert ds(x, (i, 1, 3)) == 3*x
260
+ assert ds((i + j)*KD(i, j), (j, -oo, oo)) == 2*i
261
+
262
+
263
+ def test_deltasummation_basic_symbolic():
264
+ assert ds(KD(i, j), (j, 1, 3)) == \
265
+ Piecewise((1, And(1 <= i, i <= 3)), (0, True))
266
+ assert ds(KD(i, j), (j, 1, 1)) == Piecewise((1, Eq(i, 1)), (0, True))
267
+ assert ds(KD(i, j), (j, 2, 2)) == Piecewise((1, Eq(i, 2)), (0, True))
268
+ assert ds(KD(i, j), (j, 3, 3)) == Piecewise((1, Eq(i, 3)), (0, True))
269
+ assert ds(KD(i, j), (j, 1, k)) == \
270
+ Piecewise((1, And(1 <= i, i <= k)), (0, True))
271
+ assert ds(KD(i, j), (j, k, 3)) == \
272
+ Piecewise((1, And(k <= i, i <= 3)), (0, True))
273
+ assert ds(KD(i, j), (j, k, l)) == \
274
+ Piecewise((1, And(k <= i, i <= l)), (0, True))
275
+
276
+
277
+ def test_deltasummation_mul_x_kd():
278
+ assert ds(x*KD(i, j), (j, 1, 3)) == \
279
+ Piecewise((x, And(1 <= i, i <= 3)), (0, True))
280
+ assert ds(x*KD(i, j), (j, 1, 1)) == Piecewise((x, Eq(i, 1)), (0, True))
281
+ assert ds(x*KD(i, j), (j, 2, 2)) == Piecewise((x, Eq(i, 2)), (0, True))
282
+ assert ds(x*KD(i, j), (j, 3, 3)) == Piecewise((x, Eq(i, 3)), (0, True))
283
+ assert ds(x*KD(i, j), (j, 1, k)) == \
284
+ Piecewise((x, And(1 <= i, i <= k)), (0, True))
285
+ assert ds(x*KD(i, j), (j, k, 3)) == \
286
+ Piecewise((x, And(k <= i, i <= 3)), (0, True))
287
+ assert ds(x*KD(i, j), (j, k, l)) == \
288
+ Piecewise((x, And(k <= i, i <= l)), (0, True))
289
+
290
+
291
+ def test_deltasummation_mul_add_x_y_kd():
292
+ assert ds((x + y)*KD(i, j), (j, 1, 3)) == \
293
+ Piecewise((x + y, And(1 <= i, i <= 3)), (0, True))
294
+ assert ds((x + y)*KD(i, j), (j, 1, 1)) == \
295
+ Piecewise((x + y, Eq(i, 1)), (0, True))
296
+ assert ds((x + y)*KD(i, j), (j, 2, 2)) == \
297
+ Piecewise((x + y, Eq(i, 2)), (0, True))
298
+ assert ds((x + y)*KD(i, j), (j, 3, 3)) == \
299
+ Piecewise((x + y, Eq(i, 3)), (0, True))
300
+ assert ds((x + y)*KD(i, j), (j, 1, k)) == \
301
+ Piecewise((x + y, And(1 <= i, i <= k)), (0, True))
302
+ assert ds((x + y)*KD(i, j), (j, k, 3)) == \
303
+ Piecewise((x + y, And(k <= i, i <= 3)), (0, True))
304
+ assert ds((x + y)*KD(i, j), (j, k, l)) == \
305
+ Piecewise((x + y, And(k <= i, i <= l)), (0, True))
306
+
307
+
308
+ def test_deltasummation_add_kd_kd():
309
+ assert ds(KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold(
310
+ Piecewise((1, And(1 <= i, i <= 3)), (0, True)) +
311
+ Piecewise((1, And(1 <= j, j <= 3)), (0, True)))
312
+ assert ds(KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold(
313
+ Piecewise((1, Eq(i, 1)), (0, True)) +
314
+ Piecewise((1, Eq(j, 1)), (0, True)))
315
+ assert ds(KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold(
316
+ Piecewise((1, Eq(i, 2)), (0, True)) +
317
+ Piecewise((1, Eq(j, 2)), (0, True)))
318
+ assert ds(KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold(
319
+ Piecewise((1, Eq(i, 3)), (0, True)) +
320
+ Piecewise((1, Eq(j, 3)), (0, True)))
321
+ assert ds(KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold(
322
+ Piecewise((1, And(1 <= i, i <= l)), (0, True)) +
323
+ Piecewise((1, And(1 <= j, j <= l)), (0, True)))
324
+ assert ds(KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold(
325
+ Piecewise((1, And(l <= i, i <= 3)), (0, True)) +
326
+ Piecewise((1, And(l <= j, j <= 3)), (0, True)))
327
+ assert ds(KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold(
328
+ Piecewise((1, And(l <= i, i <= m)), (0, True)) +
329
+ Piecewise((1, And(l <= j, j <= m)), (0, True)))
330
+
331
+
332
+ def test_deltasummation_add_mul_x_kd_kd():
333
+ assert ds(x*KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold(
334
+ Piecewise((x, And(1 <= i, i <= 3)), (0, True)) +
335
+ Piecewise((1, And(1 <= j, j <= 3)), (0, True)))
336
+ assert ds(x*KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold(
337
+ Piecewise((x, Eq(i, 1)), (0, True)) +
338
+ Piecewise((1, Eq(j, 1)), (0, True)))
339
+ assert ds(x*KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold(
340
+ Piecewise((x, Eq(i, 2)), (0, True)) +
341
+ Piecewise((1, Eq(j, 2)), (0, True)))
342
+ assert ds(x*KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold(
343
+ Piecewise((x, Eq(i, 3)), (0, True)) +
344
+ Piecewise((1, Eq(j, 3)), (0, True)))
345
+ assert ds(x*KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold(
346
+ Piecewise((x, And(1 <= i, i <= l)), (0, True)) +
347
+ Piecewise((1, And(1 <= j, j <= l)), (0, True)))
348
+ assert ds(x*KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold(
349
+ Piecewise((x, And(l <= i, i <= 3)), (0, True)) +
350
+ Piecewise((1, And(l <= j, j <= 3)), (0, True)))
351
+ assert ds(x*KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold(
352
+ Piecewise((x, And(l <= i, i <= m)), (0, True)) +
353
+ Piecewise((1, And(l <= j, j <= m)), (0, True)))
354
+
355
+
356
+ def test_deltasummation_mul_x_add_kd_kd():
357
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold(
358
+ Piecewise((x, And(1 <= i, i <= 3)), (0, True)) +
359
+ Piecewise((x, And(1 <= j, j <= 3)), (0, True)))
360
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold(
361
+ Piecewise((x, Eq(i, 1)), (0, True)) +
362
+ Piecewise((x, Eq(j, 1)), (0, True)))
363
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold(
364
+ Piecewise((x, Eq(i, 2)), (0, True)) +
365
+ Piecewise((x, Eq(j, 2)), (0, True)))
366
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold(
367
+ Piecewise((x, Eq(i, 3)), (0, True)) +
368
+ Piecewise((x, Eq(j, 3)), (0, True)))
369
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold(
370
+ Piecewise((x, And(1 <= i, i <= l)), (0, True)) +
371
+ Piecewise((x, And(1 <= j, j <= l)), (0, True)))
372
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold(
373
+ Piecewise((x, And(l <= i, i <= 3)), (0, True)) +
374
+ Piecewise((x, And(l <= j, j <= 3)), (0, True)))
375
+ assert ds(x*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold(
376
+ Piecewise((x, And(l <= i, i <= m)), (0, True)) +
377
+ Piecewise((x, And(l <= j, j <= m)), (0, True)))
378
+
379
+
380
+ def test_deltasummation_mul_add_x_y_add_kd_kd():
381
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold(
382
+ Piecewise((x + y, And(1 <= i, i <= 3)), (0, True)) +
383
+ Piecewise((x + y, And(1 <= j, j <= 3)), (0, True)))
384
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold(
385
+ Piecewise((x + y, Eq(i, 1)), (0, True)) +
386
+ Piecewise((x + y, Eq(j, 1)), (0, True)))
387
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold(
388
+ Piecewise((x + y, Eq(i, 2)), (0, True)) +
389
+ Piecewise((x + y, Eq(j, 2)), (0, True)))
390
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold(
391
+ Piecewise((x + y, Eq(i, 3)), (0, True)) +
392
+ Piecewise((x + y, Eq(j, 3)), (0, True)))
393
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold(
394
+ Piecewise((x + y, And(1 <= i, i <= l)), (0, True)) +
395
+ Piecewise((x + y, And(1 <= j, j <= l)), (0, True)))
396
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold(
397
+ Piecewise((x + y, And(l <= i, i <= 3)), (0, True)) +
398
+ Piecewise((x + y, And(l <= j, j <= 3)), (0, True)))
399
+ assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold(
400
+ Piecewise((x + y, And(l <= i, i <= m)), (0, True)) +
401
+ Piecewise((x + y, And(l <= j, j <= m)), (0, True)))
402
+
403
+
404
+ def test_deltasummation_add_mul_x_y_mul_x_kd():
405
+ assert ds(x*y + x*KD(i, j), (j, 1, 3)) == \
406
+ Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True))
407
+ assert ds(x*y + x*KD(i, j), (j, 1, 1)) == \
408
+ Piecewise((x*y + x, Eq(i, 1)), (x*y, True))
409
+ assert ds(x*y + x*KD(i, j), (j, 2, 2)) == \
410
+ Piecewise((x*y + x, Eq(i, 2)), (x*y, True))
411
+ assert ds(x*y + x*KD(i, j), (j, 3, 3)) == \
412
+ Piecewise((x*y + x, Eq(i, 3)), (x*y, True))
413
+ assert ds(x*y + x*KD(i, j), (j, 1, k)) == \
414
+ Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True))
415
+ assert ds(x*y + x*KD(i, j), (j, k, 3)) == \
416
+ Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
417
+ assert ds(x*y + x*KD(i, j), (j, k, l)) == Piecewise(
418
+ ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
419
+
420
+
421
+ def test_deltasummation_mul_x_add_y_kd():
422
+ assert ds(x*(y + KD(i, j)), (j, 1, 3)) == \
423
+ Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True))
424
+ assert ds(x*(y + KD(i, j)), (j, 1, 1)) == \
425
+ Piecewise((x*y + x, Eq(i, 1)), (x*y, True))
426
+ assert ds(x*(y + KD(i, j)), (j, 2, 2)) == \
427
+ Piecewise((x*y + x, Eq(i, 2)), (x*y, True))
428
+ assert ds(x*(y + KD(i, j)), (j, 3, 3)) == \
429
+ Piecewise((x*y + x, Eq(i, 3)), (x*y, True))
430
+ assert ds(x*(y + KD(i, j)), (j, 1, k)) == \
431
+ Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True))
432
+ assert ds(x*(y + KD(i, j)), (j, k, 3)) == \
433
+ Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
434
+ assert ds(x*(y + KD(i, j)), (j, k, l)) == Piecewise(
435
+ ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
436
+
437
+
438
+ def test_deltasummation_mul_x_add_y_twokd():
439
+ assert ds(x*(y + 2*KD(i, j)), (j, 1, 3)) == \
440
+ Piecewise((3*x*y + 2*x, And(1 <= i, i <= 3)), (3*x*y, True))
441
+ assert ds(x*(y + 2*KD(i, j)), (j, 1, 1)) == \
442
+ Piecewise((x*y + 2*x, Eq(i, 1)), (x*y, True))
443
+ assert ds(x*(y + 2*KD(i, j)), (j, 2, 2)) == \
444
+ Piecewise((x*y + 2*x, Eq(i, 2)), (x*y, True))
445
+ assert ds(x*(y + 2*KD(i, j)), (j, 3, 3)) == \
446
+ Piecewise((x*y + 2*x, Eq(i, 3)), (x*y, True))
447
+ assert ds(x*(y + 2*KD(i, j)), (j, 1, k)) == \
448
+ Piecewise((k*x*y + 2*x, And(1 <= i, i <= k)), (k*x*y, True))
449
+ assert ds(x*(y + 2*KD(i, j)), (j, k, 3)) == Piecewise(
450
+ ((4 - k)*x*y + 2*x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
451
+ assert ds(x*(y + 2*KD(i, j)), (j, k, l)) == Piecewise(
452
+ ((l - k + 1)*x*y + 2*x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
453
+
454
+
455
+ def test_deltasummation_mul_add_x_y_add_y_kd():
456
+ assert ds((x + y)*(y + KD(i, j)), (j, 1, 3)) == Piecewise(
457
+ (3*(x + y)*y + x + y, And(1 <= i, i <= 3)), (3*(x + y)*y, True))
458
+ assert ds((x + y)*(y + KD(i, j)), (j, 1, 1)) == \
459
+ Piecewise(((x + y)*y + x + y, Eq(i, 1)), ((x + y)*y, True))
460
+ assert ds((x + y)*(y + KD(i, j)), (j, 2, 2)) == \
461
+ Piecewise(((x + y)*y + x + y, Eq(i, 2)), ((x + y)*y, True))
462
+ assert ds((x + y)*(y + KD(i, j)), (j, 3, 3)) == \
463
+ Piecewise(((x + y)*y + x + y, Eq(i, 3)), ((x + y)*y, True))
464
+ assert ds((x + y)*(y + KD(i, j)), (j, 1, k)) == Piecewise(
465
+ (k*(x + y)*y + x + y, And(1 <= i, i <= k)), (k*(x + y)*y, True))
466
+ assert ds((x + y)*(y + KD(i, j)), (j, k, 3)) == Piecewise(
467
+ ((4 - k)*(x + y)*y + x + y, And(k <= i, i <= 3)),
468
+ ((4 - k)*(x + y)*y, True))
469
+ assert ds((x + y)*(y + KD(i, j)), (j, k, l)) == Piecewise(
470
+ ((l - k + 1)*(x + y)*y + x + y, And(k <= i, i <= l)),
471
+ ((l - k + 1)*(x + y)*y, True))
472
+
473
+
474
+ def test_deltasummation_mul_add_x_kd_add_y_kd():
475
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == piecewise_fold(
476
+ Piecewise((KD(i, k) + x, And(1 <= i, i <= 3)), (0, True)) +
477
+ 3*(KD(i, k) + x)*y)
478
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == piecewise_fold(
479
+ Piecewise((KD(i, k) + x, Eq(i, 1)), (0, True)) +
480
+ (KD(i, k) + x)*y)
481
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == piecewise_fold(
482
+ Piecewise((KD(i, k) + x, Eq(i, 2)), (0, True)) +
483
+ (KD(i, k) + x)*y)
484
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == piecewise_fold(
485
+ Piecewise((KD(i, k) + x, Eq(i, 3)), (0, True)) +
486
+ (KD(i, k) + x)*y)
487
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == piecewise_fold(
488
+ Piecewise((KD(i, k) + x, And(1 <= i, i <= k)), (0, True)) +
489
+ k*(KD(i, k) + x)*y)
490
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == piecewise_fold(
491
+ Piecewise((KD(i, k) + x, And(k <= i, i <= 3)), (0, True)) +
492
+ (4 - k)*(KD(i, k) + x)*y)
493
+ assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == piecewise_fold(
494
+ Piecewise((KD(i, k) + x, And(k <= i, i <= l)), (0, True)) +
495
+ (l - k + 1)*(KD(i, k) + x)*y)
496
+
497
+
498
+ def test_extract_delta():
499
+ raises(ValueError, lambda: _extract_delta(KD(i, j) + KD(k, l), i))
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for Gosper's algorithm for hypergeometric summation. """
2
+
3
+ from sympy.core.numbers import (Rational, pi)
4
+ from sympy.core.singleton import S
5
+ from sympy.core.symbol import Symbol
6
+ from sympy.functions.combinatorial.factorials import (binomial, factorial)
7
+ from sympy.functions.elementary.exponential import (exp, log)
8
+ from sympy.functions.elementary.miscellaneous import sqrt
9
+ from sympy.functions.special.gamma_functions import gamma
10
+ from sympy.polys.polytools import Poly
11
+ from sympy.simplify.simplify import simplify
12
+ from sympy.concrete.gosper import gosper_normal, gosper_sum, gosper_term
13
+ from sympy.abc import a, b, j, k, m, n, r, x
14
+
15
+
16
+ def test_gosper_normal():
17
+ eq = 4*n + 5, 2*(4*n + 1)*(2*n + 3), n
18
+ assert gosper_normal(*eq) == \
19
+ (Poly(Rational(1, 4), n), Poly(n + Rational(3, 2)), Poly(n + Rational(1, 4)))
20
+ assert gosper_normal(*eq, polys=False) == \
21
+ (Rational(1, 4), n + Rational(3, 2), n + Rational(1, 4))
22
+
23
+
24
+ def test_gosper_term():
25
+ assert gosper_term((4*k + 1)*factorial(
26
+ k)/factorial(2*k + 1), k) == (-k - S.Half)/(k + Rational(1, 4))
27
+
28
+
29
+ def test_gosper_sum():
30
+ assert gosper_sum(1, (k, 0, n)) == 1 + n
31
+ assert gosper_sum(k, (k, 0, n)) == n*(1 + n)/2
32
+ assert gosper_sum(k**2, (k, 0, n)) == n*(1 + n)*(1 + 2*n)/6
33
+ assert gosper_sum(k**3, (k, 0, n)) == n**2*(1 + n)**2/4
34
+
35
+ assert gosper_sum(2**k, (k, 0, n)) == 2*2**n - 1
36
+
37
+ assert gosper_sum(factorial(k), (k, 0, n)) is None
38
+ assert gosper_sum(binomial(n, k), (k, 0, n)) is None
39
+
40
+ assert gosper_sum(factorial(k)/k**2, (k, 0, n)) is None
41
+ assert gosper_sum((k - 3)*factorial(k), (k, 0, n)) is None
42
+
43
+ assert gosper_sum(k*factorial(k), k) == factorial(k)
44
+ assert gosper_sum(
45
+ k*factorial(k), (k, 0, n)) == n*factorial(n) + factorial(n) - 1
46
+
47
+ assert gosper_sum((-1)**k*binomial(n, k), (k, 0, n)) == 0
48
+ assert gosper_sum((
49
+ -1)**k*binomial(n, k), (k, 0, m)) == -(-1)**m*(m - n)*binomial(n, m)/n
50
+
51
+ assert gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) == \
52
+ (2*factorial(2*n + 1) - factorial(n))/factorial(2*n + 1)
53
+
54
+ # issue 6033:
55
+ assert gosper_sum(
56
+ n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)), \
57
+ (n, 0, m)).simplify() == -exp(m*log(a) + m*log(b))*gamma(a + 1) \
58
+ *gamma(b + 1)/(gamma(a)*gamma(b)*gamma(a + m + 1)*gamma(b + m + 1)) \
59
+ + 1/(gamma(a)*gamma(b))
60
+
61
+
62
+ def test_gosper_sum_indefinite():
63
+ assert gosper_sum(k, k) == k*(k - 1)/2
64
+ assert gosper_sum(k**2, k) == k*(k - 1)*(2*k - 1)/6
65
+
66
+ assert gosper_sum(1/(k*(k + 1)), k) == -1/k
67
+ assert gosper_sum(-(27*k**4 + 158*k**3 + 430*k**2 + 678*k + 445)*gamma(2*k
68
+ + 4)/(3*(3*k + 7)*gamma(3*k + 6)), k) == \
69
+ (3*k + 5)*(k**2 + 2*k + 5)*gamma(2*k + 4)/gamma(3*k + 6)
70
+
71
+
72
+ def test_gosper_sum_parametric():
73
+ assert gosper_sum(binomial(S.Half, m - j + 1)*binomial(S.Half, m + j), (j, 1, n)) == \
74
+ n*(1 + m - n)*(-1 + 2*m + 2*n)*binomial(S.Half, 1 + m - n)* \
75
+ binomial(S.Half, m + n)/(m*(1 + 2*m))
76
+
77
+
78
+ def test_gosper_sum_algebraic():
79
+ assert gosper_sum(
80
+ n**2 + sqrt(2), (n, 0, m)) == (m + 1)*(2*m**2 + m + 6*sqrt(2))/6
81
+
82
+
83
+ def test_gosper_sum_iterated():
84
+ f1 = binomial(2*k, k)/4**k
85
+ f2 = (1 + 2*n)*binomial(2*n, n)/4**n
86
+ f3 = (1 + 2*n)*(3 + 2*n)*binomial(2*n, n)/(3*4**n)
87
+ f4 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*binomial(2*n, n)/(15*4**n)
88
+ f5 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*(7 + 2*n)*binomial(2*n, n)/(105*4**n)
89
+
90
+ assert gosper_sum(f1, (k, 0, n)) == f2
91
+ assert gosper_sum(f2, (n, 0, n)) == f3
92
+ assert gosper_sum(f3, (n, 0, n)) == f4
93
+ assert gosper_sum(f4, (n, 0, n)) == f5
94
+
95
+ # the AeqB tests test expressions given in
96
+ # www.math.upenn.edu/~wilf/AeqB.pdf
97
+
98
+
99
+ def test_gosper_sum_AeqB_part1():
100
+ f1a = n**4
101
+ f1b = n**3*2**n
102
+ f1c = 1/(n**2 + sqrt(5)*n - 1)
103
+ f1d = n**4*4**n/binomial(2*n, n)
104
+ f1e = factorial(3*n)/(factorial(n)*factorial(n + 1)*factorial(n + 2)*27**n)
105
+ f1f = binomial(2*n, n)**2/((n + 1)*4**(2*n))
106
+ f1g = (4*n - 1)*binomial(2*n, n)**2/((2*n - 1)**2*4**(2*n))
107
+ f1h = n*factorial(n - S.Half)**2/factorial(n + 1)**2
108
+
109
+ g1a = m*(m + 1)*(2*m + 1)*(3*m**2 + 3*m - 1)/30
110
+ g1b = 26 + 2**(m + 1)*(m**3 - 3*m**2 + 9*m - 13)
111
+ g1c = (m + 1)*(m*(m**2 - 7*m + 3)*sqrt(5) - (
112
+ 3*m**3 - 7*m**2 + 19*m - 6))/(2*m**3*sqrt(5) + m**4 + 5*m**2 - 1)/6
113
+ g1d = Rational(-2, 231) + 2*4**m*(m + 1)*(63*m**4 + 112*m**3 + 18*m**2 -
114
+ 22*m + 3)/(693*binomial(2*m, m))
115
+ g1e = Rational(-9, 2) + (81*m**2 + 261*m + 200)*factorial(
116
+ 3*m + 2)/(40*27**m*factorial(m)*factorial(m + 1)*factorial(m + 2))
117
+ g1f = (2*m + 1)**2*binomial(2*m, m)**2/(4**(2*m)*(m + 1))
118
+ g1g = -binomial(2*m, m)**2/4**(2*m)
119
+ g1h = 4*pi -(2*m + 1)**2*(3*m + 4)*factorial(m - S.Half)**2/factorial(m + 1)**2
120
+
121
+ g = gosper_sum(f1a, (n, 0, m))
122
+ assert g is not None and simplify(g - g1a) == 0
123
+ g = gosper_sum(f1b, (n, 0, m))
124
+ assert g is not None and simplify(g - g1b) == 0
125
+ g = gosper_sum(f1c, (n, 0, m))
126
+ assert g is not None and simplify(g - g1c) == 0
127
+ g = gosper_sum(f1d, (n, 0, m))
128
+ assert g is not None and simplify(g - g1d) == 0
129
+ g = gosper_sum(f1e, (n, 0, m))
130
+ assert g is not None and simplify(g - g1e) == 0
131
+ g = gosper_sum(f1f, (n, 0, m))
132
+ assert g is not None and simplify(g - g1f) == 0
133
+ g = gosper_sum(f1g, (n, 0, m))
134
+ assert g is not None and simplify(g - g1g) == 0
135
+ g = gosper_sum(f1h, (n, 0, m))
136
+ # need to call rewrite(gamma) here because we have terms involving
137
+ # factorial(1/2)
138
+ assert g is not None and simplify(g - g1h).rewrite(gamma) == 0
139
+
140
+
141
+ def test_gosper_sum_AeqB_part2():
142
+ f2a = n**2*a**n
143
+ f2b = (n - r/2)*binomial(r, n)
144
+ f2c = factorial(n - 1)**2/(factorial(n - x)*factorial(n + x))
145
+
146
+ g2a = -a*(a + 1)/(a - 1)**3 + a**(
147
+ m + 1)*(a**2*m**2 - 2*a*m**2 + m**2 - 2*a*m + 2*m + a + 1)/(a - 1)**3
148
+ g2b = (m - r)*binomial(r, m)/2
149
+ ff = factorial(1 - x)*factorial(1 + x)
150
+ g2c = 1/ff*(
151
+ 1 - 1/x**2) + factorial(m)**2/(x**2*factorial(m - x)*factorial(m + x))
152
+
153
+ g = gosper_sum(f2a, (n, 0, m))
154
+ assert g is not None and simplify(g - g2a) == 0
155
+ g = gosper_sum(f2b, (n, 0, m))
156
+ assert g is not None and simplify(g - g2b) == 0
157
+ g = gosper_sum(f2c, (n, 1, m))
158
+ assert g is not None and simplify(g - g2c) == 0
159
+
160
+
161
+ def test_gosper_nan():
162
+ a = Symbol('a', positive=True)
163
+ b = Symbol('b', positive=True)
164
+ n = Symbol('n', integer=True)
165
+ m = Symbol('m', integer=True)
166
+ f2d = n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b))
167
+ g2d = 1/(factorial(a - 1)*factorial(
168
+ b - 1)) - a**(m + 1)*b**(m + 1)/(factorial(a + m)*factorial(b + m))
169
+ g = gosper_sum(f2d, (n, 0, m))
170
+ assert simplify(g - g2d) == 0
171
+
172
+
173
+ def test_gosper_sum_AeqB_part3():
174
+ f3a = 1/n**4
175
+ f3b = (6*n + 3)/(4*n**4 + 8*n**3 + 8*n**2 + 4*n + 3)
176
+ f3c = 2**n*(n**2 - 2*n - 1)/(n**2*(n + 1)**2)
177
+ f3d = n**2*4**n/((n + 1)*(n + 2))
178
+ f3e = 2**n/(n + 1)
179
+ f3f = 4*(n - 1)*(n**2 - 2*n - 1)/(n**2*(n + 1)**2*(n - 2)**2*(n - 3)**2)
180
+ f3g = (n**4 - 14*n**2 - 24*n - 9)*2**n/(n**2*(n + 1)**2*(n + 2)**2*
181
+ (n + 3)**2)
182
+
183
+ # g3a -> no closed form
184
+ g3b = m*(m + 2)/(2*m**2 + 4*m + 3)
185
+ g3c = 2**m/m**2 - 2
186
+ g3d = Rational(2, 3) + 4**(m + 1)*(m - 1)/(m + 2)/3
187
+ # g3e -> no closed form
188
+ g3f = -(Rational(-1, 16) + 1/((m - 2)**2*(m + 1)**2)) # the AeqB key is wrong
189
+ g3g = Rational(-2, 9) + 2**(m + 1)/((m + 1)**2*(m + 3)**2)
190
+
191
+ g = gosper_sum(f3a, (n, 1, m))
192
+ assert g is None
193
+ g = gosper_sum(f3b, (n, 1, m))
194
+ assert g is not None and simplify(g - g3b) == 0
195
+ g = gosper_sum(f3c, (n, 1, m - 1))
196
+ assert g is not None and simplify(g - g3c) == 0
197
+ g = gosper_sum(f3d, (n, 1, m))
198
+ assert g is not None and simplify(g - g3d) == 0
199
+ g = gosper_sum(f3e, (n, 0, m - 1))
200
+ assert g is None
201
+ g = gosper_sum(f3f, (n, 4, m))
202
+ assert g is not None and simplify(g - g3f) == 0
203
+ g = gosper_sum(f3g, (n, 1, m))
204
+ assert g is not None and simplify(g - g3g) == 0
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.concrete.guess import (
2
+ find_simple_recurrence_vector,
3
+ find_simple_recurrence,
4
+ rationalize,
5
+ guess_generating_function_rational,
6
+ guess_generating_function,
7
+ guess
8
+ )
9
+ from sympy.concrete.products import Product
10
+ from sympy.core.function import Function
11
+ from sympy.core.numbers import Rational
12
+ from sympy.core.singleton import S
13
+ from sympy.core.symbol import (Symbol, symbols)
14
+ from sympy.core.sympify import sympify
15
+ from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial)
16
+ from sympy.functions.combinatorial.numbers import fibonacci
17
+ from sympy.functions.elementary.exponential import exp
18
+
19
+
20
+ def test_find_simple_recurrence_vector():
21
+ assert find_simple_recurrence_vector(
22
+ [fibonacci(k) for k in range(12)]) == [1, -1, -1]
23
+
24
+
25
+ def test_find_simple_recurrence():
26
+ a = Function('a')
27
+ n = Symbol('n')
28
+ assert find_simple_recurrence([fibonacci(k) for k in range(12)]) == (
29
+ -a(n) - a(n + 1) + a(n + 2))
30
+
31
+ f = Function('a')
32
+ i = Symbol('n')
33
+ a = [1, 1, 1]
34
+ for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3])
35
+ assert find_simple_recurrence(a, A=f, N=i) == (
36
+ -8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3))
37
+ assert find_simple_recurrence([0, 2, 15, 74, 12, 3, 0,
38
+ 1, 2, 85, 4, 5, 63]) == 0
39
+
40
+
41
+ def test_rationalize():
42
+ from mpmath import cos, pi, mpf
43
+ assert rationalize(cos(pi/3)) == S.Half
44
+ assert rationalize(mpf("0.333333333333333")) == Rational(1, 3)
45
+ assert rationalize(mpf("-0.333333333333333")) == Rational(-1, 3)
46
+ assert rationalize(pi, maxcoeff = 250) == Rational(355, 113)
47
+
48
+
49
+ def test_guess_generating_function_rational():
50
+ x = Symbol('x')
51
+ assert guess_generating_function_rational([fibonacci(k)
52
+ for k in range(5, 15)]) == ((3*x + 5)/(-x**2 - x + 1))
53
+
54
+
55
+ def test_guess_generating_function():
56
+ x = Symbol('x')
57
+ assert guess_generating_function([fibonacci(k)
58
+ for k in range(5, 15)])['ogf'] == ((3*x + 5)/(-x**2 - x + 1))
59
+ assert guess_generating_function(
60
+ [1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])['ogf'] == (
61
+ (1/(x**4 + 2*x**2 - 4*x + 1))**S.Half)
62
+ assert guess_generating_function(sympify(
63
+ "[3/2, 11/2, 0, -121/2, -363/2, 121, 4719/2, 11495/2, -8712, -178717/2]")
64
+ )['ogf'] == (x + Rational(3, 2))/(11*x**2 - 3*x + 1)
65
+ assert guess_generating_function([factorial(k) for k in range(12)],
66
+ types=['egf'])['egf'] == 1/(-x + 1)
67
+ assert guess_generating_function([k+1 for k in range(12)],
68
+ types=['egf']) == {'egf': (x + 1)*exp(x), 'lgdegf': (x + 2)/(x + 1)}
69
+
70
+
71
+ def test_guess():
72
+ i0, i1 = symbols('i0 i1')
73
+ assert guess([1, 2, 6, 24, 120], evaluate=False) == [Product(i1 + 1, (i1, 1, i0 - 1))]
74
+ assert guess([1, 2, 6, 24, 120]) == [RisingFactorial(2, i0 - 1)]
75
+ assert guess([1, 2, 7, 42, 429, 7436, 218348, 10850216], niter=4) == [
76
+ 2**(i0 - 1)*(Rational(27, 16))**(i0**2/2 - 3*i0/2 +
77
+ 1)*Product(RisingFactorial(Rational(5, 3), i1 - 1)*RisingFactorial(Rational(7, 3), i1
78
+ - 1)/(RisingFactorial(Rational(3, 2), i1 - 1)*RisingFactorial(Rational(5, 2), i1 -
79
+ 1)), (i1, 1, i0 - 1))]
80
+ assert guess([1, 0, 2]) == []
81
+ x, y = symbols('x y')
82
+ assert guess([1, 2, 6, 24, 120], variables=[x, y]) == [RisingFactorial(2, x - 1)]
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.concrete.products import (Product, product)
2
+ from sympy.concrete.summations import Sum
3
+ from sympy.core.function import (Derivative, Function, diff)
4
+ from sympy.core.numbers import (Rational, oo, pi)
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
7
+ from sympy.functions.combinatorial.factorials import (rf, factorial)
8
+ from sympy.functions.elementary.exponential import (exp, log)
9
+ from sympy.functions.elementary.miscellaneous import sqrt
10
+ from sympy.functions.elementary.trigonometric import (cos, sin)
11
+ from sympy.functions.special.tensor_functions import KroneckerDelta
12
+ from sympy.simplify.combsimp import combsimp
13
+ from sympy.simplify.simplify import simplify
14
+ from sympy.testing.pytest import raises
15
+
16
+ a, k, n, m, x = symbols('a,k,n,m,x', integer=True)
17
+ f = Function('f')
18
+
19
+
20
+ def test_karr_convention():
21
+ # Test the Karr product convention that we want to hold.
22
+ # See his paper "Summation in Finite Terms" for a detailed
23
+ # reasoning why we really want exactly this definition.
24
+ # The convention is described for sums on page 309 and
25
+ # essentially in section 1.4, definition 3. For products
26
+ # we can find in analogy:
27
+ #
28
+ # \prod_{m <= i < n} f(i) 'has the obvious meaning' for m < n
29
+ # \prod_{m <= i < n} f(i) = 0 for m = n
30
+ # \prod_{m <= i < n} f(i) = 1 / \prod_{n <= i < m} f(i) for m > n
31
+ #
32
+ # It is important to note that he defines all products with
33
+ # the upper limit being *exclusive*.
34
+ # In contrast, SymPy and the usual mathematical notation has:
35
+ #
36
+ # prod_{i = a}^b f(i) = f(a) * f(a+1) * ... * f(b-1) * f(b)
37
+ #
38
+ # with the upper limit *inclusive*. So translating between
39
+ # the two we find that:
40
+ #
41
+ # \prod_{m <= i < n} f(i) = \prod_{i = m}^{n-1} f(i)
42
+ #
43
+ # where we intentionally used two different ways to typeset the
44
+ # products and its limits.
45
+
46
+ i = Symbol("i", integer=True)
47
+ k = Symbol("k", integer=True)
48
+ j = Symbol("j", integer=True, positive=True)
49
+
50
+ # A simple example with a concrete factors and symbolic limits.
51
+
52
+ # The normal product: m = k and n = k + j and therefore m < n:
53
+ m = k
54
+ n = k + j
55
+
56
+ a = m
57
+ b = n - 1
58
+ S1 = Product(i**2, (i, a, b)).doit()
59
+
60
+ # The reversed product: m = k + j and n = k and therefore m > n:
61
+ m = k + j
62
+ n = k
63
+
64
+ a = m
65
+ b = n - 1
66
+ S2 = Product(i**2, (i, a, b)).doit()
67
+
68
+ assert S1 * S2 == 1
69
+
70
+ # Test the empty product: m = k and n = k and therefore m = n:
71
+ m = k
72
+ n = k
73
+
74
+ a = m
75
+ b = n - 1
76
+ Sz = Product(i**2, (i, a, b)).doit()
77
+
78
+ assert Sz == 1
79
+
80
+ # Another example this time with an unspecified factor and
81
+ # numeric limits. (We can not do both tests in the same example.)
82
+ f = Function("f")
83
+
84
+ # The normal product with m < n:
85
+ m = 2
86
+ n = 11
87
+
88
+ a = m
89
+ b = n - 1
90
+ S1 = Product(f(i), (i, a, b)).doit()
91
+
92
+ # The reversed product with m > n:
93
+ m = 11
94
+ n = 2
95
+
96
+ a = m
97
+ b = n - 1
98
+ S2 = Product(f(i), (i, a, b)).doit()
99
+
100
+ assert simplify(S1 * S2) == 1
101
+
102
+ # Test the empty product with m = n:
103
+ m = 5
104
+ n = 5
105
+
106
+ a = m
107
+ b = n - 1
108
+ Sz = Product(f(i), (i, a, b)).doit()
109
+
110
+ assert Sz == 1
111
+
112
+
113
+ def test_karr_proposition_2a():
114
+ # Test Karr, page 309, proposition 2, part a
115
+ i, u, v = symbols('i u v', integer=True)
116
+
117
+ def test_the_product(m, n):
118
+ # g
119
+ g = i**3 + 2*i**2 - 3*i
120
+ # f = Delta g
121
+ f = simplify(g.subs(i, i+1) / g)
122
+ # The product
123
+ a = m
124
+ b = n - 1
125
+ P = Product(f, (i, a, b)).doit()
126
+ # Test if Product_{m <= i < n} f(i) = g(n) / g(m)
127
+ assert combsimp(P / (g.subs(i, n) / g.subs(i, m))) == 1
128
+
129
+ # m < n
130
+ test_the_product(u, u + v)
131
+ # m = n
132
+ test_the_product(u, u)
133
+ # m > n
134
+ test_the_product(u + v, u)
135
+
136
+
137
+ def test_karr_proposition_2b():
138
+ # Test Karr, page 309, proposition 2, part b
139
+ i, u, v, w = symbols('i u v w', integer=True)
140
+
141
+ def test_the_product(l, n, m):
142
+ # Productmand
143
+ s = i**3
144
+ # First product
145
+ a = l
146
+ b = n - 1
147
+ S1 = Product(s, (i, a, b)).doit()
148
+ # Second product
149
+ a = l
150
+ b = m - 1
151
+ S2 = Product(s, (i, a, b)).doit()
152
+ # Third product
153
+ a = m
154
+ b = n - 1
155
+ S3 = Product(s, (i, a, b)).doit()
156
+ # Test if S1 = S2 * S3 as required
157
+ assert combsimp(S1 / (S2 * S3)) == 1
158
+
159
+ # l < m < n
160
+ test_the_product(u, u + v, u + v + w)
161
+ # l < m = n
162
+ test_the_product(u, u + v, u + v)
163
+ # l < m > n
164
+ test_the_product(u, u + v + w, v)
165
+ # l = m < n
166
+ test_the_product(u, u, u + v)
167
+ # l = m = n
168
+ test_the_product(u, u, u)
169
+ # l = m > n
170
+ test_the_product(u + v, u + v, u)
171
+ # l > m < n
172
+ test_the_product(u + v, u, u + w)
173
+ # l > m = n
174
+ test_the_product(u + v, u, u)
175
+ # l > m > n
176
+ test_the_product(u + v + w, u + v, u)
177
+
178
+
179
+ def test_simple_products():
180
+ assert product(2, (k, a, n)) == 2**(n - a + 1)
181
+ assert product(k, (k, 1, n)) == factorial(n)
182
+ assert product(k**3, (k, 1, n)) == factorial(n)**3
183
+
184
+ assert product(k + 1, (k, 0, n - 1)) == factorial(n)
185
+ assert product(k + 1, (k, a, n - 1)) == rf(1 + a, n - a)
186
+
187
+ assert product(cos(k), (k, 0, 5)) == cos(1)*cos(2)*cos(3)*cos(4)*cos(5)
188
+ assert product(cos(k), (k, 3, 5)) == cos(3)*cos(4)*cos(5)
189
+ assert product(cos(k), (k, 1, Rational(5, 2))) != cos(1)*cos(2)
190
+
191
+ assert isinstance(product(k**k, (k, 1, n)), Product)
192
+
193
+ assert Product(x**k, (k, 1, n)).variables == [k]
194
+
195
+ raises(ValueError, lambda: Product(n))
196
+ raises(ValueError, lambda: Product(n, k))
197
+ raises(ValueError, lambda: Product(n, k, 1))
198
+ raises(ValueError, lambda: Product(n, k, 1, 10))
199
+ raises(ValueError, lambda: Product(n, (k, 1)))
200
+
201
+ assert product(1, (n, 1, oo)) == 1 # issue 8301
202
+ assert product(2, (n, 1, oo)) is oo
203
+ assert product(-1, (n, 1, oo)).func is Product
204
+
205
+
206
+ def test_multiple_products():
207
+ assert product(x, (n, 1, k), (k, 1, m)) == x**(m**2/2 + m/2)
208
+ assert product(f(n), (
209
+ n, 1, m), (m, 1, k)) == Product(f(n), (n, 1, m), (m, 1, k)).doit()
210
+ assert Product(f(n), (m, 1, k), (n, 1, k)).doit() == \
211
+ Product(Product(f(n), (m, 1, k)), (n, 1, k)).doit() == \
212
+ product(f(n), (m, 1, k), (n, 1, k)) == \
213
+ product(product(f(n), (m, 1, k)), (n, 1, k)) == \
214
+ Product(f(n)**k, (n, 1, k))
215
+ assert Product(
216
+ x, (x, 1, k), (k, 1, n)).doit() == Product(factorial(k), (k, 1, n))
217
+
218
+ assert Product(x**k, (n, 1, k), (k, 1, m)).variables == [n, k]
219
+
220
+
221
+ def test_rational_products():
222
+ assert product(1 + 1/k, (k, 1, n)) == rf(2, n)/factorial(n)
223
+
224
+
225
+ def test_special_products():
226
+ # Wallis product
227
+ assert product((4*k)**2 / (4*k**2 - 1), (k, 1, n)) == \
228
+ 4**n*factorial(n)**2/rf(S.Half, n)/rf(Rational(3, 2), n)
229
+
230
+ # Euler's product formula for sin
231
+ assert product(1 + a/k**2, (k, 1, n)) == \
232
+ rf(1 - sqrt(-a), n)*rf(1 + sqrt(-a), n)/factorial(n)**2
233
+
234
+
235
+ def test__eval_product():
236
+ from sympy.abc import i, n
237
+ # issue 4809
238
+ a = Function('a')
239
+ assert product(2*a(i), (i, 1, n)) == 2**n * Product(a(i), (i, 1, n))
240
+ # issue 4810
241
+ assert product(2**i, (i, 1, n)) == 2**(n*(n + 1)/2)
242
+ k, m = symbols('k m', integer=True)
243
+ assert product(2**i, (i, k, m)) == 2**(-k**2/2 + k/2 + m**2/2 + m/2)
244
+ n = Symbol('n', negative=True, integer=True)
245
+ p = Symbol('p', positive=True, integer=True)
246
+ assert product(2**i, (i, n, p)) == 2**(-n**2/2 + n/2 + p**2/2 + p/2)
247
+ assert product(2**i, (i, p, n)) == 2**(n**2/2 + n/2 - p**2/2 + p/2)
248
+
249
+
250
+ def test_product_pow():
251
+ # issue 4817
252
+ assert product(2**f(k), (k, 1, n)) == 2**Sum(f(k), (k, 1, n))
253
+ assert product(2**(2*f(k)), (k, 1, n)) == 2**Sum(2*f(k), (k, 1, n))
254
+
255
+
256
+ def test_infinite_product():
257
+ # issue 5737
258
+ assert isinstance(Product(2**(1/factorial(n)), (n, 0, oo)), Product)
259
+
260
+
261
+ def test_conjugate_transpose():
262
+ p = Product(x**k, (k, 1, 3))
263
+ assert p.adjoint().doit() == p.doit().adjoint()
264
+ assert p.conjugate().doit() == p.doit().conjugate()
265
+ assert p.transpose().doit() == p.doit().transpose()
266
+
267
+ A, B = symbols("A B", commutative=False)
268
+ p = Product(A*B**k, (k, 1, 3))
269
+ assert p.adjoint().doit() == p.doit().adjoint()
270
+ assert p.conjugate().doit() == p.doit().conjugate()
271
+ assert p.transpose().doit() == p.doit().transpose()
272
+
273
+ p = Product(B**k*A, (k, 1, 3))
274
+ assert p.adjoint().doit() == p.doit().adjoint()
275
+ assert p.conjugate().doit() == p.doit().conjugate()
276
+ assert p.transpose().doit() == p.doit().transpose()
277
+
278
+
279
+ def test_simplify_prod():
280
+ y, t, b, c, v, d = symbols('y, t, b, c, v, d', integer = True)
281
+
282
+ _simplify = lambda e: simplify(e, doit=False)
283
+ assert _simplify(Product(x*y, (x, n, m), (y, a, k)) * \
284
+ Product(y, (x, n, m), (y, a, k))) == \
285
+ Product(x*y**2, (x, n, m), (y, a, k))
286
+ assert _simplify(3 * y* Product(x, (x, n, m)) * Product(x, (x, m + 1, a))) \
287
+ == 3 * y * Product(x, (x, n, a))
288
+ assert _simplify(Product(x, (x, k + 1, a)) * Product(x, (x, n, k))) == \
289
+ Product(x, (x, n, a))
290
+ assert _simplify(Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))) == \
291
+ Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))
292
+ assert _simplify(Product(x, (t, a, b)) * Product(y, (t, a, b)) * \
293
+ Product(x, (t, b+1, c))) == Product(x*y, (t, a, b)) * \
294
+ Product(x, (t, b+1, c))
295
+ assert _simplify(Product(x, (t, a, b)) * Product(x, (t, b+1, c)) * \
296
+ Product(y, (t, a, b))) == Product(x*y, (t, a, b)) * \
297
+ Product(x, (t, b+1, c))
298
+ assert _simplify(Product(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \
299
+ Product(2, (t, a, b))
300
+ assert _simplify(Product(sin(t)**2 + cos(t)**2 - 1, (t, a, b))) == \
301
+ Product(0, (t, a, b))
302
+ assert _simplify(Product(v*Product(sin(t)**2 + cos(t)**2, (t, a, b)),
303
+ (v, c, d))) == Product(v*Product(1, (t, a, b)), (v, c, d))
304
+
305
+
306
+ def test_change_index():
307
+ b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
308
+
309
+ assert Product(x, (x, a, b)).change_index(x, x + 1, y) == \
310
+ Product(y - 1, (y, a + 1, b + 1))
311
+ assert Product(x**2, (x, a, b)).change_index(x, x - 1) == \
312
+ Product((x + 1)**2, (x, a - 1, b - 1))
313
+ assert Product(x**2, (x, a, b)).change_index(x, -x, y) == \
314
+ Product((-y)**2, (y, -b, -a))
315
+ assert Product(x, (x, a, b)).change_index(x, -x - 1) == \
316
+ Product(-x - 1, (x, - b - 1, -a - 1))
317
+ assert Product(x*y, (x, a, b), (y, c, d)).change_index(x, x - 1, z) == \
318
+ Product((z + 1)*y, (z, a - 1, b - 1), (y, c, d))
319
+
320
+
321
+ def test_reorder():
322
+ b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
323
+
324
+ assert Product(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \
325
+ Product(x*y, (y, c, d), (x, a, b))
326
+ assert Product(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \
327
+ Product(x, (x, c, d), (x, a, b))
328
+ assert Product(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\
329
+ (2, 0), (0, 1)) == Product(x*y + z, (z, m, n), (y, c, d), (x, a, b))
330
+ assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
331
+ (0, 1), (1, 2), (0, 2)) == \
332
+ Product(x*y*z, (x, a, b), (z, m, n), (y, c, d))
333
+ assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
334
+ (x, y), (y, z), (x, z)) == \
335
+ Product(x*y*z, (x, a, b), (z, m, n), (y, c, d))
336
+ assert Product(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \
337
+ Product(x*y, (y, c, d), (x, a, b))
338
+ assert Product(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \
339
+ Product(x*y, (y, c, d), (x, a, b))
340
+
341
+
342
+ def test_Product_is_convergent():
343
+ assert Product(1/n**2, (n, 1, oo)).is_convergent() is S.false
344
+ assert Product(exp(1/n**2), (n, 1, oo)).is_convergent() is S.true
345
+ assert Product(1/n, (n, 1, oo)).is_convergent() is S.false
346
+ assert Product(1 + 1/n, (n, 1, oo)).is_convergent() is S.false
347
+ assert Product(1 + 1/n**2, (n, 1, oo)).is_convergent() is S.true
348
+
349
+
350
+ def test_reverse_order():
351
+ x, y, a, b, c, d= symbols('x, y, a, b, c, d', integer = True)
352
+
353
+ assert Product(x, (x, 0, 3)).reverse_order(0) == Product(1/x, (x, 4, -1))
354
+ assert Product(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \
355
+ Product(x*y, (x, 6, 0), (y, 7, -1))
356
+ assert Product(x, (x, 1, 2)).reverse_order(0) == Product(1/x, (x, 3, 0))
357
+ assert Product(x, (x, 1, 3)).reverse_order(0) == Product(1/x, (x, 4, 0))
358
+ assert Product(x, (x, 1, a)).reverse_order(0) == Product(1/x, (x, a + 1, 0))
359
+ assert Product(x, (x, a, 5)).reverse_order(0) == Product(1/x, (x, 6, a - 1))
360
+ assert Product(x, (x, a + 1, a + 5)).reverse_order(0) == \
361
+ Product(1/x, (x, a + 6, a))
362
+ assert Product(x, (x, a + 1, a + 2)).reverse_order(0) == \
363
+ Product(1/x, (x, a + 3, a))
364
+ assert Product(x, (x, a + 1, a + 1)).reverse_order(0) == \
365
+ Product(1/x, (x, a + 2, a))
366
+ assert Product(x, (x, a, b)).reverse_order(0) == Product(1/x, (x, b + 1, a - 1))
367
+ assert Product(x, (x, a, b)).reverse_order(x) == Product(1/x, (x, b + 1, a - 1))
368
+ assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \
369
+ Product(x*y, (x, b + 1, a - 1), (y, 6, 1))
370
+ assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \
371
+ Product(x*y, (x, b + 1, a - 1), (y, 6, 1))
372
+
373
+
374
+ def test_issue_9983():
375
+ n = Symbol('n', integer=True, positive=True)
376
+ p = Product(1 + 1/n**Rational(2, 3), (n, 1, oo))
377
+ assert p.is_convergent() is S.false
378
+ assert product(1 + 1/n**Rational(2, 3), (n, 1, oo)) == p.doit()
379
+
380
+
381
+ def test_issue_13546():
382
+ n = Symbol('n')
383
+ k = Symbol('k')
384
+ p = Product(n + 1 / 2**k, (k, 0, n-1)).doit()
385
+ assert p.subs(n, 2).doit() == Rational(15, 2)
386
+
387
+
388
+ def test_issue_14036():
389
+ a, n = symbols('a n')
390
+ assert product(1 - a**2 / (n*pi)**2, [n, 1, oo]) != 0
391
+
392
+
393
+ def test_rewrite_Sum():
394
+ assert Product(1 - S.Half**2/k**2, (k, 1, oo)).rewrite(Sum) == \
395
+ exp(Sum(log(1 - 1/(4*k**2)), (k, 1, oo)))
396
+
397
+
398
+ def test_KroneckerDelta_Product():
399
+ y = Symbol('y')
400
+ assert Product(x*KroneckerDelta(x, y), (x, 0, 1)).doit() == 0
401
+
402
+
403
+ def test_issue_20848():
404
+ _i = Dummy('i')
405
+ t, y, z = symbols('t y z')
406
+ assert diff(Product(x, (y, 1, z)), x).as_dummy() == Sum(Product(x, (y, 1, _i - 1))*Product(x, (y, _i + 1, z)), (_i, 1, z)).as_dummy()
407
+ assert diff(Product(x, (y, 1, z)), x).doit() == x**(z - 1)*z
408
+ assert diff(Product(x, (y, x, z)), x) == Derivative(Product(x, (y, x, z)), x)
409
+ assert diff(Product(t, (x, 1, z)), x) == S(0)
410
+ assert Product(sin(n*x), (n, -1, 1)).diff(x).doit() == S(0)
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py ADDED
@@ -0,0 +1,1676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from math import prod
2
+
3
+ from sympy.concrete.expr_with_intlimits import ReorderError
4
+ from sympy.concrete.products import (Product, product)
5
+ from sympy.concrete.summations import (Sum, summation, telescopic,
6
+ eval_sum_residue, _dummy_with_inherited_properties_concrete)
7
+ from sympy.core.function import (Derivative, Function)
8
+ from sympy.core import (Catalan, EulerGamma)
9
+ from sympy.core.facts import InconsistentAssumptions
10
+ from sympy.core.mod import Mod
11
+ from sympy.core.numbers import (E, I, Rational, nan, oo, pi)
12
+ from sympy.core.relational import Eq, Ne
13
+ from sympy.core.numbers import Float
14
+ from sympy.core.singleton import S
15
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
16
+ from sympy.core.sympify import sympify
17
+ from sympy.functions.combinatorial.factorials import (rf, binomial, factorial)
18
+ from sympy.functions.combinatorial.numbers import harmonic
19
+ from sympy.functions.elementary.complexes import Abs, re
20
+ from sympy.functions.elementary.exponential import (exp, log)
21
+ from sympy.functions.elementary.hyperbolic import (sinh, tanh)
22
+ from sympy.functions.elementary.integers import floor
23
+ from sympy.functions.elementary.miscellaneous import sqrt
24
+ from sympy.functions.elementary.piecewise import Piecewise
25
+ from sympy.functions.elementary.trigonometric import (cos, sin, atan)
26
+ from sympy.functions.special.gamma_functions import (gamma, lowergamma)
27
+ from sympy.functions.special.tensor_functions import KroneckerDelta
28
+ from sympy.functions.special.zeta_functions import zeta
29
+ from sympy.integrals.integrals import Integral
30
+ from sympy.logic.boolalg import And, Or
31
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
32
+ from sympy.matrices.expressions.special import Identity
33
+ from sympy.matrices import (Matrix, SparseMatrix,
34
+ ImmutableDenseMatrix, ImmutableSparseMatrix, diag)
35
+ from sympy.sets.contains import Contains
36
+ from sympy.sets.fancysets import Range
37
+ from sympy.sets.sets import Interval
38
+ from sympy.simplify.combsimp import combsimp
39
+ from sympy.simplify.simplify import simplify
40
+ from sympy.tensor.indexed import (Idx, Indexed, IndexedBase)
41
+ from sympy.testing.pytest import XFAIL, raises, slow
42
+ from sympy.abc import a, b, c, d, k, m, x, y, z
43
+
44
+ n = Symbol('n', integer=True)
45
+ f, g = symbols('f g', cls=Function)
46
+
47
+ def test_karr_convention():
48
+ # Test the Karr summation convention that we want to hold.
49
+ # See his paper "Summation in Finite Terms" for a detailed
50
+ # reasoning why we really want exactly this definition.
51
+ # The convention is described on page 309 and essentially
52
+ # in section 1.4, definition 3:
53
+ #
54
+ # \sum_{m <= i < n} f(i) 'has the obvious meaning' for m < n
55
+ # \sum_{m <= i < n} f(i) = 0 for m = n
56
+ # \sum_{m <= i < n} f(i) = - \sum_{n <= i < m} f(i) for m > n
57
+ #
58
+ # It is important to note that he defines all sums with
59
+ # the upper limit being *exclusive*.
60
+ # In contrast, SymPy and the usual mathematical notation has:
61
+ #
62
+ # sum_{i = a}^b f(i) = f(a) + f(a+1) + ... + f(b-1) + f(b)
63
+ #
64
+ # with the upper limit *inclusive*. So translating between
65
+ # the two we find that:
66
+ #
67
+ # \sum_{m <= i < n} f(i) = \sum_{i = m}^{n-1} f(i)
68
+ #
69
+ # where we intentionally used two different ways to typeset the
70
+ # sum and its limits.
71
+
72
+ i = Symbol("i", integer=True)
73
+ k = Symbol("k", integer=True)
74
+ j = Symbol("j", integer=True)
75
+
76
+ # A simple example with a concrete summand and symbolic limits.
77
+
78
+ # The normal sum: m = k and n = k + j and therefore m < n:
79
+ m = k
80
+ n = k + j
81
+
82
+ a = m
83
+ b = n - 1
84
+ S1 = Sum(i**2, (i, a, b)).doit()
85
+
86
+ # The reversed sum: m = k + j and n = k and therefore m > n:
87
+ m = k + j
88
+ n = k
89
+
90
+ a = m
91
+ b = n - 1
92
+ S2 = Sum(i**2, (i, a, b)).doit()
93
+
94
+ assert simplify(S1 + S2) == 0
95
+
96
+ # Test the empty sum: m = k and n = k and therefore m = n:
97
+ m = k
98
+ n = k
99
+
100
+ a = m
101
+ b = n - 1
102
+ Sz = Sum(i**2, (i, a, b)).doit()
103
+
104
+ assert Sz == 0
105
+
106
+ # Another example this time with an unspecified summand and
107
+ # numeric limits. (We can not do both tests in the same example.)
108
+
109
+ # The normal sum with m < n:
110
+ m = 2
111
+ n = 11
112
+
113
+ a = m
114
+ b = n - 1
115
+ S1 = Sum(f(i), (i, a, b)).doit()
116
+
117
+ # The reversed sum with m > n:
118
+ m = 11
119
+ n = 2
120
+
121
+ a = m
122
+ b = n - 1
123
+ S2 = Sum(f(i), (i, a, b)).doit()
124
+
125
+ assert simplify(S1 + S2) == 0
126
+
127
+ # Test the empty sum with m = n:
128
+ m = 5
129
+ n = 5
130
+
131
+ a = m
132
+ b = n - 1
133
+ Sz = Sum(f(i), (i, a, b)).doit()
134
+
135
+ assert Sz == 0
136
+
137
+ e = Piecewise((exp(-i), Mod(i, 2) > 0), (0, True))
138
+ s = Sum(e, (i, 0, 11))
139
+ assert s.n(3) == s.doit().n(3)
140
+
141
+ # issue #27893
142
+ n = Symbol('n', integer=True)
143
+ assert Sum(1/(x**2 + 1), (x, oo, 0)).doit(deep=False) == Rational(-1, 2) + pi / (2 * tanh(pi))
144
+ assert Sum(c**x/factorial(x), (x, oo, 0)).doit(deep=False).simplify() == exp(c) - 1 # exponential series
145
+ assert Sum((-1)**x/x, (x, oo,0)).doit() == -log(2) # alternating harmnic series
146
+ assert Sum((1/2)**x,(x, oo, -1)).doit() == S(2) # geometric series
147
+ assert Sum(1/x, (x, oo, 0)).doit() == oo # harmonic series, divergent
148
+ assert Sum((-1)**x/(2*x+1), (x, oo, -1)).doit() == pi/4 # leibniz series
149
+ assert Sum((((-1)**x) * c**(2*x+1)) / factorial(2*x+1), (x, oo, -1)).doit() == sin(c) # sinusoidal series
150
+ assert Sum((((-1)**x) * c**(2*x+1)) / (2*x+1), (x, 0, oo)).doit() \
151
+ == Piecewise((atan(c), Ne(c**2, -1) & (Abs(c**2) <= 1)), \
152
+ (Sum((-1)**x*c**(2*x + 1)/(2*x + 1), (x, 0, oo)), True)) # arctangent series
153
+ assert Sum(binomial(n, x) * c**x, (x, 0, oo)).doit() \
154
+ == Piecewise(((c + 1)**n, \
155
+ ((n <= -1) & (Abs(c) < 1)) \
156
+ | ((n > 0) & (Abs(c) <= 1)) \
157
+ | ((n <= 0) & (n > -1) & Ne(c, -1) & (Abs(c) <= 1))), \
158
+ (Sum(c**x*binomial(n, x), (x, 0, oo)), True)) # binomial series
159
+ assert Sum(1/x**n, (x, oo, 0)).doit() \
160
+ == Piecewise((zeta(n), n > 1), (Sum(x**(-n), (x, oo, 0)), True)) # Euler's zeta function
161
+
162
+ def test_karr_proposition_2a():
163
+ # Test Karr, page 309, proposition 2, part a
164
+ i = Symbol("i", integer=True)
165
+ u = Symbol("u", integer=True)
166
+ v = Symbol("v", integer=True)
167
+
168
+ def test_the_sum(m, n):
169
+ # g
170
+ g = i**3 + 2*i**2 - 3*i
171
+ # f = Delta g
172
+ f = simplify(g.subs(i, i+1) - g)
173
+ # The sum
174
+ a = m
175
+ b = n - 1
176
+ S = Sum(f, (i, a, b)).doit()
177
+ # Test if Sum_{m <= i < n} f(i) = g(n) - g(m)
178
+ assert simplify(S - (g.subs(i, n) - g.subs(i, m))) == 0
179
+
180
+ # m < n
181
+ test_the_sum(u, u+v)
182
+ # m = n
183
+ test_the_sum(u, u )
184
+ # m > n
185
+ test_the_sum(u+v, u )
186
+
187
+
188
+ def test_karr_proposition_2b():
189
+ # Test Karr, page 309, proposition 2, part b
190
+ i = Symbol("i", integer=True)
191
+ u = Symbol("u", integer=True)
192
+ v = Symbol("v", integer=True)
193
+ w = Symbol("w", integer=True)
194
+
195
+ def test_the_sum(l, n, m):
196
+ # Summand
197
+ s = i**3
198
+ # First sum
199
+ a = l
200
+ b = n - 1
201
+ S1 = Sum(s, (i, a, b)).doit()
202
+ # Second sum
203
+ a = l
204
+ b = m - 1
205
+ S2 = Sum(s, (i, a, b)).doit()
206
+ # Third sum
207
+ a = m
208
+ b = n - 1
209
+ S3 = Sum(s, (i, a, b)).doit()
210
+ # Test if S1 = S2 + S3 as required
211
+ assert S1 - (S2 + S3) == 0
212
+
213
+ # l < m < n
214
+ test_the_sum(u, u+v, u+v+w)
215
+ # l < m = n
216
+ test_the_sum(u, u+v, u+v )
217
+ # l < m > n
218
+ test_the_sum(u, u+v+w, v )
219
+ # l = m < n
220
+ test_the_sum(u, u, u+v )
221
+ # l = m = n
222
+ test_the_sum(u, u, u )
223
+ # l = m > n
224
+ test_the_sum(u+v, u+v, u )
225
+ # l > m < n
226
+ test_the_sum(u+v, u, u+w )
227
+ # l > m = n
228
+ test_the_sum(u+v, u, u )
229
+ # l > m > n
230
+ test_the_sum(u+v+w, u+v, u )
231
+
232
+
233
+ def test_arithmetic_sums():
234
+ assert summation(1, (n, a, b)) == b - a + 1
235
+ assert Sum(S.NaN, (n, a, b)) is S.NaN
236
+ assert Sum(x, (n, a, a)).doit() == x
237
+ assert Sum(x, (x, a, a)).doit() == a
238
+ assert Sum(x, (n, 1, a)).doit() == a*x
239
+ assert Sum(x, (x, Range(1, 11))).doit() == 55
240
+ assert Sum(x, (x, Range(1, 11, 2))).doit() == 25
241
+ assert Sum(x, (x, Range(1, 10, 2))) == Sum(x, (x, Range(9, 0, -2)))
242
+ lo, hi = 1, 2
243
+ s1 = Sum(n, (n, lo, hi))
244
+ s2 = Sum(n, (n, hi, lo))
245
+ assert s1 != s2
246
+ assert s1.doit() == 3 and s2.doit() == 0
247
+ lo, hi = x, x + 1
248
+ s1 = Sum(n, (n, lo, hi))
249
+ s2 = Sum(n, (n, hi, lo))
250
+ assert s1 != s2
251
+ assert s1.doit() == 2*x + 1 and s2.doit() == 0
252
+ assert Sum(Integral(x, (x, 1, y)) + x, (x, 1, 2)).doit() == \
253
+ y**2 + 2
254
+ assert summation(1, (n, 1, 10)) == 10
255
+ assert summation(2*n, (n, 0, 10**10)) == 100000000010000000000
256
+ assert summation(4*n*m, (n, a, 1), (m, 1, d)).expand() == \
257
+ 2*d + 2*d**2 + a*d + a*d**2 - d*a**2 - a**2*d**2
258
+ assert summation(cos(n), (n, -2, 1)) == cos(-2) + cos(-1) + cos(0) + cos(1)
259
+ assert summation(cos(n), (n, x, x + 2)) == cos(x) + cos(x + 1) + cos(x + 2)
260
+ assert isinstance(summation(cos(n), (n, x, x + S.Half)), Sum)
261
+ assert summation(k, (k, 0, oo)) is oo
262
+ assert summation(k, (k, Range(1, 11))) == 55
263
+
264
+
265
+ def test_polynomial_sums():
266
+ assert summation(n**2, (n, 3, 8)) == 199
267
+ assert summation(n, (n, a, b)) == \
268
+ ((a + b)*(b - a + 1)/2).expand()
269
+ assert summation(n**2, (n, 1, b)) == \
270
+ ((2*b**3 + 3*b**2 + b)/6).expand()
271
+ assert summation(n**3, (n, 1, b)) == \
272
+ ((b**4 + 2*b**3 + b**2)/4).expand()
273
+ assert summation(n**6, (n, 1, b)) == \
274
+ ((6*b**7 + 21*b**6 + 21*b**5 - 7*b**3 + b)/42).expand()
275
+
276
+
277
+ def test_geometric_sums():
278
+ assert summation(pi**n, (n, 0, b)) == (1 - pi**(b + 1)) / (1 - pi)
279
+ assert summation(2 * 3**n, (n, 0, b)) == 3**(b + 1) - 1
280
+ assert summation(S.Half**n, (n, 1, oo)) == 1
281
+ assert summation(2**n, (n, 0, b)) == 2**(b + 1) - 1
282
+ assert summation(2**n, (n, 1, oo)) is oo
283
+ assert summation(2**(-n), (n, 1, oo)) == 1
284
+ assert summation(3**(-n), (n, 4, oo)) == Rational(1, 54)
285
+ assert summation(2**(-4*n + 3), (n, 1, oo)) == Rational(8, 15)
286
+ assert summation(2**(n + 1), (n, 1, b)).expand() == 4*(2**b - 1)
287
+
288
+ # issue 6664:
289
+ assert summation(x**n, (n, 0, oo)) == \
290
+ Piecewise((1/(-x + 1), Abs(x) < 1), (Sum(x**n, (n, 0, oo)), True))
291
+
292
+ assert summation(-2**n, (n, 0, oo)) is -oo
293
+ assert summation(I**n, (n, 0, oo)) == Sum(I**n, (n, 0, oo))
294
+
295
+ # issue 6802:
296
+ assert summation((-1)**(2*x + 2), (x, 0, n)) == n + 1
297
+ assert summation((-2)**(2*x + 2), (x, 0, n)) == 4*4**(n + 1)/S(3) - Rational(4, 3)
298
+ assert summation((-1)**x, (x, 0, n)) == -(-1)**(n + 1)/S(2) + S.Half
299
+ assert summation(y**x, (x, a, b)) == \
300
+ Piecewise((-a + b + 1, Eq(y, 1)), ((y**a - y**(b + 1))/(-y + 1), True))
301
+ assert summation((-2)**(y*x + 2), (x, 0, n)) == \
302
+ 4*Piecewise((n + 1, Eq((-2)**y, 1)),
303
+ ((-(-2)**(y*(n + 1)) + 1)/(-(-2)**y + 1), True))
304
+
305
+ # issue 8251:
306
+ assert summation((1/(n + 1)**2)*n**2, (n, 0, oo)) is oo
307
+
308
+ #issue 9908:
309
+ assert Sum(1/(n**3 - 1), (n, -oo, -2)).doit() == summation(1/(n**3 - 1), (n, -oo, -2))
310
+
311
+ #issue 11642:
312
+ result = Sum(0.5**n, (n, 1, oo)).doit()
313
+ assert result == 1.0
314
+ assert result.is_Float
315
+
316
+ result = Sum(0.25**n, (n, 1, oo)).doit()
317
+ assert result == 1/3.
318
+ assert result.is_Float
319
+
320
+ result = Sum(0.99999**n, (n, 1, oo)).doit()
321
+ assert result == 99999.0
322
+ assert result.is_Float
323
+
324
+ result = Sum(S.Half**n, (n, 1, oo)).doit()
325
+ assert result == 1
326
+ assert not result.is_Float
327
+
328
+ result = Sum(Rational(3, 5)**n, (n, 1, oo)).doit()
329
+ assert result == Rational(3, 2)
330
+ assert not result.is_Float
331
+
332
+ assert Sum(1.0**n, (n, 1, oo)).doit() is oo
333
+ assert Sum(2.43**n, (n, 1, oo)).doit() is oo
334
+
335
+ # Issue 13979
336
+ i, k, q = symbols('i k q', integer=True)
337
+ result = summation(
338
+ exp(-2*I*pi*k*i/n) * exp(2*I*pi*q*i/n) / n, (i, 0, n - 1)
339
+ )
340
+ assert result.simplify() == Piecewise(
341
+ (1, Eq(exp(-2*I*pi*(k - q)/n), 1)), (0, True)
342
+ )
343
+
344
+ #Issue 23491
345
+ assert Sum(1/(n**2 + 1), (n, 1, oo)).doit() == S(-1)/2 + pi/(2*tanh(pi))
346
+
347
+ def test_harmonic_sums():
348
+ assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n))
349
+ assert summation(1/k, (k, 1, n)) == harmonic(n)
350
+ assert summation(n/k, (k, 1, n)) == n*harmonic(n)
351
+ assert summation(1/k, (k, 5, n)) == harmonic(n) - harmonic(4)
352
+
353
+
354
+ def test_composite_sums():
355
+ f = S.Half*(7 - 6*n + Rational(1, 7)*n**3)
356
+ s = summation(f, (n, a, b))
357
+ assert not isinstance(s, Sum)
358
+ A = 0
359
+ for i in range(-3, 5):
360
+ A += f.subs(n, i)
361
+ B = s.subs(a, -3).subs(b, 4)
362
+ assert A == B
363
+
364
+
365
+ def test_hypergeometric_sums():
366
+ assert summation(
367
+ binomial(2*k, k)/4**k, (k, 0, n)) == (1 + 2*n)*binomial(2*n, n)/4**n
368
+ assert summation(binomial(2*k, k)/5**k, (k, -oo, oo)) == sqrt(5)
369
+
370
+
371
+ def test_other_sums():
372
+ f = m**2 + m*exp(m)
373
+ g = 3*exp(Rational(3, 2))/2 + exp(S.Half)/2 - exp(Rational(-1, 2))/2 - 3*exp(Rational(-3, 2))/2 + 5
374
+
375
+ assert summation(f, (m, Rational(-3, 2), Rational(3, 2))) == g
376
+ assert summation(f, (m, -1.5, 1.5)).evalf().epsilon_eq(g.evalf(), 1e-10)
377
+
378
+ fac = factorial
379
+
380
+
381
+ def NS(e, n=15, **options):
382
+ return str(sympify(e).evalf(n, **options))
383
+
384
+
385
+ def test_evalf_fast_series():
386
+ # Euler transformed series for sqrt(1+x)
387
+ assert NS(Sum(
388
+ fac(2*n + 1)/fac(n)**2/2**(3*n + 1), (n, 0, oo)), 100) == NS(sqrt(2), 100)
389
+
390
+ # Some series for exp(1)
391
+ estr = NS(E, 100)
392
+ assert NS(Sum(1/fac(n), (n, 0, oo)), 100) == estr
393
+ assert NS(1/Sum((1 - 2*n)/fac(2*n), (n, 0, oo)), 100) == estr
394
+ assert NS(Sum((2*n + 1)/fac(2*n), (n, 0, oo)), 100) == estr
395
+ assert NS(Sum((4*n + 3)/2**(2*n + 1)/fac(2*n + 1), (n, 0, oo))**2, 100) == estr
396
+
397
+ pistr = NS(pi, 100)
398
+ # Ramanujan series for pi
399
+ assert NS(9801/sqrt(8)/Sum(fac(
400
+ 4*n)*(1103 + 26390*n)/fac(n)**4/396**(4*n), (n, 0, oo)), 100) == pistr
401
+ assert NS(1/Sum(
402
+ binomial(2*n, n)**3 * (42*n + 5)/2**(12*n + 4), (n, 0, oo)), 100) == pistr
403
+ # Machin's formula for pi
404
+ assert NS(16*Sum((-1)**n/(2*n + 1)/5**(2*n + 1), (n, 0, oo)) -
405
+ 4*Sum((-1)**n/(2*n + 1)/239**(2*n + 1), (n, 0, oo)), 100) == pistr
406
+
407
+ # Apery's constant
408
+ astr = NS(zeta(3), 100)
409
+ P = 126392*n**5 + 412708*n**4 + 531578*n**3 + 336367*n**2 + 104000* \
410
+ n + 12463
411
+ assert NS(Sum((-1)**n * P / 24 * (fac(2*n + 1)*fac(2*n)*fac(
412
+ n))**3 / fac(3*n + 2) / fac(4*n + 3)**3, (n, 0, oo)), 100) == astr
413
+ assert NS(Sum((-1)**n * (205*n**2 + 250*n + 77)/64 * fac(n)**10 /
414
+ fac(2*n + 1)**5, (n, 0, oo)), 100) == astr
415
+
416
+
417
+ def test_evalf_fast_series_issue_4021():
418
+ # Catalan's constant
419
+ assert NS(Sum((-1)**(n - 1)*2**(8*n)*(40*n**2 - 24*n + 3)*fac(2*n)**3*
420
+ fac(n)**2/n**3/(2*n - 1)/fac(4*n)**2, (n, 1, oo))/64, 100) == \
421
+ NS(Catalan, 100)
422
+ astr = NS(zeta(3), 100)
423
+ assert NS(5*Sum(
424
+ (-1)**(n - 1)*fac(n)**2 / n**3 / fac(2*n), (n, 1, oo))/2, 100) == astr
425
+ assert NS(Sum((-1)**(n - 1)*(56*n**2 - 32*n + 5) / (2*n - 1)**2 * fac(n - 1)
426
+ **3 / fac(3*n), (n, 1, oo))/4, 100) == astr
427
+
428
+
429
+ def test_evalf_slow_series():
430
+ assert NS(Sum((-1)**n / n, (n, 1, oo)), 15) == NS(-log(2), 15)
431
+ assert NS(Sum((-1)**n / n, (n, 1, oo)), 50) == NS(-log(2), 50)
432
+ assert NS(Sum(1/n**2, (n, 1, oo)), 15) == NS(pi**2/6, 15)
433
+ assert NS(Sum(1/n**2, (n, 1, oo)), 100) == NS(pi**2/6, 100)
434
+ assert NS(Sum(1/n**2, (n, 1, oo)), 500) == NS(pi**2/6, 500)
435
+ assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 15) == NS(pi**3/32, 15)
436
+ assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 50) == NS(pi**3/32, 50)
437
+
438
+
439
+ def test_evalf_oo_to_oo():
440
+ # There used to be an error in certain cases
441
+ # Does not evaluate, but at least do not throw an error
442
+ # Evaluates symbolically to 0, which is not correct
443
+ assert Sum(1/(n**2+1), (n, -oo, oo)).evalf() == Sum(1/(n**2+1), (n, -oo, oo))
444
+ # This evaluates if from 1 to oo and symbolically
445
+ assert Sum(1/(factorial(abs(n))), (n, -oo, -1)).evalf() == Sum(1/(factorial(abs(n))), (n, -oo, -1))
446
+
447
+
448
+ def test_euler_maclaurin():
449
+ # Exact polynomial sums with E-M
450
+ def check_exact(f, a, b, m, n):
451
+ A = Sum(f, (k, a, b))
452
+ s, e = A.euler_maclaurin(m, n)
453
+ assert (e == 0) and (s.expand() == A.doit())
454
+ check_exact(k**4, a, b, 0, 2)
455
+ check_exact(k**4 + 2*k, a, b, 1, 2)
456
+ check_exact(k**4 + k**2, a, b, 1, 5)
457
+ check_exact(k**5, 2, 6, 1, 2)
458
+ check_exact(k**5, 2, 6, 1, 3)
459
+ assert Sum(x-1, (x, 0, 2)).euler_maclaurin(m=30, n=30, eps=2**-15) == (0, 0)
460
+ # Not exact
461
+ assert Sum(k**6, (k, a, b)).euler_maclaurin(0, 2)[1] != 0
462
+ # Numerical test
463
+ for mi, ni in [(2, 4), (2, 20), (10, 20), (18, 20)]:
464
+ A = Sum(1/k**3, (k, 1, oo))
465
+ s, e = A.euler_maclaurin(mi, ni)
466
+ assert abs((s - zeta(3)).evalf()) < e.evalf()
467
+
468
+ raises(ValueError, lambda: Sum(1, (x, 0, 1), (k, 0, 1)).euler_maclaurin())
469
+
470
+
471
+ @slow
472
+ def test_evalf_euler_maclaurin():
473
+ assert NS(Sum(1/k**k, (k, 1, oo)), 15) == '1.29128599706266'
474
+ assert NS(Sum(1/k**k, (k, 1, oo)),
475
+ 50) == '1.2912859970626635404072825905956005414986193682745'
476
+ assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 15) == NS(EulerGamma, 15)
477
+ assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 50) == NS(EulerGamma, 50)
478
+ assert NS(Sum(log(k)/k**2, (k, 1, oo)), 15) == '0.937548254315844'
479
+ assert NS(Sum(log(k)/k**2, (k, 1, oo)),
480
+ 50) == '0.93754825431584375370257409456786497789786028861483'
481
+ assert NS(Sum(1/k, (k, 1000000, 2000000)), 15) == '0.693147930560008'
482
+ assert NS(Sum(1/k, (k, 1000000, 2000000)),
483
+ 50) == '0.69314793056000780941723211364567656807940638436025'
484
+
485
+
486
+ def test_evalf_symbolic():
487
+ # issue 6328
488
+ expr = Sum(f(x), (x, 1, 3)) + Sum(g(x), (x, 1, 3))
489
+ assert expr.evalf() == expr
490
+
491
+
492
+ def test_evalf_issue_3273():
493
+ assert Sum(0, (k, 1, oo)).evalf() == 0
494
+
495
+
496
+ def test_simple_products():
497
+ assert Product(S.NaN, (x, 1, 3)) is S.NaN
498
+ assert product(S.NaN, (x, 1, 3)) is S.NaN
499
+ assert Product(x, (n, a, a)).doit() == x
500
+ assert Product(x, (x, a, a)).doit() == a
501
+ assert Product(x, (y, 1, a)).doit() == x**a
502
+
503
+ lo, hi = 1, 2
504
+ s1 = Product(n, (n, lo, hi))
505
+ s2 = Product(n, (n, hi, lo))
506
+ assert s1 != s2
507
+ # This IS correct according to Karr product convention
508
+ assert s1.doit() == 2
509
+ assert s2.doit() == 1
510
+
511
+ lo, hi = x, x + 1
512
+ s1 = Product(n, (n, lo, hi))
513
+ s2 = Product(n, (n, hi, lo))
514
+ s3 = 1 / Product(n, (n, hi + 1, lo - 1))
515
+ assert s1 != s2
516
+ # This IS correct according to Karr product convention
517
+ assert s1.doit() == x*(x + 1)
518
+ assert s2.doit() == 1
519
+ assert s3.doit() == x*(x + 1)
520
+
521
+ assert Product(Integral(2*x, (x, 1, y)) + 2*x, (x, 1, 2)).doit() == \
522
+ (y**2 + 1)*(y**2 + 3)
523
+ assert product(2, (n, a, b)) == 2**(b - a + 1)
524
+ assert product(n, (n, 1, b)) == factorial(b)
525
+ assert product(n**3, (n, 1, b)) == factorial(b)**3
526
+ assert product(3**(2 + n), (n, a, b)) \
527
+ == 3**(2*(1 - a + b) + b/2 + (b**2)/2 + a/2 - (a**2)/2)
528
+ assert product(cos(n), (n, 3, 5)) == cos(3)*cos(4)*cos(5)
529
+ assert product(cos(n), (n, x, x + 2)) == cos(x)*cos(x + 1)*cos(x + 2)
530
+ assert isinstance(product(cos(n), (n, x, x + S.Half)), Product)
531
+ # If Product managed to evaluate this one, it most likely got it wrong!
532
+ assert isinstance(Product(n**n, (n, 1, b)), Product)
533
+
534
+
535
+ def test_rational_products():
536
+ assert combsimp(product(1 + 1/n, (n, a, b))) == (1 + b)/a
537
+ assert combsimp(product(n + 1, (n, a, b))) == gamma(2 + b)/gamma(1 + a)
538
+ assert combsimp(product((n + 1)/(n - 1), (n, a, b))) == b*(1 + b)/(a*(a - 1))
539
+ assert combsimp(product(n/(n + 1)/(n + 2), (n, a, b))) == \
540
+ a*gamma(a + 2)/(b + 1)/gamma(b + 3)
541
+ assert combsimp(product(n*(n + 1)/(n - 1)/(n - 2), (n, a, b))) == \
542
+ b**2*(b - 1)*(1 + b)/(a - 1)**2/(a*(a - 2))
543
+
544
+
545
+ def test_wallis_product():
546
+ # Wallis product, given in two different forms to ensure that Product
547
+ # can factor simple rational expressions
548
+ A = Product(4*n**2 / (4*n**2 - 1), (n, 1, b))
549
+ B = Product((2*n)*(2*n)/(2*n - 1)/(2*n + 1), (n, 1, b))
550
+ R = pi*gamma(b + 1)**2/(2*gamma(b + S.Half)*gamma(b + Rational(3, 2)))
551
+ assert simplify(A.doit()) == R
552
+ assert simplify(B.doit()) == R
553
+ # This one should eventually also be doable (Euler's product formula for sin)
554
+ # assert Product(1+x/n**2, (n, 1, b)) == ...
555
+
556
+
557
+ def test_telescopic_sums():
558
+ #checks also input 2 of comment 1 issue 4127
559
+ assert Sum(1/k - 1/(k + 1), (k, 1, n)).doit() == 1 - 1/(1 + n)
560
+ assert Sum(
561
+ f(k) - f(k + 2), (k, m, n)).doit() == -f(1 + n) - f(2 + n) + f(m) + f(1 + m)
562
+ assert Sum(cos(k) - cos(k + 3), (k, 1, n)).doit() == -cos(1 + n) - \
563
+ cos(2 + n) - cos(3 + n) + cos(1) + cos(2) + cos(3)
564
+
565
+ # dummy variable shouldn't matter
566
+ assert telescopic(1/m, -m/(1 + m), (m, n - 1, n)) == \
567
+ telescopic(1/k, -k/(1 + k), (k, n - 1, n))
568
+
569
+ assert Sum(1/x/(x - 1), (x, a, b)).doit() == 1/(a - 1) - 1/b
570
+ eq = 1/((5*n + 2)*(5*(n + 1) + 2))
571
+ assert Sum(eq, (n, 0, oo)).doit() == S(1)/10
572
+ nz = symbols('nz', nonzero=True)
573
+ v = Sum(eq.subs(5, nz), (n, 0, oo)).doit()
574
+ assert v.subs(nz, 5).simplify() == S(1)/10
575
+ # check that apart is being used in non-symbolic case
576
+ s = Sum(eq, (n, 0, k)).doit()
577
+ v = Sum(eq, (n, 0, 10**100)).doit()
578
+ assert v == s.subs(k, 10**100)
579
+
580
+
581
+ def test_sum_reconstruct():
582
+ s = Sum(n**2, (n, -1, 1))
583
+ assert s == Sum(*s.args)
584
+ raises(ValueError, lambda: Sum(x, x))
585
+ raises(ValueError, lambda: Sum(x, (x, 1)))
586
+
587
+
588
+ def test_limit_subs():
589
+ for F in (Sum, Product, Integral):
590
+ assert F(a*exp(a), (a, -2, 2)) == F(a*exp(a), (a, -b, b)).subs(b, 2)
591
+ assert F(a, (a, F(b, (b, 1, 2)), 4)).subs(F(b, (b, 1, 2)), c) == \
592
+ F(a, (a, c, 4))
593
+ assert F(x, (x, 1, x + y)).subs(x, 1) == F(x, (x, 1, y + 1))
594
+
595
+
596
+ def test_function_subs():
597
+ S = Sum(x*f(y),(x,0,oo),(y,0,oo))
598
+ assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo))
599
+ assert S.subs(f(x),x) == S
600
+ raises(ValueError, lambda: S.subs(f(y),x+y) )
601
+ S = Sum(x*log(y),(x,0,oo),(y,0,oo))
602
+ assert S.subs(log(y),y) == S
603
+ S = Sum(x*f(y),(x,0,oo),(y,0,oo))
604
+ assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo))
605
+
606
+
607
+ def test_equality():
608
+ # if this fails remove special handling below
609
+ raises(ValueError, lambda: Sum(x, x))
610
+ r = symbols('x', real=True)
611
+ for F in (Sum, Product, Integral):
612
+ try:
613
+ assert F(x, x) != F(y, y)
614
+ assert F(x, (x, 1, 2)) != F(x, x)
615
+ assert F(x, (x, x)) != F(x, x) # or else they print the same
616
+ assert F(1, x) != F(1, y)
617
+ except ValueError:
618
+ pass
619
+ assert F(a, (x, 1, 2)) != F(a, (x, 1, 3)) # diff limit
620
+ assert F(a, (x, 1, x)) != F(a, (y, 1, y))
621
+ assert F(a, (x, 1, 2)) != F(b, (x, 1, 2)) # diff expression
622
+ assert F(x, (x, 1, 2)) != F(r, (r, 1, 2)) # diff assumptions
623
+ assert F(1, (x, 1, x)) != F(1, (y, 1, x)) # only dummy is diff
624
+ assert F(1, (x, 1, x)).dummy_eq(F(1, (y, 1, x)))
625
+
626
+ # issue 5265
627
+ assert Sum(x, (x, 1, x)).subs(x, a) == Sum(x, (x, 1, a))
628
+
629
+
630
+ def test_Sum_doit():
631
+ assert Sum(n*Integral(a**2), (n, 0, 2)).doit() == a**3
632
+ assert Sum(n*Integral(a**2), (n, 0, 2)).doit(deep=False) == \
633
+ 3*Integral(a**2)
634
+ assert summation(n*Integral(a**2), (n, 0, 2)) == 3*Integral(a**2)
635
+
636
+ # test nested sum evaluation
637
+ s = Sum( Sum( Sum(2,(z,1,n+1)), (y,x+1,n)), (x,1,n))
638
+ assert 0 == (s.doit() - n*(n+1)*(n-1)).factor()
639
+
640
+ # Integer assumes finite
641
+ assert Sum(KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((1, And(-oo < y, y < oo)), (0, True))
642
+ assert Sum(KroneckerDelta(m, n), (m, -oo, oo)).doit() == 1
643
+ assert Sum(m*KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((m, And(-oo < y, y < oo)), (0, True))
644
+ assert Sum(x*KroneckerDelta(m, n), (m, -oo, oo)).doit() == x
645
+ assert Sum(Sum(KroneckerDelta(m, n), (m, 1, 3)), (n, 1, 3)).doit() == 3
646
+ assert Sum(Sum(KroneckerDelta(k, m), (m, 1, 3)), (n, 1, 3)).doit() == \
647
+ 3 * Piecewise((1, And(1 <= k, k <= 3)), (0, True))
648
+ assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, 3)).doit() == \
649
+ f(1) + f(2) + f(3)
650
+ assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, oo)).doit() == \
651
+ Sum(f(n), (n, 1, oo))
652
+
653
+ # issue 2597
654
+ nmax = symbols('N', integer=True, positive=True)
655
+ pw = Piecewise((1, And(1 <= n, n <= nmax)), (0, True))
656
+ assert Sum(pw, (n, 1, nmax)).doit() == Sum(Piecewise((1, nmax >= n),
657
+ (0, True)), (n, 1, nmax))
658
+
659
+ q, s = symbols('q, s')
660
+ assert summation(1/n**(2*s), (n, 1, oo)) == Piecewise((zeta(2*s), 2*re(s) > 1),
661
+ (Sum(n**(-2*s), (n, 1, oo)), True))
662
+ assert summation(1/(n+1)**s, (n, 0, oo)) == Piecewise((zeta(s), re(s) > 1),
663
+ (Sum((n + 1)**(-s), (n, 0, oo)), True))
664
+ assert summation(1/(n+q)**s, (n, 0, oo)) == Piecewise(
665
+ (zeta(s, q), And(~Contains(-q, S.Naturals0), re(s) > 1)),
666
+ (Sum((n + q)**(-s), (n, 0, oo)), True))
667
+ assert summation(1/(n+q)**s, (n, q, oo)) == Piecewise(
668
+ (zeta(s, 2*q), And(~Contains(-2*q, S.Naturals0), re(s) > 1)),
669
+ (Sum((n + q)**(-s), (n, q, oo)), True))
670
+ assert summation(1/n**2, (n, 1, oo)) == zeta(2)
671
+ assert summation(1/n**s, (n, 0, oo)) == Sum(n**(-s), (n, 0, oo))
672
+ assert summation(1/(n+1)**(2+I), (n, 0, oo)) == zeta(2+I)
673
+ t = symbols('t', real=True, positive=True)
674
+ assert summation(1/(n+I)**(t+1), (n, 0, oo)) == zeta(t+1, I)
675
+
676
+
677
+ def test_Product_doit():
678
+ assert Product(n*Integral(a**2), (n, 1, 3)).doit() == 2 * a**9 / 9
679
+ assert Product(n*Integral(a**2), (n, 1, 3)).doit(deep=False) == \
680
+ 6*Integral(a**2)**3
681
+ assert product(n*Integral(a**2), (n, 1, 3)) == 6*Integral(a**2)**3
682
+
683
+
684
+ def test_Sum_interface():
685
+ assert isinstance(Sum(0, (n, 0, 2)), Sum)
686
+ assert Sum(nan, (n, 0, 2)) is nan
687
+ assert Sum(nan, (n, 0, oo)) is nan
688
+ assert Sum(0, (n, 0, 2)).doit() == 0
689
+ assert isinstance(Sum(0, (n, 0, oo)), Sum)
690
+ assert Sum(0, (n, 0, oo)).doit() == 0
691
+ raises(ValueError, lambda: Sum(1))
692
+ raises(ValueError, lambda: summation(1))
693
+
694
+
695
+ def test_diff():
696
+ assert Sum(x, (x, 1, 2)).diff(x) == 0
697
+ assert Sum(x*y, (x, 1, 2)).diff(x) == 0
698
+ assert Sum(x*y, (y, 1, 2)).diff(x) == Sum(y, (y, 1, 2))
699
+ e = Sum(x*y, (x, 1, a))
700
+ assert e.diff(a) == Derivative(e, a)
701
+ assert Sum(x*y, (x, 1, 3), (a, 2, 5)).diff(y).doit() == \
702
+ Sum(x*y, (x, 1, 3), (a, 2, 5)).doit().diff(y) == 24
703
+ assert Sum(x, (x, 1, 2)).diff(y) == 0
704
+
705
+
706
+ def test_hypersum():
707
+ assert simplify(summation(x**n/fac(n), (n, 1, oo))) == -1 + exp(x)
708
+ assert summation((-1)**n * x**(2*n) / fac(2*n), (n, 0, oo)) == cos(x)
709
+ assert simplify(summation((-1)**n*x**(2*n + 1) /
710
+ factorial(2*n + 1), (n, 3, oo))) == -x + sin(x) + x**3/6 - x**5/120
711
+
712
+ assert summation(1/(n + 2)**3, (n, 1, oo)) == Rational(-9, 8) + zeta(3)
713
+ assert summation(1/n**4, (n, 1, oo)) == pi**4/90
714
+
715
+ s = summation(x**n*n, (n, -oo, 0))
716
+ assert s.is_Piecewise
717
+ assert s.args[0].args[0] == -1/(x*(1 - 1/x)**2)
718
+ assert s.args[0].args[1] == (abs(1/x) < 1)
719
+
720
+ m = Symbol('n', integer=True, positive=True)
721
+ assert summation(binomial(m, k), (k, 0, m)) == 2**m
722
+
723
+
724
+ def test_issue_4170():
725
+ assert summation(1/factorial(k), (k, 0, oo)) == E
726
+
727
+
728
+ def test_is_commutative():
729
+ from sympy.physics.secondquant import NO, F, Fd
730
+ m = Symbol('m', commutative=False)
731
+ for f in (Sum, Product, Integral):
732
+ assert f(z, (z, 1, 1)).is_commutative is True
733
+ assert f(z*y, (z, 1, 6)).is_commutative is True
734
+ assert f(m*x, (x, 1, 2)).is_commutative is False
735
+
736
+ assert f(NO(Fd(x)*F(y))*z, (z, 1, 2)).is_commutative is False
737
+
738
+
739
+ def test_is_zero():
740
+ for func in [Sum, Product]:
741
+ assert func(0, (x, 1, 1)).is_zero is True
742
+ assert func(x, (x, 1, 1)).is_zero is None
743
+
744
+ assert Sum(0, (x, 1, 0)).is_zero is True
745
+ assert Product(0, (x, 1, 0)).is_zero is False
746
+
747
+
748
+ def test_is_number():
749
+ # is number should not rely on evaluation or assumptions,
750
+ # it should be equivalent to `not foo.free_symbols`
751
+ assert Sum(1, (x, 1, 1)).is_number is True
752
+ assert Sum(1, (x, 1, x)).is_number is False
753
+ assert Sum(0, (x, y, z)).is_number is False
754
+ assert Sum(x, (y, 1, 2)).is_number is False
755
+ assert Sum(x, (y, 1, 1)).is_number is False
756
+ assert Sum(x, (x, 1, 2)).is_number is True
757
+ assert Sum(x*y, (x, 1, 2), (y, 1, 3)).is_number is True
758
+
759
+ assert Product(2, (x, 1, 1)).is_number is True
760
+ assert Product(2, (x, 1, y)).is_number is False
761
+ assert Product(0, (x, y, z)).is_number is False
762
+ assert Product(1, (x, y, z)).is_number is False
763
+ assert Product(x, (y, 1, x)).is_number is False
764
+ assert Product(x, (y, 1, 2)).is_number is False
765
+ assert Product(x, (y, 1, 1)).is_number is False
766
+ assert Product(x, (x, 1, 2)).is_number is True
767
+
768
+
769
+ def test_free_symbols():
770
+ for func in [Sum, Product]:
771
+ assert func(1, (x, 1, 2)).free_symbols == set()
772
+ assert func(0, (x, 1, y)).free_symbols == {y}
773
+ assert func(2, (x, 1, y)).free_symbols == {y}
774
+ assert func(x, (x, 1, 2)).free_symbols == set()
775
+ assert func(x, (x, 1, y)).free_symbols == {y}
776
+ assert func(x, (y, 1, y)).free_symbols == {x, y}
777
+ assert func(x, (y, 1, 2)).free_symbols == {x}
778
+ assert func(x, (y, 1, 1)).free_symbols == {x}
779
+ assert func(x, (y, 1, z)).free_symbols == {x, z}
780
+ assert func(x, (x, 1, y), (y, 1, 2)).free_symbols == set()
781
+ assert func(x, (x, 1, y), (y, 1, z)).free_symbols == {z}
782
+ assert func(x, (x, 1, y), (y, 1, y)).free_symbols == {y}
783
+ assert func(x, (y, 1, y), (y, 1, z)).free_symbols == {x, z}
784
+ assert Sum(1, (x, 1, y)).free_symbols == {y}
785
+ # free_symbols answers whether the object *as written* has free symbols,
786
+ # not whether the evaluated expression has free symbols
787
+ assert Product(1, (x, 1, y)).free_symbols == {y}
788
+ # don't count free symbols that are not independent of integration
789
+ # variable(s)
790
+ assert func(f(x), (f(x), 1, 2)).free_symbols == set()
791
+ assert func(f(x), (f(x), 1, x)).free_symbols == {x}
792
+ assert func(f(x), (f(x), 1, y)).free_symbols == {y}
793
+ assert func(f(x), (z, 1, y)).free_symbols == {x, y}
794
+
795
+
796
+ def test_conjugate_transpose():
797
+ A, B = symbols("A B", commutative=False)
798
+ p = Sum(A*B**n, (n, 1, 3))
799
+ assert p.adjoint().doit() == p.doit().adjoint()
800
+ assert p.conjugate().doit() == p.doit().conjugate()
801
+ assert p.transpose().doit() == p.doit().transpose()
802
+
803
+ p = Sum(B**n*A, (n, 1, 3))
804
+ assert p.adjoint().doit() == p.doit().adjoint()
805
+ assert p.conjugate().doit() == p.doit().conjugate()
806
+ assert p.transpose().doit() == p.doit().transpose()
807
+
808
+
809
+ def test_noncommutativity_honoured():
810
+ A, B = symbols("A B", commutative=False)
811
+ M = symbols('M', integer=True, positive=True)
812
+ p = Sum(A*B**n, (n, 1, M))
813
+ assert p.doit() == A*Piecewise((M, Eq(B, 1)),
814
+ ((B - B**(M + 1))*(1 - B)**(-1), True))
815
+
816
+ p = Sum(B**n*A, (n, 1, M))
817
+ assert p.doit() == Piecewise((M, Eq(B, 1)),
818
+ ((B - B**(M + 1))*(1 - B)**(-1), True))*A
819
+
820
+ p = Sum(B**n*A*B**n, (n, 1, M))
821
+ assert p.doit() == p
822
+
823
+
824
+ def test_issue_4171():
825
+ assert summation(factorial(2*k + 1)/factorial(2*k), (k, 0, oo)) is oo
826
+ assert summation(2*k + 1, (k, 0, oo)) is oo
827
+
828
+
829
+ def test_issue_6273():
830
+ assert Sum(x, (x, 1, n)).n(2, subs={n: 1}) == Float(1, 2)
831
+
832
+
833
+ def test_issue_6274():
834
+ assert Sum(x, (x, 1, 0)).doit() == 0
835
+ assert NS(Sum(x, (x, 1, 0))) == '0'
836
+ assert Sum(n, (n, 10, 5)).doit() == -30
837
+ assert NS(Sum(n, (n, 10, 5))) == '-30.0000000000000'
838
+
839
+
840
+ def test_simplify_sum():
841
+ y, t, v = symbols('y, t, v')
842
+
843
+ _simplify = lambda e: simplify(e, doit=False)
844
+ assert _simplify(Sum(x*y, (x, n, m), (y, a, k)) + \
845
+ Sum(y, (x, n, m), (y, a, k))) == Sum(y * (x + 1), (x, n, m), (y, a, k))
846
+ assert _simplify(Sum(x, (x, n, m)) + Sum(x, (x, m + 1, a))) == \
847
+ Sum(x, (x, n, a))
848
+ assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x, (x, n, k))) == \
849
+ Sum(x, (x, n, a))
850
+ assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x + 1, (x, n, k))) == \
851
+ Sum(x, (x, n, a)) + Sum(1, (x, n, k))
852
+ assert _simplify(Sum(x, (x, 0, 3)) * 3 + 3 * Sum(x, (x, 4, 6)) + \
853
+ 4 * Sum(z, (z, 0, 1))) == 4*Sum(z, (z, 0, 1)) + 3*Sum(x, (x, 0, 6))
854
+ assert _simplify(3*Sum(x**2, (x, a, b)) + Sum(x, (x, a, b))) == \
855
+ Sum(x*(3*x + 1), (x, a, b))
856
+ assert _simplify(Sum(x**3, (x, n, k)) * 3 + 3 * Sum(x, (x, n, k)) + \
857
+ 4 * y * Sum(z, (z, n, k))) + 1 == \
858
+ 4*y*Sum(z, (z, n, k)) + 3*Sum(x**3 + x, (x, n, k)) + 1
859
+ assert _simplify(Sum(x, (x, a, b)) + 1 + Sum(x, (x, b + 1, c))) == \
860
+ 1 + Sum(x, (x, a, c))
861
+ assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + \
862
+ Sum(x, (t, b+1, c))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b))
863
+ assert _simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + \
864
+ Sum(y, (t, a, b))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b))
865
+ assert _simplify(Sum(x, (t, a, b)) + 2 * Sum(x, (t, b+1, c))) == \
866
+ _simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + Sum(x, (t, b+1, c)))
867
+ assert _simplify(Sum(x, (x, a, b))*Sum(x**2, (x, a, b))) == \
868
+ Sum(x, (x, a, b)) * Sum(x**2, (x, a, b))
869
+ assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b))) \
870
+ == (x + y + z) * Sum(1, (t, a, b)) # issue 8596
871
+ assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b)) + \
872
+ Sum(v, (t, a, b))) == (x + y + z + v) * Sum(1, (t, a, b)) # issue 8596
873
+ assert _simplify(Sum(x * y, (x, a, b)) / (3 * y)) == \
874
+ (Sum(x, (x, a, b)) / 3)
875
+ assert _simplify(Sum(f(x) * y * z, (x, a, b)) / (y * z)) \
876
+ == Sum(f(x), (x, a, b))
877
+ assert _simplify(Sum(c * x, (x, a, b)) - c * Sum(x, (x, a, b))) == 0
878
+ assert _simplify(c * (Sum(x, (x, a, b)) + y)) == c * (y + Sum(x, (x, a, b)))
879
+ assert _simplify(c * (Sum(x, (x, a, b)) + y * Sum(x, (x, a, b)))) == \
880
+ c * (y + 1) * Sum(x, (x, a, b))
881
+ assert _simplify(Sum(Sum(c * x, (x, a, b)), (y, a, b))) == \
882
+ c * Sum(x, (x, a, b), (y, a, b))
883
+ assert _simplify(Sum((3 + y) * Sum(c * x, (x, a, b)), (y, a, b))) == \
884
+ c * Sum((3 + y), (y, a, b)) * Sum(x, (x, a, b))
885
+ assert _simplify(Sum((3 + t) * Sum(c * t, (x, a, b)), (y, a, b))) == \
886
+ c*t*(t + 3)*Sum(1, (x, a, b))*Sum(1, (y, a, b))
887
+ assert _simplify(Sum(Sum(d * t, (x, a, b - 1)) + \
888
+ Sum(d * t, (x, b, c)), (t, a, b))) == \
889
+ d * Sum(1, (x, a, c)) * Sum(t, (t, a, b))
890
+ assert _simplify(Sum(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \
891
+ 2 * Sum(1, (t, a, b))
892
+
893
+
894
+ def test_change_index():
895
+ b, v, w = symbols('b, v, w', integer = True)
896
+
897
+ assert Sum(x, (x, a, b)).change_index(x, x + 1, y) == \
898
+ Sum(y - 1, (y, a + 1, b + 1))
899
+ assert Sum(x**2, (x, a, b)).change_index( x, x - 1) == \
900
+ Sum((x+1)**2, (x, a - 1, b - 1))
901
+ assert Sum(x**2, (x, a, b)).change_index( x, -x, y) == \
902
+ Sum((-y)**2, (y, -b, -a))
903
+ assert Sum(x, (x, a, b)).change_index( x, -x - 1) == \
904
+ Sum(-x - 1, (x, -b - 1, -a - 1))
905
+ assert Sum(x*y, (x, a, b), (y, c, d)).change_index( x, x - 1, z) == \
906
+ Sum((z + 1)*y, (z, a - 1, b - 1), (y, c, d))
907
+ assert Sum(x, (x, a, b)).change_index( x, x + v) == \
908
+ Sum(-v + x, (x, a + v, b + v))
909
+ assert Sum(x, (x, a, b)).change_index( x, -x - v) == \
910
+ Sum(-v - x, (x, -b - v, -a - v))
911
+ assert Sum(x, (x, a, b)).change_index(x, w*x, v) == \
912
+ Sum(v/w, (v, b*w, a*w))
913
+ raises(ValueError, lambda: Sum(x, (x, a, b)).change_index(x, 2*x))
914
+
915
+
916
+ def test_reorder():
917
+ b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
918
+
919
+ assert Sum(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \
920
+ Sum(x*y, (y, c, d), (x, a, b))
921
+ assert Sum(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \
922
+ Sum(x, (x, c, d), (x, a, b))
923
+ assert Sum(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\
924
+ (2, 0), (0, 1)) == Sum(x*y + z, (z, m, n), (y, c, d), (x, a, b))
925
+ assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
926
+ (0, 1), (1, 2), (0, 2)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d))
927
+ assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
928
+ (x, y), (y, z), (x, z)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d))
929
+ assert Sum(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \
930
+ Sum(x*y, (y, c, d), (x, a, b))
931
+ assert Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \
932
+ Sum(x*y, (y, c, d), (x, a, b))
933
+
934
+
935
+ def test_reverse_order():
936
+ assert Sum(x, (x, 0, 3)).reverse_order(0) == Sum(-x, (x, 4, -1))
937
+ assert Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \
938
+ Sum(x*y, (x, 6, 0), (y, 7, -1))
939
+ assert Sum(x, (x, 1, 2)).reverse_order(0) == Sum(-x, (x, 3, 0))
940
+ assert Sum(x, (x, 1, 3)).reverse_order(0) == Sum(-x, (x, 4, 0))
941
+ assert Sum(x, (x, 1, a)).reverse_order(0) == Sum(-x, (x, a + 1, 0))
942
+ assert Sum(x, (x, a, 5)).reverse_order(0) == Sum(-x, (x, 6, a - 1))
943
+ assert Sum(x, (x, a + 1, a + 5)).reverse_order(0) == \
944
+ Sum(-x, (x, a + 6, a))
945
+ assert Sum(x, (x, a + 1, a + 2)).reverse_order(0) == \
946
+ Sum(-x, (x, a + 3, a))
947
+ assert Sum(x, (x, a + 1, a + 1)).reverse_order(0) == \
948
+ Sum(-x, (x, a + 2, a))
949
+ assert Sum(x, (x, a, b)).reverse_order(0) == Sum(-x, (x, b + 1, a - 1))
950
+ assert Sum(x, (x, a, b)).reverse_order(x) == Sum(-x, (x, b + 1, a - 1))
951
+ assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \
952
+ Sum(x*y, (x, b + 1, a - 1), (y, 6, 1))
953
+ assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \
954
+ Sum(x*y, (x, b + 1, a - 1), (y, 6, 1))
955
+
956
+
957
+ def test_issue_7097():
958
+ assert sum(x**n/n for n in range(1, 401)) == summation(x**n/n, (n, 1, 400))
959
+
960
+
961
+ def test_factor_expand_subs():
962
+ # test factoring
963
+ assert Sum(4 * x, (x, 1, y)).factor() == 4 * Sum(x, (x, 1, y))
964
+ assert Sum(x * a, (x, 1, y)).factor() == a * Sum(x, (x, 1, y))
965
+ assert Sum(4 * x * a, (x, 1, y)).factor() == 4 * a * Sum(x, (x, 1, y))
966
+ assert Sum(4 * x * y, (x, 1, y)).factor() == 4 * y * Sum(x, (x, 1, y))
967
+
968
+ # test expand
969
+ _x = Symbol('x', zero=False)
970
+ assert Sum(x+1,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(1,(x,1,y))
971
+ assert Sum(x+a*x**2,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(a*x**2,(x,1,y))
972
+ assert Sum(_x**(n + 1)*(n + 1), (n, -1, oo)).expand() \
973
+ == Sum(n*_x*_x**n + _x*_x**n, (n, -1, oo))
974
+ assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(power_exp=False) \
975
+ == Sum(n*x**(n + 1) + x**(n + 1), (n, -1, oo))
976
+ assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(force=True) \
977
+ == Sum(x*x**n, (n, -1, oo)) + Sum(n*x*x**n, (n, -1, oo))
978
+ assert Sum(a*n+a*n**2,(n,0,4)).expand() \
979
+ == Sum(a*n,(n,0,4)) + Sum(a*n**2,(n,0,4))
980
+ assert Sum(_x**a*_x**n,(x,0,3)) \
981
+ == Sum(_x**(a+n),(x,0,3)).expand(power_exp=True)
982
+ _a, _n = symbols('a n', positive=True)
983
+ assert Sum(x**(_a+_n),(x,0,3)).expand(power_exp=True) \
984
+ == Sum(x**_a*x**_n, (x, 0, 3))
985
+ assert Sum(x**(_a-_n),(x,0,3)).expand(power_exp=True) \
986
+ == Sum(x**(_a-_n),(x,0,3)).expand(power_exp=False)
987
+
988
+ # test subs
989
+ assert Sum(1/(1+a*x**2),(x,0,3)).subs([(a,3)]) == Sum(1/(1+3*x**2),(x,0,3))
990
+ assert Sum(x*y,(x,0,y),(y,0,x)).subs([(x,3)]) == Sum(x*y,(x,0,y),(y,0,3))
991
+ assert Sum(x,(x,1,10)).subs([(x,y-2)]) == Sum(x,(x,1,10))
992
+ assert Sum(1/x,(x,1,10)).subs([(x,(3+n)**3)]) == Sum(1/x,(x,1,10))
993
+ assert Sum(1/x,(x,1,10)).subs([(x,3*x-2)]) == Sum(1/x,(x,1,10))
994
+
995
+
996
+ def test_distribution_over_equality():
997
+ assert Product(Eq(x*2, f(x)), (x, 1, 3)).doit() == Eq(48, f(1)*f(2)*f(3))
998
+ assert Sum(Eq(f(x), x**2), (x, 0, y)) == \
999
+ Eq(Sum(f(x), (x, 0, y)), Sum(x**2, (x, 0, y)))
1000
+
1001
+
1002
+ def test_issue_2787():
1003
+ n, k = symbols('n k', positive=True, integer=True)
1004
+ p = symbols('p', positive=True)
1005
+ binomial_dist = binomial(n, k)*p**k*(1 - p)**(n - k)
1006
+ s = Sum(binomial_dist*k, (k, 0, n))
1007
+ res = s.doit().simplify()
1008
+ ans = Piecewise(
1009
+ (n*p, x),
1010
+ (Sum(k*p**k*binomial(n, k)*(1 - p)**(n - k), (k, 0, n)),
1011
+ True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1))
1012
+ ans2 = Piecewise(
1013
+ (n*p, x),
1014
+ (factorial(n)*Sum(p**k*(1 - p)**(-k + n)/
1015
+ (factorial(-k + n)*factorial(k - 1)), (k, 0, n)),
1016
+ True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1))
1017
+ assert res in [ans, ans2] # XXX system dependent
1018
+ # Issue #17165: make sure that another simplify does not complicate
1019
+ # the result by much. Why didn't first simplify replace
1020
+ # Eq(n, 1) | (n > 1) with True?
1021
+ assert res.simplify().count_ops() <= res.count_ops() + 2
1022
+
1023
+
1024
+ def test_issue_4668():
1025
+ assert summation(1/n, (n, 2, oo)) is oo
1026
+
1027
+
1028
+ def test_matrix_sum():
1029
+ A = Matrix([[0, 1], [n, 0]])
1030
+
1031
+ result = Sum(A, (n, 0, 3)).doit()
1032
+ assert result == Matrix([[0, 4], [6, 0]])
1033
+ assert result.__class__ == ImmutableDenseMatrix
1034
+
1035
+ A = SparseMatrix([[0, 1], [n, 0]])
1036
+
1037
+ result = Sum(A, (n, 0, 3)).doit()
1038
+ assert result.__class__ == ImmutableSparseMatrix
1039
+
1040
+
1041
+ def test_failing_matrix_sum():
1042
+ n = Symbol('n')
1043
+ # TODO Implement matrix geometric series summation.
1044
+ A = Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 0]])
1045
+ assert Sum(A ** n, (n, 1, 4)).doit() == \
1046
+ Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
1047
+ # issue sympy/sympy#16989
1048
+ assert summation(A**n, (n, 1, 1)) == A
1049
+
1050
+
1051
+ def test_indexed_idx_sum():
1052
+ i = symbols('i', cls=Idx)
1053
+ r = Indexed('r', i)
1054
+ assert Sum(r, (i, 0, 3)).doit() == sum(r.xreplace({i: j}) for j in range(4))
1055
+ assert Product(r, (i, 0, 3)).doit() == prod([r.xreplace({i: j}) for j in range(4)])
1056
+
1057
+ j = symbols('j', integer=True)
1058
+ assert Sum(r, (i, j, j+2)).doit() == sum(r.xreplace({i: j+k}) for k in range(3))
1059
+ assert Product(r, (i, j, j+2)).doit() == prod([r.xreplace({i: j+k}) for k in range(3)])
1060
+
1061
+ k = Idx('k', range=(1, 3))
1062
+ A = IndexedBase('A')
1063
+ assert Sum(A[k], k).doit() == sum(A[Idx(j, (1, 3))] for j in range(1, 4))
1064
+ assert Product(A[k], k).doit() == prod([A[Idx(j, (1, 3))] for j in range(1, 4)])
1065
+
1066
+ raises(ValueError, lambda: Sum(A[k], (k, 1, 4)))
1067
+ raises(ValueError, lambda: Sum(A[k], (k, 0, 3)))
1068
+ raises(ValueError, lambda: Sum(A[k], (k, 2, oo)))
1069
+
1070
+ raises(ValueError, lambda: Product(A[k], (k, 1, 4)))
1071
+ raises(ValueError, lambda: Product(A[k], (k, 0, 3)))
1072
+ raises(ValueError, lambda: Product(A[k], (k, 2, oo)))
1073
+
1074
+
1075
+ @slow
1076
+ def test_is_convergent():
1077
+ # divergence tests --
1078
+ assert Sum(n/(2*n + 1), (n, 1, oo)).is_convergent() is S.false
1079
+ assert Sum(factorial(n)/5**n, (n, 1, oo)).is_convergent() is S.false
1080
+ assert Sum(3**(-2*n - 1)*n**n, (n, 1, oo)).is_convergent() is S.false
1081
+ assert Sum((-1)**n*n, (n, 3, oo)).is_convergent() is S.false
1082
+ assert Sum((-1)**n, (n, 1, oo)).is_convergent() is S.false
1083
+ assert Sum(log(1/n), (n, 2, oo)).is_convergent() is S.false
1084
+ assert Sum(sin(n), (n, 1, oo)).is_convergent() is S.false
1085
+
1086
+ # Raabe's test --
1087
+ assert Sum(Product((3*m),(m,1,n))/Product((3*m+4),(m,1,n)),(n,1,oo)).is_convergent() is S.true
1088
+
1089
+ # root test --
1090
+ assert Sum((-12)**n/n, (n, 1, oo)).is_convergent() is S.false
1091
+
1092
+ # integral test --
1093
+
1094
+ # p-series test --
1095
+ assert Sum(1/(n**2 + 1), (n, 1, oo)).is_convergent() is S.true
1096
+ assert Sum(1/n**Rational(6, 5), (n, 1, oo)).is_convergent() is S.true
1097
+ assert Sum(2/(n*sqrt(n - 1)), (n, 2, oo)).is_convergent() is S.true
1098
+ assert Sum(1/(sqrt(n)*sqrt(n)), (n, 2, oo)).is_convergent() is S.false
1099
+ assert Sum(factorial(n) / factorial(n+2), (n, 1, oo)).is_convergent() is S.true
1100
+ assert Sum(rf(5,n)/rf(7,n),(n,1,oo)).is_convergent() is S.true
1101
+ assert Sum((rf(1, n)*rf(2, n))/(rf(3, n)*factorial(n)),(n,1,oo)).is_convergent() is S.false
1102
+
1103
+ # comparison test --
1104
+ assert Sum(1/(n + log(n)), (n, 1, oo)).is_convergent() is S.false
1105
+ assert Sum(1/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true
1106
+ assert Sum(1/(n*log(n)), (n, 2, oo)).is_convergent() is S.false
1107
+ assert Sum(2/(n*log(n)*log(log(n))**2), (n, 5, oo)).is_convergent() is S.true
1108
+ assert Sum(2/(n*log(n)**2), (n, 2, oo)).is_convergent() is S.true
1109
+ assert Sum((n - 1)/(n**2*log(n)**3), (n, 2, oo)).is_convergent() is S.true
1110
+ assert Sum(1/(n*log(n)*log(log(n))), (n, 5, oo)).is_convergent() is S.false
1111
+ assert Sum((n - 1)/(n*log(n)**3), (n, 3, oo)).is_convergent() is S.false
1112
+ assert Sum(2/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true
1113
+ assert Sum(1/(n*sqrt(log(n))*log(log(n))), (n, 100, oo)).is_convergent() is S.false
1114
+ assert Sum(log(log(n))/(n*log(n)**2), (n, 100, oo)).is_convergent() is S.true
1115
+ assert Sum(log(n)/n**2, (n, 5, oo)).is_convergent() is S.true
1116
+
1117
+ # alternating series tests --
1118
+ assert Sum((-1)**(n - 1)/(n**2 - 1), (n, 3, oo)).is_convergent() is S.true
1119
+
1120
+ # with -negativeInfinite Limits
1121
+ assert Sum(1/(n**2 + 1), (n, -oo, 1)).is_convergent() is S.true
1122
+ assert Sum(1/(n - 1), (n, -oo, -1)).is_convergent() is S.false
1123
+ assert Sum(1/(n**2 - 1), (n, -oo, -5)).is_convergent() is S.true
1124
+ assert Sum(1/(n**2 - 1), (n, -oo, 2)).is_convergent() is S.true
1125
+ assert Sum(1/(n**2 - 1), (n, -oo, oo)).is_convergent() is S.true
1126
+
1127
+ # piecewise functions
1128
+ f = Piecewise((n**(-2), n <= 1), (n**2, n > 1))
1129
+ assert Sum(f, (n, 1, oo)).is_convergent() is S.false
1130
+ assert Sum(f, (n, -oo, oo)).is_convergent() is S.false
1131
+ assert Sum(f, (n, 1, 100)).is_convergent() is S.true
1132
+ #assert Sum(f, (n, -oo, 1)).is_convergent() is S.true
1133
+
1134
+ # integral test
1135
+
1136
+ assert Sum(log(n)/n**3, (n, 1, oo)).is_convergent() is S.true
1137
+ assert Sum(-log(n)/n**3, (n, 1, oo)).is_convergent() is S.true
1138
+ # the following function has maxima located at (x, y) =
1139
+ # (1.2, 0.43), (3.0, -0.25) and (6.8, 0.050)
1140
+ eq = (x - 2)*(x**2 - 6*x + 4)*exp(-x)
1141
+ assert Sum(eq, (x, 1, oo)).is_convergent() is S.true
1142
+ assert Sum(eq, (x, 1, 2)).is_convergent() is S.true
1143
+ assert Sum(1/(x**3), (x, 1, oo)).is_convergent() is S.true
1144
+ assert Sum(1/(x**S.Half), (x, 1, oo)).is_convergent() is S.false
1145
+
1146
+ # issue 19545
1147
+ assert Sum(1/n - 3/(3*n +2), (n, 1, oo)).is_convergent() is S.true
1148
+
1149
+ # issue 19836
1150
+ assert Sum(4/(n + 2) - 5/(n + 1) + 1/n,(n, 7, oo)).is_convergent() is S.true
1151
+
1152
+
1153
+ def test_is_absolutely_convergent():
1154
+ assert Sum((-1)**n, (n, 1, oo)).is_absolutely_convergent() is S.false
1155
+ assert Sum((-1)**n/n**2, (n, 1, oo)).is_absolutely_convergent() is S.true
1156
+
1157
+
1158
+ @XFAIL
1159
+ def test_convergent_failing():
1160
+ # dirichlet tests
1161
+ assert Sum(sin(n)/n, (n, 1, oo)).is_convergent() is S.true
1162
+ assert Sum(sin(2*n)/n, (n, 1, oo)).is_convergent() is S.true
1163
+
1164
+
1165
+ def test_issue_6966():
1166
+ i, k, m = symbols('i k m', integer=True)
1167
+ z_i, q_i = symbols('z_i q_i')
1168
+ a_k = Sum(-q_i*z_i/k,(i,1,m))
1169
+ b_k = a_k.diff(z_i)
1170
+ assert isinstance(b_k, Sum)
1171
+ assert b_k == Sum(-q_i/k,(i,1,m))
1172
+
1173
+
1174
+ def test_issue_10156():
1175
+ cx = Sum(2*y**2*x, (x, 1,3))
1176
+ e = 2*y*Sum(2*cx*x**2, (x, 1, 9))
1177
+ assert e.factor() == \
1178
+ 8*y**3*Sum(x, (x, 1, 3))*Sum(x**2, (x, 1, 9))
1179
+
1180
+
1181
+ def test_issue_10973():
1182
+ assert Sum((-n + (n**3 + 1)**(S(1)/3))/log(n), (n, 1, oo)).is_convergent() is S.true
1183
+
1184
+
1185
+ def test_issue_14103():
1186
+ assert Sum(sin(n)**2 + cos(n)**2 - 1, (n, 1, oo)).is_convergent() is S.true
1187
+ assert Sum(sin(pi*n), (n, 1, oo)).is_convergent() is S.true
1188
+
1189
+
1190
+ def test_issue_14129():
1191
+ x = Symbol('x', zero=False)
1192
+ assert Sum( k*x**k, (k, 0, n-1)).doit() == \
1193
+ Piecewise((n**2/2 - n/2, Eq(x, 1)), ((n*x*x**n -
1194
+ n*x**n - x*x**n + x)/(x - 1)**2, True))
1195
+ assert Sum( x**k, (k, 0, n-1)).doit() == \
1196
+ Piecewise((n, Eq(x, 1)), ((-x**n + 1)/(-x + 1), True))
1197
+ assert Sum( k*(x/y+x)**k, (k, 0, n-1)).doit() == \
1198
+ Piecewise((n*(n - 1)/2, Eq(x, y/(y + 1))),
1199
+ (x*(y + 1)*(n*x*y*(x + x/y)**(n - 1) +
1200
+ n*x*(x + x/y)**(n - 1) - n*y*(x + x/y)**(n - 1) -
1201
+ x*y*(x + x/y)**(n - 1) - x*(x + x/y)**(n - 1) + y)/
1202
+ (x*y + x - y)**2, True))
1203
+
1204
+
1205
+ def test_issue_14112():
1206
+ assert Sum((-1)**n/sqrt(n), (n, 1, oo)).is_absolutely_convergent() is S.false
1207
+ assert Sum((-1)**(2*n)/n, (n, 1, oo)).is_convergent() is S.false
1208
+ assert Sum((-2)**n + (-3)**n, (n, 1, oo)).is_convergent() is S.false
1209
+
1210
+
1211
+ def test_issue_14219():
1212
+ A = diag(0, 2, -3)
1213
+ res = diag(1, 15, -20)
1214
+ assert Sum(A**n, (n, 0, 3)).doit() == res
1215
+
1216
+
1217
+ def test_sin_times_absolutely_convergent():
1218
+ assert Sum(sin(n) / n**3, (n, 1, oo)).is_convergent() is S.true
1219
+ assert Sum(sin(n) * log(n) / n**3, (n, 1, oo)).is_convergent() is S.true
1220
+
1221
+
1222
+ def test_issue_14111():
1223
+ assert Sum(1/log(log(n)), (n, 22, oo)).is_convergent() is S.false
1224
+
1225
+
1226
+ def test_issue_14484():
1227
+ assert Sum(sin(n)/log(log(n)), (n, 22, oo)).is_convergent() is S.false
1228
+
1229
+
1230
+ def test_issue_14640():
1231
+ i, n = symbols("i n", integer=True)
1232
+ a, b, c = symbols("a b c", zero=False)
1233
+
1234
+ assert Sum(a**-i/(a - b), (i, 0, n)).doit() == Sum(
1235
+ 1/(a*a**i - a**i*b), (i, 0, n)).doit() == Piecewise(
1236
+ (n + 1, Eq(1/a, 1)),
1237
+ ((-a**(-n - 1) + 1)/(1 - 1/a), True))/(a - b)
1238
+
1239
+ assert Sum((b*a**i - c*a**i)**-2, (i, 0, n)).doit() == Piecewise(
1240
+ (n + 1, Eq(a**(-2), 1)),
1241
+ ((-a**(-2*n - 2) + 1)/(1 - 1/a**2), True))/(b - c)**2
1242
+
1243
+ s = Sum(i*(a**(n - i) - b**(n - i))/(a - b), (i, 0, n)).doit()
1244
+ assert not s.has(Sum)
1245
+ assert s.subs({a: 2, b: 3, n: 5}) == 122
1246
+
1247
+
1248
+ def test_issue_15943():
1249
+ s = Sum(binomial(n, k)*factorial(n - k), (k, 0, n)).doit().rewrite(gamma)
1250
+ assert s == -E*(n + 1)*gamma(n + 1)*lowergamma(n + 1, 1)/gamma(n + 2
1251
+ ) + E*gamma(n + 1)
1252
+ assert s.simplify() == E*(factorial(n) - lowergamma(n + 1, 1))
1253
+
1254
+
1255
+ def test_Sum_dummy_eq():
1256
+ assert not Sum(x, (x, a, b)).dummy_eq(1)
1257
+ assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b), (a, 1, 2)))
1258
+ assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, c)))
1259
+ assert Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b)))
1260
+ d = Dummy()
1261
+ assert Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c)), c)
1262
+ assert not Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c)))
1263
+ assert Sum(x, (x, a, c)).dummy_eq(Sum(y, (y, a, c)))
1264
+ assert Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c)), c)
1265
+ assert not Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c)))
1266
+
1267
+
1268
+ def test_issue_15852():
1269
+ assert summation(x**y*y, (y, -oo, oo)).doit() == Sum(x**y*y, (y, -oo, oo))
1270
+
1271
+
1272
+ def test_exceptions():
1273
+ S = Sum(x, (x, a, b))
1274
+ raises(ValueError, lambda: S.change_index(x, x**2, y))
1275
+ S = Sum(x, (x, a, b), (x, 1, 4))
1276
+ raises(ValueError, lambda: S.index(x))
1277
+ S = Sum(x, (x, a, b), (y, 1, 4))
1278
+ raises(ValueError, lambda: S.reorder([x]))
1279
+ S = Sum(x, (x, y, b), (y, 1, 4))
1280
+ raises(ReorderError, lambda: S.reorder_limit(0, 1))
1281
+ S = Sum(x*y, (x, a, b), (y, 1, 4))
1282
+ raises(NotImplementedError, lambda: S.is_convergent())
1283
+
1284
+
1285
+ def test_sumproducts_assumptions():
1286
+ M = Symbol('M', integer=True, positive=True)
1287
+
1288
+ m = Symbol('m', integer=True)
1289
+ for func in [Sum, Product]:
1290
+ assert func(m, (m, -M, M)).is_positive is None
1291
+ assert func(m, (m, -M, M)).is_nonpositive is None
1292
+ assert func(m, (m, -M, M)).is_negative is None
1293
+ assert func(m, (m, -M, M)).is_nonnegative is None
1294
+ assert func(m, (m, -M, M)).is_finite is True
1295
+
1296
+ m = Symbol('m', integer=True, nonnegative=True)
1297
+ for func in [Sum, Product]:
1298
+ assert func(m, (m, 0, M)).is_positive is None
1299
+ assert func(m, (m, 0, M)).is_nonpositive is None
1300
+ assert func(m, (m, 0, M)).is_negative is False
1301
+ assert func(m, (m, 0, M)).is_nonnegative is True
1302
+ assert func(m, (m, 0, M)).is_finite is True
1303
+
1304
+ m = Symbol('m', integer=True, positive=True)
1305
+ for func in [Sum, Product]:
1306
+ assert func(m, (m, 1, M)).is_positive is True
1307
+ assert func(m, (m, 1, M)).is_nonpositive is False
1308
+ assert func(m, (m, 1, M)).is_negative is False
1309
+ assert func(m, (m, 1, M)).is_nonnegative is True
1310
+ assert func(m, (m, 1, M)).is_finite is True
1311
+
1312
+ m = Symbol('m', integer=True, negative=True)
1313
+ assert Sum(m, (m, -M, -1)).is_positive is False
1314
+ assert Sum(m, (m, -M, -1)).is_nonpositive is True
1315
+ assert Sum(m, (m, -M, -1)).is_negative is True
1316
+ assert Sum(m, (m, -M, -1)).is_nonnegative is False
1317
+ assert Sum(m, (m, -M, -1)).is_finite is True
1318
+ assert Product(m, (m, -M, -1)).is_positive is None
1319
+ assert Product(m, (m, -M, -1)).is_nonpositive is None
1320
+ assert Product(m, (m, -M, -1)).is_negative is None
1321
+ assert Product(m, (m, -M, -1)).is_nonnegative is None
1322
+ assert Product(m, (m, -M, -1)).is_finite is True
1323
+
1324
+ m = Symbol('m', integer=True, nonpositive=True)
1325
+ assert Sum(m, (m, -M, 0)).is_positive is False
1326
+ assert Sum(m, (m, -M, 0)).is_nonpositive is True
1327
+ assert Sum(m, (m, -M, 0)).is_negative is None
1328
+ assert Sum(m, (m, -M, 0)).is_nonnegative is None
1329
+ assert Sum(m, (m, -M, 0)).is_finite is True
1330
+ assert Product(m, (m, -M, 0)).is_positive is None
1331
+ assert Product(m, (m, -M, 0)).is_nonpositive is None
1332
+ assert Product(m, (m, -M, 0)).is_negative is None
1333
+ assert Product(m, (m, -M, 0)).is_nonnegative is None
1334
+ assert Product(m, (m, -M, 0)).is_finite is True
1335
+
1336
+ m = Symbol('m', integer=True)
1337
+ assert Sum(2, (m, 0, oo)).is_positive is None
1338
+ assert Sum(2, (m, 0, oo)).is_nonpositive is None
1339
+ assert Sum(2, (m, 0, oo)).is_negative is None
1340
+ assert Sum(2, (m, 0, oo)).is_nonnegative is None
1341
+ assert Sum(2, (m, 0, oo)).is_finite is None
1342
+
1343
+ assert Product(2, (m, 0, oo)).is_positive is None
1344
+ assert Product(2, (m, 0, oo)).is_nonpositive is None
1345
+ assert Product(2, (m, 0, oo)).is_negative is False
1346
+ assert Product(2, (m, 0, oo)).is_nonnegative is None
1347
+ assert Product(2, (m, 0, oo)).is_finite is None
1348
+
1349
+ assert Product(0, (x, M, M-1)).is_positive is True
1350
+ assert Product(0, (x, M, M-1)).is_finite is True
1351
+
1352
+
1353
+ def test_expand_with_assumptions():
1354
+ M = Symbol('M', integer=True, positive=True)
1355
+ x = Symbol('x', positive=True)
1356
+ m = Symbol('m', nonnegative=True)
1357
+ assert log(Product(x**m, (m, 0, M))).expand() == Sum(m*log(x), (m, 0, M))
1358
+ assert log(Product(exp(x**m), (m, 0, M))).expand() == Sum(x**m, (m, 0, M))
1359
+ assert log(Product(x**m, (m, 0, M))).rewrite(Sum).expand() == Sum(m*log(x), (m, 0, M))
1360
+ assert log(Product(exp(x**m), (m, 0, M))).rewrite(Sum).expand() == Sum(x**m, (m, 0, M))
1361
+
1362
+ n = Symbol('n', nonnegative=True)
1363
+ i, j = symbols('i,j', positive=True, integer=True)
1364
+ x, y = symbols('x,y', positive=True)
1365
+ assert log(Product(x**i*y**j, (i, 1, n), (j, 1, m))).expand() \
1366
+ == Sum(i*log(x) + j*log(y), (i, 1, n), (j, 1, m))
1367
+
1368
+ m = Symbol('m', nonnegative=True, integer=True)
1369
+ s = Sum(x**m, (m, 0, M))
1370
+ s_as_product = s.rewrite(Product)
1371
+ assert s_as_product.has(Product)
1372
+ assert s_as_product == log(Product(exp(x**m), (m, 0, M)))
1373
+ assert s_as_product.expand() == s
1374
+ s5 = s.subs(M, 5)
1375
+ s5_as_product = s5.rewrite(Product)
1376
+ assert s5_as_product.has(Product)
1377
+ assert s5_as_product.doit().expand() == s5.doit()
1378
+
1379
+
1380
+ def test_has_finite_limits():
1381
+ x = Symbol('x')
1382
+ assert Sum(1, (x, 1, 9)).has_finite_limits is True
1383
+ assert Sum(1, (x, 1, oo)).has_finite_limits is False
1384
+ M = Symbol('M')
1385
+ assert Sum(1, (x, 1, M)).has_finite_limits is None
1386
+ M = Symbol('M', positive=True)
1387
+ assert Sum(1, (x, 1, M)).has_finite_limits is True
1388
+ x = Symbol('x', positive=True)
1389
+ M = Symbol('M')
1390
+ assert Sum(1, (x, 1, M)).has_finite_limits is True
1391
+
1392
+ assert Sum(1, (x, 1, M), (y, -oo, oo)).has_finite_limits is False
1393
+
1394
+ def test_has_reversed_limits():
1395
+ assert Sum(1, (x, 1, 1)).has_reversed_limits is False
1396
+ assert Sum(1, (x, 1, 9)).has_reversed_limits is False
1397
+ assert Sum(1, (x, 1, -9)).has_reversed_limits is True
1398
+ assert Sum(1, (x, 1, 0)).has_reversed_limits is True
1399
+ assert Sum(1, (x, 1, oo)).has_reversed_limits is False
1400
+ M = Symbol('M')
1401
+ assert Sum(1, (x, 1, M)).has_reversed_limits is None
1402
+ M = Symbol('M', positive=True, integer=True)
1403
+ assert Sum(1, (x, 1, M)).has_reversed_limits is False
1404
+ assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is False
1405
+ M = Symbol('M', negative=True)
1406
+ assert Sum(1, (x, 1, M)).has_reversed_limits is True
1407
+
1408
+ assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is True
1409
+ assert Sum(1, (x, oo, oo)).has_reversed_limits is None
1410
+
1411
+
1412
+ def test_has_empty_sequence():
1413
+ assert Sum(1, (x, 1, 1)).has_empty_sequence is False
1414
+ assert Sum(1, (x, 1, 9)).has_empty_sequence is False
1415
+ assert Sum(1, (x, 1, -9)).has_empty_sequence is False
1416
+ assert Sum(1, (x, 1, 0)).has_empty_sequence is True
1417
+ assert Sum(1, (x, y, y - 1)).has_empty_sequence is True
1418
+ assert Sum(1, (x, 3, 2), (y, -oo, oo)).has_empty_sequence is True
1419
+ assert Sum(1, (y, -oo, oo), (x, 3, 2)).has_empty_sequence is True
1420
+ assert Sum(1, (x, oo, oo)).has_empty_sequence is False
1421
+
1422
+
1423
+ def test_empty_sequence():
1424
+ assert Product(x*y, (x, -oo, oo), (y, 1, 0)).doit() == 1
1425
+ assert Product(x*y, (y, 1, 0), (x, -oo, oo)).doit() == 1
1426
+ assert Sum(x, (x, -oo, oo), (y, 1, 0)).doit() == 0
1427
+ assert Sum(x, (y, 1, 0), (x, -oo, oo)).doit() == 0
1428
+
1429
+
1430
+ def test_issue_8016():
1431
+ k = Symbol('k', integer=True)
1432
+ n, m = symbols('n, m', integer=True, positive=True)
1433
+ s = Sum(binomial(m, k)*binomial(m, n - k)*(-1)**k, (k, 0, n))
1434
+ assert s.doit().simplify() == \
1435
+ cos(pi*n/2)*gamma(m + 1)/gamma(n/2 + 1)/gamma(m - n/2 + 1)
1436
+
1437
+
1438
+ def test_issue_14313():
1439
+ assert Sum(S.Half**floor(n/2), (n, 1, oo)).is_convergent()
1440
+
1441
+
1442
+ def test_issue_14563():
1443
+ # The assertion was failing due to no assumptions methods in Sums and Product
1444
+ assert 1 % Sum(1, (x, 0, 1)) == 1
1445
+
1446
+
1447
+ def test_issue_16735():
1448
+ assert Sum(5**n/gamma(n+1), (n, 1, oo)).is_convergent() is S.true
1449
+
1450
+
1451
+ def test_issue_14871():
1452
+ assert Sum((Rational(1, 10))**n*rf(0, n)/factorial(n), (n, 0, oo)).rewrite(factorial).doit() == 1
1453
+
1454
+
1455
+ def test_issue_17165():
1456
+ n = symbols("n", integer=True)
1457
+ x = symbols('x')
1458
+ s = (x*Sum(x**n, (n, -1, oo)))
1459
+ ssimp = s.doit().simplify()
1460
+
1461
+ assert ssimp == Piecewise((-1/(x - 1), (x > -1) & (x < 1)),
1462
+ (x*Sum(x**n, (n, -1, oo)), True)), ssimp
1463
+ assert ssimp.simplify() == ssimp
1464
+
1465
+
1466
+ def test_issue_19379():
1467
+ assert Sum(factorial(n)/factorial(n + 2), (n, 1, oo)).is_convergent() is S.true
1468
+
1469
+
1470
+ def test_issue_20777():
1471
+ assert Sum(exp(x*sin(n/m)), (n, 1, m)).doit() == Sum(exp(x*sin(n/m)), (n, 1, m))
1472
+
1473
+
1474
+ def test__dummy_with_inherited_properties_concrete():
1475
+ x = Symbol('x')
1476
+
1477
+ from sympy.core.containers import Tuple
1478
+ d = _dummy_with_inherited_properties_concrete(Tuple(x, 0, 5))
1479
+ assert d.is_real
1480
+ assert d.is_integer
1481
+ assert d.is_nonnegative
1482
+ assert d.is_extended_nonnegative
1483
+
1484
+ d = _dummy_with_inherited_properties_concrete(Tuple(x, 1, 9))
1485
+ assert d.is_real
1486
+ assert d.is_integer
1487
+ assert d.is_positive
1488
+ assert d.is_odd is None
1489
+
1490
+ d = _dummy_with_inherited_properties_concrete(Tuple(x, -5, 5))
1491
+ assert d.is_real
1492
+ assert d.is_integer
1493
+ assert d.is_positive is None
1494
+ assert d.is_extended_nonnegative is None
1495
+ assert d.is_odd is None
1496
+
1497
+ d = _dummy_with_inherited_properties_concrete(Tuple(x, -1.5, 1.5))
1498
+ assert d.is_real
1499
+ assert d.is_integer is None
1500
+ assert d.is_positive is None
1501
+ assert d.is_extended_nonnegative is None
1502
+
1503
+ N = Symbol('N', integer=True, positive=True)
1504
+ d = _dummy_with_inherited_properties_concrete(Tuple(x, 2, N))
1505
+ assert d.is_real
1506
+ assert d.is_positive
1507
+ assert d.is_integer
1508
+
1509
+ # Return None if no assumptions are added
1510
+ N = Symbol('N', integer=True, positive=True)
1511
+ d = _dummy_with_inherited_properties_concrete(Tuple(N, 2, 4))
1512
+ assert d is None
1513
+
1514
+ x = Symbol('x', negative=True)
1515
+ raises(InconsistentAssumptions,
1516
+ lambda: _dummy_with_inherited_properties_concrete(Tuple(x, 1, 5)))
1517
+
1518
+
1519
+ def test_matrixsymbol_summation_numerical_limits():
1520
+ A = MatrixSymbol('A', 3, 3)
1521
+ n = Symbol('n', integer=True)
1522
+
1523
+ assert Sum(A**n, (n, 0, 2)).doit() == Identity(3) + A + A**2
1524
+ assert Sum(A, (n, 0, 2)).doit() == 3*A
1525
+ assert Sum(n*A, (n, 0, 2)).doit() == 3*A
1526
+
1527
+ B = Matrix([[0, n, 0], [-1, 0, 0], [0, 0, 2]])
1528
+ ans = Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]]) + 4*A
1529
+ assert Sum(A+B, (n, 0, 3)).doit() == ans
1530
+ ans = A*Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]])
1531
+ assert Sum(A*B, (n, 0, 3)).doit() == ans
1532
+
1533
+ ans = (A**2*Matrix([[-2, 0, 0], [0,-2, 0], [0, 0, 4]]) +
1534
+ A**3*Matrix([[0, -9, 0], [3, 0, 0], [0, 0, 8]]) +
1535
+ A*Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 2]]))
1536
+ assert Sum(A**n*B**n, (n, 1, 3)).doit() == ans
1537
+
1538
+
1539
+ def test_issue_21651():
1540
+ i = Symbol('i')
1541
+ a = Sum(floor(2*2**(-i)), (i, S.One, 2))
1542
+ assert a.doit() == S.One
1543
+
1544
+
1545
+ @XFAIL
1546
+ def test_matrixsymbol_summation_symbolic_limits():
1547
+ N = Symbol('N', integer=True, positive=True)
1548
+
1549
+ A = MatrixSymbol('A', 3, 3)
1550
+ n = Symbol('n', integer=True)
1551
+ assert Sum(A, (n, 0, N)).doit() == (N+1)*A
1552
+ assert Sum(n*A, (n, 0, N)).doit() == (N**2/2+N/2)*A
1553
+
1554
+
1555
+ def test_summation_by_residues():
1556
+ x = Symbol('x')
1557
+
1558
+ # Examples from Nakhle H. Asmar, Loukas Grafakos,
1559
+ # Complex Analysis with Applications
1560
+ assert eval_sum_residue(1 / (x**2 + 1), (x, -oo, oo)) == pi/tanh(pi)
1561
+ assert eval_sum_residue(1 / x**6, (x, S(1), oo)) == pi**6/945
1562
+ assert eval_sum_residue(1 / (x**2 + 9), (x, -oo, oo)) == pi/(3*tanh(3*pi))
1563
+ assert eval_sum_residue(1 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \
1564
+ (-pi**2*tanh(pi)**2 + pi*tanh(pi) + pi**2)/(2*tanh(pi)**2)
1565
+ assert eval_sum_residue(x**2 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \
1566
+ (-pi**2 + pi*tanh(pi) + pi**2*tanh(pi)**2)/(2*tanh(pi)**2)
1567
+ assert eval_sum_residue(1 / (4*x**2 - 1), (x, -oo, oo)) == 0
1568
+ assert eval_sum_residue(x**2 / (x**2 - S(1)/4)**2, (x, -oo, oo)) == pi**2/2
1569
+ assert eval_sum_residue(1 / (4*x**2 - 1)**2, (x, -oo, oo)) == pi**2/8
1570
+ assert eval_sum_residue(1 / ((x - S(1)/2)**2 + 1), (x, -oo, oo)) == pi*tanh(pi)
1571
+ assert eval_sum_residue(1 / x**2, (x, S(1), oo)) == pi**2/6
1572
+ assert eval_sum_residue(1 / x**4, (x, S(1), oo)) == pi**4/90
1573
+ assert eval_sum_residue(1 / x**2 / (x**2 + 4), (x, S(1), oo)) == \
1574
+ -pi*(-pi/12 - 1/(16*pi) + 1/(8*tanh(2*pi)))/2
1575
+
1576
+ # Some examples made from 1 / (x**2 + 1)
1577
+ assert eval_sum_residue(1 / (x**2 + 1), (x, S(0), oo)) == \
1578
+ S(1)/2 + pi/(2*tanh(pi))
1579
+ assert eval_sum_residue(1 / (x**2 + 1), (x, S(1), oo)) == \
1580
+ -S(1)/2 + pi/(2*tanh(pi))
1581
+ assert eval_sum_residue(1 / (x**2 + 1), (x, S(-1), oo)) == \
1582
+ 1 + pi/(2*tanh(pi))
1583
+ assert eval_sum_residue((-1)**x / (x**2 + 1), (x, -oo, oo)) == \
1584
+ pi/sinh(pi)
1585
+ assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(0), oo)) == \
1586
+ pi/(2*sinh(pi)) + S(1)/2
1587
+ assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(1), oo)) == \
1588
+ -S(1)/2 + pi/(2*sinh(pi))
1589
+ assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(-1), oo)) == \
1590
+ pi/(2*sinh(pi))
1591
+
1592
+ # Some examples made from shifting of 1 / (x**2 + 1)
1593
+ assert eval_sum_residue(1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*tanh(pi))
1594
+ assert eval_sum_residue(1 / (x**2 + 4*x + 5), (x, S(-2), oo)) == S(1)/2 + pi/(2*tanh(pi))
1595
+ assert eval_sum_residue(1 / (x**2 - 2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*tanh(pi))
1596
+ assert eval_sum_residue(1 / (x**2 - 4*x + 5), (x, S(2), oo)) == S(1)/2 + pi/(2*tanh(pi))
1597
+ assert eval_sum_residue((-1)**x * -1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*sinh(pi))
1598
+ assert eval_sum_residue((-1)**x * -1 / (x**2 -2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*sinh(pi))
1599
+
1600
+ # Some examples made from 1 / x**2
1601
+ assert eval_sum_residue(1 / x**2, (x, S(2), oo)) == -1 + pi**2/6
1602
+ assert eval_sum_residue(1 / x**2, (x, S(3), oo)) == -S(5)/4 + pi**2/6
1603
+ assert eval_sum_residue((-1)**x / x**2, (x, S(1), oo)) == -pi**2/12
1604
+ assert eval_sum_residue((-1)**x / x**2, (x, S(2), oo)) == 1 - pi**2/12
1605
+
1606
+
1607
+ @slow
1608
+ def test_summation_by_residues_failing():
1609
+ x = Symbol('x')
1610
+
1611
+ # Failing because of the bug in residue computation
1612
+ assert eval_sum_residue(x**2 / (x**4 + 1), (x, S(1), oo))
1613
+ assert eval_sum_residue(1 / ((x - 1)*(x - 2) + 1), (x, -oo, oo)) != 0
1614
+
1615
+
1616
+ def test_process_limits():
1617
+ from sympy.concrete.expr_with_limits import _process_limits
1618
+
1619
+ # these should be (x, Range(3)) not Range(3)
1620
+ raises(ValueError, lambda: _process_limits(
1621
+ Range(3), discrete=True))
1622
+ raises(ValueError, lambda: _process_limits(
1623
+ Range(3), discrete=False))
1624
+ # these should be (x, union) not union
1625
+ # (but then we would get a TypeError because we don't
1626
+ # handle non-contiguous sets: see below use of `union`)
1627
+ union = Or(x < 1, x > 3).as_set()
1628
+ raises(ValueError, lambda: _process_limits(
1629
+ union, discrete=True))
1630
+ raises(ValueError, lambda: _process_limits(
1631
+ union, discrete=False))
1632
+
1633
+ # error not triggered if not needed
1634
+ assert _process_limits((x, 1, 2)) == ([(x, 1, 2)], 1)
1635
+
1636
+ # this equivalence is used to detect Reals in _process_limits
1637
+ assert isinstance(S.Reals, Interval)
1638
+
1639
+ C = Integral # continuous limits
1640
+ assert C(x, x >= 5) == C(x, (x, 5, oo))
1641
+ assert C(x, x < 3) == C(x, (x, -oo, 3))
1642
+ ans = C(x, (x, 0, 3))
1643
+ assert C(x, And(x >= 0, x < 3)) == ans
1644
+ assert C(x, (x, Interval.Ropen(0, 3))) == ans
1645
+ raises(TypeError, lambda: C(x, (x, Range(3))))
1646
+
1647
+ # discrete limits
1648
+ for D in (Sum, Product):
1649
+ r, ans = Range(3, 10, 2), D(2*x + 3, (x, 0, 3))
1650
+ assert D(x, (x, r)) == ans
1651
+ assert D(x, (x, r.reversed)) == ans
1652
+ r, ans = Range(3, oo, 2), D(2*x + 3, (x, 0, oo))
1653
+ assert D(x, (x, r)) == ans
1654
+ assert D(x, (x, r.reversed)) == ans
1655
+ r, ans = Range(-oo, 5, 2), D(3 - 2*x, (x, 0, oo))
1656
+ assert D(x, (x, r)) == ans
1657
+ assert D(x, (x, r.reversed)) == ans
1658
+ raises(TypeError, lambda: D(x, x > 0))
1659
+ raises(ValueError, lambda: D(x, Interval(1, 3)))
1660
+ raises(NotImplementedError, lambda: D(x, (x, union)))
1661
+
1662
+
1663
+ def test_pr_22677():
1664
+ b = Symbol('b', integer=True, positive=True)
1665
+ assert Sum(1/x**2,(x, 0, b)).doit() == Sum(x**(-2), (x, 0, b))
1666
+ assert Sum(1/(x - b)**2,(x, 0, b-1)).doit() == Sum(
1667
+ (-b + x)**(-2), (x, 0, b - 1))
1668
+
1669
+
1670
+ def test_issue_23952():
1671
+ p, q = symbols("p q", real=True, nonnegative=True)
1672
+ k1, k2 = symbols("k1 k2", integer=True, nonnegative=True)
1673
+ n = Symbol("n", integer=True, positive=True)
1674
+ expr = Sum(abs(k1 - k2)*p**k1 *(1 - q)**(n - k2),
1675
+ (k1, 0, n), (k2, 0, n))
1676
+ assert expr.subs(p,0).subs(q,1).subs(n, 3).doit() == 3
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py ADDED
@@ -0,0 +1,851 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.holonomic import (DifferentialOperator, HolonomicFunction,
2
+ DifferentialOperators, from_hyper,
3
+ from_meijerg, expr_to_holonomic)
4
+ from sympy.holonomic.recurrence import RecurrenceOperators, HolonomicSequence
5
+ from sympy.core import EulerGamma
6
+ from sympy.core.numbers import (I, Rational, pi)
7
+ from sympy.core.singleton import S
8
+ from sympy.core.symbol import (Symbol, symbols)
9
+ from sympy.functions.elementary.exponential import (exp, log)
10
+ from sympy.functions.elementary.hyperbolic import (asinh, cosh)
11
+ from sympy.functions.elementary.miscellaneous import sqrt
12
+ from sympy.functions.elementary.trigonometric import (cos, sin)
13
+ from sympy.functions.special.bessel import besselj
14
+ from sympy.functions.special.beta_functions import beta
15
+ from sympy.functions.special.error_functions import (Ci, Si, erf, erfc)
16
+ from sympy.functions.special.gamma_functions import gamma
17
+ from sympy.functions.special.hyper import (hyper, meijerg)
18
+ from sympy.printing.str import sstr
19
+ from sympy.series.order import O
20
+ from sympy.simplify.hyperexpand import hyperexpand
21
+ from sympy.polys.domains.integerring import ZZ
22
+ from sympy.polys.domains.rationalfield import QQ
23
+ from sympy.polys.domains.realfield import RR
24
+
25
+
26
+ def test_DifferentialOperator():
27
+ x = symbols('x')
28
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
29
+ assert Dx == R.derivative_operator
30
+ assert Dx == DifferentialOperator([R.base.zero, R.base.one], R)
31
+ assert x * Dx + x**2 * Dx**2 == DifferentialOperator([0, x, x**2], R)
32
+ assert (x**2 + 1) + Dx + x * \
33
+ Dx**5 == DifferentialOperator([x**2 + 1, 1, 0, 0, 0, x], R)
34
+ assert (x * Dx + x**2 + 1 - Dx * (x**3 + x))**3 == (-48 * x**6) + \
35
+ (-57 * x**7) * Dx + (-15 * x**8) * Dx**2 + (-x**9) * Dx**3
36
+ p = (x * Dx**2 + (x**2 + 3) * Dx**5) * (Dx + x**2)
37
+ q = (2 * x) + (4 * x**2) * Dx + (x**3) * Dx**2 + \
38
+ (20 * x**2 + x + 60) * Dx**3 + (10 * x**3 + 30 * x) * Dx**4 + \
39
+ (x**4 + 3 * x**2) * Dx**5 + (x**2 + 3) * Dx**6
40
+ assert p == q
41
+
42
+
43
+ def test_HolonomicFunction_addition():
44
+ x = symbols('x')
45
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
46
+ p = HolonomicFunction(Dx**2 * x, x)
47
+ q = HolonomicFunction((2) * Dx + (x) * Dx**2, x)
48
+ assert p == q
49
+ p = HolonomicFunction(x * Dx + 1, x)
50
+ q = HolonomicFunction(Dx + 1, x)
51
+ r = HolonomicFunction((x - 2) + (x**2 - 2) * Dx + (x**2 - x) * Dx**2, x)
52
+ assert p + q == r
53
+ p = HolonomicFunction(x * Dx + Dx**2 * (x**2 + 2), x)
54
+ q = HolonomicFunction(Dx - 3, x)
55
+ r = HolonomicFunction((-54 * x**2 - 126 * x - 150) + (-135 * x**3 - 252 * x**2 - 270 * x + 140) * Dx +\
56
+ (-27 * x**4 - 24 * x**2 + 14 * x - 150) * Dx**2 + \
57
+ (9 * x**4 + 15 * x**3 + 38 * x**2 + 30 * x +40) * Dx**3, x)
58
+ assert p + q == r
59
+ p = HolonomicFunction(Dx**5 - 1, x)
60
+ q = HolonomicFunction(x**3 + Dx, x)
61
+ r = HolonomicFunction((-x**18 + 45*x**14 - 525*x**10 + 1575*x**6 - x**3 - 630*x**2) + \
62
+ (-x**15 + 30*x**11 - 195*x**7 + 210*x**3 - 1)*Dx + (x**18 - 45*x**14 + 525*x**10 - \
63
+ 1575*x**6 + x**3 + 630*x**2)*Dx**5 + (x**15 - 30*x**11 + 195*x**7 - 210*x**3 + \
64
+ 1)*Dx**6, x)
65
+ assert p+q == r
66
+
67
+ p = x**2 + 3*x + 8
68
+ q = x**3 - 7*x + 5
69
+ p = p*Dx - p.diff()
70
+ q = q*Dx - q.diff()
71
+ r = HolonomicFunction(p, x) + HolonomicFunction(q, x)
72
+ s = HolonomicFunction((6*x**2 + 18*x + 14) + (-4*x**3 - 18*x**2 - 62*x + 10)*Dx +\
73
+ (x**4 + 6*x**3 + 31*x**2 - 10*x - 71)*Dx**2, x)
74
+ assert r == s
75
+
76
+
77
+ def test_HolonomicFunction_multiplication():
78
+ x = symbols('x')
79
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
80
+ p = HolonomicFunction(Dx+x+x*Dx**2, x)
81
+ q = HolonomicFunction(x*Dx+Dx*x+Dx**2, x)
82
+ r = HolonomicFunction((8*x**6 + 4*x**4 + 6*x**2 + 3) + (24*x**5 - 4*x**3 + 24*x)*Dx + \
83
+ (8*x**6 + 20*x**4 + 12*x**2 + 2)*Dx**2 + (8*x**5 + 4*x**3 + 4*x)*Dx**3 + \
84
+ (2*x**4 + x**2)*Dx**4, x)
85
+ assert p*q == r
86
+ p = HolonomicFunction(Dx**2+1, x)
87
+ q = HolonomicFunction(Dx-1, x)
88
+ r = HolonomicFunction((2) + (-2)*Dx + (1)*Dx**2, x)
89
+ assert p*q == r
90
+ p = HolonomicFunction(Dx**2+1+x+Dx, x)
91
+ q = HolonomicFunction((Dx*x-1)**2, x)
92
+ r = HolonomicFunction((4*x**7 + 11*x**6 + 16*x**5 + 4*x**4 - 6*x**3 - 7*x**2 - 8*x - 2) + \
93
+ (8*x**6 + 26*x**5 + 24*x**4 - 3*x**3 - 11*x**2 - 6*x - 2)*Dx + \
94
+ (8*x**6 + 18*x**5 + 15*x**4 - 3*x**3 - 6*x**2 - 6*x - 2)*Dx**2 + (8*x**5 + \
95
+ 10*x**4 + 6*x**3 - 2*x**2 - 4*x)*Dx**3 + (4*x**5 + 3*x**4 - x**2)*Dx**4, x)
96
+ assert p*q == r
97
+ p = HolonomicFunction(x*Dx**2-1, x)
98
+ q = HolonomicFunction(Dx*x-x, x)
99
+ r = HolonomicFunction((x - 3) + (-2*x + 2)*Dx + (x)*Dx**2, x)
100
+ assert p*q == r
101
+
102
+
103
+ def test_HolonomicFunction_power():
104
+ x = symbols('x')
105
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
106
+ p = HolonomicFunction(Dx+x+x*Dx**2, x)
107
+ a = HolonomicFunction(Dx, x)
108
+ for n in range(10):
109
+ assert a == p**n
110
+ a *= p
111
+
112
+
113
+ def test_addition_initial_condition():
114
+ x = symbols('x')
115
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
116
+ p = HolonomicFunction(Dx-1, x, 0, [3])
117
+ q = HolonomicFunction(Dx**2+1, x, 0, [1, 0])
118
+ r = HolonomicFunction(-1 + Dx - Dx**2 + Dx**3, x, 0, [4, 3, 2])
119
+ assert p + q == r
120
+ p = HolonomicFunction(Dx - x + Dx**2, x, 0, [1, 2])
121
+ q = HolonomicFunction(Dx**2 + x, x, 0, [1, 0])
122
+ r = HolonomicFunction((-x**4 - x**3/4 - x**2 + Rational(1, 4)) + (x**3 + x**2/4 + x*Rational(3, 4) + 1)*Dx + \
123
+ (x*Rational(-3, 2) + Rational(7, 4))*Dx**2 + (x**2 - x*Rational(7, 4) + Rational(1, 4))*Dx**3 + (x**2 + x/4 + S.Half)*Dx**4, x, 0, [2, 2, -2, 2])
124
+ assert p + q == r
125
+ p = HolonomicFunction(Dx**2 + 4*x*Dx + x**2, x, 0, [3, 4])
126
+ q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1])
127
+ r = HolonomicFunction((x**6 + 2*x**4 - 5*x**2 - 6) + (4*x**5 + 36*x**3 - 32*x)*Dx + \
128
+ (x**6 + 3*x**4 + 5*x**2 - 9)*Dx**2 + (4*x**5 + 36*x**3 - 32*x)*Dx**3 + (x**4 + \
129
+ 10*x**2 - 3)*Dx**4, x, 0, [4, 5, -1, -17])
130
+ assert p + q == r
131
+ q = HolonomicFunction(Dx**3 + x, x, 2, [3, 0, 1])
132
+ p = HolonomicFunction(Dx - 1, x, 2, [1])
133
+ r = HolonomicFunction((-x**2 - x + 1) + (x**2 + x)*Dx + (-x - 2)*Dx**3 + \
134
+ (x + 1)*Dx**4, x, 2, [4, 1, 2, -5 ])
135
+ assert p + q == r
136
+ p = expr_to_holonomic(sin(x))
137
+ q = expr_to_holonomic(1/x, x0=1)
138
+ r = HolonomicFunction((x**2 + 6) + (x**3 + 2*x)*Dx + (x**2 + 6)*Dx**2 + (x**3 + 2*x)*Dx**3, \
139
+ x, 1, [sin(1) + 1, -1 + cos(1), -sin(1) + 2])
140
+ assert p + q == r
141
+ C_1 = symbols('C_1')
142
+ p = expr_to_holonomic(sqrt(x))
143
+ q = expr_to_holonomic(sqrt(x**2-x))
144
+ r = (p + q).to_expr().subs(C_1, -I/2).expand()
145
+ assert r == I*sqrt(x)*sqrt(-x + 1) + sqrt(x)
146
+
147
+
148
+ def test_multiplication_initial_condition():
149
+ x = symbols('x')
150
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
151
+ p = HolonomicFunction(Dx**2 + x*Dx - 1, x, 0, [3, 1])
152
+ q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1])
153
+ r = HolonomicFunction((x**4 + 14*x**2 + 60) + 4*x*Dx + (x**4 + 9*x**2 + 20)*Dx**2 + \
154
+ (2*x**3 + 18*x)*Dx**3 + (x**2 + 10)*Dx**4, x, 0, [3, 4, 2, 3])
155
+ assert p * q == r
156
+ p = HolonomicFunction(Dx**2 + x, x, 0, [1, 0])
157
+ q = HolonomicFunction(Dx**3 - x**2, x, 0, [3, 3, 3])
158
+ r = HolonomicFunction((x**8 - 37*x**7/27 - 10*x**6/27 - 164*x**5/9 - 184*x**4/9 + \
159
+ 160*x**3/27 + 404*x**2/9 + 8*x + Rational(40, 3)) + (6*x**7 - 128*x**6/9 - 98*x**5/9 - 28*x**4/9 + \
160
+ 8*x**3/9 + 28*x**2 + x*Rational(40, 9) - 40)*Dx + (3*x**6 - 82*x**5/9 + 76*x**4/9 + 4*x**3/3 + \
161
+ 220*x**2/9 - x*Rational(80, 3))*Dx**2 + (-2*x**6 + 128*x**5/27 - 2*x**4/3 -80*x**2/9 + Rational(200, 9))*Dx**3 + \
162
+ (3*x**5 - 64*x**4/9 - 28*x**3/9 + 6*x**2 - x*Rational(20, 9) - Rational(20, 3))*Dx**4 + (-4*x**3 + 64*x**2/9 + \
163
+ x*Rational(8, 3))*Dx**5 + (x**4 - 64*x**3/27 - 4*x**2/3 + Rational(20, 9))*Dx**6, x, 0, [3, 3, 3, -3, -12, -24])
164
+ assert p * q == r
165
+ p = HolonomicFunction(Dx - 1, x, 0, [2])
166
+ q = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
167
+ r = HolonomicFunction(2 -2*Dx + Dx**2, x, 0, [0, 2])
168
+ assert p * q == r
169
+ q = HolonomicFunction(x*Dx**2 + 1 + 2*Dx, x, 0,[0, 1])
170
+ r = HolonomicFunction((x - 1) + (-2*x + 2)*Dx + x*Dx**2, x, 0, [0, 2])
171
+ assert p * q == r
172
+ p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 3])
173
+ q = HolonomicFunction(Dx**3 + 1, x, 0, [1, 2, 1])
174
+ r = HolonomicFunction(6*Dx + 3*Dx**2 + 2*Dx**3 - 3*Dx**4 + Dx**6, x, 0, [1, 5, 14, 17, 17, 2])
175
+ assert p * q == r
176
+ p = expr_to_holonomic(sin(x))
177
+ q = expr_to_holonomic(1/x, x0=1)
178
+ r = HolonomicFunction(x + 2*Dx + x*Dx**2, x, 1, [sin(1), -sin(1) + cos(1)])
179
+ assert p * q == r
180
+ p = expr_to_holonomic(sqrt(x))
181
+ q = expr_to_holonomic(sqrt(x**2-x))
182
+ r = (p * q).to_expr()
183
+ assert r == I*x*sqrt(-x + 1)
184
+
185
+
186
+ def test_HolonomicFunction_composition():
187
+ x = symbols('x')
188
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
189
+ p = HolonomicFunction(Dx-1, x).composition(x**2+x)
190
+ r = HolonomicFunction((-2*x - 1) + Dx, x)
191
+ assert p == r
192
+ p = HolonomicFunction(Dx**2+1, x).composition(x**5+x**2+1)
193
+ r = HolonomicFunction((125*x**12 + 150*x**9 + 60*x**6 + 8*x**3) + (-20*x**3 - 2)*Dx + \
194
+ (5*x**4 + 2*x)*Dx**2, x)
195
+ assert p == r
196
+ p = HolonomicFunction(Dx**2*x+x, x).composition(2*x**3+x**2+1)
197
+ r = HolonomicFunction((216*x**9 + 324*x**8 + 180*x**7 + 152*x**6 + 112*x**5 + \
198
+ 36*x**4 + 4*x**3) + (24*x**4 + 16*x**3 + 3*x**2 - 6*x - 1)*Dx + (6*x**5 + 5*x**4 + \
199
+ x**3 + 3*x**2 + x)*Dx**2, x)
200
+ assert p == r
201
+ p = HolonomicFunction(Dx**2+1, x).composition(1-x**2)
202
+ r = HolonomicFunction((4*x**3) - Dx + x*Dx**2, x)
203
+ assert p == r
204
+ p = HolonomicFunction(Dx**2+1, x).composition(x - 2/(x**2 + 1))
205
+ r = HolonomicFunction((x**12 + 6*x**10 + 12*x**9 + 15*x**8 + 48*x**7 + 68*x**6 + \
206
+ 72*x**5 + 111*x**4 + 112*x**3 + 54*x**2 + 12*x + 1) + (12*x**8 + 32*x**6 + \
207
+ 24*x**4 - 4)*Dx + (x**12 + 6*x**10 + 4*x**9 + 15*x**8 + 16*x**7 + 20*x**6 + 24*x**5+ \
208
+ 15*x**4 + 16*x**3 + 6*x**2 + 4*x + 1)*Dx**2, x)
209
+ assert p == r
210
+
211
+
212
+ def test_from_hyper():
213
+ x = symbols('x')
214
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
215
+ p = hyper([1, 1], [Rational(3, 2)], x**2/4)
216
+ q = HolonomicFunction((4*x) + (5*x**2 - 8)*Dx + (x**3 - 4*x)*Dx**2, x, 1, [2*sqrt(3)*pi/9, -4*sqrt(3)*pi/27 + Rational(4, 3)])
217
+ r = from_hyper(p)
218
+ assert r == q
219
+ p = from_hyper(hyper([1], [Rational(3, 2)], x**2/4))
220
+ q = HolonomicFunction(-x + (-x**2/2 + 2)*Dx + x*Dx**2, x)
221
+ # x0 = 1
222
+ y0 = '[sqrt(pi)*exp(1/4)*erf(1/2), -sqrt(pi)*exp(1/4)*erf(1/2)/2 + 1]'
223
+ assert sstr(p.y0) == y0
224
+ assert q.annihilator == p.annihilator
225
+
226
+
227
+ def test_from_meijerg():
228
+ x = symbols('x')
229
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
230
+ p = from_meijerg(meijerg(([], [Rational(3, 2)]), ([S.Half], [S.Half, 1]), x))
231
+ q = HolonomicFunction(x/2 - Rational(1, 4) + (-x**2 + x/4)*Dx + x**2*Dx**2 + x**3*Dx**3, x, 1, \
232
+ [1/sqrt(pi), 1/(2*sqrt(pi)), -1/(4*sqrt(pi))])
233
+ assert p == q
234
+ p = from_meijerg(meijerg(([], []), ([0], []), x))
235
+ q = HolonomicFunction(1 + Dx, x, 0, [1])
236
+ assert p == q
237
+ p = from_meijerg(meijerg(([1], []), ([S.Half], [0]), x))
238
+ q = HolonomicFunction((x + S.Half)*Dx + x*Dx**2, x, 1, [sqrt(pi)*erf(1), exp(-1)])
239
+ assert p == q
240
+ p = from_meijerg(meijerg(([0], [1]), ([0], []), 2*x**2))
241
+ q = HolonomicFunction((3*x**2 - 1)*Dx + x**3*Dx**2, x, 1, [-exp(Rational(-1, 2)) + 1, -exp(Rational(-1, 2))])
242
+ assert p == q
243
+
244
+
245
+ def test_to_Sequence():
246
+ x = symbols('x')
247
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
248
+ n = symbols('n', integer=True)
249
+ _, Sn = RecurrenceOperators(ZZ.old_poly_ring(n), 'Sn')
250
+ p = HolonomicFunction(x**2*Dx**4 + x + Dx, x).to_sequence()
251
+ q = [(HolonomicSequence(1 + (n + 2)*Sn**2 + (n**4 + 6*n**3 + 11*n**2 + 6*n)*Sn**3), 0, 1)]
252
+ assert p == q
253
+ p = HolonomicFunction(x**2*Dx**4 + x**3 + Dx**2, x).to_sequence()
254
+ q = [(HolonomicSequence(1 + (n**4 + 14*n**3 + 72*n**2 + 163*n + 140)*Sn**5), 0, 0)]
255
+ assert p == q
256
+ p = HolonomicFunction(x**3*Dx**4 + 1 + Dx**2, x).to_sequence()
257
+ q = [(HolonomicSequence(1 + (n**4 - 2*n**3 - n**2 + 2*n)*Sn + (n**2 + 3*n + 2)*Sn**2), 0, 0)]
258
+ assert p == q
259
+ p = HolonomicFunction(3*x**3*Dx**4 + 2*x*Dx + x*Dx**3, x).to_sequence()
260
+ q = [(HolonomicSequence(2*n + (3*n**4 - 6*n**3 - 3*n**2 + 6*n)*Sn + (n**3 + 3*n**2 + 2*n)*Sn**2), 0, 1)]
261
+ assert p == q
262
+
263
+
264
+ def test_to_Sequence_Initial_Coniditons():
265
+ x = symbols('x')
266
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
267
+ n = symbols('n', integer=True)
268
+ _, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
269
+ p = HolonomicFunction(Dx - 1, x, 0, [1]).to_sequence()
270
+ q = [(HolonomicSequence(-1 + (n + 1)*Sn, 1), 0)]
271
+ assert p == q
272
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).to_sequence()
273
+ q = [(HolonomicSequence(1 + (n**2 + 3*n + 2)*Sn**2, [0, 1]), 0)]
274
+ assert p == q
275
+ p = HolonomicFunction(Dx**2 + 1 + x**3*Dx, x, 0, [2, 3]).to_sequence()
276
+ q = [(HolonomicSequence(n + Sn**2 + (n**2 + 7*n + 12)*Sn**4, [2, 3, -1, Rational(-1, 2), Rational(1, 12)]), 1)]
277
+ assert p == q
278
+ p = HolonomicFunction(x**3*Dx**5 + 1 + Dx, x).to_sequence()
279
+ q = [(HolonomicSequence(1 + (n + 1)*Sn + (n**5 - 5*n**3 + 4*n)*Sn**2), 0, 3)]
280
+ assert p == q
281
+ C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
282
+ p = expr_to_holonomic(log(1+x**2))
283
+ q = [(HolonomicSequence(n**2 + (n**2 + 2*n)*Sn**2, [0, 0, C_2]), 0, 1)]
284
+ assert p.to_sequence() == q
285
+ p = p.diff()
286
+ q = [(HolonomicSequence((n + 2) + (n + 2)*Sn**2, [C_0, 0]), 1, 0)]
287
+ assert p.to_sequence() == q
288
+ p = expr_to_holonomic(erf(x) + x).to_sequence()
289
+ q = [(HolonomicSequence((2*n**2 - 2*n) + (n**3 + 2*n**2 - n - 2)*Sn**2, [0, 1 + 2/sqrt(pi), 0, C_3]), 0, 2)]
290
+ assert p == q
291
+
292
+ def test_series():
293
+ x = symbols('x')
294
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
295
+ p = HolonomicFunction(Dx**2 + 2*x*Dx, x, 0, [0, 1]).series(n=10)
296
+ q = x - x**3/3 + x**5/10 - x**7/42 + x**9/216 + O(x**10)
297
+ assert p == q
298
+ p = HolonomicFunction(Dx - 1, x).composition(x**2, 0, [1]) # e^(x**2)
299
+ q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) # cos(x)
300
+ r = (p * q).series(n=10) # expansion of cos(x) * exp(x**2)
301
+ s = 1 + x**2/2 + x**4/24 - 31*x**6/720 - 179*x**8/8064 + O(x**10)
302
+ assert r == s
303
+ t = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]) # log(1 + x)
304
+ r = (p * t + q).series(n=10)
305
+ s = 1 + x - x**2 + 4*x**3/3 - 17*x**4/24 + 31*x**5/30 - 481*x**6/720 +\
306
+ 71*x**7/105 - 20159*x**8/40320 + 379*x**9/840 + O(x**10)
307
+ assert r == s
308
+ p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \
309
+ (4-6*x**3+2*x**4)*Dx**2, x, 0, [0, 1]).series(n=7)
310
+ q = x + x**3/6 - 3*x**4/16 + x**5/20 - 23*x**6/960 + O(x**7)
311
+ assert p == q
312
+ p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \
313
+ (4-6*x**3+2*x**4)*Dx**2, x, 0, [1, 0]).series(n=7)
314
+ q = 1 - 3*x**2/4 - x**3/4 - 5*x**4/32 - 3*x**5/40 - 17*x**6/384 + O(x**7)
315
+ assert p == q
316
+ p = expr_to_holonomic(erf(x) + x).series(n=10)
317
+ C_3 = symbols('C_3')
318
+ q = (erf(x) + x).series(n=10)
319
+ assert p.subs(C_3, -2/(3*sqrt(pi))) == q
320
+ assert expr_to_holonomic(sqrt(x**3 + x)).series(n=10) == sqrt(x**3 + x).series(n=10)
321
+ assert expr_to_holonomic((2*x - 3*x**2)**Rational(1, 3)).series() == ((2*x - 3*x**2)**Rational(1, 3)).series()
322
+ assert expr_to_holonomic(sqrt(x**2-x)).series() == (sqrt(x**2-x)).series()
323
+ assert expr_to_holonomic(cos(x)**2/x**2, y0={-2: [1, 0, -1]}).series(n=10) == (cos(x)**2/x**2).series(n=10)
324
+ assert expr_to_holonomic(cos(x)**2/x**2, x0=1).series(n=10).together() == (cos(x)**2/x**2).series(n=10, x0=1).together()
325
+ assert expr_to_holonomic(cos(x-1)**2/(x-1)**2, x0=1, y0={-2: [1, 0, -1]}).series(n=10) \
326
+ == (cos(x-1)**2/(x-1)**2).series(x0=1, n=10)
327
+
328
+ def test_evalf_euler():
329
+ x = symbols('x')
330
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
331
+
332
+ # log(1+x)
333
+ p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1])
334
+
335
+ # path taken is a straight line from 0 to 1, on the real axis
336
+ r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
337
+ s = '0.699525841805253' # approx. equal to log(2) i.e. 0.693147180559945
338
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
339
+
340
+ # path taken is a triangle 0-->1+i-->2
341
+ r = [0.1 + 0.1*I]
342
+ for i in range(9):
343
+ r.append(r[-1]+0.1+0.1*I)
344
+ for i in range(10):
345
+ r.append(r[-1]+0.1-0.1*I)
346
+
347
+ # close to the exact solution 1.09861228866811
348
+ # imaginary part also close to zero
349
+ s = '1.07530466271334 - 0.0251200594793912*I'
350
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
351
+
352
+ # sin(x)
353
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
354
+ s = '0.905546532085401 - 6.93889390390723e-18*I'
355
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
356
+
357
+ # computing sin(pi/2) using this method
358
+ # using a linear path from 0 to pi/2
359
+ r = [0.1]
360
+ for i in range(14):
361
+ r.append(r[-1] + 0.1)
362
+ r.append(pi/2)
363
+ s = '1.08016557252834' # close to 1.0 (exact solution)
364
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
365
+
366
+ # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2)
367
+ # computing the same value sin(pi/2) using different path
368
+ r = [0.1*I]
369
+ for i in range(9):
370
+ r.append(r[-1]+0.1*I)
371
+ for i in range(15):
372
+ r.append(r[-1]+0.1)
373
+ r.append(pi/2+I)
374
+ for i in range(10):
375
+ r.append(r[-1]-0.1*I)
376
+
377
+ # close to 1.0
378
+ s = '0.976882381836257 - 1.65557671738537e-16*I'
379
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
380
+
381
+ # cos(x)
382
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0])
383
+ # compute cos(pi) along 0-->pi
384
+ r = [0.05]
385
+ for i in range(61):
386
+ r.append(r[-1]+0.05)
387
+ r.append(pi)
388
+ # close to -1 (exact answer)
389
+ s = '-1.08140824719196'
390
+ assert sstr(p.evalf(r, method='Euler')[-1]) == s
391
+
392
+ # a rectangular path (0 -> i -> 2+i -> 2)
393
+ r = [0.1*I]
394
+ for i in range(9):
395
+ r.append(r[-1]+0.1*I)
396
+ for i in range(20):
397
+ r.append(r[-1]+0.1)
398
+ for i in range(10):
399
+ r.append(r[-1]-0.1*I)
400
+
401
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r, method='Euler')
402
+ s = '0.501421652861245 - 3.88578058618805e-16*I'
403
+ assert sstr(p[-1]) == s
404
+
405
+ def test_evalf_rk4():
406
+ x = symbols('x')
407
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
408
+
409
+ # log(1+x)
410
+ p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1])
411
+
412
+ # path taken is a straight line from 0 to 1, on the real axis
413
+ r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
414
+ s = '0.693146363174626' # approx. equal to log(2) i.e. 0.693147180559945
415
+ assert sstr(p.evalf(r)[-1]) == s
416
+
417
+ # path taken is a triangle 0-->1+i-->2
418
+ r = [0.1 + 0.1*I]
419
+ for i in range(9):
420
+ r.append(r[-1]+0.1+0.1*I)
421
+ for i in range(10):
422
+ r.append(r[-1]+0.1-0.1*I)
423
+
424
+ # close to the exact solution 1.09861228866811
425
+ # imaginary part also close to zero
426
+ s = '1.098616 + 1.36083e-7*I'
427
+ assert sstr(p.evalf(r)[-1].n(7)) == s
428
+
429
+ # sin(x)
430
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
431
+ s = '0.90929463522785 + 1.52655665885959e-16*I'
432
+ assert sstr(p.evalf(r)[-1]) == s
433
+
434
+ # computing sin(pi/2) using this method
435
+ # using a linear path from 0 to pi/2
436
+ r = [0.1]
437
+ for i in range(14):
438
+ r.append(r[-1] + 0.1)
439
+ r.append(pi/2)
440
+ s = '0.999999895088917' # close to 1.0 (exact solution)
441
+ assert sstr(p.evalf(r)[-1]) == s
442
+
443
+ # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2)
444
+ # computing the same value sin(pi/2) using different path
445
+ r = [0.1*I]
446
+ for i in range(9):
447
+ r.append(r[-1]+0.1*I)
448
+ for i in range(15):
449
+ r.append(r[-1]+0.1)
450
+ r.append(pi/2+I)
451
+ for i in range(10):
452
+ r.append(r[-1]-0.1*I)
453
+
454
+ # close to 1.0
455
+ s = '1.00000003415141 + 6.11940487991086e-16*I'
456
+ assert sstr(p.evalf(r)[-1]) == s
457
+
458
+ # cos(x)
459
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0])
460
+ # compute cos(pi) along 0-->pi
461
+ r = [0.05]
462
+ for i in range(61):
463
+ r.append(r[-1]+0.05)
464
+ r.append(pi)
465
+ # close to -1 (exact answer)
466
+ s = '-0.999999993238714'
467
+ assert sstr(p.evalf(r)[-1]) == s
468
+
469
+ # a rectangular path (0 -> i -> 2+i -> 2)
470
+ r = [0.1*I]
471
+ for i in range(9):
472
+ r.append(r[-1]+0.1*I)
473
+ for i in range(20):
474
+ r.append(r[-1]+0.1)
475
+ for i in range(10):
476
+ r.append(r[-1]-0.1*I)
477
+
478
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r)
479
+ s = '0.493152791638442 - 1.41553435639707e-15*I'
480
+ assert sstr(p[-1]) == s
481
+
482
+
483
+ def test_expr_to_holonomic():
484
+ x = symbols('x')
485
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
486
+ p = expr_to_holonomic((sin(x)/x)**2)
487
+ q = HolonomicFunction(8*x + (4*x**2 + 6)*Dx + 6*x*Dx**2 + x**2*Dx**3, x, 0, \
488
+ [1, 0, Rational(-2, 3)])
489
+ assert p == q
490
+ p = expr_to_holonomic(1/(1+x**2)**2)
491
+ q = HolonomicFunction(4*x + (x**2 + 1)*Dx, x, 0, [1])
492
+ assert p == q
493
+ p = expr_to_holonomic(exp(x)*sin(x)+x*log(1+x))
494
+ q = HolonomicFunction((2*x**3 + 10*x**2 + 20*x + 18) + (-2*x**4 - 10*x**3 - 20*x**2 \
495
+ - 18*x)*Dx + (2*x**5 + 6*x**4 + 7*x**3 + 8*x**2 + 10*x - 4)*Dx**2 + \
496
+ (-2*x**5 - 5*x**4 - 2*x**3 + 2*x**2 - x + 4)*Dx**3 + (x**5 + 2*x**4 - x**3 - \
497
+ 7*x**2/2 + x + Rational(5, 2))*Dx**4, x, 0, [0, 1, 4, -1])
498
+ assert p == q
499
+ p = expr_to_holonomic(x*exp(x)+cos(x)+1)
500
+ q = HolonomicFunction((-x - 3)*Dx + (x + 2)*Dx**2 + (-x - 3)*Dx**3 + (x + 2)*Dx**4, x, \
501
+ 0, [2, 1, 1, 3])
502
+ assert p == q
503
+ assert (x*exp(x)+cos(x)+1).series(n=10) == p.series(n=10)
504
+ p = expr_to_holonomic(log(1 + x)**2 + 1)
505
+ q = HolonomicFunction(Dx + (3*x + 3)*Dx**2 + (x**2 + 2*x + 1)*Dx**3, x, 0, [1, 0, 2])
506
+ assert p == q
507
+ p = expr_to_holonomic(erf(x)**2 + x)
508
+ q = HolonomicFunction((8*x**4 - 2*x**2 + 2)*Dx**2 + (6*x**3 - x/2)*Dx**3 + \
509
+ (x**2+ Rational(1, 4))*Dx**4, x, 0, [0, 1, 8/pi, 0])
510
+ assert p == q
511
+ p = expr_to_holonomic(cosh(x)*x)
512
+ q = HolonomicFunction((-x**2 + 2) -2*x*Dx + x**2*Dx**2, x, 0, [0, 1])
513
+ assert p == q
514
+ p = expr_to_holonomic(besselj(2, x))
515
+ q = HolonomicFunction((x**2 - 4) + x*Dx + x**2*Dx**2, x, 0, [0, 0])
516
+ assert p == q
517
+ p = expr_to_holonomic(besselj(0, x) + exp(x))
518
+ q = HolonomicFunction((-x**2 - x/2 + S.Half) + (x**2 - x/2 - Rational(3, 2))*Dx + (-x**2 + x/2 + 1)*Dx**2 +\
519
+ (x**2 + x/2)*Dx**3, x, 0, [2, 1, S.Half])
520
+ assert p == q
521
+ p = expr_to_holonomic(sin(x)**2/x)
522
+ q = HolonomicFunction(4 + 4*x*Dx + 3*Dx**2 + x*Dx**3, x, 0, [0, 1, 0])
523
+ assert p == q
524
+ p = expr_to_holonomic(sin(x)**2/x, x0=2)
525
+ q = HolonomicFunction((4) + (4*x)*Dx + (3)*Dx**2 + (x)*Dx**3, x, 2, [sin(2)**2/2,
526
+ sin(2)*cos(2) - sin(2)**2/4, -3*sin(2)**2/4 + cos(2)**2 - sin(2)*cos(2)])
527
+ assert p == q
528
+ p = expr_to_holonomic(log(x)/2 - Ci(2*x)/2 + Ci(2)/2)
529
+ q = HolonomicFunction(4*Dx + 4*x*Dx**2 + 3*Dx**3 + x*Dx**4, x, 0, \
530
+ [-log(2)/2 - EulerGamma/2 + Ci(2)/2, 0, 1, 0])
531
+ assert p == q
532
+ p = p.to_expr()
533
+ q = log(x)/2 - Ci(2*x)/2 + Ci(2)/2
534
+ assert p == q
535
+ p = expr_to_holonomic(x**S.Half, x0=1)
536
+ q = HolonomicFunction(x*Dx - S.Half, x, 1, [1])
537
+ assert p == q
538
+ p = expr_to_holonomic(sqrt(1 + x**2))
539
+ q = HolonomicFunction((-x) + (x**2 + 1)*Dx, x, 0, [1])
540
+ assert p == q
541
+ assert (expr_to_holonomic(sqrt(x) + sqrt(2*x)).to_expr()-\
542
+ (sqrt(x) + sqrt(2*x))).simplify() == 0
543
+ assert expr_to_holonomic(3*x+2*sqrt(x)).to_expr() == 3*x+2*sqrt(x)
544
+ p = expr_to_holonomic((x**4+x**3+5*x**2+3*x+2)/x**2, lenics=3)
545
+ q = HolonomicFunction((-2*x**4 - x**3 + 3*x + 4) + (x**5 + x**4 + 5*x**3 + 3*x**2 + \
546
+ 2*x)*Dx, x, 0, {-2: [2, 3, 5]})
547
+ assert p == q
548
+ p = expr_to_holonomic(1/(x-1)**2, lenics=3, x0=1)
549
+ q = HolonomicFunction((2) + (x - 1)*Dx, x, 1, {-2: [1, 0, 0]})
550
+ assert p == q
551
+ a = symbols("a")
552
+ p = expr_to_holonomic(sqrt(a*x), x=x)
553
+ assert p.to_expr() == sqrt(a)*sqrt(x)
554
+
555
+ def test_to_hyper():
556
+ x = symbols('x')
557
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
558
+ p = HolonomicFunction(Dx - 2, x, 0, [3]).to_hyper()
559
+ q = 3 * hyper([], [], 2*x)
560
+ assert p == q
561
+ p = hyperexpand(HolonomicFunction((1 + x) * Dx - 3, x, 0, [2]).to_hyper()).expand()
562
+ q = 2*x**3 + 6*x**2 + 6*x + 2
563
+ assert p == q
564
+ p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]).to_hyper()
565
+ q = -x**2*hyper((2, 2, 1), (3, 2), -x)/2 + x
566
+ assert p == q
567
+ p = HolonomicFunction(2*x*Dx + Dx**2, x, 0, [0, 2/sqrt(pi)]).to_hyper()
568
+ q = 2*x*hyper((S.Half,), (Rational(3, 2),), -x**2)/sqrt(pi)
569
+ assert p == q
570
+ p = hyperexpand(HolonomicFunction(2*x*Dx + Dx**2, x, 0, [1, -2/sqrt(pi)]).to_hyper())
571
+ q = erfc(x)
572
+ assert p.rewrite(erfc) == q
573
+ p = hyperexpand(HolonomicFunction((x**2 - 1) + x*Dx + x**2*Dx**2,
574
+ x, 0, [0, S.Half]).to_hyper())
575
+ q = besselj(1, x)
576
+ assert p == q
577
+ p = hyperexpand(HolonomicFunction(x*Dx**2 + Dx + x, x, 0, [1, 0]).to_hyper())
578
+ q = besselj(0, x)
579
+ assert p == q
580
+
581
+ def test_to_expr():
582
+ x = symbols('x')
583
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
584
+ p = HolonomicFunction(Dx - 1, x, 0, [1]).to_expr()
585
+ q = exp(x)
586
+ assert p == q
587
+ p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]).to_expr()
588
+ q = cos(x)
589
+ assert p == q
590
+ p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0]).to_expr()
591
+ q = cosh(x)
592
+ assert p == q
593
+ p = HolonomicFunction(2 + (4*x - 1)*Dx + \
594
+ (x**2 - x)*Dx**2, x, 0, [1, 2]).to_expr().expand()
595
+ q = 1/(x**2 - 2*x + 1)
596
+ assert p == q
597
+ p = expr_to_holonomic(sin(x)**2/x).integrate((x, 0, x)).to_expr()
598
+ q = (sin(x)**2/x).integrate((x, 0, x))
599
+ assert p == q
600
+ C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
601
+ p = expr_to_holonomic(log(1+x**2)).to_expr()
602
+ q = C_2*log(x**2 + 1)
603
+ assert p == q
604
+ p = expr_to_holonomic(log(1+x**2)).diff().to_expr()
605
+ q = C_0*x/(x**2 + 1)
606
+ assert p == q
607
+ p = expr_to_holonomic(erf(x) + x).to_expr()
608
+ q = 3*C_3*x - 3*sqrt(pi)*C_3*erf(x)/2 + x + 2*x/sqrt(pi)
609
+ assert p == q
610
+ p = expr_to_holonomic(sqrt(x), x0=1).to_expr()
611
+ assert p == sqrt(x)
612
+ assert expr_to_holonomic(sqrt(x)).to_expr() == sqrt(x)
613
+ p = expr_to_holonomic(sqrt(1 + x**2)).to_expr()
614
+ assert p == sqrt(1+x**2)
615
+ p = expr_to_holonomic((2*x**2 + 1)**Rational(2, 3)).to_expr()
616
+ assert p == (2*x**2 + 1)**Rational(2, 3)
617
+ p = expr_to_holonomic(sqrt(-x**2+2*x)).to_expr()
618
+ assert p == sqrt(x)*sqrt(-x + 2)
619
+ p = expr_to_holonomic((-2*x**3+7*x)**Rational(2, 3)).to_expr()
620
+ q = x**Rational(2, 3)*(-2*x**2 + 7)**Rational(2, 3)
621
+ assert p == q
622
+ p = from_hyper(hyper((-2, -3), (S.Half, ), x))
623
+ s = hyperexpand(hyper((-2, -3), (S.Half, ), x))
624
+ D_0 = Symbol('D_0')
625
+ C_0 = Symbol('C_0')
626
+ assert (p.to_expr().subs({C_0:1, D_0:0}) - s).simplify() == 0
627
+ p.y0 = {0: [1], S.Half: [0]}
628
+ assert p.to_expr() == s
629
+ assert expr_to_holonomic(x**5).to_expr() == x**5
630
+ assert expr_to_holonomic(2*x**3-3*x**2).to_expr().expand() == \
631
+ 2*x**3-3*x**2
632
+ a = symbols("a")
633
+ p = (expr_to_holonomic(1.4*x)*expr_to_holonomic(a*x, x)).to_expr()
634
+ q = 1.4*a*x**2
635
+ assert p == q
636
+ p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(a*x, x)).to_expr()
637
+ q = x*(a + 1.4)
638
+ assert p == q
639
+ p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(x)).to_expr()
640
+ assert p == 2.4*x
641
+
642
+
643
+ def test_integrate():
644
+ x = symbols('x')
645
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
646
+ p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 2, 3))
647
+ q = '0.166270406994788'
648
+ assert sstr(p) == q
649
+ p = expr_to_holonomic(sin(x)).integrate((x, 0, x)).to_expr()
650
+ q = 1 - cos(x)
651
+ assert p == q
652
+ p = expr_to_holonomic(sin(x)).integrate((x, 0, 3))
653
+ q = 1 - cos(3)
654
+ assert p == q
655
+ p = expr_to_holonomic(sin(x)/x, x0=1).integrate((x, 1, 2))
656
+ q = '0.659329913368450'
657
+ assert sstr(p) == q
658
+ p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 1, 0))
659
+ q = '-0.423690480850035'
660
+ assert sstr(p) == q
661
+ p = expr_to_holonomic(sin(x)/x)
662
+ assert p.integrate(x).to_expr() == Si(x)
663
+ assert p.integrate((x, 0, 2)) == Si(2)
664
+ p = expr_to_holonomic(sin(x)**2/x)
665
+ q = p.to_expr()
666
+ assert p.integrate(x).to_expr() == q.integrate((x, 0, x))
667
+ assert p.integrate((x, 0, 1)) == q.integrate((x, 0, 1))
668
+ assert expr_to_holonomic(1/x, x0=1).integrate(x).to_expr() == log(x)
669
+ p = expr_to_holonomic((x + 1)**3*exp(-x), x0=-1).integrate(x).to_expr()
670
+ q = (-x**3 - 6*x**2 - 15*x + 6*exp(x + 1) - 16)*exp(-x)
671
+ assert p == q
672
+ p = expr_to_holonomic(cos(x)**2/x**2, y0={-2: [1, 0, -1]}).integrate(x).to_expr()
673
+ q = -Si(2*x) - cos(x)**2/x
674
+ assert p == q
675
+ p = expr_to_holonomic(sqrt(x**2+x)).integrate(x).to_expr()
676
+ q = (x**Rational(3, 2)*(2*x**2 + 3*x + 1) - x*sqrt(x + 1)*asinh(sqrt(x)))/(4*x*sqrt(x + 1))
677
+ assert p == q
678
+ p = expr_to_holonomic(sqrt(x**2+1)).integrate(x).to_expr()
679
+ q = (sqrt(x**2+1)).integrate(x)
680
+ assert (p-q).simplify() == 0
681
+ p = expr_to_holonomic(1/x**2, y0={-2:[1, 0, 0]})
682
+ r = expr_to_holonomic(1/x**2, lenics=3)
683
+ assert p == r
684
+ q = expr_to_holonomic(cos(x)**2)
685
+ assert (r*q).integrate(x).to_expr() == -Si(2*x) - cos(x)**2/x
686
+
687
+
688
+ def test_diff():
689
+ x, y = symbols('x, y')
690
+ R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
691
+ p = HolonomicFunction(x*Dx**2 + 1, x, 0, [0, 1])
692
+ assert p.diff().to_expr() == p.to_expr().diff().simplify()
693
+ p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0])
694
+ assert p.diff(x, 2).to_expr() == p.to_expr()
695
+ p = expr_to_holonomic(Si(x))
696
+ assert p.diff().to_expr() == sin(x)/x
697
+ assert p.diff(y) == 0
698
+ C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
699
+ q = Si(x)
700
+ assert p.diff(x).to_expr() == q.diff()
701
+ assert p.diff(x, 2).to_expr().subs(C_0, Rational(-1, 3)).cancel() == q.diff(x, 2).cancel()
702
+ assert p.diff(x, 3).series().subs({C_3: Rational(-1, 3), C_0: 0}) == q.diff(x, 3).series()
703
+
704
+
705
+ def test_extended_domain_in_expr_to_holonomic():
706
+ x = symbols('x')
707
+ p = expr_to_holonomic(1.2*cos(3.1*x))
708
+ assert p.to_expr() == 1.2*cos(3.1*x)
709
+ assert sstr(p.integrate(x).to_expr()) == '0.387096774193548*sin(3.1*x)'
710
+ _, Dx = DifferentialOperators(RR.old_poly_ring(x), 'Dx')
711
+ p = expr_to_holonomic(1.1329138213*x)
712
+ q = HolonomicFunction((-1.1329138213) + (1.1329138213*x)*Dx, x, 0, {1: [1.1329138213]})
713
+ assert p == q
714
+ assert p.to_expr() == 1.1329138213*x
715
+ assert sstr(p.integrate((x, 1, 2))) == sstr((1.1329138213*x).integrate((x, 1, 2)))
716
+ y, z = symbols('y, z')
717
+ p = expr_to_holonomic(sin(x*y*z), x=x)
718
+ assert p.to_expr() == sin(x*y*z)
719
+ assert p.integrate(x).to_expr() == (-cos(x*y*z) + 1)/(y*z)
720
+ p = expr_to_holonomic(sin(x*y + z), x=x).integrate(x).to_expr()
721
+ q = (cos(z) - cos(x*y + z))/y
722
+ assert p == q
723
+ a = symbols('a')
724
+ p = expr_to_holonomic(a*x, x)
725
+ assert p.to_expr() == a*x
726
+ assert p.integrate(x).to_expr() == a*x**2/2
727
+ D_2, C_1 = symbols("D_2, C_1")
728
+ p = expr_to_holonomic(x) + expr_to_holonomic(1.2*cos(x))
729
+ p = p.to_expr().subs(D_2, 0)
730
+ assert p - x - 1.2*cos(1.0*x) == 0
731
+ p = expr_to_holonomic(x) * expr_to_holonomic(1.2*cos(x))
732
+ p = p.to_expr().subs(C_1, 0)
733
+ assert p - 1.2*x*cos(1.0*x) == 0
734
+
735
+
736
+ def test_to_meijerg():
737
+ x = symbols('x')
738
+ assert hyperexpand(expr_to_holonomic(sin(x)).to_meijerg()) == sin(x)
739
+ assert hyperexpand(expr_to_holonomic(cos(x)).to_meijerg()) == cos(x)
740
+ assert hyperexpand(expr_to_holonomic(exp(x)).to_meijerg()) == exp(x)
741
+ assert hyperexpand(expr_to_holonomic(log(x)).to_meijerg()).simplify() == log(x)
742
+ assert expr_to_holonomic(4*x**2/3 + 7).to_meijerg() == 4*x**2/3 + 7
743
+ assert hyperexpand(expr_to_holonomic(besselj(2, x), lenics=3).to_meijerg()) == besselj(2, x)
744
+ p = hyper((Rational(-1, 2), -3), (), x)
745
+ assert from_hyper(p).to_meijerg() == hyperexpand(p)
746
+ p = hyper((S.One, S(3)), (S(2), ), x)
747
+ assert (hyperexpand(from_hyper(p).to_meijerg()) - hyperexpand(p)).expand() == 0
748
+ p = from_hyper(hyper((-2, -3), (S.Half, ), x))
749
+ s = hyperexpand(hyper((-2, -3), (S.Half, ), x))
750
+ C_0 = Symbol('C_0')
751
+ C_1 = Symbol('C_1')
752
+ D_0 = Symbol('D_0')
753
+ assert (hyperexpand(p.to_meijerg()).subs({C_0:1, D_0:0}) - s).simplify() == 0
754
+ p.y0 = {0: [1], S.Half: [0]}
755
+ assert (hyperexpand(p.to_meijerg()) - s).simplify() == 0
756
+ p = expr_to_holonomic(besselj(S.Half, x), initcond=False)
757
+ assert (p.to_expr() - (D_0*sin(x) + C_0*cos(x) + C_1*sin(x))/sqrt(x)).simplify() == 0
758
+ p = expr_to_holonomic(besselj(S.Half, x), y0={Rational(-1, 2): [sqrt(2)/sqrt(pi), sqrt(2)/sqrt(pi)]})
759
+ assert (p.to_expr() - besselj(S.Half, x) - besselj(Rational(-1, 2), x)).simplify() == 0
760
+
761
+
762
+ def test_gaussian():
763
+ mu, x = symbols("mu x")
764
+ sd = symbols("sd", positive=True)
765
+ Q = QQ[mu, sd].get_field()
766
+ e = sqrt(2)*exp(-(-mu + x)**2/(2*sd**2))/(2*sqrt(pi)*sd)
767
+ h1 = expr_to_holonomic(e, x, domain=Q)
768
+
769
+ _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
770
+ h2 = HolonomicFunction((-mu/sd**2 + x/sd**2) + (1)*Dx, x)
771
+
772
+ assert h1 == h2
773
+
774
+
775
+ def test_beta():
776
+ a, b, x = symbols("a b x", positive=True)
777
+ e = x**(a - 1)*(-x + 1)**(b - 1)/beta(a, b)
778
+ Q = QQ[a, b].get_field()
779
+ h1 = expr_to_holonomic(e, x, domain=Q)
780
+
781
+ _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
782
+ h2 = HolonomicFunction((a + x*(-a - b + 2) - 1) + (x**2 - x)*Dx, x)
783
+
784
+ assert h1 == h2
785
+
786
+
787
+ def test_gamma():
788
+ a, b, x = symbols("a b x", positive=True)
789
+ e = b**(-a)*x**(a - 1)*exp(-x/b)/gamma(a)
790
+ Q = QQ[a, b].get_field()
791
+ h1 = expr_to_holonomic(e, x, domain=Q)
792
+
793
+ _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
794
+ h2 = HolonomicFunction((-a + 1 + x/b) + (x)*Dx, x)
795
+
796
+ assert h1 == h2
797
+
798
+
799
+ def test_symbolic_power():
800
+ x, n = symbols("x n")
801
+ Q = QQ[n].get_field()
802
+ _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
803
+ h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -n
804
+ h2 = HolonomicFunction((n) + (x)*Dx, x)
805
+
806
+ assert h1 == h2
807
+
808
+
809
+ def test_negative_power():
810
+ x = symbols("x")
811
+ _, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
812
+ h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -2
813
+ h2 = HolonomicFunction((2) + (x)*Dx, x)
814
+
815
+ assert h1 == h2
816
+
817
+
818
+ def test_expr_in_power():
819
+ x, n = symbols("x n")
820
+ Q = QQ[n].get_field()
821
+ _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
822
+ h1 = HolonomicFunction((-1) + (x)*Dx, x) ** (n - 3)
823
+ h2 = HolonomicFunction((-n + 3) + (x)*Dx, x)
824
+
825
+ assert h1 == h2
826
+
827
+
828
+ def test_DifferentialOperatorEqPoly():
829
+ x = symbols('x', integer=True)
830
+ R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
831
+ do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R)
832
+ do2 = DifferentialOperator([x**2, 1, x], R)
833
+ assert not do == do2
834
+
835
+ # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799
836
+ # should work once that is solved
837
+ # p = do.listofpoly[0]
838
+ # assert do == p
839
+
840
+ p2 = do2.listofpoly[0]
841
+ assert not do2 == p2
842
+
843
+
844
+ def test_DifferentialOperatorPow():
845
+ x = symbols('x', integer=True)
846
+ R, _ = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
847
+ do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R)
848
+ a = DifferentialOperator([R.base.one], R)
849
+ for n in range(10):
850
+ assert a == do**n
851
+ a *= do
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.holonomic.recurrence import RecurrenceOperators, RecurrenceOperator
2
+ from sympy.core.symbol import symbols
3
+ from sympy.polys.domains.rationalfield import QQ
4
+
5
+
6
+ def test_RecurrenceOperator():
7
+ n = symbols('n', integer=True)
8
+ R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
9
+ assert Sn*n == (n + 1)*Sn
10
+ assert Sn*n**2 == (n**2+1+2*n)*Sn
11
+ assert Sn**2*n**2 == (n**2 + 4*n + 4)*Sn**2
12
+ p = (Sn**3*n**2 + Sn*n)**2
13
+ q = (n**2 + 3*n + 2)*Sn**2 + (2*n**3 + 19*n**2 + 57*n + 52)*Sn**4 + (n**4 + 18*n**3 + \
14
+ 117*n**2 + 324*n + 324)*Sn**6
15
+ assert p == q
16
+
17
+
18
+ def test_RecurrenceOperatorEqPoly():
19
+ n = symbols('n', integer=True)
20
+ R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
21
+ rr = RecurrenceOperator([n**2, 0, 0], R)
22
+ rr2 = RecurrenceOperator([n**2, 1, n], R)
23
+ assert not rr == rr2
24
+
25
+ # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799
26
+ # should work once that is solved
27
+ # d = rr.listofpoly[0]
28
+ # assert rr == d
29
+
30
+ d2 = rr2.listofpoly[0]
31
+ assert not rr2 == d2
32
+
33
+
34
+ def test_RecurrenceOperatorPow():
35
+ n = symbols('n', integer=True)
36
+ R, _ = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
37
+ rr = RecurrenceOperator([n**2, 0, 0], R)
38
+ a = RecurrenceOperator([R.base.one], R)
39
+ for m in range(10):
40
+ assert a == rr**m
41
+ a *= rr
.venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of mathematical domains. """
2
+
3
+ __all__ = [
4
+ 'Domain', 'FiniteField', 'IntegerRing', 'RationalField', 'RealField',
5
+ 'ComplexField', 'AlgebraicField', 'PolynomialRing', 'FractionField',
6
+ 'ExpressionDomain', 'PythonRational',
7
+
8
+ 'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW',
9
+ ]
10
+
11
+ from .domain import Domain
12
+ from .finitefield import FiniteField, FF, GF
13
+ from .integerring import IntegerRing, ZZ
14
+ from .rationalfield import RationalField, QQ
15
+ from .algebraicfield import AlgebraicField
16
+ from .gaussiandomains import ZZ_I, QQ_I
17
+ from .realfield import RealField, RR
18
+ from .complexfield import ComplexField, CC
19
+ from .polynomialring import PolynomialRing
20
+ from .fractionfield import FractionField
21
+ from .expressiondomain import ExpressionDomain, EX
22
+ from .expressionrawdomain import EXRAW
23
+ from .pythonrational import PythonRational
24
+
25
+
26
+ # This is imported purely for backwards compatibility because some parts of
27
+ # the codebase used to import this from here and it's possible that downstream
28
+ # does as well:
29
+ from sympy.external.gmpy import GROUND_TYPES # noqa: F401
30
+
31
+ #
32
+ # The rest of these are obsolete and provided only for backwards
33
+ # compatibility:
34
+ #
35
+
36
+ from .pythonfinitefield import PythonFiniteField
37
+ from .gmpyfinitefield import GMPYFiniteField
38
+ from .pythonintegerring import PythonIntegerRing
39
+ from .gmpyintegerring import GMPYIntegerRing
40
+ from .pythonrationalfield import PythonRationalField
41
+ from .gmpyrationalfield import GMPYRationalField
42
+
43
+ FF_python = PythonFiniteField
44
+ FF_gmpy = GMPYFiniteField
45
+
46
+ ZZ_python = PythonIntegerRing
47
+ ZZ_gmpy = GMPYIntegerRing
48
+
49
+ QQ_python = PythonRationalField
50
+ QQ_gmpy = GMPYRationalField
51
+
52
+ __all__.extend((
53
+ 'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing',
54
+ 'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField',
55
+
56
+ 'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy',
57
+ ))
.venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`AlgebraicField` class. """
2
+
3
+
4
+ from sympy.core.add import Add
5
+ from sympy.core.mul import Mul
6
+ from sympy.core.singleton import S
7
+ from sympy.core.symbol import Dummy, symbols
8
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
9
+ from sympy.polys.domains.field import Field
10
+ from sympy.polys.domains.simpledomain import SimpleDomain
11
+ from sympy.polys.polyclasses import ANP
12
+ from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
13
+ from sympy.utilities import public
14
+
15
+ @public
16
+ class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
17
+ r"""Algebraic number field :ref:`QQ(a)`
18
+
19
+ A :ref:`QQ(a)` domain represents an `algebraic number field`_
20
+ `\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
21
+ :ref:`polys-domainsintro`).
22
+
23
+ A :py:class:`~.Poly` created from an expression involving `algebraic
24
+ numbers`_ will treat the algebraic numbers as generators if the generators
25
+ argument is not specified.
26
+
27
+ >>> from sympy import Poly, Symbol, sqrt
28
+ >>> x = Symbol('x')
29
+ >>> Poly(x**2 + sqrt(2))
30
+ Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
31
+
32
+ That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
33
+ generators (variables). If the generators are explicitly specified then
34
+ ``sqrt(2)`` will be considered to be a coefficient but by default the
35
+ :ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
36
+ domain the argument ``extension=True`` can be given.
37
+
38
+ >>> Poly(x**2 + sqrt(2), x)
39
+ Poly(x**2 + sqrt(2), x, domain='EX')
40
+ >>> Poly(x**2 + sqrt(2), x, extension=True)
41
+ Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
42
+
43
+ A generator of the algebraic field extension can also be specified
44
+ explicitly which is particularly useful if the coefficients are all
45
+ rational but an extension field is needed (e.g. to factor the
46
+ polynomial).
47
+
48
+ >>> Poly(x**2 + 1)
49
+ Poly(x**2 + 1, x, domain='ZZ')
50
+ >>> Poly(x**2 + 1, extension=sqrt(2))
51
+ Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
52
+
53
+ It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
54
+ the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
55
+ explicitly.
56
+
57
+ >>> from sympy import factor, QQ
58
+ >>> factor(x**2 - 2)
59
+ x**2 - 2
60
+ >>> factor(x**2 - 2, extension=sqrt(2))
61
+ (x - sqrt(2))*(x + sqrt(2))
62
+ >>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
63
+ (x - sqrt(2))*(x + sqrt(2))
64
+ >>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
65
+ (x - sqrt(2))*(x + sqrt(2))
66
+
67
+ The ``extension=True`` argument can be used but will only create an
68
+ extension that contains the coefficients which is usually not enough to
69
+ factorise the polynomial.
70
+
71
+ >>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
72
+ >>> factor(p) # treats sqrt(2) as a symbol
73
+ (x + sqrt(2))*(x**2 - 2)
74
+ >>> factor(p, extension=True)
75
+ (x - sqrt(2))*(x + sqrt(2))**2
76
+ >>> factor(x**2 - 2, extension=True) # all rational coefficients
77
+ x**2 - 2
78
+
79
+ It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
80
+ and :py:func:`~.gcd` functions.
81
+
82
+ >>> from sympy import cancel, gcd
83
+ >>> cancel((x**2 - 2)/(x - sqrt(2)))
84
+ (x**2 - 2)/(x - sqrt(2))
85
+ >>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
86
+ x + sqrt(2)
87
+ >>> gcd(x**2 - 2, x - sqrt(2))
88
+ 1
89
+ >>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
90
+ x - sqrt(2)
91
+
92
+ When using the domain directly :ref:`QQ(a)` can be used as a constructor
93
+ to create instances which then support the operations ``+,-,*,**,/``. The
94
+ :py:meth:`~.Domain.algebraic_field` method is used to construct a
95
+ particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
96
+ can be used to create domain elements from normal SymPy expressions.
97
+
98
+ >>> K = QQ.algebraic_field(sqrt(2))
99
+ >>> K
100
+ QQ<sqrt(2)>
101
+ >>> xk = K.from_sympy(3 + 4*sqrt(2))
102
+ >>> xk # doctest: +SKIP
103
+ ANP([4, 3], [1, 0, -2], QQ)
104
+
105
+ Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
106
+ limited printing support. The raw display shows the internal
107
+ representation of the element as the list ``[4, 3]`` representing the
108
+ coefficients of ``1`` and ``sqrt(2)`` for this element in the form
109
+ ``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
110
+ The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
111
+ the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
112
+ :py:meth:`~.Domain.to_sympy` to get a better printed form for the
113
+ elements and to see the results of operations.
114
+
115
+ >>> xk = K.from_sympy(3 + 4*sqrt(2))
116
+ >>> yk = K.from_sympy(2 + 3*sqrt(2))
117
+ >>> xk * yk # doctest: +SKIP
118
+ ANP([17, 30], [1, 0, -2], QQ)
119
+ >>> K.to_sympy(xk * yk)
120
+ 17*sqrt(2) + 30
121
+ >>> K.to_sympy(xk + yk)
122
+ 5 + 7*sqrt(2)
123
+ >>> K.to_sympy(xk ** 2)
124
+ 24*sqrt(2) + 41
125
+ >>> K.to_sympy(xk / yk)
126
+ sqrt(2)/14 + 9/7
127
+
128
+ Any expression representing an algebraic number can be used to generate
129
+ a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
130
+ The function :py:func:`~.minpoly` function is used for this.
131
+
132
+ >>> from sympy import exp, I, pi, minpoly
133
+ >>> g = exp(2*I*pi/3)
134
+ >>> g
135
+ exp(2*I*pi/3)
136
+ >>> g.is_algebraic
137
+ True
138
+ >>> minpoly(g, x)
139
+ x**2 + x + 1
140
+ >>> factor(x**3 - 1, extension=g)
141
+ (x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
142
+
143
+ It is also possible to make an algebraic field from multiple extension
144
+ elements.
145
+
146
+ >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
147
+ >>> K
148
+ QQ<sqrt(2) + sqrt(3)>
149
+ >>> p = x**4 - 5*x**2 + 6
150
+ >>> factor(p)
151
+ (x**2 - 3)*(x**2 - 2)
152
+ >>> factor(p, domain=K)
153
+ (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
154
+ >>> factor(p, extension=[sqrt(2), sqrt(3)])
155
+ (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
156
+
157
+ Multiple extension elements are always combined together to make a single
158
+ `primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
159
+ element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
160
+ as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
161
+ element is computed using the :py:func:`~.primitive_element` function.
162
+
163
+ >>> from sympy import primitive_element
164
+ >>> primitive_element([sqrt(2), sqrt(3)], x)
165
+ (x**4 - 10*x**2 + 1, [1, 1])
166
+ >>> minpoly(sqrt(2) + sqrt(3), x)
167
+ x**4 - 10*x**2 + 1
168
+
169
+ The extension elements that generate the domain can be accessed from the
170
+ domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
171
+ instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
172
+ the primitive element as a :py:class:`~.DMP` instance is available as
173
+ :py:attr:`~.mod`.
174
+
175
+ >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
176
+ >>> K
177
+ QQ<sqrt(2) + sqrt(3)>
178
+ >>> K.ext
179
+ sqrt(2) + sqrt(3)
180
+ >>> K.orig_ext
181
+ (sqrt(2), sqrt(3))
182
+ >>> K.mod # doctest: +SKIP
183
+ DMP_Python([1, 0, -10, 0, 1], QQ)
184
+
185
+ The `discriminant`_ of the field can be obtained from the
186
+ :py:meth:`~.discriminant` method, and an `integral basis`_ from the
187
+ :py:meth:`~.integral_basis` method. The latter returns a list of
188
+ :py:class:`~.ANP` instances by default, but can be made to return instances
189
+ of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
190
+ argument. The maximal order, or ring of integers, of the field can also be
191
+ obtained from the :py:meth:`~.maximal_order` method, as a
192
+ :py:class:`~sympy.polys.numberfields.modules.Submodule`.
193
+
194
+ >>> zeta5 = exp(2*I*pi/5)
195
+ >>> K = QQ.algebraic_field(zeta5)
196
+ >>> K
197
+ QQ<exp(2*I*pi/5)>
198
+ >>> K.discriminant()
199
+ 125
200
+ >>> K = QQ.algebraic_field(sqrt(5))
201
+ >>> K
202
+ QQ<sqrt(5)>
203
+ >>> K.integral_basis(fmt='sympy')
204
+ [1, 1/2 + sqrt(5)/2]
205
+ >>> K.maximal_order()
206
+ Submodule[[2, 0], [1, 1]]/2
207
+
208
+ The factorization of a rational prime into prime ideals of the field is
209
+ computed by the :py:meth:`~.primes_above` method, which returns a list
210
+ of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
211
+
212
+ >>> zeta7 = exp(2*I*pi/7)
213
+ >>> K = QQ.algebraic_field(zeta7)
214
+ >>> K
215
+ QQ<exp(2*I*pi/7)>
216
+ >>> K.primes_above(11)
217
+ [(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)]
218
+
219
+ The Galois group of the Galois closure of the field can be computed (when
220
+ the minimal polynomial of the field is of sufficiently small degree).
221
+
222
+ >>> K.galois_group(by_name=True)[0]
223
+ S6TransitiveSubgroups.C6
224
+
225
+ Notes
226
+ =====
227
+
228
+ It is not currently possible to generate an algebraic extension over any
229
+ domain other than :ref:`QQ`. Ideally it would be possible to generate
230
+ extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
231
+ quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
232
+ implementations of this kind of quotient ring/extension in the
233
+ :py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
234
+ classes. Each of those implementations needs some work to make them fully
235
+ usable though.
236
+
237
+ .. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
238
+ .. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
239
+ .. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
240
+ .. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
241
+ .. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
242
+ .. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
243
+ """
244
+
245
+ dtype = ANP
246
+
247
+ is_AlgebraicField = is_Algebraic = True
248
+ is_Numerical = True
249
+
250
+ has_assoc_Ring = False
251
+ has_assoc_Field = True
252
+
253
+ def __init__(self, dom, *ext, alias=None):
254
+ r"""
255
+ Parameters
256
+ ==========
257
+
258
+ dom : :py:class:`~.Domain`
259
+ The base field over which this is an extension field.
260
+ Currently only :ref:`QQ` is accepted.
261
+
262
+ *ext : One or more :py:class:`~.Expr`
263
+ Generators of the extension. These should be expressions that are
264
+ algebraic over `\mathbb{Q}`.
265
+
266
+ alias : str, :py:class:`~.Symbol`, None, optional (default=None)
267
+ If provided, this will be used as the alias symbol for the
268
+ primitive element of the :py:class:`~.AlgebraicField`.
269
+ If ``None``, while ``ext`` consists of exactly one
270
+ :py:class:`~.AlgebraicNumber`, its alias (if any) will be used.
271
+ """
272
+ if not dom.is_QQ:
273
+ raise DomainError("ground domain must be a rational field")
274
+
275
+ from sympy.polys.numberfields import to_number_field
276
+ if len(ext) == 1 and isinstance(ext[0], tuple):
277
+ orig_ext = ext[0][1:]
278
+ else:
279
+ orig_ext = ext
280
+
281
+ if alias is None and len(ext) == 1:
282
+ alias = getattr(ext[0], 'alias', None)
283
+
284
+ self.orig_ext = orig_ext
285
+ """
286
+ Original elements given to generate the extension.
287
+
288
+ >>> from sympy import QQ, sqrt
289
+ >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
290
+ >>> K.orig_ext
291
+ (sqrt(2), sqrt(3))
292
+ """
293
+
294
+ self.ext = to_number_field(ext, alias=alias)
295
+ """
296
+ Primitive element used for the extension.
297
+
298
+ >>> from sympy import QQ, sqrt
299
+ >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
300
+ >>> K.ext
301
+ sqrt(2) + sqrt(3)
302
+ """
303
+
304
+ self.mod = self.ext.minpoly.rep
305
+ """
306
+ Minimal polynomial for the primitive element of the extension.
307
+
308
+ >>> from sympy import QQ, sqrt
309
+ >>> K = QQ.algebraic_field(sqrt(2))
310
+ >>> K.mod
311
+ DMP([1, 0, -2], QQ)
312
+ """
313
+
314
+ self.domain = self.dom = dom
315
+
316
+ self.ngens = 1
317
+ self.symbols = self.gens = (self.ext,)
318
+ self.unit = self([dom(1), dom(0)])
319
+
320
+ self.zero = self.dtype.zero(self.mod.to_list(), dom)
321
+ self.one = self.dtype.one(self.mod.to_list(), dom)
322
+
323
+ self._maximal_order = None
324
+ self._discriminant = None
325
+ self._nilradicals_mod_p = {}
326
+
327
+ def new(self, element):
328
+ return self.dtype(element, self.mod.to_list(), self.dom)
329
+
330
+ def __str__(self):
331
+ return str(self.dom) + '<' + str(self.ext) + '>'
332
+
333
+ def __hash__(self):
334
+ return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
335
+
336
+ def __eq__(self, other):
337
+ """Returns ``True`` if two domains are equivalent. """
338
+ if isinstance(other, AlgebraicField):
339
+ return self.dtype == other.dtype and self.ext == other.ext
340
+ else:
341
+ return NotImplemented
342
+
343
+ def algebraic_field(self, *extension, alias=None):
344
+ r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
345
+ return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias)
346
+
347
+ def to_alg_num(self, a):
348
+ """Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
349
+ return self.ext.field_element(a)
350
+
351
+ def to_sympy(self, a):
352
+ """Convert ``a`` of ``dtype`` to a SymPy object. """
353
+ # Precompute a converter to be reused:
354
+ if not hasattr(self, '_converter'):
355
+ self._converter = _make_converter(self)
356
+
357
+ return self._converter(a)
358
+
359
+ def from_sympy(self, a):
360
+ """Convert SymPy's expression to ``dtype``. """
361
+ try:
362
+ return self([self.dom.from_sympy(a)])
363
+ except CoercionFailed:
364
+ pass
365
+
366
+ from sympy.polys.numberfields import to_number_field
367
+
368
+ try:
369
+ return self(to_number_field(a, self.ext).native_coeffs())
370
+ except (NotAlgebraic, IsomorphismFailed):
371
+ raise CoercionFailed(
372
+ "%s is not a valid algebraic number in %s" % (a, self))
373
+
374
+ def from_ZZ(K1, a, K0):
375
+ """Convert a Python ``int`` object to ``dtype``. """
376
+ return K1(K1.dom.convert(a, K0))
377
+
378
+ def from_ZZ_python(K1, a, K0):
379
+ """Convert a Python ``int`` object to ``dtype``. """
380
+ return K1(K1.dom.convert(a, K0))
381
+
382
+ def from_QQ(K1, a, K0):
383
+ """Convert a Python ``Fraction`` object to ``dtype``. """
384
+ return K1(K1.dom.convert(a, K0))
385
+
386
+ def from_QQ_python(K1, a, K0):
387
+ """Convert a Python ``Fraction`` object to ``dtype``. """
388
+ return K1(K1.dom.convert(a, K0))
389
+
390
+ def from_ZZ_gmpy(K1, a, K0):
391
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
392
+ return K1(K1.dom.convert(a, K0))
393
+
394
+ def from_QQ_gmpy(K1, a, K0):
395
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
396
+ return K1(K1.dom.convert(a, K0))
397
+
398
+ def from_RealField(K1, a, K0):
399
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
400
+ return K1(K1.dom.convert(a, K0))
401
+
402
+ def get_ring(self):
403
+ """Returns a ring associated with ``self``. """
404
+ raise DomainError('there is no ring associated with %s' % self)
405
+
406
+ def is_positive(self, a):
407
+ """Returns True if ``a`` is positive. """
408
+ return self.dom.is_positive(a.LC())
409
+
410
+ def is_negative(self, a):
411
+ """Returns True if ``a`` is negative. """
412
+ return self.dom.is_negative(a.LC())
413
+
414
+ def is_nonpositive(self, a):
415
+ """Returns True if ``a`` is non-positive. """
416
+ return self.dom.is_nonpositive(a.LC())
417
+
418
+ def is_nonnegative(self, a):
419
+ """Returns True if ``a`` is non-negative. """
420
+ return self.dom.is_nonnegative(a.LC())
421
+
422
+ def numer(self, a):
423
+ """Returns numerator of ``a``. """
424
+ return a
425
+
426
+ def denom(self, a):
427
+ """Returns denominator of ``a``. """
428
+ return self.one
429
+
430
+ def from_AlgebraicField(K1, a, K0):
431
+ """Convert AlgebraicField element 'a' to another AlgebraicField """
432
+ return K1.from_sympy(K0.to_sympy(a))
433
+
434
+ def from_GaussianIntegerRing(K1, a, K0):
435
+ """Convert a GaussianInteger element 'a' to ``dtype``. """
436
+ return K1.from_sympy(K0.to_sympy(a))
437
+
438
+ def from_GaussianRationalField(K1, a, K0):
439
+ """Convert a GaussianRational element 'a' to ``dtype``. """
440
+ return K1.from_sympy(K0.to_sympy(a))
441
+
442
+ def _do_round_two(self):
443
+ from sympy.polys.numberfields.basis import round_two
444
+ ZK, dK = round_two(self, radicals=self._nilradicals_mod_p)
445
+ self._maximal_order = ZK
446
+ self._discriminant = dK
447
+
448
+ def maximal_order(self):
449
+ """
450
+ Compute the maximal order, or ring of integers, of the field.
451
+
452
+ Returns
453
+ =======
454
+
455
+ :py:class:`~sympy.polys.numberfields.modules.Submodule`.
456
+
457
+ See Also
458
+ ========
459
+
460
+ integral_basis
461
+
462
+ """
463
+ if self._maximal_order is None:
464
+ self._do_round_two()
465
+ return self._maximal_order
466
+
467
+ def integral_basis(self, fmt=None):
468
+ r"""
469
+ Get an integral basis for the field.
470
+
471
+ Parameters
472
+ ==========
473
+
474
+ fmt : str, None, optional (default=None)
475
+ If ``None``, return a list of :py:class:`~.ANP` instances.
476
+ If ``"sympy"``, convert each element of the list to an
477
+ :py:class:`~.Expr`, using ``self.to_sympy()``.
478
+ If ``"alg"``, convert each element of the list to an
479
+ :py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
480
+
481
+ Examples
482
+ ========
483
+
484
+ >>> from sympy import QQ, AlgebraicNumber, sqrt
485
+ >>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
486
+ >>> k = QQ.algebraic_field(alpha)
487
+ >>> B0 = k.integral_basis()
488
+ >>> B1 = k.integral_basis(fmt='sympy')
489
+ >>> B2 = k.integral_basis(fmt='alg')
490
+ >>> print(B0[1]) # doctest: +SKIP
491
+ ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
492
+ >>> print(B1[1])
493
+ 1/2 + alpha/2
494
+ >>> print(B2[1])
495
+ alpha/2 + 1/2
496
+
497
+ In the last two cases we get legible expressions, which print somewhat
498
+ differently because of the different types involved:
499
+
500
+ >>> print(type(B1[1]))
501
+ <class 'sympy.core.add.Add'>
502
+ >>> print(type(B2[1]))
503
+ <class 'sympy.core.numbers.AlgebraicNumber'>
504
+
505
+ See Also
506
+ ========
507
+
508
+ to_sympy
509
+ to_alg_num
510
+ maximal_order
511
+ """
512
+ ZK = self.maximal_order()
513
+ M = ZK.QQ_matrix
514
+ n = M.shape[1]
515
+ B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
516
+ if fmt == 'sympy':
517
+ return [self.to_sympy(b) for b in B]
518
+ elif fmt == 'alg':
519
+ return [self.to_alg_num(b) for b in B]
520
+ return B
521
+
522
+ def discriminant(self):
523
+ """Get the discriminant of the field."""
524
+ if self._discriminant is None:
525
+ self._do_round_two()
526
+ return self._discriminant
527
+
528
+ def primes_above(self, p):
529
+ """Compute the prime ideals lying above a given rational prime *p*."""
530
+ from sympy.polys.numberfields.primes import prime_decomp
531
+ ZK = self.maximal_order()
532
+ dK = self.discriminant()
533
+ rad = self._nilradicals_mod_p.get(p)
534
+ return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
535
+
536
+ def galois_group(self, by_name=False, max_tries=30, randomize=False):
537
+ """
538
+ Compute the Galois group of the Galois closure of this field.
539
+
540
+ Examples
541
+ ========
542
+
543
+ If the field is Galois, the order of the group will equal the degree
544
+ of the field:
545
+
546
+ >>> from sympy import QQ
547
+ >>> from sympy.abc import x
548
+ >>> k = QQ.alg_field_from_poly(x**4 + 1)
549
+ >>> G, _ = k.galois_group()
550
+ >>> G.order()
551
+ 4
552
+
553
+ If the field is not Galois, then its Galois closure is a proper
554
+ extension, and the order of the Galois group will be greater than the
555
+ degree of the field:
556
+
557
+ >>> k = QQ.alg_field_from_poly(x**4 - 2)
558
+ >>> G, _ = k.galois_group()
559
+ >>> G.order()
560
+ 8
561
+
562
+ See Also
563
+ ========
564
+
565
+ sympy.polys.numberfields.galoisgroups.galois_group
566
+
567
+ """
568
+ return self.ext.minpoly_of_element().galois_group(
569
+ by_name=by_name, max_tries=max_tries, randomize=randomize)
570
+
571
+
572
+ def _make_converter(K):
573
+ """Construct the converter to convert back to Expr"""
574
+ # Precompute the effect of converting to SymPy and expanding expressions
575
+ # like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
576
+ # conversion from K to Expr is slow. Here we compute the expansions for
577
+ # each power of the generator and collect together the resulting algebraic
578
+ # terms and the rational coefficients into a matrix.
579
+
580
+ ext = K.ext.as_expr()
581
+ todom = K.dom.from_sympy
582
+ toexpr = K.dom.to_sympy
583
+
584
+ if not ext.is_Add:
585
+ powers = [ext**n for n in range(K.mod.degree())]
586
+ else:
587
+ # primitive_element generates a QQ-linear combination of lower degree
588
+ # algebraic numbers to generate the higher degree extension e.g.
589
+ # QQ<sqrt(2)+sqrt(3)> That means that we end up having high powers of low
590
+ # degree algebraic numbers that can be reduced. Here we will use the
591
+ # minimal polynomials of the algebraic numbers to reduce those powers
592
+ # before converting to Expr.
593
+ from sympy.polys.numberfields.minpoly import minpoly
594
+
595
+ # Decompose ext as a linear combination of gens and make a symbol for
596
+ # each gen.
597
+ gens, coeffs = zip(*ext.as_coefficients_dict().items())
598
+ syms = symbols(f'a:{len(gens)}', cls=Dummy)
599
+ sym2gen = dict(zip(syms, gens))
600
+
601
+ # Make a polynomial ring that can express ext and minpolys of all gens
602
+ # in terms of syms.
603
+ R = K.dom[syms]
604
+ monoms = [R.ring.monomial_basis(i) for i in range(R.ngens)]
605
+ ext_dict = {m: todom(c) for m, c in zip(monoms, coeffs)}
606
+ ext_poly = R.ring.from_dict(ext_dict)
607
+ minpolys = [R.from_sympy(minpoly(g, s)) for s, g in sym2gen.items()]
608
+
609
+ # Compute all powers of ext_poly reduced modulo minpolys
610
+ powers = [R.one, ext_poly]
611
+ for n in range(2, K.mod.degree()):
612
+ ext_poly_n = (powers[-1] * ext_poly).rem(minpolys)
613
+ powers.append(ext_poly_n)
614
+
615
+ # Convert the powers back to Expr. This will recombine some things like
616
+ # sqrt(2)*sqrt(3) -> sqrt(6).
617
+ powers = [p.as_expr().xreplace(sym2gen) for p in powers]
618
+
619
+ # This also expands some rational powers
620
+ powers = [p.expand() for p in powers]
621
+
622
+ # Collect the rational coefficients and algebraic Expr that can
623
+ # map the ANP coefficients into an expanded SymPy expression
624
+ terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
625
+ algebraics = set().union(*terms)
626
+ matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
627
+
628
+ # Create a function to do the conversion efficiently:
629
+
630
+ def converter(a):
631
+ """Convert a to Expr using converter"""
632
+ ai = a.to_list()[::-1]
633
+ coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
634
+ coeffs_sympy = [toexpr(c) for c in coeffs_dom]
635
+ res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
636
+ return res
637
+
638
+ return converter
.venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`CharacteristicZero` class. """
2
+
3
+
4
+ from sympy.polys.domains.domain import Domain
5
+ from sympy.utilities import public
6
+
7
+ @public
8
+ class CharacteristicZero(Domain):
9
+ """Domain that has infinite number of elements. """
10
+
11
+ has_CharacteristicZero = True
12
+
13
+ def characteristic(self):
14
+ """Return the characteristic of this domain. """
15
+ return 0
.venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`ComplexField` class. """
2
+
3
+
4
+ from sympy.external.gmpy import SYMPY_INTS
5
+ from sympy.core.numbers import Float, I
6
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
7
+ from sympy.polys.domains.field import Field
8
+ from sympy.polys.domains.gaussiandomains import QQ_I
9
+ from sympy.polys.domains.simpledomain import SimpleDomain
10
+ from sympy.polys.polyerrors import DomainError, CoercionFailed
11
+ from sympy.utilities import public
12
+
13
+ from mpmath import MPContext
14
+
15
+
16
+ @public
17
+ class ComplexField(Field, CharacteristicZero, SimpleDomain):
18
+ """Complex numbers up to the given precision. """
19
+
20
+ rep = 'CC'
21
+
22
+ is_ComplexField = is_CC = True
23
+
24
+ is_Exact = False
25
+ is_Numerical = True
26
+
27
+ has_assoc_Ring = False
28
+ has_assoc_Field = True
29
+
30
+ _default_precision = 53
31
+
32
+ @property
33
+ def has_default_precision(self):
34
+ return self.precision == self._default_precision
35
+
36
+ @property
37
+ def precision(self):
38
+ return self._context.prec
39
+
40
+ @property
41
+ def dps(self):
42
+ return self._context.dps
43
+
44
+ @property
45
+ def tolerance(self):
46
+ return self._tolerance
47
+
48
+ def __init__(self, prec=None, dps=None, tol=None):
49
+ # XXX: The tolerance parameter is ignored but is kept for backward
50
+ # compatibility for now.
51
+
52
+ context = MPContext()
53
+
54
+ if prec is None and dps is None:
55
+ context.prec = self._default_precision
56
+ elif dps is None:
57
+ context.prec = prec
58
+ elif prec is None:
59
+ context.dps = dps
60
+ else:
61
+ raise TypeError("Cannot set both prec and dps")
62
+
63
+ self._context = context
64
+
65
+ self._dtype = context.mpc
66
+ self.zero = self.dtype(0)
67
+ self.one = self.dtype(1)
68
+
69
+ # XXX: Neither of these is actually used anywhere.
70
+ self._max_denom = max(2**context.prec // 200, 99)
71
+ self._tolerance = self.one / self._max_denom
72
+
73
+ @property
74
+ def tp(self):
75
+ # XXX: Domain treats tp as an alias of dtype. Here we need two separate
76
+ # things: dtype is a callable to make/convert instances. We use tp with
77
+ # isinstance to check if an object is an instance of the domain
78
+ # already.
79
+ return self._dtype
80
+
81
+ def dtype(self, x, y=0):
82
+ # XXX: This is needed because mpmath does not recognise fmpz.
83
+ # It might be better to add conversion routines to mpmath and if that
84
+ # happens then this can be removed.
85
+ if isinstance(x, SYMPY_INTS):
86
+ x = int(x)
87
+ if isinstance(y, SYMPY_INTS):
88
+ y = int(y)
89
+ return self._dtype(x, y)
90
+
91
+ def __eq__(self, other):
92
+ return isinstance(other, ComplexField) and self.precision == other.precision
93
+
94
+ def __hash__(self):
95
+ return hash((self.__class__.__name__, self._dtype, self.precision))
96
+
97
+ def to_sympy(self, element):
98
+ """Convert ``element`` to SymPy number. """
99
+ return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
100
+
101
+ def from_sympy(self, expr):
102
+ """Convert SymPy's number to ``dtype``. """
103
+ number = expr.evalf(n=self.dps)
104
+ real, imag = number.as_real_imag()
105
+
106
+ if real.is_Number and imag.is_Number:
107
+ return self.dtype(real, imag)
108
+ else:
109
+ raise CoercionFailed("expected complex number, got %s" % expr)
110
+
111
+ def from_ZZ(self, element, base):
112
+ return self.dtype(element)
113
+
114
+ def from_ZZ_gmpy(self, element, base):
115
+ return self.dtype(int(element))
116
+
117
+ def from_ZZ_python(self, element, base):
118
+ return self.dtype(element)
119
+
120
+ def from_QQ(self, element, base):
121
+ return self.dtype(int(element.numerator)) / int(element.denominator)
122
+
123
+ def from_QQ_python(self, element, base):
124
+ return self.dtype(element.numerator) / element.denominator
125
+
126
+ def from_QQ_gmpy(self, element, base):
127
+ return self.dtype(int(element.numerator)) / int(element.denominator)
128
+
129
+ def from_GaussianIntegerRing(self, element, base):
130
+ return self.dtype(int(element.x), int(element.y))
131
+
132
+ def from_GaussianRationalField(self, element, base):
133
+ x = element.x
134
+ y = element.y
135
+ return (self.dtype(int(x.numerator)) / int(x.denominator) +
136
+ self.dtype(0, int(y.numerator)) / int(y.denominator))
137
+
138
+ def from_AlgebraicField(self, element, base):
139
+ return self.from_sympy(base.to_sympy(element).evalf(self.dps))
140
+
141
+ def from_RealField(self, element, base):
142
+ return self.dtype(element)
143
+
144
+ def from_ComplexField(self, element, base):
145
+ return self.dtype(element)
146
+
147
+ def get_ring(self):
148
+ """Returns a ring associated with ``self``. """
149
+ raise DomainError("there is no ring associated with %s" % self)
150
+
151
+ def get_exact(self):
152
+ """Returns an exact domain associated with ``self``. """
153
+ return QQ_I
154
+
155
+ def is_negative(self, element):
156
+ """Returns ``False`` for any ``ComplexElement``. """
157
+ return False
158
+
159
+ def is_positive(self, element):
160
+ """Returns ``False`` for any ``ComplexElement``. """
161
+ return False
162
+
163
+ def is_nonnegative(self, element):
164
+ """Returns ``False`` for any ``ComplexElement``. """
165
+ return False
166
+
167
+ def is_nonpositive(self, element):
168
+ """Returns ``False`` for any ``ComplexElement``. """
169
+ return False
170
+
171
+ def gcd(self, a, b):
172
+ """Returns GCD of ``a`` and ``b``. """
173
+ return self.one
174
+
175
+ def lcm(self, a, b):
176
+ """Returns LCM of ``a`` and ``b``. """
177
+ return a*b
178
+
179
+ def almosteq(self, a, b, tolerance=None):
180
+ """Check if ``a`` and ``b`` are almost equal. """
181
+ return self._context.almosteq(a, b, tolerance)
182
+
183
+ def is_square(self, a):
184
+ """Returns ``True``. Every complex number has a complex square root."""
185
+ return True
186
+
187
+ def exsqrt(self, a):
188
+ r"""Returns the principal complex square root of ``a``.
189
+
190
+ Explanation
191
+ ===========
192
+ The argument of the principal square root is always within
193
+ $(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be
194
+ slightly inaccurate due to floating point rounding error.
195
+ """
196
+ return a ** 0.5
197
+
198
+ CC = ComplexField()
.venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`CompositeDomain` class. """
2
+
3
+
4
+ from sympy.polys.domains.domain import Domain
5
+ from sympy.polys.polyerrors import GeneratorsError
6
+
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class CompositeDomain(Domain):
11
+ """Base class for composite domains, e.g. ZZ[x], ZZ(X). """
12
+
13
+ is_Composite = True
14
+
15
+ gens, ngens, symbols, domain = [None]*4
16
+
17
+ def inject(self, *symbols):
18
+ """Inject generators into this domain. """
19
+ if not (set(self.symbols) & set(symbols)):
20
+ return self.__class__(self.domain, self.symbols + symbols, self.order)
21
+ else:
22
+ raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols))
23
+
24
+ def drop(self, *symbols):
25
+ """Drop generators from this domain. """
26
+ symset = set(symbols)
27
+ newsyms = tuple(s for s in self.symbols if s not in symset)
28
+ domain = self.domain.drop(*symbols)
29
+ if not newsyms:
30
+ return domain
31
+ else:
32
+ return self.__class__(domain, newsyms, self.order)
33
+
34
+ def set_domain(self, domain):
35
+ """Set the ground domain of this domain. """
36
+ return self.__class__(domain, self.symbols, self.order)
37
+
38
+ @property
39
+ def is_Exact(self):
40
+ """Returns ``True`` if this domain is exact. """
41
+ return self.domain.is_Exact
42
+
43
+ def get_exact(self):
44
+ """Returns an exact version of this domain. """
45
+ return self.set_domain(self.domain.get_exact())
46
+
47
+ @property
48
+ def has_CharacteristicZero(self):
49
+ return self.domain.has_CharacteristicZero
50
+
51
+ def characteristic(self):
52
+ return self.domain.characteristic()
.venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py ADDED
@@ -0,0 +1,1382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`Domain` class. """
2
+
3
+ from __future__ import annotations
4
+ from typing import Any
5
+
6
+ from sympy.core.numbers import AlgebraicNumber
7
+ from sympy.core import Basic, sympify
8
+ from sympy.core.sorting import ordered
9
+ from sympy.external.gmpy import GROUND_TYPES
10
+ from sympy.polys.domains.domainelement import DomainElement
11
+ from sympy.polys.orderings import lex
12
+ from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError
13
+ from sympy.polys.polyutils import _unify_gens, _not_a_coeff
14
+ from sympy.utilities import public
15
+ from sympy.utilities.iterables import is_sequence
16
+
17
+
18
+ @public
19
+ class Domain:
20
+ """Superclass for all domains in the polys domains system.
21
+
22
+ See :ref:`polys-domainsintro` for an introductory explanation of the
23
+ domains system.
24
+
25
+ The :py:class:`~.Domain` class is an abstract base class for all of the
26
+ concrete domain types. There are many different :py:class:`~.Domain`
27
+ subclasses each of which has an associated ``dtype`` which is a class
28
+ representing the elements of the domain. The coefficients of a
29
+ :py:class:`~.Poly` are elements of a domain which must be a subclass of
30
+ :py:class:`~.Domain`.
31
+
32
+ Examples
33
+ ========
34
+
35
+ The most common example domains are the integers :ref:`ZZ` and the
36
+ rationals :ref:`QQ`.
37
+
38
+ >>> from sympy import Poly, symbols, Domain
39
+ >>> x, y = symbols('x, y')
40
+ >>> p = Poly(x**2 + y)
41
+ >>> p
42
+ Poly(x**2 + y, x, y, domain='ZZ')
43
+ >>> p.domain
44
+ ZZ
45
+ >>> isinstance(p.domain, Domain)
46
+ True
47
+ >>> Poly(x**2 + y/2)
48
+ Poly(x**2 + 1/2*y, x, y, domain='QQ')
49
+
50
+ The domains can be used directly in which case the domain object e.g.
51
+ (:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of
52
+ ``dtype``.
53
+
54
+ >>> from sympy import ZZ, QQ
55
+ >>> ZZ(2)
56
+ 2
57
+ >>> ZZ.dtype # doctest: +SKIP
58
+ <class 'int'>
59
+ >>> type(ZZ(2)) # doctest: +SKIP
60
+ <class 'int'>
61
+ >>> QQ(1, 2)
62
+ 1/2
63
+ >>> type(QQ(1, 2)) # doctest: +SKIP
64
+ <class 'sympy.polys.domains.pythonrational.PythonRational'>
65
+
66
+ The corresponding domain elements can be used with the arithmetic
67
+ operations ``+,-,*,**`` and depending on the domain some combination of
68
+ ``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor
69
+ division) and ``%`` (modulo division) can be used but ``/`` (true
70
+ division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements
71
+ can be used with ``/`` but ``//`` and ``%`` should not be used. Some
72
+ domains have a :py:meth:`~.Domain.gcd` method.
73
+
74
+ >>> ZZ(2) + ZZ(3)
75
+ 5
76
+ >>> ZZ(5) // ZZ(2)
77
+ 2
78
+ >>> ZZ(5) % ZZ(2)
79
+ 1
80
+ >>> QQ(1, 2) / QQ(2, 3)
81
+ 3/4
82
+ >>> ZZ.gcd(ZZ(4), ZZ(2))
83
+ 2
84
+ >>> QQ.gcd(QQ(2,7), QQ(5,3))
85
+ 1/21
86
+ >>> ZZ.is_Field
87
+ False
88
+ >>> QQ.is_Field
89
+ True
90
+
91
+ There are also many other domains including:
92
+
93
+ 1. :ref:`GF(p)` for finite fields of prime order.
94
+ 2. :ref:`RR` for real (floating point) numbers.
95
+ 3. :ref:`CC` for complex (floating point) numbers.
96
+ 4. :ref:`QQ(a)` for algebraic number fields.
97
+ 5. :ref:`K[x]` for polynomial rings.
98
+ 6. :ref:`K(x)` for rational function fields.
99
+ 7. :ref:`EX` for arbitrary expressions.
100
+
101
+ Each domain is represented by a domain object and also an implementation
102
+ class (``dtype``) for the elements of the domain. For example the
103
+ :ref:`K[x]` domains are represented by a domain object which is an
104
+ instance of :py:class:`~.PolynomialRing` and the elements are always
105
+ instances of :py:class:`~.PolyElement`. The implementation class
106
+ represents particular types of mathematical expressions in a way that is
107
+ more efficient than a normal SymPy expression which is of type
108
+ :py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and
109
+ :py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr`
110
+ to a domain element and vice versa.
111
+
112
+ >>> from sympy import Symbol, ZZ, Expr
113
+ >>> x = Symbol('x')
114
+ >>> K = ZZ[x] # polynomial ring domain
115
+ >>> K
116
+ ZZ[x]
117
+ >>> type(K) # class of the domain
118
+ <class 'sympy.polys.domains.polynomialring.PolynomialRing'>
119
+ >>> K.dtype # doctest: +SKIP
120
+ <class 'sympy.polys.rings.PolyElement'>
121
+ >>> p_expr = x**2 + 1 # Expr
122
+ >>> p_expr
123
+ x**2 + 1
124
+ >>> type(p_expr)
125
+ <class 'sympy.core.add.Add'>
126
+ >>> isinstance(p_expr, Expr)
127
+ True
128
+ >>> p_domain = K.from_sympy(p_expr)
129
+ >>> p_domain # domain element
130
+ x**2 + 1
131
+ >>> type(p_domain)
132
+ <class 'sympy.polys.rings.PolyElement'>
133
+ >>> K.to_sympy(p_domain) == p_expr
134
+ True
135
+
136
+ The :py:meth:`~.Domain.convert_from` method is used to convert domain
137
+ elements from one domain to another.
138
+
139
+ >>> from sympy import ZZ, QQ
140
+ >>> ez = ZZ(2)
141
+ >>> eq = QQ.convert_from(ez, ZZ)
142
+ >>> type(ez) # doctest: +SKIP
143
+ <class 'int'>
144
+ >>> type(eq) # doctest: +SKIP
145
+ <class 'sympy.polys.domains.pythonrational.PythonRational'>
146
+
147
+ Elements from different domains should not be mixed in arithmetic or other
148
+ operations: they should be converted to a common domain first. The domain
149
+ method :py:meth:`~.Domain.unify` is used to find a domain that can
150
+ represent all the elements of two given domains.
151
+
152
+ >>> from sympy import ZZ, QQ, symbols
153
+ >>> x, y = symbols('x, y')
154
+ >>> ZZ.unify(QQ)
155
+ QQ
156
+ >>> ZZ[x].unify(QQ)
157
+ QQ[x]
158
+ >>> ZZ[x].unify(QQ[y])
159
+ QQ[x,y]
160
+
161
+ If a domain is a :py:class:`~.Ring` then is might have an associated
162
+ :py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and
163
+ :py:meth:`~.Domain.get_ring` methods will find or create the associated
164
+ domain.
165
+
166
+ >>> from sympy import ZZ, QQ, Symbol
167
+ >>> x = Symbol('x')
168
+ >>> ZZ.has_assoc_Field
169
+ True
170
+ >>> ZZ.get_field()
171
+ QQ
172
+ >>> QQ.has_assoc_Ring
173
+ True
174
+ >>> QQ.get_ring()
175
+ ZZ
176
+ >>> K = QQ[x]
177
+ >>> K
178
+ QQ[x]
179
+ >>> K.get_field()
180
+ QQ(x)
181
+
182
+ See also
183
+ ========
184
+
185
+ DomainElement: abstract base class for domain elements
186
+ construct_domain: construct a minimal domain for some expressions
187
+
188
+ """
189
+
190
+ dtype: type | None = None
191
+ """The type (class) of the elements of this :py:class:`~.Domain`:
192
+
193
+ >>> from sympy import ZZ, QQ, Symbol
194
+ >>> ZZ.dtype
195
+ <class 'int'>
196
+ >>> z = ZZ(2)
197
+ >>> z
198
+ 2
199
+ >>> type(z)
200
+ <class 'int'>
201
+ >>> type(z) == ZZ.dtype
202
+ True
203
+
204
+ Every domain has an associated **dtype** ("datatype") which is the
205
+ class of the associated domain elements.
206
+
207
+ See also
208
+ ========
209
+
210
+ of_type
211
+ """
212
+
213
+ zero: Any = None
214
+ """The zero element of the :py:class:`~.Domain`:
215
+
216
+ >>> from sympy import QQ
217
+ >>> QQ.zero
218
+ 0
219
+ >>> QQ.of_type(QQ.zero)
220
+ True
221
+
222
+ See also
223
+ ========
224
+
225
+ of_type
226
+ one
227
+ """
228
+
229
+ one: Any = None
230
+ """The one element of the :py:class:`~.Domain`:
231
+
232
+ >>> from sympy import QQ
233
+ >>> QQ.one
234
+ 1
235
+ >>> QQ.of_type(QQ.one)
236
+ True
237
+
238
+ See also
239
+ ========
240
+
241
+ of_type
242
+ zero
243
+ """
244
+
245
+ is_Ring = False
246
+ """Boolean flag indicating if the domain is a :py:class:`~.Ring`.
247
+
248
+ >>> from sympy import ZZ
249
+ >>> ZZ.is_Ring
250
+ True
251
+
252
+ Basically every :py:class:`~.Domain` represents a ring so this flag is
253
+ not that useful.
254
+
255
+ See also
256
+ ========
257
+
258
+ is_PID
259
+ is_Field
260
+ get_ring
261
+ has_assoc_Ring
262
+ """
263
+
264
+ is_Field = False
265
+ """Boolean flag indicating if the domain is a :py:class:`~.Field`.
266
+
267
+ >>> from sympy import ZZ, QQ
268
+ >>> ZZ.is_Field
269
+ False
270
+ >>> QQ.is_Field
271
+ True
272
+
273
+ See also
274
+ ========
275
+
276
+ is_PID
277
+ is_Ring
278
+ get_field
279
+ has_assoc_Field
280
+ """
281
+
282
+ has_assoc_Ring = False
283
+ """Boolean flag indicating if the domain has an associated
284
+ :py:class:`~.Ring`.
285
+
286
+ >>> from sympy import QQ
287
+ >>> QQ.has_assoc_Ring
288
+ True
289
+ >>> QQ.get_ring()
290
+ ZZ
291
+
292
+ See also
293
+ ========
294
+
295
+ is_Field
296
+ get_ring
297
+ """
298
+
299
+ has_assoc_Field = False
300
+ """Boolean flag indicating if the domain has an associated
301
+ :py:class:`~.Field`.
302
+
303
+ >>> from sympy import ZZ
304
+ >>> ZZ.has_assoc_Field
305
+ True
306
+ >>> ZZ.get_field()
307
+ QQ
308
+
309
+ See also
310
+ ========
311
+
312
+ is_Field
313
+ get_field
314
+ """
315
+
316
+ is_FiniteField = is_FF = False
317
+ is_IntegerRing = is_ZZ = False
318
+ is_RationalField = is_QQ = False
319
+ is_GaussianRing = is_ZZ_I = False
320
+ is_GaussianField = is_QQ_I = False
321
+ is_RealField = is_RR = False
322
+ is_ComplexField = is_CC = False
323
+ is_AlgebraicField = is_Algebraic = False
324
+ is_PolynomialRing = is_Poly = False
325
+ is_FractionField = is_Frac = False
326
+ is_SymbolicDomain = is_EX = False
327
+ is_SymbolicRawDomain = is_EXRAW = False
328
+ is_FiniteExtension = False
329
+
330
+ is_Exact = True
331
+ is_Numerical = False
332
+
333
+ is_Simple = False
334
+ is_Composite = False
335
+
336
+ is_PID = False
337
+ """Boolean flag indicating if the domain is a `principal ideal domain`_.
338
+
339
+ >>> from sympy import ZZ
340
+ >>> ZZ.has_assoc_Field
341
+ True
342
+ >>> ZZ.get_field()
343
+ QQ
344
+
345
+ .. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain
346
+
347
+ See also
348
+ ========
349
+
350
+ is_Field
351
+ get_field
352
+ """
353
+
354
+ has_CharacteristicZero = False
355
+
356
+ rep: str | None = None
357
+ alias: str | None = None
358
+
359
+ def __init__(self):
360
+ raise NotImplementedError
361
+
362
+ def __str__(self):
363
+ return self.rep
364
+
365
+ def __repr__(self):
366
+ return str(self)
367
+
368
+ def __hash__(self):
369
+ return hash((self.__class__.__name__, self.dtype))
370
+
371
+ def new(self, *args):
372
+ return self.dtype(*args)
373
+
374
+ @property
375
+ def tp(self):
376
+ """Alias for :py:attr:`~.Domain.dtype`"""
377
+ return self.dtype
378
+
379
+ def __call__(self, *args):
380
+ """Construct an element of ``self`` domain from ``args``. """
381
+ return self.new(*args)
382
+
383
+ def normal(self, *args):
384
+ return self.dtype(*args)
385
+
386
+ def convert_from(self, element, base):
387
+ """Convert ``element`` to ``self.dtype`` given the base domain. """
388
+ if base.alias is not None:
389
+ method = "from_" + base.alias
390
+ else:
391
+ method = "from_" + base.__class__.__name__
392
+
393
+ _convert = getattr(self, method)
394
+
395
+ if _convert is not None:
396
+ result = _convert(element, base)
397
+
398
+ if result is not None:
399
+ return result
400
+
401
+ raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self))
402
+
403
+ def convert(self, element, base=None):
404
+ """Convert ``element`` to ``self.dtype``. """
405
+
406
+ if base is not None:
407
+ if _not_a_coeff(element):
408
+ raise CoercionFailed('%s is not in any domain' % element)
409
+ return self.convert_from(element, base)
410
+
411
+ if self.of_type(element):
412
+ return element
413
+
414
+ if _not_a_coeff(element):
415
+ raise CoercionFailed('%s is not in any domain' % element)
416
+
417
+ from sympy.polys.domains import ZZ, QQ, RealField, ComplexField
418
+
419
+ if ZZ.of_type(element):
420
+ return self.convert_from(element, ZZ)
421
+
422
+ if isinstance(element, int):
423
+ return self.convert_from(ZZ(element), ZZ)
424
+
425
+ if GROUND_TYPES != 'python':
426
+ if isinstance(element, ZZ.tp):
427
+ return self.convert_from(element, ZZ)
428
+ if isinstance(element, QQ.tp):
429
+ return self.convert_from(element, QQ)
430
+
431
+ if isinstance(element, float):
432
+ parent = RealField()
433
+ return self.convert_from(parent(element), parent)
434
+
435
+ if isinstance(element, complex):
436
+ parent = ComplexField()
437
+ return self.convert_from(parent(element), parent)
438
+
439
+ if type(element).__name__ == 'mpf':
440
+ parent = RealField()
441
+ return self.convert_from(parent(element), parent)
442
+
443
+ if type(element).__name__ == 'mpc':
444
+ parent = ComplexField()
445
+ return self.convert_from(parent(element), parent)
446
+
447
+ if isinstance(element, DomainElement):
448
+ return self.convert_from(element, element.parent())
449
+
450
+ # TODO: implement this in from_ methods
451
+ if self.is_Numerical and getattr(element, 'is_ground', False):
452
+ return self.convert(element.LC())
453
+
454
+ if isinstance(element, Basic):
455
+ try:
456
+ return self.from_sympy(element)
457
+ except (TypeError, ValueError):
458
+ pass
459
+ else: # TODO: remove this branch
460
+ if not is_sequence(element):
461
+ try:
462
+ element = sympify(element, strict=True)
463
+ if isinstance(element, Basic):
464
+ return self.from_sympy(element)
465
+ except (TypeError, ValueError):
466
+ pass
467
+
468
+ raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self))
469
+
470
+ def of_type(self, element):
471
+ """Check if ``a`` is of type ``dtype``. """
472
+ return isinstance(element, self.tp)
473
+
474
+ def __contains__(self, a):
475
+ """Check if ``a`` belongs to this domain. """
476
+ try:
477
+ if _not_a_coeff(a):
478
+ raise CoercionFailed
479
+ self.convert(a) # this might raise, too
480
+ except CoercionFailed:
481
+ return False
482
+
483
+ return True
484
+
485
+ def to_sympy(self, a):
486
+ """Convert domain element *a* to a SymPy expression (Expr).
487
+
488
+ Explanation
489
+ ===========
490
+
491
+ Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most
492
+ public SymPy functions work with objects of type :py:class:`~.Expr`.
493
+ The elements of a :py:class:`~.Domain` have a different internal
494
+ representation. It is not possible to mix domain elements with
495
+ :py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and
496
+ :py:meth:`~.Domain.from_sympy` methods to convert its domain elements
497
+ to and from :py:class:`~.Expr`.
498
+
499
+ Parameters
500
+ ==========
501
+
502
+ a: domain element
503
+ An element of this :py:class:`~.Domain`.
504
+
505
+ Returns
506
+ =======
507
+
508
+ expr: Expr
509
+ A normal SymPy expression of type :py:class:`~.Expr`.
510
+
511
+ Examples
512
+ ========
513
+
514
+ Construct an element of the :ref:`QQ` domain and then convert it to
515
+ :py:class:`~.Expr`.
516
+
517
+ >>> from sympy import QQ, Expr
518
+ >>> q_domain = QQ(2)
519
+ >>> q_domain
520
+ 2
521
+ >>> q_expr = QQ.to_sympy(q_domain)
522
+ >>> q_expr
523
+ 2
524
+
525
+ Although the printed forms look similar these objects are not of the
526
+ same type.
527
+
528
+ >>> isinstance(q_domain, Expr)
529
+ False
530
+ >>> isinstance(q_expr, Expr)
531
+ True
532
+
533
+ Construct an element of :ref:`K[x]` and convert to
534
+ :py:class:`~.Expr`.
535
+
536
+ >>> from sympy import Symbol
537
+ >>> x = Symbol('x')
538
+ >>> K = QQ[x]
539
+ >>> x_domain = K.gens[0] # generator x as a domain element
540
+ >>> p_domain = x_domain**2/3 + 1
541
+ >>> p_domain
542
+ 1/3*x**2 + 1
543
+ >>> p_expr = K.to_sympy(p_domain)
544
+ >>> p_expr
545
+ x**2/3 + 1
546
+
547
+ The :py:meth:`~.Domain.from_sympy` method is used for the opposite
548
+ conversion from a normal SymPy expression to a domain element.
549
+
550
+ >>> p_domain == p_expr
551
+ False
552
+ >>> K.from_sympy(p_expr) == p_domain
553
+ True
554
+ >>> K.to_sympy(p_domain) == p_expr
555
+ True
556
+ >>> K.from_sympy(K.to_sympy(p_domain)) == p_domain
557
+ True
558
+ >>> K.to_sympy(K.from_sympy(p_expr)) == p_expr
559
+ True
560
+
561
+ The :py:meth:`~.Domain.from_sympy` method makes it easier to construct
562
+ domain elements interactively.
563
+
564
+ >>> from sympy import Symbol
565
+ >>> x = Symbol('x')
566
+ >>> K = QQ[x]
567
+ >>> K.from_sympy(x**2/3 + 1)
568
+ 1/3*x**2 + 1
569
+
570
+ See also
571
+ ========
572
+
573
+ from_sympy
574
+ convert_from
575
+ """
576
+ raise NotImplementedError
577
+
578
+ def from_sympy(self, a):
579
+ """Convert a SymPy expression to an element of this domain.
580
+
581
+ Explanation
582
+ ===========
583
+
584
+ See :py:meth:`~.Domain.to_sympy` for explanation and examples.
585
+
586
+ Parameters
587
+ ==========
588
+
589
+ expr: Expr
590
+ A normal SymPy expression of type :py:class:`~.Expr`.
591
+
592
+ Returns
593
+ =======
594
+
595
+ a: domain element
596
+ An element of this :py:class:`~.Domain`.
597
+
598
+ See also
599
+ ========
600
+
601
+ to_sympy
602
+ convert_from
603
+ """
604
+ raise NotImplementedError
605
+
606
+ def sum(self, args):
607
+ return sum(args, start=self.zero)
608
+
609
+ def from_FF(K1, a, K0):
610
+ """Convert ``ModularInteger(int)`` to ``dtype``. """
611
+ return None
612
+
613
+ def from_FF_python(K1, a, K0):
614
+ """Convert ``ModularInteger(int)`` to ``dtype``. """
615
+ return None
616
+
617
+ def from_ZZ_python(K1, a, K0):
618
+ """Convert a Python ``int`` object to ``dtype``. """
619
+ return None
620
+
621
+ def from_QQ_python(K1, a, K0):
622
+ """Convert a Python ``Fraction`` object to ``dtype``. """
623
+ return None
624
+
625
+ def from_FF_gmpy(K1, a, K0):
626
+ """Convert ``ModularInteger(mpz)`` to ``dtype``. """
627
+ return None
628
+
629
+ def from_ZZ_gmpy(K1, a, K0):
630
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
631
+ return None
632
+
633
+ def from_QQ_gmpy(K1, a, K0):
634
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
635
+ return None
636
+
637
+ def from_RealField(K1, a, K0):
638
+ """Convert a real element object to ``dtype``. """
639
+ return None
640
+
641
+ def from_ComplexField(K1, a, K0):
642
+ """Convert a complex element to ``dtype``. """
643
+ return None
644
+
645
+ def from_AlgebraicField(K1, a, K0):
646
+ """Convert an algebraic number to ``dtype``. """
647
+ return None
648
+
649
+ def from_PolynomialRing(K1, a, K0):
650
+ """Convert a polynomial to ``dtype``. """
651
+ if a.is_ground:
652
+ return K1.convert(a.LC, K0.dom)
653
+
654
+ def from_FractionField(K1, a, K0):
655
+ """Convert a rational function to ``dtype``. """
656
+ return None
657
+
658
+ def from_MonogenicFiniteExtension(K1, a, K0):
659
+ """Convert an ``ExtensionElement`` to ``dtype``. """
660
+ return K1.convert_from(a.rep, K0.ring)
661
+
662
+ def from_ExpressionDomain(K1, a, K0):
663
+ """Convert a ``EX`` object to ``dtype``. """
664
+ return K1.from_sympy(a.ex)
665
+
666
+ def from_ExpressionRawDomain(K1, a, K0):
667
+ """Convert a ``EX`` object to ``dtype``. """
668
+ return K1.from_sympy(a)
669
+
670
+ def from_GlobalPolynomialRing(K1, a, K0):
671
+ """Convert a polynomial to ``dtype``. """
672
+ if a.degree() <= 0:
673
+ return K1.convert(a.LC(), K0.dom)
674
+
675
+ def from_GeneralizedPolynomialRing(K1, a, K0):
676
+ return K1.from_FractionField(a, K0)
677
+
678
+ def unify_with_symbols(K0, K1, symbols):
679
+ if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))):
680
+ raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols)))
681
+
682
+ return K0.unify(K1)
683
+
684
+ def unify_composite(K0, K1):
685
+ """Unify two domains where at least one is composite."""
686
+ K0_ground = K0.dom if K0.is_Composite else K0
687
+ K1_ground = K1.dom if K1.is_Composite else K1
688
+
689
+ K0_symbols = K0.symbols if K0.is_Composite else ()
690
+ K1_symbols = K1.symbols if K1.is_Composite else ()
691
+
692
+ domain = K0_ground.unify(K1_ground)
693
+ symbols = _unify_gens(K0_symbols, K1_symbols)
694
+ order = K0.order if K0.is_Composite else K1.order
695
+
696
+ # E.g. ZZ[x].unify(QQ.frac_field(x)) -> ZZ.frac_field(x)
697
+ if ((K0.is_FractionField and K1.is_PolynomialRing or
698
+ K1.is_FractionField and K0.is_PolynomialRing) and
699
+ (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field
700
+ and domain.has_assoc_Ring):
701
+ domain = domain.get_ring()
702
+
703
+ if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing):
704
+ cls = K0.__class__
705
+ else:
706
+ cls = K1.__class__
707
+
708
+ # Here cls might be PolynomialRing, FractionField, GlobalPolynomialRing
709
+ # (dense/old Polynomialring) or dense/old FractionField.
710
+
711
+ from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing
712
+ if cls == GlobalPolynomialRing:
713
+ return cls(domain, symbols)
714
+
715
+ return cls(domain, symbols, order)
716
+
717
+ def unify(K0, K1, symbols=None):
718
+ """
719
+ Construct a minimal domain that contains elements of ``K0`` and ``K1``.
720
+
721
+ Known domains (from smallest to largest):
722
+
723
+ - ``GF(p)``
724
+ - ``ZZ``
725
+ - ``QQ``
726
+ - ``RR(prec, tol)``
727
+ - ``CC(prec, tol)``
728
+ - ``ALG(a, b, c)``
729
+ - ``K[x, y, z]``
730
+ - ``K(x, y, z)``
731
+ - ``EX``
732
+
733
+ """
734
+ if symbols is not None:
735
+ return K0.unify_with_symbols(K1, symbols)
736
+
737
+ if K0 == K1:
738
+ return K0
739
+
740
+ if not (K0.has_CharacteristicZero and K1.has_CharacteristicZero):
741
+ # Reject unification of domains with different characteristics.
742
+ if K0.characteristic() != K1.characteristic():
743
+ raise UnificationFailed("Cannot unify %s with %s" % (K0, K1))
744
+
745
+ # We do not get here if K0 == K1. The two domains have the same
746
+ # characteristic but are unequal so at least one is composite and
747
+ # we are unifying something like GF(3).unify(GF(3)[x]).
748
+ return K0.unify_composite(K1)
749
+
750
+ # From here we know both domains have characteristic zero and it can be
751
+ # acceptable to fall back on EX.
752
+
753
+ if K0.is_EXRAW:
754
+ return K0
755
+ if K1.is_EXRAW:
756
+ return K1
757
+
758
+ if K0.is_EX:
759
+ return K0
760
+ if K1.is_EX:
761
+ return K1
762
+
763
+ if K0.is_FiniteExtension or K1.is_FiniteExtension:
764
+ if K1.is_FiniteExtension:
765
+ K0, K1 = K1, K0
766
+ if K1.is_FiniteExtension:
767
+ # Unifying two extensions.
768
+ # Try to ensure that K0.unify(K1) == K1.unify(K0)
769
+ if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus:
770
+ K0, K1 = K1, K0
771
+ return K1.set_domain(K0)
772
+ else:
773
+ # Drop the generator from other and unify with the base domain
774
+ K1 = K1.drop(K0.symbol)
775
+ K1 = K0.domain.unify(K1)
776
+ return K0.set_domain(K1)
777
+
778
+ if K0.is_Composite or K1.is_Composite:
779
+ return K0.unify_composite(K1)
780
+
781
+ if K1.is_ComplexField:
782
+ K0, K1 = K1, K0
783
+ if K0.is_ComplexField:
784
+ if K1.is_ComplexField or K1.is_RealField:
785
+ if K0.precision >= K1.precision:
786
+ return K0
787
+ else:
788
+ from sympy.polys.domains.complexfield import ComplexField
789
+ return ComplexField(prec=K1.precision)
790
+ else:
791
+ return K0
792
+
793
+ if K1.is_RealField:
794
+ K0, K1 = K1, K0
795
+ if K0.is_RealField:
796
+ if K1.is_RealField:
797
+ if K0.precision >= K1.precision:
798
+ return K0
799
+ else:
800
+ return K1
801
+ elif K1.is_GaussianRing or K1.is_GaussianField:
802
+ from sympy.polys.domains.complexfield import ComplexField
803
+ return ComplexField(prec=K0.precision)
804
+ else:
805
+ return K0
806
+
807
+ if K1.is_AlgebraicField:
808
+ K0, K1 = K1, K0
809
+ if K0.is_AlgebraicField:
810
+ if K1.is_GaussianRing:
811
+ K1 = K1.get_field()
812
+ if K1.is_GaussianField:
813
+ K1 = K1.as_AlgebraicField()
814
+ if K1.is_AlgebraicField:
815
+ return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext))
816
+ else:
817
+ return K0
818
+
819
+ if K0.is_GaussianField:
820
+ return K0
821
+ if K1.is_GaussianField:
822
+ return K1
823
+
824
+ if K0.is_GaussianRing:
825
+ if K1.is_RationalField:
826
+ K0 = K0.get_field()
827
+ return K0
828
+ if K1.is_GaussianRing:
829
+ if K0.is_RationalField:
830
+ K1 = K1.get_field()
831
+ return K1
832
+
833
+ if K0.is_RationalField:
834
+ return K0
835
+ if K1.is_RationalField:
836
+ return K1
837
+
838
+ if K0.is_IntegerRing:
839
+ return K0
840
+ if K1.is_IntegerRing:
841
+ return K1
842
+
843
+ from sympy.polys.domains import EX
844
+ return EX
845
+
846
+ def __eq__(self, other):
847
+ """Returns ``True`` if two domains are equivalent. """
848
+ # XXX: Remove this.
849
+ return isinstance(other, Domain) and self.dtype == other.dtype
850
+
851
+ def __ne__(self, other):
852
+ """Returns ``False`` if two domains are equivalent. """
853
+ return not self == other
854
+
855
+ def map(self, seq):
856
+ """Rersively apply ``self`` to all elements of ``seq``. """
857
+ result = []
858
+
859
+ for elt in seq:
860
+ if isinstance(elt, list):
861
+ result.append(self.map(elt))
862
+ else:
863
+ result.append(self(elt))
864
+
865
+ return result
866
+
867
+ def get_ring(self):
868
+ """Returns a ring associated with ``self``. """
869
+ raise DomainError('there is no ring associated with %s' % self)
870
+
871
+ def get_field(self):
872
+ """Returns a field associated with ``self``. """
873
+ raise DomainError('there is no field associated with %s' % self)
874
+
875
+ def get_exact(self):
876
+ """Returns an exact domain associated with ``self``. """
877
+ return self
878
+
879
+ def __getitem__(self, symbols):
880
+ """The mathematical way to make a polynomial ring. """
881
+ if hasattr(symbols, '__iter__'):
882
+ return self.poly_ring(*symbols)
883
+ else:
884
+ return self.poly_ring(symbols)
885
+
886
+ def poly_ring(self, *symbols, order=lex):
887
+ """Returns a polynomial ring, i.e. `K[X]`. """
888
+ from sympy.polys.domains.polynomialring import PolynomialRing
889
+ return PolynomialRing(self, symbols, order)
890
+
891
+ def frac_field(self, *symbols, order=lex):
892
+ """Returns a fraction field, i.e. `K(X)`. """
893
+ from sympy.polys.domains.fractionfield import FractionField
894
+ return FractionField(self, symbols, order)
895
+
896
+ def old_poly_ring(self, *symbols, **kwargs):
897
+ """Returns a polynomial ring, i.e. `K[X]`. """
898
+ from sympy.polys.domains.old_polynomialring import PolynomialRing
899
+ return PolynomialRing(self, *symbols, **kwargs)
900
+
901
+ def old_frac_field(self, *symbols, **kwargs):
902
+ """Returns a fraction field, i.e. `K(X)`. """
903
+ from sympy.polys.domains.old_fractionfield import FractionField
904
+ return FractionField(self, *symbols, **kwargs)
905
+
906
+ def algebraic_field(self, *extension, alias=None):
907
+ r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """
908
+ raise DomainError("Cannot create algebraic field over %s" % self)
909
+
910
+ def alg_field_from_poly(self, poly, alias=None, root_index=-1):
911
+ r"""
912
+ Convenience method to construct an algebraic extension on a root of a
913
+ polynomial, chosen by root index.
914
+
915
+ Parameters
916
+ ==========
917
+
918
+ poly : :py:class:`~.Poly`
919
+ The polynomial whose root generates the extension.
920
+ alias : str, optional (default=None)
921
+ Symbol name for the generator of the extension.
922
+ E.g. "alpha" or "theta".
923
+ root_index : int, optional (default=-1)
924
+ Specifies which root of the polynomial is desired. The ordering is
925
+ as defined by the :py:class:`~.ComplexRootOf` class. The default of
926
+ ``-1`` selects the most natural choice in the common cases of
927
+ quadratic and cyclotomic fields (the square root on the positive
928
+ real or imaginary axis, resp. $\mathrm{e}^{2\pi i/n}$).
929
+
930
+ Examples
931
+ ========
932
+
933
+ >>> from sympy import QQ, Poly
934
+ >>> from sympy.abc import x
935
+ >>> f = Poly(x**2 - 2)
936
+ >>> K = QQ.alg_field_from_poly(f)
937
+ >>> K.ext.minpoly == f
938
+ True
939
+ >>> g = Poly(8*x**3 - 6*x - 1)
940
+ >>> L = QQ.alg_field_from_poly(g, "alpha")
941
+ >>> L.ext.minpoly == g
942
+ True
943
+ >>> L.to_sympy(L([1, 1, 1]))
944
+ alpha**2 + alpha + 1
945
+
946
+ """
947
+ from sympy.polys.rootoftools import CRootOf
948
+ root = CRootOf(poly, root_index)
949
+ alpha = AlgebraicNumber(root, alias=alias)
950
+ return self.algebraic_field(alpha, alias=alias)
951
+
952
+ def cyclotomic_field(self, n, ss=False, alias="zeta", gen=None, root_index=-1):
953
+ r"""
954
+ Convenience method to construct a cyclotomic field.
955
+
956
+ Parameters
957
+ ==========
958
+
959
+ n : int
960
+ Construct the nth cyclotomic field.
961
+ ss : boolean, optional (default=False)
962
+ If True, append *n* as a subscript on the alias string.
963
+ alias : str, optional (default="zeta")
964
+ Symbol name for the generator.
965
+ gen : :py:class:`~.Symbol`, optional (default=None)
966
+ Desired variable for the cyclotomic polynomial that defines the
967
+ field. If ``None``, a dummy variable will be used.
968
+ root_index : int, optional (default=-1)
969
+ Specifies which root of the polynomial is desired. The ordering is
970
+ as defined by the :py:class:`~.ComplexRootOf` class. The default of
971
+ ``-1`` selects the root $\mathrm{e}^{2\pi i/n}$.
972
+
973
+ Examples
974
+ ========
975
+
976
+ >>> from sympy import QQ, latex
977
+ >>> K = QQ.cyclotomic_field(5)
978
+ >>> K.to_sympy(K([-1, 1]))
979
+ 1 - zeta
980
+ >>> L = QQ.cyclotomic_field(7, True)
981
+ >>> a = L.to_sympy(L([-1, 1]))
982
+ >>> print(a)
983
+ 1 - zeta7
984
+ >>> print(latex(a))
985
+ 1 - \zeta_{7}
986
+
987
+ """
988
+ from sympy.polys.specialpolys import cyclotomic_poly
989
+ if ss:
990
+ alias += str(n)
991
+ return self.alg_field_from_poly(cyclotomic_poly(n, gen), alias=alias,
992
+ root_index=root_index)
993
+
994
+ def inject(self, *symbols):
995
+ """Inject generators into this domain. """
996
+ raise NotImplementedError
997
+
998
+ def drop(self, *symbols):
999
+ """Drop generators from this domain. """
1000
+ if self.is_Simple:
1001
+ return self
1002
+ raise NotImplementedError # pragma: no cover
1003
+
1004
+ def is_zero(self, a):
1005
+ """Returns True if ``a`` is zero. """
1006
+ return not a
1007
+
1008
+ def is_one(self, a):
1009
+ """Returns True if ``a`` is one. """
1010
+ return a == self.one
1011
+
1012
+ def is_positive(self, a):
1013
+ """Returns True if ``a`` is positive. """
1014
+ return a > 0
1015
+
1016
+ def is_negative(self, a):
1017
+ """Returns True if ``a`` is negative. """
1018
+ return a < 0
1019
+
1020
+ def is_nonpositive(self, a):
1021
+ """Returns True if ``a`` is non-positive. """
1022
+ return a <= 0
1023
+
1024
+ def is_nonnegative(self, a):
1025
+ """Returns True if ``a`` is non-negative. """
1026
+ return a >= 0
1027
+
1028
+ def canonical_unit(self, a):
1029
+ if self.is_negative(a):
1030
+ return -self.one
1031
+ else:
1032
+ return self.one
1033
+
1034
+ def abs(self, a):
1035
+ """Absolute value of ``a``, implies ``__abs__``. """
1036
+ return abs(a)
1037
+
1038
+ def neg(self, a):
1039
+ """Returns ``a`` negated, implies ``__neg__``. """
1040
+ return -a
1041
+
1042
+ def pos(self, a):
1043
+ """Returns ``a`` positive, implies ``__pos__``. """
1044
+ return +a
1045
+
1046
+ def add(self, a, b):
1047
+ """Sum of ``a`` and ``b``, implies ``__add__``. """
1048
+ return a + b
1049
+
1050
+ def sub(self, a, b):
1051
+ """Difference of ``a`` and ``b``, implies ``__sub__``. """
1052
+ return a - b
1053
+
1054
+ def mul(self, a, b):
1055
+ """Product of ``a`` and ``b``, implies ``__mul__``. """
1056
+ return a * b
1057
+
1058
+ def pow(self, a, b):
1059
+ """Raise ``a`` to power ``b``, implies ``__pow__``. """
1060
+ return a ** b
1061
+
1062
+ def exquo(self, a, b):
1063
+ """Exact quotient of *a* and *b*. Analogue of ``a / b``.
1064
+
1065
+ Explanation
1066
+ ===========
1067
+
1068
+ This is essentially the same as ``a / b`` except that an error will be
1069
+ raised if the division is inexact (if there is any remainder) and the
1070
+ result will always be a domain element. When working in a
1071
+ :py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ`
1072
+ or :ref:`K[x]`) ``exquo`` should be used instead of ``/``.
1073
+
1074
+ The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does
1075
+ not raise an exception) then ``a == b*q``.
1076
+
1077
+ Examples
1078
+ ========
1079
+
1080
+ We can use ``K.exquo`` instead of ``/`` for exact division.
1081
+
1082
+ >>> from sympy import ZZ
1083
+ >>> ZZ.exquo(ZZ(4), ZZ(2))
1084
+ 2
1085
+ >>> ZZ.exquo(ZZ(5), ZZ(2))
1086
+ Traceback (most recent call last):
1087
+ ...
1088
+ ExactQuotientFailed: 2 does not divide 5 in ZZ
1089
+
1090
+ Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero
1091
+ divisor) is always exact so in that case ``/`` can be used instead of
1092
+ :py:meth:`~.Domain.exquo`.
1093
+
1094
+ >>> from sympy import QQ
1095
+ >>> QQ.exquo(QQ(5), QQ(2))
1096
+ 5/2
1097
+ >>> QQ(5) / QQ(2)
1098
+ 5/2
1099
+
1100
+ Parameters
1101
+ ==========
1102
+
1103
+ a: domain element
1104
+ The dividend
1105
+ b: domain element
1106
+ The divisor
1107
+
1108
+ Returns
1109
+ =======
1110
+
1111
+ q: domain element
1112
+ The exact quotient
1113
+
1114
+ Raises
1115
+ ======
1116
+
1117
+ ExactQuotientFailed: if exact division is not possible.
1118
+ ZeroDivisionError: when the divisor is zero.
1119
+
1120
+ See also
1121
+ ========
1122
+
1123
+ quo: Analogue of ``a // b``
1124
+ rem: Analogue of ``a % b``
1125
+ div: Analogue of ``divmod(a, b)``
1126
+
1127
+ Notes
1128
+ =====
1129
+
1130
+ Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int``
1131
+ (or ``mpz``) division as ``a / b`` should not be used as it would give
1132
+ a ``float`` which is not a domain element.
1133
+
1134
+ >>> ZZ(4) / ZZ(2) # doctest: +SKIP
1135
+ 2.0
1136
+ >>> ZZ(5) / ZZ(2) # doctest: +SKIP
1137
+ 2.5
1138
+
1139
+ On the other hand with `SYMPY_GROUND_TYPES=flint` elements of :ref:`ZZ`
1140
+ are ``flint.fmpz`` and division would raise an exception:
1141
+
1142
+ >>> ZZ(4) / ZZ(2) # doctest: +SKIP
1143
+ Traceback (most recent call last):
1144
+ ...
1145
+ TypeError: unsupported operand type(s) for /: 'fmpz' and 'fmpz'
1146
+
1147
+ Using ``/`` with :ref:`ZZ` will lead to incorrect results so
1148
+ :py:meth:`~.Domain.exquo` should be used instead.
1149
+
1150
+ """
1151
+ raise NotImplementedError
1152
+
1153
+ def quo(self, a, b):
1154
+ """Quotient of *a* and *b*. Analogue of ``a // b``.
1155
+
1156
+ ``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See
1157
+ :py:meth:`~.Domain.div` for more explanation.
1158
+
1159
+ See also
1160
+ ========
1161
+
1162
+ rem: Analogue of ``a % b``
1163
+ div: Analogue of ``divmod(a, b)``
1164
+ exquo: Analogue of ``a / b``
1165
+ """
1166
+ raise NotImplementedError
1167
+
1168
+ def rem(self, a, b):
1169
+ """Modulo division of *a* and *b*. Analogue of ``a % b``.
1170
+
1171
+ ``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See
1172
+ :py:meth:`~.Domain.div` for more explanation.
1173
+
1174
+ See also
1175
+ ========
1176
+
1177
+ quo: Analogue of ``a // b``
1178
+ div: Analogue of ``divmod(a, b)``
1179
+ exquo: Analogue of ``a / b``
1180
+ """
1181
+ raise NotImplementedError
1182
+
1183
+ def div(self, a, b):
1184
+ """Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)``
1185
+
1186
+ Explanation
1187
+ ===========
1188
+
1189
+ This is essentially the same as ``divmod(a, b)`` except that is more
1190
+ consistent when working over some :py:class:`~.Field` domains such as
1191
+ :ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the
1192
+ :py:meth:`~.Domain.div` method should be used instead of ``divmod``.
1193
+
1194
+ The key invariant is that if ``q, r = K.div(a, b)`` then
1195
+ ``a == b*q + r``.
1196
+
1197
+ The result of ``K.div(a, b)`` is the same as the tuple
1198
+ ``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and
1199
+ remainder are needed then it is more efficient to use
1200
+ :py:meth:`~.Domain.div`.
1201
+
1202
+ Examples
1203
+ ========
1204
+
1205
+ We can use ``K.div`` instead of ``divmod`` for floor division and
1206
+ remainder.
1207
+
1208
+ >>> from sympy import ZZ, QQ
1209
+ >>> ZZ.div(ZZ(5), ZZ(2))
1210
+ (2, 1)
1211
+
1212
+ If ``K`` is a :py:class:`~.Field` then the division is always exact
1213
+ with a remainder of :py:attr:`~.Domain.zero`.
1214
+
1215
+ >>> QQ.div(QQ(5), QQ(2))
1216
+ (5/2, 0)
1217
+
1218
+ Parameters
1219
+ ==========
1220
+
1221
+ a: domain element
1222
+ The dividend
1223
+ b: domain element
1224
+ The divisor
1225
+
1226
+ Returns
1227
+ =======
1228
+
1229
+ (q, r): tuple of domain elements
1230
+ The quotient and remainder
1231
+
1232
+ Raises
1233
+ ======
1234
+
1235
+ ZeroDivisionError: when the divisor is zero.
1236
+
1237
+ See also
1238
+ ========
1239
+
1240
+ quo: Analogue of ``a // b``
1241
+ rem: Analogue of ``a % b``
1242
+ exquo: Analogue of ``a / b``
1243
+
1244
+ Notes
1245
+ =====
1246
+
1247
+ If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as
1248
+ the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type
1249
+ defines ``divmod`` in a way that is undesirable so
1250
+ :py:meth:`~.Domain.div` should be used instead of ``divmod``.
1251
+
1252
+ >>> a = QQ(1)
1253
+ >>> b = QQ(3, 2)
1254
+ >>> a # doctest: +SKIP
1255
+ mpq(1,1)
1256
+ >>> b # doctest: +SKIP
1257
+ mpq(3,2)
1258
+ >>> divmod(a, b) # doctest: +SKIP
1259
+ (mpz(0), mpq(1,1))
1260
+ >>> QQ.div(a, b) # doctest: +SKIP
1261
+ (mpq(2,3), mpq(0,1))
1262
+
1263
+ Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so
1264
+ :py:meth:`~.Domain.div` should be used instead.
1265
+
1266
+ """
1267
+ raise NotImplementedError
1268
+
1269
+ def invert(self, a, b):
1270
+ """Returns inversion of ``a mod b``, implies something. """
1271
+ raise NotImplementedError
1272
+
1273
+ def revert(self, a):
1274
+ """Returns ``a**(-1)`` if possible. """
1275
+ raise NotImplementedError
1276
+
1277
+ def numer(self, a):
1278
+ """Returns numerator of ``a``. """
1279
+ raise NotImplementedError
1280
+
1281
+ def denom(self, a):
1282
+ """Returns denominator of ``a``. """
1283
+ raise NotImplementedError
1284
+
1285
+ def half_gcdex(self, a, b):
1286
+ """Half extended GCD of ``a`` and ``b``. """
1287
+ s, t, h = self.gcdex(a, b)
1288
+ return s, h
1289
+
1290
+ def gcdex(self, a, b):
1291
+ """Extended GCD of ``a`` and ``b``. """
1292
+ raise NotImplementedError
1293
+
1294
+ def cofactors(self, a, b):
1295
+ """Returns GCD and cofactors of ``a`` and ``b``. """
1296
+ gcd = self.gcd(a, b)
1297
+ cfa = self.quo(a, gcd)
1298
+ cfb = self.quo(b, gcd)
1299
+ return gcd, cfa, cfb
1300
+
1301
+ def gcd(self, a, b):
1302
+ """Returns GCD of ``a`` and ``b``. """
1303
+ raise NotImplementedError
1304
+
1305
+ def lcm(self, a, b):
1306
+ """Returns LCM of ``a`` and ``b``. """
1307
+ raise NotImplementedError
1308
+
1309
+ def log(self, a, b):
1310
+ """Returns b-base logarithm of ``a``. """
1311
+ raise NotImplementedError
1312
+
1313
+ def sqrt(self, a):
1314
+ """Returns a (possibly inexact) square root of ``a``.
1315
+
1316
+ Explanation
1317
+ ===========
1318
+ There is no universal definition of "inexact square root" for all
1319
+ domains. It is not recommended to implement this method for domains
1320
+ other then :ref:`ZZ`.
1321
+
1322
+ See also
1323
+ ========
1324
+ exsqrt
1325
+ """
1326
+ raise NotImplementedError
1327
+
1328
+ def is_square(self, a):
1329
+ """Returns whether ``a`` is a square in the domain.
1330
+
1331
+ Explanation
1332
+ ===========
1333
+ Returns ``True`` if there is an element ``b`` in the domain such that
1334
+ ``b * b == a``, otherwise returns ``False``. For inexact domains like
1335
+ :ref:`RR` and :ref:`CC`, a tiny difference in this equality can be
1336
+ tolerated.
1337
+
1338
+ See also
1339
+ ========
1340
+ exsqrt
1341
+ """
1342
+ raise NotImplementedError
1343
+
1344
+ def exsqrt(self, a):
1345
+ """Principal square root of a within the domain if ``a`` is square.
1346
+
1347
+ Explanation
1348
+ ===========
1349
+ The implementation of this method should return an element ``b`` in the
1350
+ domain such that ``b * b == a``, or ``None`` if there is no such ``b``.
1351
+ For inexact domains like :ref:`RR` and :ref:`CC`, a tiny difference in
1352
+ this equality can be tolerated. The choice of a "principal" square root
1353
+ should follow a consistent rule whenever possible.
1354
+
1355
+ See also
1356
+ ========
1357
+ sqrt, is_square
1358
+ """
1359
+ raise NotImplementedError
1360
+
1361
+ def evalf(self, a, prec=None, **options):
1362
+ """Returns numerical approximation of ``a``. """
1363
+ return self.to_sympy(a).evalf(prec, **options)
1364
+
1365
+ n = evalf
1366
+
1367
+ def real(self, a):
1368
+ return a
1369
+
1370
+ def imag(self, a):
1371
+ return self.zero
1372
+
1373
+ def almosteq(self, a, b, tolerance=None):
1374
+ """Check if ``a`` and ``b`` are almost equal. """
1375
+ return a == b
1376
+
1377
+ def characteristic(self):
1378
+ """Return the characteristic of this domain. """
1379
+ raise NotImplementedError('characteristic()')
1380
+
1381
+
1382
+ __all__ = ['Domain']
.venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Trait for implementing domain elements. """
2
+
3
+
4
+ from sympy.utilities import public
5
+
6
+ @public
7
+ class DomainElement:
8
+ """
9
+ Represents an element of a domain.
10
+
11
+ Mix in this trait into a class whose instances should be recognized as
12
+ elements of a domain. Method ``parent()`` gives that domain.
13
+ """
14
+
15
+ __slots__ = ()
16
+
17
+ def parent(self):
18
+ """Get the domain associated with ``self``
19
+
20
+ Examples
21
+ ========
22
+
23
+ >>> from sympy import ZZ, symbols
24
+ >>> x, y = symbols('x, y')
25
+ >>> K = ZZ[x,y]
26
+ >>> p = K(x)**2 + K(y)**2
27
+ >>> p
28
+ x**2 + y**2
29
+ >>> p.parent()
30
+ ZZ[x,y]
31
+
32
+ Notes
33
+ =====
34
+
35
+ This is used by :py:meth:`~.Domain.convert` to identify the domain
36
+ associated with a domain element.
37
+ """
38
+ raise NotImplementedError("abstract method")
.venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`ExpressionDomain` class. """
2
+
3
+
4
+ from sympy.core import sympify, SympifyError
5
+ from sympy.polys.domains.domainelement import DomainElement
6
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
7
+ from sympy.polys.domains.field import Field
8
+ from sympy.polys.domains.simpledomain import SimpleDomain
9
+ from sympy.polys.polyutils import PicklableWithSlots
10
+ from sympy.utilities import public
11
+
12
+ eflags = {"deep": False, "mul": True, "power_exp": False, "power_base": False,
13
+ "basic": False, "multinomial": False, "log": False}
14
+
15
+ @public
16
+ class ExpressionDomain(Field, CharacteristicZero, SimpleDomain):
17
+ """A class for arbitrary expressions. """
18
+
19
+ is_SymbolicDomain = is_EX = True
20
+
21
+ class Expression(DomainElement, PicklableWithSlots):
22
+ """An arbitrary expression. """
23
+
24
+ __slots__ = ('ex',)
25
+
26
+ def __init__(self, ex):
27
+ if not isinstance(ex, self.__class__):
28
+ self.ex = sympify(ex)
29
+ else:
30
+ self.ex = ex.ex
31
+
32
+ def __repr__(f):
33
+ return 'EX(%s)' % repr(f.ex)
34
+
35
+ def __str__(f):
36
+ return 'EX(%s)' % str(f.ex)
37
+
38
+ def __hash__(self):
39
+ return hash((self.__class__.__name__, self.ex))
40
+
41
+ def parent(self):
42
+ return EX
43
+
44
+ def as_expr(f):
45
+ return f.ex
46
+
47
+ def numer(f):
48
+ return f.__class__(f.ex.as_numer_denom()[0])
49
+
50
+ def denom(f):
51
+ return f.__class__(f.ex.as_numer_denom()[1])
52
+
53
+ def simplify(f, ex):
54
+ return f.__class__(ex.cancel().expand(**eflags))
55
+
56
+ def __abs__(f):
57
+ return f.__class__(abs(f.ex))
58
+
59
+ def __neg__(f):
60
+ return f.__class__(-f.ex)
61
+
62
+ def _to_ex(f, g):
63
+ try:
64
+ return f.__class__(g)
65
+ except SympifyError:
66
+ return None
67
+
68
+ def __lt__(f, g):
69
+ return f.ex.sort_key() < g.ex.sort_key()
70
+
71
+ def __add__(f, g):
72
+ g = f._to_ex(g)
73
+
74
+ if g is None:
75
+ return NotImplemented
76
+ elif g == EX.zero:
77
+ return f
78
+ elif f == EX.zero:
79
+ return g
80
+ else:
81
+ return f.simplify(f.ex + g.ex)
82
+
83
+ def __radd__(f, g):
84
+ return f.simplify(f.__class__(g).ex + f.ex)
85
+
86
+ def __sub__(f, g):
87
+ g = f._to_ex(g)
88
+
89
+ if g is None:
90
+ return NotImplemented
91
+ elif g == EX.zero:
92
+ return f
93
+ elif f == EX.zero:
94
+ return -g
95
+ else:
96
+ return f.simplify(f.ex - g.ex)
97
+
98
+ def __rsub__(f, g):
99
+ return f.simplify(f.__class__(g).ex - f.ex)
100
+
101
+ def __mul__(f, g):
102
+ g = f._to_ex(g)
103
+
104
+ if g is None:
105
+ return NotImplemented
106
+
107
+ if EX.zero in (f, g):
108
+ return EX.zero
109
+ elif f.ex.is_Number and g.ex.is_Number:
110
+ return f.__class__(f.ex*g.ex)
111
+
112
+ return f.simplify(f.ex*g.ex)
113
+
114
+ def __rmul__(f, g):
115
+ return f.simplify(f.__class__(g).ex*f.ex)
116
+
117
+ def __pow__(f, n):
118
+ n = f._to_ex(n)
119
+
120
+ if n is not None:
121
+ return f.simplify(f.ex**n.ex)
122
+ else:
123
+ return NotImplemented
124
+
125
+ def __truediv__(f, g):
126
+ g = f._to_ex(g)
127
+
128
+ if g is not None:
129
+ return f.simplify(f.ex/g.ex)
130
+ else:
131
+ return NotImplemented
132
+
133
+ def __rtruediv__(f, g):
134
+ return f.simplify(f.__class__(g).ex/f.ex)
135
+
136
+ def __eq__(f, g):
137
+ return f.ex == f.__class__(g).ex
138
+
139
+ def __ne__(f, g):
140
+ return not f == g
141
+
142
+ def __bool__(f):
143
+ return not f.ex.is_zero
144
+
145
+ def gcd(f, g):
146
+ from sympy.polys import gcd
147
+ return f.__class__(gcd(f.ex, f.__class__(g).ex))
148
+
149
+ def lcm(f, g):
150
+ from sympy.polys import lcm
151
+ return f.__class__(lcm(f.ex, f.__class__(g).ex))
152
+
153
+ dtype = Expression
154
+
155
+ zero = Expression(0)
156
+ one = Expression(1)
157
+
158
+ rep = 'EX'
159
+
160
+ has_assoc_Ring = False
161
+ has_assoc_Field = True
162
+
163
+ def __init__(self):
164
+ pass
165
+
166
+ def __eq__(self, other):
167
+ if isinstance(other, ExpressionDomain):
168
+ return True
169
+ else:
170
+ return NotImplemented
171
+
172
+ def __hash__(self):
173
+ return hash("EX")
174
+
175
+ def to_sympy(self, a):
176
+ """Convert ``a`` to a SymPy object. """
177
+ return a.as_expr()
178
+
179
+ def from_sympy(self, a):
180
+ """Convert SymPy's expression to ``dtype``. """
181
+ return self.dtype(a)
182
+
183
+ def from_ZZ(K1, a, K0):
184
+ """Convert a Python ``int`` object to ``dtype``. """
185
+ return K1(K0.to_sympy(a))
186
+
187
+ def from_ZZ_python(K1, a, K0):
188
+ """Convert a Python ``int`` object to ``dtype``. """
189
+ return K1(K0.to_sympy(a))
190
+
191
+ def from_QQ(K1, a, K0):
192
+ """Convert a Python ``Fraction`` object to ``dtype``. """
193
+ return K1(K0.to_sympy(a))
194
+
195
+ def from_QQ_python(K1, a, K0):
196
+ """Convert a Python ``Fraction`` object to ``dtype``. """
197
+ return K1(K0.to_sympy(a))
198
+
199
+ def from_ZZ_gmpy(K1, a, K0):
200
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
201
+ return K1(K0.to_sympy(a))
202
+
203
+ def from_QQ_gmpy(K1, a, K0):
204
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
205
+ return K1(K0.to_sympy(a))
206
+
207
+ def from_GaussianIntegerRing(K1, a, K0):
208
+ """Convert a ``GaussianRational`` object to ``dtype``. """
209
+ return K1(K0.to_sympy(a))
210
+
211
+ def from_GaussianRationalField(K1, a, K0):
212
+ """Convert a ``GaussianRational`` object to ``dtype``. """
213
+ return K1(K0.to_sympy(a))
214
+
215
+ def from_AlgebraicField(K1, a, K0):
216
+ """Convert an ``ANP`` object to ``dtype``. """
217
+ return K1(K0.to_sympy(a))
218
+
219
+ def from_RealField(K1, a, K0):
220
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
221
+ return K1(K0.to_sympy(a))
222
+
223
+ def from_ComplexField(K1, a, K0):
224
+ """Convert a mpmath ``mpc`` object to ``dtype``. """
225
+ return K1(K0.to_sympy(a))
226
+
227
+ def from_PolynomialRing(K1, a, K0):
228
+ """Convert a ``DMP`` object to ``dtype``. """
229
+ return K1(K0.to_sympy(a))
230
+
231
+ def from_FractionField(K1, a, K0):
232
+ """Convert a ``DMF`` object to ``dtype``. """
233
+ return K1(K0.to_sympy(a))
234
+
235
+ def from_ExpressionDomain(K1, a, K0):
236
+ """Convert a ``EX`` object to ``dtype``. """
237
+ return a
238
+
239
+ def get_ring(self):
240
+ """Returns a ring associated with ``self``. """
241
+ return self # XXX: EX is not a ring but we don't have much choice here.
242
+
243
+ def get_field(self):
244
+ """Returns a field associated with ``self``. """
245
+ return self
246
+
247
+ def is_positive(self, a):
248
+ """Returns True if ``a`` is positive. """
249
+ return a.ex.as_coeff_mul()[0].is_positive
250
+
251
+ def is_negative(self, a):
252
+ """Returns True if ``a`` is negative. """
253
+ return a.ex.could_extract_minus_sign()
254
+
255
+ def is_nonpositive(self, a):
256
+ """Returns True if ``a`` is non-positive. """
257
+ return a.ex.as_coeff_mul()[0].is_nonpositive
258
+
259
+ def is_nonnegative(self, a):
260
+ """Returns True if ``a`` is non-negative. """
261
+ return a.ex.as_coeff_mul()[0].is_nonnegative
262
+
263
+ def numer(self, a):
264
+ """Returns numerator of ``a``. """
265
+ return a.numer()
266
+
267
+ def denom(self, a):
268
+ """Returns denominator of ``a``. """
269
+ return a.denom()
270
+
271
+ def gcd(self, a, b):
272
+ return self(1)
273
+
274
+ def lcm(self, a, b):
275
+ return a.lcm(b)
276
+
277
+
278
+ EX = ExpressionDomain()
.venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`ExpressionRawDomain` class. """
2
+
3
+
4
+ from sympy.core import Expr, S, sympify, Add
5
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
6
+ from sympy.polys.domains.field import Field
7
+ from sympy.polys.domains.simpledomain import SimpleDomain
8
+ from sympy.polys.polyerrors import CoercionFailed
9
+ from sympy.utilities import public
10
+
11
+
12
+ @public
13
+ class ExpressionRawDomain(Field, CharacteristicZero, SimpleDomain):
14
+ """A class for arbitrary expressions but without automatic simplification. """
15
+
16
+ is_SymbolicRawDomain = is_EXRAW = True
17
+
18
+ dtype = Expr
19
+
20
+ zero = S.Zero
21
+ one = S.One
22
+
23
+ rep = 'EXRAW'
24
+
25
+ has_assoc_Ring = False
26
+ has_assoc_Field = True
27
+
28
+ def __init__(self):
29
+ pass
30
+
31
+ @classmethod
32
+ def new(self, a):
33
+ return sympify(a)
34
+
35
+ def to_sympy(self, a):
36
+ """Convert ``a`` to a SymPy object. """
37
+ return a
38
+
39
+ def from_sympy(self, a):
40
+ """Convert SymPy's expression to ``dtype``. """
41
+ if not isinstance(a, Expr):
42
+ raise CoercionFailed(f"Expecting an Expr instance but found: {type(a).__name__}")
43
+ return a
44
+
45
+ def convert_from(self, a, K):
46
+ """Convert a domain element from another domain to EXRAW"""
47
+ return K.to_sympy(a)
48
+
49
+ def get_field(self):
50
+ """Returns a field associated with ``self``. """
51
+ return self
52
+
53
+ def sum(self, items):
54
+ return Add(*items)
55
+
56
+
57
+ EXRAW = ExpressionRawDomain()
.venv/lib/python3.13/site-packages/sympy/polys/domains/field.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`Field` class. """
2
+
3
+
4
+ from sympy.polys.domains.ring import Ring
5
+ from sympy.polys.polyerrors import NotReversible, DomainError
6
+ from sympy.utilities import public
7
+
8
+ @public
9
+ class Field(Ring):
10
+ """Represents a field domain. """
11
+
12
+ is_Field = True
13
+ is_PID = True
14
+
15
+ def get_ring(self):
16
+ """Returns a ring associated with ``self``. """
17
+ raise DomainError('there is no ring associated with %s' % self)
18
+
19
+ def get_field(self):
20
+ """Returns a field associated with ``self``. """
21
+ return self
22
+
23
+ def exquo(self, a, b):
24
+ """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
25
+ return a / b
26
+
27
+ def quo(self, a, b):
28
+ """Quotient of ``a`` and ``b``, implies ``__truediv__``. """
29
+ return a / b
30
+
31
+ def rem(self, a, b):
32
+ """Remainder of ``a`` and ``b``, implies nothing. """
33
+ return self.zero
34
+
35
+ def div(self, a, b):
36
+ """Division of ``a`` and ``b``, implies ``__truediv__``. """
37
+ return a / b, self.zero
38
+
39
+ def gcd(self, a, b):
40
+ """
41
+ Returns GCD of ``a`` and ``b``.
42
+
43
+ This definition of GCD over fields allows to clear denominators
44
+ in `primitive()`.
45
+
46
+ Examples
47
+ ========
48
+
49
+ >>> from sympy.polys.domains import QQ
50
+ >>> from sympy import S, gcd, primitive
51
+ >>> from sympy.abc import x
52
+
53
+ >>> QQ.gcd(QQ(2, 3), QQ(4, 9))
54
+ 2/9
55
+ >>> gcd(S(2)/3, S(4)/9)
56
+ 2/9
57
+ >>> primitive(2*x/3 + S(4)/9)
58
+ (2/9, 3*x + 2)
59
+
60
+ """
61
+ try:
62
+ ring = self.get_ring()
63
+ except DomainError:
64
+ return self.one
65
+
66
+ p = ring.gcd(self.numer(a), self.numer(b))
67
+ q = ring.lcm(self.denom(a), self.denom(b))
68
+
69
+ return self.convert(p, ring)/q
70
+
71
+ def gcdex(self, a, b):
72
+ """
73
+ Returns x, y, g such that a * x + b * y == g == gcd(a, b)
74
+ """
75
+ d = self.gcd(a, b)
76
+
77
+ if a == self.zero:
78
+ if b == self.zero:
79
+ return self.zero, self.one, self.zero
80
+ else:
81
+ return self.zero, d/b, d
82
+ else:
83
+ return d/a, self.zero, d
84
+
85
+ def lcm(self, a, b):
86
+ """
87
+ Returns LCM of ``a`` and ``b``.
88
+
89
+ >>> from sympy.polys.domains import QQ
90
+ >>> from sympy import S, lcm
91
+
92
+ >>> QQ.lcm(QQ(2, 3), QQ(4, 9))
93
+ 4/3
94
+ >>> lcm(S(2)/3, S(4)/9)
95
+ 4/3
96
+
97
+ """
98
+
99
+ try:
100
+ ring = self.get_ring()
101
+ except DomainError:
102
+ return a*b
103
+
104
+ p = ring.lcm(self.numer(a), self.numer(b))
105
+ q = ring.gcd(self.denom(a), self.denom(b))
106
+
107
+ return self.convert(p, ring)/q
108
+
109
+ def revert(self, a):
110
+ """Returns ``a**(-1)`` if possible. """
111
+ if a:
112
+ return 1/a
113
+ else:
114
+ raise NotReversible('zero is not reversible')
115
+
116
+ def is_unit(self, a):
117
+ """Return true if ``a`` is a invertible"""
118
+ return bool(a)
.venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`FiniteField` class. """
2
+
3
+ import operator
4
+
5
+ from sympy.external.gmpy import GROUND_TYPES
6
+ from sympy.utilities.decorator import doctest_depends_on
7
+
8
+ from sympy.core.numbers import int_valued
9
+ from sympy.polys.domains.field import Field
10
+
11
+ from sympy.polys.domains.modularinteger import ModularIntegerFactory
12
+ from sympy.polys.domains.simpledomain import SimpleDomain
13
+ from sympy.polys.galoistools import gf_zassenhaus, gf_irred_p_rabin
14
+ from sympy.polys.polyerrors import CoercionFailed
15
+ from sympy.utilities import public
16
+ from sympy.polys.domains.groundtypes import SymPyInteger
17
+
18
+
19
+ if GROUND_TYPES == 'flint':
20
+ __doctest_skip__ = ['FiniteField']
21
+
22
+
23
+ if GROUND_TYPES == 'flint':
24
+ import flint
25
+ # Don't use python-flint < 0.5.0 because nmod was missing some features in
26
+ # previous versions of python-flint and fmpz_mod was not yet added.
27
+ _major, _minor, *_ = flint.__version__.split('.')
28
+ if (int(_major), int(_minor)) < (0, 5):
29
+ flint = None
30
+ else:
31
+ flint = None
32
+
33
+
34
+ def _modular_int_factory_nmod(mod):
35
+ # nmod only recognises int
36
+ index = operator.index
37
+ mod = index(mod)
38
+ nmod = flint.nmod
39
+ nmod_poly = flint.nmod_poly
40
+
41
+ # flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine)
42
+ try:
43
+ nmod(0, mod)
44
+ except OverflowError:
45
+ return None, None
46
+
47
+ def ctx(x):
48
+ try:
49
+ return nmod(x, mod)
50
+ except TypeError:
51
+ return nmod(index(x), mod)
52
+
53
+ def poly_ctx(cs):
54
+ return nmod_poly(cs, mod)
55
+
56
+ return ctx, poly_ctx
57
+
58
+
59
+ def _modular_int_factory_fmpz_mod(mod):
60
+ index = operator.index
61
+ fctx = flint.fmpz_mod_ctx(mod)
62
+ fctx_poly = flint.fmpz_mod_poly_ctx(mod)
63
+ fmpz_mod_poly = flint.fmpz_mod_poly
64
+
65
+ def ctx(x):
66
+ try:
67
+ return fctx(x)
68
+ except TypeError:
69
+ # x might be Integer
70
+ return fctx(index(x))
71
+
72
+ def poly_ctx(cs):
73
+ return fmpz_mod_poly(cs, fctx_poly)
74
+
75
+ return ctx, poly_ctx
76
+
77
+
78
+ def _modular_int_factory(mod, dom, symmetric, self):
79
+ # Convert the modulus to ZZ
80
+ try:
81
+ mod = dom.convert(mod)
82
+ except CoercionFailed:
83
+ raise ValueError('modulus must be an integer, got %s' % mod)
84
+
85
+ ctx, poly_ctx, is_flint = None, None, False
86
+
87
+ # Don't use flint if the modulus is not prime as it often crashes.
88
+ if flint is not None and mod.is_prime():
89
+
90
+ is_flint = True
91
+
92
+ # Try to use flint's nmod first
93
+ ctx, poly_ctx = _modular_int_factory_nmod(mod)
94
+
95
+ if ctx is None:
96
+ # Use fmpz_mod for larger moduli
97
+ ctx, poly_ctx = _modular_int_factory_fmpz_mod(mod)
98
+
99
+ if ctx is None:
100
+ # Use the Python implementation if flint is not available or the
101
+ # modulus is not prime.
102
+ ctx = ModularIntegerFactory(mod, dom, symmetric, self)
103
+ poly_ctx = None # not used
104
+
105
+ return ctx, poly_ctx, is_flint
106
+
107
+
108
+ @public
109
+ @doctest_depends_on(modules=['python', 'gmpy'])
110
+ class FiniteField(Field, SimpleDomain):
111
+ r"""Finite field of prime order :ref:`GF(p)`
112
+
113
+ A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime
114
+ order as :py:class:`~.Domain` in the domain system (see
115
+ :ref:`polys-domainsintro`).
116
+
117
+ A :py:class:`~.Poly` created from an expression with integer
118
+ coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p``
119
+ option is given then the domain will be a finite field instead.
120
+
121
+ >>> from sympy import Poly, Symbol
122
+ >>> x = Symbol('x')
123
+ >>> p = Poly(x**2 + 1)
124
+ >>> p
125
+ Poly(x**2 + 1, x, domain='ZZ')
126
+ >>> p.domain
127
+ ZZ
128
+ >>> p2 = Poly(x**2 + 1, modulus=2)
129
+ >>> p2
130
+ Poly(x**2 + 1, x, modulus=2)
131
+ >>> p2.domain
132
+ GF(2)
133
+
134
+ It is possible to factorise a polynomial over :ref:`GF(p)` using the
135
+ modulus argument to :py:func:`~.factor` or by specifying the domain
136
+ explicitly. The domain can also be given as a string.
137
+
138
+ >>> from sympy import factor, GF
139
+ >>> factor(x**2 + 1)
140
+ x**2 + 1
141
+ >>> factor(x**2 + 1, modulus=2)
142
+ (x + 1)**2
143
+ >>> factor(x**2 + 1, domain=GF(2))
144
+ (x + 1)**2
145
+ >>> factor(x**2 + 1, domain='GF(2)')
146
+ (x + 1)**2
147
+
148
+ It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel`
149
+ and :py:func:`~.gcd` functions.
150
+
151
+ >>> from sympy import cancel, gcd
152
+ >>> cancel((x**2 + 1)/(x + 1))
153
+ (x**2 + 1)/(x + 1)
154
+ >>> cancel((x**2 + 1)/(x + 1), domain=GF(2))
155
+ x + 1
156
+ >>> gcd(x**2 + 1, x + 1)
157
+ 1
158
+ >>> gcd(x**2 + 1, x + 1, domain=GF(2))
159
+ x + 1
160
+
161
+ When using the domain directly :ref:`GF(p)` can be used as a constructor
162
+ to create instances which then support the operations ``+,-,*,**,/``
163
+
164
+ >>> from sympy import GF
165
+ >>> K = GF(5)
166
+ >>> K
167
+ GF(5)
168
+ >>> x = K(3)
169
+ >>> y = K(2)
170
+ >>> x
171
+ 3 mod 5
172
+ >>> y
173
+ 2 mod 5
174
+ >>> x * y
175
+ 1 mod 5
176
+ >>> x / y
177
+ 4 mod 5
178
+
179
+ Notes
180
+ =====
181
+
182
+ It is also possible to create a :ref:`GF(p)` domain of **non-prime**
183
+ order but the resulting ring is **not** a field: it is just the ring of
184
+ the integers modulo ``n``.
185
+
186
+ >>> K = GF(9)
187
+ >>> z = K(3)
188
+ >>> z
189
+ 3 mod 9
190
+ >>> z**2
191
+ 0 mod 9
192
+
193
+ It would be good to have a proper implementation of prime power fields
194
+ (``GF(p**n)``) but these are not yet implemented in SymPY.
195
+
196
+ .. _finite field: https://en.wikipedia.org/wiki/Finite_field
197
+ """
198
+
199
+ rep = 'FF'
200
+ alias = 'FF'
201
+
202
+ is_FiniteField = is_FF = True
203
+ is_Numerical = True
204
+
205
+ has_assoc_Ring = False
206
+ has_assoc_Field = True
207
+
208
+ dom = None
209
+ mod = None
210
+
211
+ def __init__(self, mod, symmetric=True):
212
+ from sympy.polys.domains import ZZ
213
+ dom = ZZ
214
+
215
+ if mod <= 0:
216
+ raise ValueError('modulus must be a positive integer, got %s' % mod)
217
+
218
+ ctx, poly_ctx, is_flint = _modular_int_factory(mod, dom, symmetric, self)
219
+
220
+ self.dtype = ctx
221
+ self._poly_ctx = poly_ctx
222
+ self._is_flint = is_flint
223
+
224
+ self.zero = self.dtype(0)
225
+ self.one = self.dtype(1)
226
+ self.dom = dom
227
+ self.mod = mod
228
+ self.sym = symmetric
229
+ self._tp = type(self.zero)
230
+
231
+ @property
232
+ def tp(self):
233
+ return self._tp
234
+
235
+ @property
236
+ def is_Field(self):
237
+ is_field = getattr(self, '_is_field', None)
238
+ if is_field is None:
239
+ from sympy.ntheory.primetest import isprime
240
+ self._is_field = is_field = isprime(self.mod)
241
+ return is_field
242
+
243
+ def __str__(self):
244
+ return 'GF(%s)' % self.mod
245
+
246
+ def __hash__(self):
247
+ return hash((self.__class__.__name__, self.dtype, self.mod, self.dom))
248
+
249
+ def __eq__(self, other):
250
+ """Returns ``True`` if two domains are equivalent. """
251
+ return isinstance(other, FiniteField) and \
252
+ self.mod == other.mod and self.dom == other.dom
253
+
254
+ def characteristic(self):
255
+ """Return the characteristic of this domain. """
256
+ return self.mod
257
+
258
+ def get_field(self):
259
+ """Returns a field associated with ``self``. """
260
+ return self
261
+
262
+ def to_sympy(self, a):
263
+ """Convert ``a`` to a SymPy object. """
264
+ return SymPyInteger(self.to_int(a))
265
+
266
+ def from_sympy(self, a):
267
+ """Convert SymPy's Integer to SymPy's ``Integer``. """
268
+ if a.is_Integer:
269
+ return self.dtype(self.dom.dtype(int(a)))
270
+ elif int_valued(a):
271
+ return self.dtype(self.dom.dtype(int(a)))
272
+ else:
273
+ raise CoercionFailed("expected an integer, got %s" % a)
274
+
275
+ def to_int(self, a):
276
+ """Convert ``val`` to a Python ``int`` object. """
277
+ aval = int(a)
278
+ if self.sym and aval > self.mod // 2:
279
+ aval -= self.mod
280
+ return aval
281
+
282
+ def is_positive(self, a):
283
+ """Returns True if ``a`` is positive. """
284
+ return bool(a)
285
+
286
+ def is_nonnegative(self, a):
287
+ """Returns True if ``a`` is non-negative. """
288
+ return True
289
+
290
+ def is_negative(self, a):
291
+ """Returns True if ``a`` is negative. """
292
+ return False
293
+
294
+ def is_nonpositive(self, a):
295
+ """Returns True if ``a`` is non-positive. """
296
+ return not a
297
+
298
+ def from_FF(K1, a, K0=None):
299
+ """Convert ``ModularInteger(int)`` to ``dtype``. """
300
+ return K1.dtype(K1.dom.from_ZZ(int(a), K0.dom))
301
+
302
+ def from_FF_python(K1, a, K0=None):
303
+ """Convert ``ModularInteger(int)`` to ``dtype``. """
304
+ return K1.dtype(K1.dom.from_ZZ_python(int(a), K0.dom))
305
+
306
+ def from_ZZ(K1, a, K0=None):
307
+ """Convert Python's ``int`` to ``dtype``. """
308
+ return K1.dtype(K1.dom.from_ZZ_python(a, K0))
309
+
310
+ def from_ZZ_python(K1, a, K0=None):
311
+ """Convert Python's ``int`` to ``dtype``. """
312
+ return K1.dtype(K1.dom.from_ZZ_python(a, K0))
313
+
314
+ def from_QQ(K1, a, K0=None):
315
+ """Convert Python's ``Fraction`` to ``dtype``. """
316
+ if a.denominator == 1:
317
+ return K1.from_ZZ_python(a.numerator)
318
+
319
+ def from_QQ_python(K1, a, K0=None):
320
+ """Convert Python's ``Fraction`` to ``dtype``. """
321
+ if a.denominator == 1:
322
+ return K1.from_ZZ_python(a.numerator)
323
+
324
+ def from_FF_gmpy(K1, a, K0=None):
325
+ """Convert ``ModularInteger(mpz)`` to ``dtype``. """
326
+ return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom))
327
+
328
+ def from_ZZ_gmpy(K1, a, K0=None):
329
+ """Convert GMPY's ``mpz`` to ``dtype``. """
330
+ return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0))
331
+
332
+ def from_QQ_gmpy(K1, a, K0=None):
333
+ """Convert GMPY's ``mpq`` to ``dtype``. """
334
+ if a.denominator == 1:
335
+ return K1.from_ZZ_gmpy(a.numerator)
336
+
337
+ def from_RealField(K1, a, K0):
338
+ """Convert mpmath's ``mpf`` to ``dtype``. """
339
+ p, q = K0.to_rational(a)
340
+
341
+ if q == 1:
342
+ return K1.dtype(K1.dom.dtype(p))
343
+
344
+ def is_square(self, a):
345
+ """Returns True if ``a`` is a quadratic residue modulo p. """
346
+ # a is not a square <=> x**2-a is irreducible
347
+ poly = [int(x) for x in [self.one, self.zero, -a]]
348
+ return not gf_irred_p_rabin(poly, self.mod, self.dom)
349
+
350
+ def exsqrt(self, a):
351
+ """Square root modulo p of ``a`` if it is a quadratic residue.
352
+
353
+ Explanation
354
+ ===========
355
+ Always returns the square root that is no larger than ``p // 2``.
356
+ """
357
+ # x**2-a is not square-free if a=0 or the field is characteristic 2
358
+ if self.mod == 2 or a == 0:
359
+ return a
360
+ # Otherwise, use square-free factorization routine to factorize x**2-a
361
+ poly = [int(x) for x in [self.one, self.zero, -a]]
362
+ for factor in gf_zassenhaus(poly, self.mod, self.dom):
363
+ if len(factor) == 2 and factor[1] <= self.mod // 2:
364
+ return self.dtype(factor[1])
365
+ return None
366
+
367
+
368
+ FF = GF = FiniteField
.venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`FractionField` class. """
2
+
3
+
4
+ from sympy.polys.domains.compositedomain import CompositeDomain
5
+ from sympy.polys.domains.field import Field
6
+ from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class FractionField(Field, CompositeDomain):
11
+ """A class for representing multivariate rational function fields. """
12
+
13
+ is_FractionField = is_Frac = True
14
+
15
+ has_assoc_Ring = True
16
+ has_assoc_Field = True
17
+
18
+ def __init__(self, domain_or_field, symbols=None, order=None):
19
+ from sympy.polys.fields import FracField
20
+
21
+ if isinstance(domain_or_field, FracField) and symbols is None and order is None:
22
+ field = domain_or_field
23
+ else:
24
+ field = FracField(symbols, domain_or_field, order)
25
+
26
+ self.field = field
27
+ self.dtype = field.dtype
28
+
29
+ self.gens = field.gens
30
+ self.ngens = field.ngens
31
+ self.symbols = field.symbols
32
+ self.domain = field.domain
33
+
34
+ # TODO: remove this
35
+ self.dom = self.domain
36
+
37
+ def new(self, element):
38
+ return self.field.field_new(element)
39
+
40
+ def of_type(self, element):
41
+ """Check if ``a`` is of type ``dtype``. """
42
+ return self.field.is_element(element)
43
+
44
+ @property
45
+ def zero(self):
46
+ return self.field.zero
47
+
48
+ @property
49
+ def one(self):
50
+ return self.field.one
51
+
52
+ @property
53
+ def order(self):
54
+ return self.field.order
55
+
56
+ def __str__(self):
57
+ return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')'
58
+
59
+ def __hash__(self):
60
+ return hash((self.__class__.__name__, self.field, self.domain, self.symbols))
61
+
62
+ def __eq__(self, other):
63
+ """Returns ``True`` if two domains are equivalent. """
64
+ if not isinstance(other, FractionField):
65
+ return NotImplemented
66
+ return self.field == other.field
67
+
68
+ def to_sympy(self, a):
69
+ """Convert ``a`` to a SymPy object. """
70
+ return a.as_expr()
71
+
72
+ def from_sympy(self, a):
73
+ """Convert SymPy's expression to ``dtype``. """
74
+ return self.field.from_expr(a)
75
+
76
+ def from_ZZ(K1, a, K0):
77
+ """Convert a Python ``int`` object to ``dtype``. """
78
+ return K1(K1.domain.convert(a, K0))
79
+
80
+ def from_ZZ_python(K1, a, K0):
81
+ """Convert a Python ``int`` object to ``dtype``. """
82
+ return K1(K1.domain.convert(a, K0))
83
+
84
+ def from_QQ(K1, a, K0):
85
+ """Convert a Python ``Fraction`` object to ``dtype``. """
86
+ dom = K1.domain
87
+ conv = dom.convert_from
88
+ if dom.is_ZZ:
89
+ return K1(conv(K0.numer(a), K0)) / K1(conv(K0.denom(a), K0))
90
+ else:
91
+ return K1(conv(a, K0))
92
+
93
+ def from_QQ_python(K1, a, K0):
94
+ """Convert a Python ``Fraction`` object to ``dtype``. """
95
+ return K1(K1.domain.convert(a, K0))
96
+
97
+ def from_ZZ_gmpy(K1, a, K0):
98
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
99
+ return K1(K1.domain.convert(a, K0))
100
+
101
+ def from_QQ_gmpy(K1, a, K0):
102
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
103
+ return K1(K1.domain.convert(a, K0))
104
+
105
+ def from_GaussianRationalField(K1, a, K0):
106
+ """Convert a ``GaussianRational`` object to ``dtype``. """
107
+ return K1(K1.domain.convert(a, K0))
108
+
109
+ def from_GaussianIntegerRing(K1, a, K0):
110
+ """Convert a ``GaussianInteger`` object to ``dtype``. """
111
+ return K1(K1.domain.convert(a, K0))
112
+
113
+ def from_RealField(K1, a, K0):
114
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
115
+ return K1(K1.domain.convert(a, K0))
116
+
117
+ def from_ComplexField(K1, a, K0):
118
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
119
+ return K1(K1.domain.convert(a, K0))
120
+
121
+ def from_AlgebraicField(K1, a, K0):
122
+ """Convert an algebraic number to ``dtype``. """
123
+ if K1.domain != K0:
124
+ a = K1.domain.convert_from(a, K0)
125
+ if a is not None:
126
+ return K1.new(a)
127
+
128
+ def from_PolynomialRing(K1, a, K0):
129
+ """Convert a polynomial to ``dtype``. """
130
+ if a.is_ground:
131
+ return K1.convert_from(a.coeff(1), K0.domain)
132
+ try:
133
+ return K1.new(a.set_ring(K1.field.ring))
134
+ except (CoercionFailed, GeneratorsError):
135
+ # XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y]
136
+ # and the poly a in K0 has non-integer coefficients.
137
+ # It seems that K1.new can handle this but K1.new doesn't work
138
+ # when K0.domain is an algebraic field...
139
+ try:
140
+ return K1.new(a)
141
+ except (CoercionFailed, GeneratorsError):
142
+ return None
143
+
144
+ def from_FractionField(K1, a, K0):
145
+ """Convert a rational function to ``dtype``. """
146
+ try:
147
+ return a.set_field(K1.field)
148
+ except (CoercionFailed, GeneratorsError):
149
+ return None
150
+
151
+ def get_ring(self):
152
+ """Returns a field associated with ``self``. """
153
+ return self.field.to_ring().to_domain()
154
+
155
+ def is_positive(self, a):
156
+ """Returns True if ``LC(a)`` is positive. """
157
+ return self.domain.is_positive(a.numer.LC)
158
+
159
+ def is_negative(self, a):
160
+ """Returns True if ``LC(a)`` is negative. """
161
+ return self.domain.is_negative(a.numer.LC)
162
+
163
+ def is_nonpositive(self, a):
164
+ """Returns True if ``LC(a)`` is non-positive. """
165
+ return self.domain.is_nonpositive(a.numer.LC)
166
+
167
+ def is_nonnegative(self, a):
168
+ """Returns True if ``LC(a)`` is non-negative. """
169
+ return self.domain.is_nonnegative(a.numer.LC)
170
+
171
+ def numer(self, a):
172
+ """Returns numerator of ``a``. """
173
+ return a.numer
174
+
175
+ def denom(self, a):
176
+ """Returns denominator of ``a``. """
177
+ return a.denom
178
+
179
+ def factorial(self, a):
180
+ """Returns factorial of ``a``. """
181
+ return self.dtype(self.domain.factorial(a))
.venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py ADDED
@@ -0,0 +1,706 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Domains of Gaussian type."""
2
+
3
+ from __future__ import annotations
4
+ from sympy.core.numbers import I
5
+ from sympy.polys.polyclasses import DMP
6
+ from sympy.polys.polyerrors import CoercionFailed
7
+ from sympy.polys.domains.integerring import ZZ
8
+ from sympy.polys.domains.rationalfield import QQ
9
+ from sympy.polys.domains.algebraicfield import AlgebraicField
10
+ from sympy.polys.domains.domain import Domain
11
+ from sympy.polys.domains.domainelement import DomainElement
12
+ from sympy.polys.domains.field import Field
13
+ from sympy.polys.domains.ring import Ring
14
+
15
+
16
+ class GaussianElement(DomainElement):
17
+ """Base class for elements of Gaussian type domains."""
18
+ base: Domain
19
+ _parent: Domain
20
+
21
+ __slots__ = ('x', 'y')
22
+
23
+ def __new__(cls, x, y=0):
24
+ conv = cls.base.convert
25
+ return cls.new(conv(x), conv(y))
26
+
27
+ @classmethod
28
+ def new(cls, x, y):
29
+ """Create a new GaussianElement of the same domain."""
30
+ obj = super().__new__(cls)
31
+ obj.x = x
32
+ obj.y = y
33
+ return obj
34
+
35
+ def parent(self):
36
+ """The domain that this is an element of (ZZ_I or QQ_I)"""
37
+ return self._parent
38
+
39
+ def __hash__(self):
40
+ return hash((self.x, self.y))
41
+
42
+ def __eq__(self, other):
43
+ if isinstance(other, self.__class__):
44
+ return self.x == other.x and self.y == other.y
45
+ else:
46
+ return NotImplemented
47
+
48
+ def __lt__(self, other):
49
+ if not isinstance(other, GaussianElement):
50
+ return NotImplemented
51
+ return [self.y, self.x] < [other.y, other.x]
52
+
53
+ def __pos__(self):
54
+ return self
55
+
56
+ def __neg__(self):
57
+ return self.new(-self.x, -self.y)
58
+
59
+ def __repr__(self):
60
+ return "%s(%s, %s)" % (self._parent.rep, self.x, self.y)
61
+
62
+ def __str__(self):
63
+ return str(self._parent.to_sympy(self))
64
+
65
+ @classmethod
66
+ def _get_xy(cls, other):
67
+ if not isinstance(other, cls):
68
+ try:
69
+ other = cls._parent.convert(other)
70
+ except CoercionFailed:
71
+ return None, None
72
+ return other.x, other.y
73
+
74
+ def __add__(self, other):
75
+ x, y = self._get_xy(other)
76
+ if x is not None:
77
+ return self.new(self.x + x, self.y + y)
78
+ else:
79
+ return NotImplemented
80
+
81
+ __radd__ = __add__
82
+
83
+ def __sub__(self, other):
84
+ x, y = self._get_xy(other)
85
+ if x is not None:
86
+ return self.new(self.x - x, self.y - y)
87
+ else:
88
+ return NotImplemented
89
+
90
+ def __rsub__(self, other):
91
+ x, y = self._get_xy(other)
92
+ if x is not None:
93
+ return self.new(x - self.x, y - self.y)
94
+ else:
95
+ return NotImplemented
96
+
97
+ def __mul__(self, other):
98
+ x, y = self._get_xy(other)
99
+ if x is not None:
100
+ return self.new(self.x*x - self.y*y, self.x*y + self.y*x)
101
+ else:
102
+ return NotImplemented
103
+
104
+ __rmul__ = __mul__
105
+
106
+ def __pow__(self, exp):
107
+ if exp == 0:
108
+ return self.new(1, 0)
109
+ if exp < 0:
110
+ self, exp = 1/self, -exp
111
+ if exp == 1:
112
+ return self
113
+ pow2 = self
114
+ prod = self if exp % 2 else self._parent.one
115
+ exp //= 2
116
+ while exp:
117
+ pow2 *= pow2
118
+ if exp % 2:
119
+ prod *= pow2
120
+ exp //= 2
121
+ return prod
122
+
123
+ def __bool__(self):
124
+ return bool(self.x) or bool(self.y)
125
+
126
+ def quadrant(self):
127
+ """Return quadrant index 0-3.
128
+
129
+ 0 is included in quadrant 0.
130
+ """
131
+ if self.y > 0:
132
+ return 0 if self.x > 0 else 1
133
+ elif self.y < 0:
134
+ return 2 if self.x < 0 else 3
135
+ else:
136
+ return 0 if self.x >= 0 else 2
137
+
138
+ def __rdivmod__(self, other):
139
+ try:
140
+ other = self._parent.convert(other)
141
+ except CoercionFailed:
142
+ return NotImplemented
143
+ else:
144
+ return other.__divmod__(self)
145
+
146
+ def __rtruediv__(self, other):
147
+ try:
148
+ other = QQ_I.convert(other)
149
+ except CoercionFailed:
150
+ return NotImplemented
151
+ else:
152
+ return other.__truediv__(self)
153
+
154
+ def __floordiv__(self, other):
155
+ qr = self.__divmod__(other)
156
+ return qr if qr is NotImplemented else qr[0]
157
+
158
+ def __rfloordiv__(self, other):
159
+ qr = self.__rdivmod__(other)
160
+ return qr if qr is NotImplemented else qr[0]
161
+
162
+ def __mod__(self, other):
163
+ qr = self.__divmod__(other)
164
+ return qr if qr is NotImplemented else qr[1]
165
+
166
+ def __rmod__(self, other):
167
+ qr = self.__rdivmod__(other)
168
+ return qr if qr is NotImplemented else qr[1]
169
+
170
+
171
+ class GaussianInteger(GaussianElement):
172
+ """Gaussian integer: domain element for :ref:`ZZ_I`
173
+
174
+ >>> from sympy import ZZ_I
175
+ >>> z = ZZ_I(2, 3)
176
+ >>> z
177
+ (2 + 3*I)
178
+ >>> type(z)
179
+ <class 'sympy.polys.domains.gaussiandomains.GaussianInteger'>
180
+ """
181
+ base = ZZ
182
+
183
+ def __truediv__(self, other):
184
+ """Return a Gaussian rational."""
185
+ return QQ_I.convert(self)/other
186
+
187
+ def __divmod__(self, other):
188
+ if not other:
189
+ raise ZeroDivisionError('divmod({}, 0)'.format(self))
190
+ x, y = self._get_xy(other)
191
+ if x is None:
192
+ return NotImplemented
193
+
194
+ # multiply self and other by x - I*y
195
+ # self/other == (a + I*b)/c
196
+ a, b = self.x*x + self.y*y, -self.x*y + self.y*x
197
+ c = x*x + y*y
198
+
199
+ # find integers qx and qy such that
200
+ # |a - qx*c| <= c/2 and |b - qy*c| <= c/2
201
+ qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c
202
+ qy = (2*b + c) // (2*c)
203
+
204
+ q = GaussianInteger(qx, qy)
205
+ # |self/other - q| < 1 since
206
+ # |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1
207
+
208
+ return q, self - q*other # |r| < |other|
209
+
210
+
211
+ class GaussianRational(GaussianElement):
212
+ """Gaussian rational: domain element for :ref:`QQ_I`
213
+
214
+ >>> from sympy import QQ_I, QQ
215
+ >>> z = QQ_I(QQ(2, 3), QQ(4, 5))
216
+ >>> z
217
+ (2/3 + 4/5*I)
218
+ >>> type(z)
219
+ <class 'sympy.polys.domains.gaussiandomains.GaussianRational'>
220
+ """
221
+ base = QQ
222
+
223
+ def __truediv__(self, other):
224
+ """Return a Gaussian rational."""
225
+ if not other:
226
+ raise ZeroDivisionError('{} / 0'.format(self))
227
+ x, y = self._get_xy(other)
228
+ if x is None:
229
+ return NotImplemented
230
+ c = x*x + y*y
231
+
232
+ return GaussianRational((self.x*x + self.y*y)/c,
233
+ (-self.x*y + self.y*x)/c)
234
+
235
+ def __divmod__(self, other):
236
+ try:
237
+ other = self._parent.convert(other)
238
+ except CoercionFailed:
239
+ return NotImplemented
240
+ if not other:
241
+ raise ZeroDivisionError('{} % 0'.format(self))
242
+ else:
243
+ return self/other, QQ_I.zero
244
+
245
+
246
+ class GaussianDomain():
247
+ """Base class for Gaussian domains."""
248
+ dom: Domain
249
+
250
+ is_Numerical = True
251
+ is_Exact = True
252
+
253
+ has_assoc_Ring = True
254
+ has_assoc_Field = True
255
+
256
+ def to_sympy(self, a):
257
+ """Convert ``a`` to a SymPy object. """
258
+ conv = self.dom.to_sympy
259
+ return conv(a.x) + I*conv(a.y)
260
+
261
+ def from_sympy(self, a):
262
+ """Convert a SymPy object to ``self.dtype``."""
263
+ r, b = a.as_coeff_Add()
264
+ x = self.dom.from_sympy(r) # may raise CoercionFailed
265
+ if not b:
266
+ return self.new(x, 0)
267
+ r, b = b.as_coeff_Mul()
268
+ y = self.dom.from_sympy(r)
269
+ if b is I:
270
+ return self.new(x, y)
271
+ else:
272
+ raise CoercionFailed("{} is not Gaussian".format(a))
273
+
274
+ def inject(self, *gens):
275
+ """Inject generators into this domain. """
276
+ return self.poly_ring(*gens)
277
+
278
+ def canonical_unit(self, d):
279
+ unit = self.units[-d.quadrant()] # - for inverse power
280
+ return unit
281
+
282
+ def is_negative(self, element):
283
+ """Returns ``False`` for any ``GaussianElement``. """
284
+ return False
285
+
286
+ def is_positive(self, element):
287
+ """Returns ``False`` for any ``GaussianElement``. """
288
+ return False
289
+
290
+ def is_nonnegative(self, element):
291
+ """Returns ``False`` for any ``GaussianElement``. """
292
+ return False
293
+
294
+ def is_nonpositive(self, element):
295
+ """Returns ``False`` for any ``GaussianElement``. """
296
+ return False
297
+
298
+ def from_ZZ_gmpy(K1, a, K0):
299
+ """Convert a GMPY mpz to ``self.dtype``."""
300
+ return K1(a)
301
+
302
+ def from_ZZ(K1, a, K0):
303
+ """Convert a ZZ_python element to ``self.dtype``."""
304
+ return K1(a)
305
+
306
+ def from_ZZ_python(K1, a, K0):
307
+ """Convert a ZZ_python element to ``self.dtype``."""
308
+ return K1(a)
309
+
310
+ def from_QQ(K1, a, K0):
311
+ """Convert a GMPY mpq to ``self.dtype``."""
312
+ return K1(a)
313
+
314
+ def from_QQ_gmpy(K1, a, K0):
315
+ """Convert a GMPY mpq to ``self.dtype``."""
316
+ return K1(a)
317
+
318
+ def from_QQ_python(K1, a, K0):
319
+ """Convert a QQ_python element to ``self.dtype``."""
320
+ return K1(a)
321
+
322
+ def from_AlgebraicField(K1, a, K0):
323
+ """Convert an element from ZZ<I> or QQ<I> to ``self.dtype``."""
324
+ if K0.ext.args[0] == I:
325
+ return K1.from_sympy(K0.to_sympy(a))
326
+
327
+
328
+ class GaussianIntegerRing(GaussianDomain, Ring):
329
+ r"""Ring of Gaussian integers ``ZZ_I``
330
+
331
+ The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]`
332
+ as a :py:class:`~.Domain` in the domain system (see
333
+ :ref:`polys-domainsintro`).
334
+
335
+ By default a :py:class:`~.Poly` created from an expression with
336
+ coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`)
337
+ will have the domain :ref:`ZZ_I`.
338
+
339
+ >>> from sympy import Poly, Symbol, I
340
+ >>> x = Symbol('x')
341
+ >>> p = Poly(x**2 + I)
342
+ >>> p
343
+ Poly(x**2 + I, x, domain='ZZ_I')
344
+ >>> p.domain
345
+ ZZ_I
346
+
347
+ The :ref:`ZZ_I` domain can be used to factorise polynomials that are
348
+ reducible over the Gaussian integers.
349
+
350
+ >>> from sympy import factor
351
+ >>> factor(x**2 + 1)
352
+ x**2 + 1
353
+ >>> factor(x**2 + 1, domain='ZZ_I')
354
+ (x - I)*(x + I)
355
+
356
+ The corresponding `field of fractions`_ is the domain of the Gaussian
357
+ rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_
358
+ of :ref:`QQ_I`.
359
+
360
+ >>> from sympy import ZZ_I, QQ_I
361
+ >>> ZZ_I.get_field()
362
+ QQ_I
363
+ >>> QQ_I.get_ring()
364
+ ZZ_I
365
+
366
+ When using the domain directly :ref:`ZZ_I` can be used as a constructor.
367
+
368
+ >>> ZZ_I(3, 4)
369
+ (3 + 4*I)
370
+ >>> ZZ_I(5)
371
+ (5 + 0*I)
372
+
373
+ The domain elements of :ref:`ZZ_I` are instances of
374
+ :py:class:`~.GaussianInteger` which support the rings operations
375
+ ``+,-,*,**``.
376
+
377
+ >>> z1 = ZZ_I(5, 1)
378
+ >>> z2 = ZZ_I(2, 3)
379
+ >>> z1
380
+ (5 + 1*I)
381
+ >>> z2
382
+ (2 + 3*I)
383
+ >>> z1 + z2
384
+ (7 + 4*I)
385
+ >>> z1 * z2
386
+ (7 + 17*I)
387
+ >>> z1 ** 2
388
+ (24 + 10*I)
389
+
390
+ Both floor (``//``) and modulo (``%``) division work with
391
+ :py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method).
392
+
393
+ >>> z3, z4 = ZZ_I(5), ZZ_I(1, 3)
394
+ >>> z3 // z4 # floor division
395
+ (1 + -1*I)
396
+ >>> z3 % z4 # modulo division (remainder)
397
+ (1 + -2*I)
398
+ >>> (z3//z4)*z4 + z3%z4 == z3
399
+ True
400
+
401
+ True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The
402
+ :py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when
403
+ exact division is possible.
404
+
405
+ >>> z1 / z2
406
+ (1 + -1*I)
407
+ >>> ZZ_I.exquo(z1, z2)
408
+ (1 + -1*I)
409
+ >>> z3 / z4
410
+ (1/2 + -3/2*I)
411
+ >>> ZZ_I.exquo(z3, z4)
412
+ Traceback (most recent call last):
413
+ ...
414
+ ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I
415
+
416
+ The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any
417
+ two elements.
418
+
419
+ >>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2))
420
+ (2 + 0*I)
421
+ >>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1))
422
+ (2 + 1*I)
423
+
424
+ .. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer
425
+ .. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor
426
+
427
+ """
428
+ dom = ZZ
429
+ mod = DMP([ZZ.one, ZZ.zero, ZZ.one], ZZ)
430
+ dtype = GaussianInteger
431
+ zero = dtype(ZZ(0), ZZ(0))
432
+ one = dtype(ZZ(1), ZZ(0))
433
+ imag_unit = dtype(ZZ(0), ZZ(1))
434
+ units = (one, imag_unit, -one, -imag_unit) # powers of i
435
+
436
+ rep = 'ZZ_I'
437
+
438
+ is_GaussianRing = True
439
+ is_ZZ_I = True
440
+ is_PID = True
441
+
442
+ def __init__(self): # override Domain.__init__
443
+ """For constructing ZZ_I."""
444
+
445
+ def __eq__(self, other):
446
+ """Returns ``True`` if two domains are equivalent. """
447
+ if isinstance(other, GaussianIntegerRing):
448
+ return True
449
+ else:
450
+ return NotImplemented
451
+
452
+ def __hash__(self):
453
+ """Compute hash code of ``self``. """
454
+ return hash('ZZ_I')
455
+
456
+ @property
457
+ def has_CharacteristicZero(self):
458
+ return True
459
+
460
+ def characteristic(self):
461
+ return 0
462
+
463
+ def get_ring(self):
464
+ """Returns a ring associated with ``self``. """
465
+ return self
466
+
467
+ def get_field(self):
468
+ """Returns a field associated with ``self``. """
469
+ return QQ_I
470
+
471
+ def normalize(self, d, *args):
472
+ """Return first quadrant element associated with ``d``.
473
+
474
+ Also multiply the other arguments by the same power of i.
475
+ """
476
+ unit = self.canonical_unit(d)
477
+ d *= unit
478
+ args = tuple(a*unit for a in args)
479
+ return (d,) + args if args else d
480
+
481
+ def gcd(self, a, b):
482
+ """Greatest common divisor of a and b over ZZ_I."""
483
+ while b:
484
+ a, b = b, a % b
485
+ return self.normalize(a)
486
+
487
+ def gcdex(self, a, b):
488
+ """Return x, y, g such that x * a + y * b = g = gcd(a, b)"""
489
+ x_a = self.one
490
+ x_b = self.zero
491
+ y_a = self.zero
492
+ y_b = self.one
493
+ while b:
494
+ q = a // b
495
+ a, b = b, a - q * b
496
+ x_a, x_b = x_b, x_a - q * x_b
497
+ y_a, y_b = y_b, y_a - q * y_b
498
+
499
+ a, x_a, y_a = self.normalize(a, x_a, y_a)
500
+ return x_a, y_a, a
501
+
502
+ def lcm(self, a, b):
503
+ """Least common multiple of a and b over ZZ_I."""
504
+ return (a * b) // self.gcd(a, b)
505
+
506
+ def from_GaussianIntegerRing(K1, a, K0):
507
+ """Convert a ZZ_I element to ZZ_I."""
508
+ return a
509
+
510
+ def from_GaussianRationalField(K1, a, K0):
511
+ """Convert a QQ_I element to ZZ_I."""
512
+ return K1.new(ZZ.convert(a.x), ZZ.convert(a.y))
513
+
514
+ ZZ_I = GaussianInteger._parent = GaussianIntegerRing()
515
+
516
+
517
+ class GaussianRationalField(GaussianDomain, Field):
518
+ r"""Field of Gaussian rationals ``QQ_I``
519
+
520
+ The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)`
521
+ as a :py:class:`~.Domain` in the domain system (see
522
+ :ref:`polys-domainsintro`).
523
+
524
+ By default a :py:class:`~.Poly` created from an expression with
525
+ coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`)
526
+ will have the domain :ref:`QQ_I`.
527
+
528
+ >>> from sympy import Poly, Symbol, I
529
+ >>> x = Symbol('x')
530
+ >>> p = Poly(x**2 + I/2)
531
+ >>> p
532
+ Poly(x**2 + I/2, x, domain='QQ_I')
533
+ >>> p.domain
534
+ QQ_I
535
+
536
+ The polys option ``gaussian=True`` can be used to specify that the domain
537
+ should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are
538
+ all integers.
539
+
540
+ >>> Poly(x**2)
541
+ Poly(x**2, x, domain='ZZ')
542
+ >>> Poly(x**2 + I)
543
+ Poly(x**2 + I, x, domain='ZZ_I')
544
+ >>> Poly(x**2/2)
545
+ Poly(1/2*x**2, x, domain='QQ')
546
+ >>> Poly(x**2, gaussian=True)
547
+ Poly(x**2, x, domain='QQ_I')
548
+ >>> Poly(x**2 + I, gaussian=True)
549
+ Poly(x**2 + I, x, domain='QQ_I')
550
+ >>> Poly(x**2/2, gaussian=True)
551
+ Poly(1/2*x**2, x, domain='QQ_I')
552
+
553
+ The :ref:`QQ_I` domain can be used to factorise polynomials that are
554
+ reducible over the Gaussian rationals.
555
+
556
+ >>> from sympy import factor, QQ_I
557
+ >>> factor(x**2/4 + 1)
558
+ (x**2 + 4)/4
559
+ >>> factor(x**2/4 + 1, domain='QQ_I')
560
+ (x - 2*I)*(x + 2*I)/4
561
+ >>> factor(x**2/4 + 1, domain=QQ_I)
562
+ (x - 2*I)*(x + 2*I)/4
563
+
564
+ It is also possible to specify the :ref:`QQ_I` domain explicitly with
565
+ polys functions like :py:func:`~.apart`.
566
+
567
+ >>> from sympy import apart
568
+ >>> apart(1/(1 + x**2))
569
+ 1/(x**2 + 1)
570
+ >>> apart(1/(1 + x**2), domain=QQ_I)
571
+ I/(2*(x + I)) - I/(2*(x - I))
572
+
573
+ The corresponding `ring of integers`_ is the domain of the Gaussian
574
+ integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_
575
+ of :ref:`ZZ_I`.
576
+
577
+ >>> from sympy import ZZ_I, QQ_I, QQ
578
+ >>> ZZ_I.get_field()
579
+ QQ_I
580
+ >>> QQ_I.get_ring()
581
+ ZZ_I
582
+
583
+ When using the domain directly :ref:`QQ_I` can be used as a constructor.
584
+
585
+ >>> QQ_I(3, 4)
586
+ (3 + 4*I)
587
+ >>> QQ_I(5)
588
+ (5 + 0*I)
589
+ >>> QQ_I(QQ(2, 3), QQ(4, 5))
590
+ (2/3 + 4/5*I)
591
+
592
+ The domain elements of :ref:`QQ_I` are instances of
593
+ :py:class:`~.GaussianRational` which support the field operations
594
+ ``+,-,*,**,/``.
595
+
596
+ >>> z1 = QQ_I(5, 1)
597
+ >>> z2 = QQ_I(2, QQ(1, 2))
598
+ >>> z1
599
+ (5 + 1*I)
600
+ >>> z2
601
+ (2 + 1/2*I)
602
+ >>> z1 + z2
603
+ (7 + 3/2*I)
604
+ >>> z1 * z2
605
+ (19/2 + 9/2*I)
606
+ >>> z2 ** 2
607
+ (15/4 + 2*I)
608
+
609
+ True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and
610
+ is always exact.
611
+
612
+ >>> z1 / z2
613
+ (42/17 + -2/17*I)
614
+ >>> QQ_I.exquo(z1, z2)
615
+ (42/17 + -2/17*I)
616
+ >>> z1 == (z1/z2)*z2
617
+ True
618
+
619
+ Both floor (``//``) and modulo (``%``) division can be used with
620
+ :py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`)
621
+ but division is always exact so there is no remainder.
622
+
623
+ >>> z1 // z2
624
+ (42/17 + -2/17*I)
625
+ >>> z1 % z2
626
+ (0 + 0*I)
627
+ >>> QQ_I.div(z1, z2)
628
+ ((42/17 + -2/17*I), (0 + 0*I))
629
+ >>> (z1//z2)*z2 + z1%z2 == z1
630
+ True
631
+
632
+ .. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational
633
+ """
634
+ dom = QQ
635
+ mod = DMP([QQ.one, QQ.zero, QQ.one], QQ)
636
+ dtype = GaussianRational
637
+ zero = dtype(QQ(0), QQ(0))
638
+ one = dtype(QQ(1), QQ(0))
639
+ imag_unit = dtype(QQ(0), QQ(1))
640
+ units = (one, imag_unit, -one, -imag_unit) # powers of i
641
+
642
+ rep = 'QQ_I'
643
+
644
+ is_GaussianField = True
645
+ is_QQ_I = True
646
+
647
+ def __init__(self): # override Domain.__init__
648
+ """For constructing QQ_I."""
649
+
650
+ def __eq__(self, other):
651
+ """Returns ``True`` if two domains are equivalent. """
652
+ if isinstance(other, GaussianRationalField):
653
+ return True
654
+ else:
655
+ return NotImplemented
656
+
657
+ def __hash__(self):
658
+ """Compute hash code of ``self``. """
659
+ return hash('QQ_I')
660
+
661
+ @property
662
+ def has_CharacteristicZero(self):
663
+ return True
664
+
665
+ def characteristic(self):
666
+ return 0
667
+
668
+ def get_ring(self):
669
+ """Returns a ring associated with ``self``. """
670
+ return ZZ_I
671
+
672
+ def get_field(self):
673
+ """Returns a field associated with ``self``. """
674
+ return self
675
+
676
+ def as_AlgebraicField(self):
677
+ """Get equivalent domain as an ``AlgebraicField``. """
678
+ return AlgebraicField(self.dom, I)
679
+
680
+ def numer(self, a):
681
+ """Get the numerator of ``a``."""
682
+ ZZ_I = self.get_ring()
683
+ return ZZ_I.convert(a * self.denom(a))
684
+
685
+ def denom(self, a):
686
+ """Get the denominator of ``a``."""
687
+ ZZ = self.dom.get_ring()
688
+ QQ = self.dom
689
+ ZZ_I = self.get_ring()
690
+ denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y))
691
+ return ZZ_I(denom_ZZ, ZZ.zero)
692
+
693
+ def from_GaussianIntegerRing(K1, a, K0):
694
+ """Convert a ZZ_I element to QQ_I."""
695
+ return K1.new(a.x, a.y)
696
+
697
+ def from_GaussianRationalField(K1, a, K0):
698
+ """Convert a QQ_I element to QQ_I."""
699
+ return a
700
+
701
+ def from_ComplexField(K1, a, K0):
702
+ """Convert a ComplexField element to QQ_I."""
703
+ return K1.new(QQ.convert(a.real), QQ.convert(a.imag))
704
+
705
+
706
+ QQ_I = GaussianRational._parent = GaussianRationalField()
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`GMPYFiniteField` class. """
2
+
3
+
4
+ from sympy.polys.domains.finitefield import FiniteField
5
+ from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing
6
+
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class GMPYFiniteField(FiniteField):
11
+ """Finite field based on GMPY integers. """
12
+
13
+ alias = 'FF_gmpy'
14
+
15
+ def __init__(self, mod, symmetric=True):
16
+ super().__init__(mod, GMPYIntegerRing(), symmetric)
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`GMPYIntegerRing` class. """
2
+
3
+
4
+ from sympy.polys.domains.groundtypes import (
5
+ GMPYInteger, SymPyInteger,
6
+ factorial as gmpy_factorial,
7
+ gmpy_gcdex, gmpy_gcd, gmpy_lcm, sqrt as gmpy_sqrt,
8
+ )
9
+ from sympy.core.numbers import int_valued
10
+ from sympy.polys.domains.integerring import IntegerRing
11
+ from sympy.polys.polyerrors import CoercionFailed
12
+ from sympy.utilities import public
13
+
14
+ @public
15
+ class GMPYIntegerRing(IntegerRing):
16
+ """Integer ring based on GMPY's ``mpz`` type.
17
+
18
+ This will be the implementation of :ref:`ZZ` if ``gmpy`` or ``gmpy2`` is
19
+ installed. Elements will be of type ``gmpy.mpz``.
20
+ """
21
+
22
+ dtype = GMPYInteger
23
+ zero = dtype(0)
24
+ one = dtype(1)
25
+ tp = type(one)
26
+ alias = 'ZZ_gmpy'
27
+
28
+ def __init__(self):
29
+ """Allow instantiation of this domain. """
30
+
31
+ def to_sympy(self, a):
32
+ """Convert ``a`` to a SymPy object. """
33
+ return SymPyInteger(int(a))
34
+
35
+ def from_sympy(self, a):
36
+ """Convert SymPy's Integer to ``dtype``. """
37
+ if a.is_Integer:
38
+ return GMPYInteger(a.p)
39
+ elif int_valued(a):
40
+ return GMPYInteger(int(a))
41
+ else:
42
+ raise CoercionFailed("expected an integer, got %s" % a)
43
+
44
+ def from_FF_python(K1, a, K0):
45
+ """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
46
+ return K0.to_int(a)
47
+
48
+ def from_ZZ_python(K1, a, K0):
49
+ """Convert Python's ``int`` to GMPY's ``mpz``. """
50
+ return GMPYInteger(a)
51
+
52
+ def from_QQ(K1, a, K0):
53
+ """Convert Python's ``Fraction`` to GMPY's ``mpz``. """
54
+ if a.denominator == 1:
55
+ return GMPYInteger(a.numerator)
56
+
57
+ def from_QQ_python(K1, a, K0):
58
+ """Convert Python's ``Fraction`` to GMPY's ``mpz``. """
59
+ if a.denominator == 1:
60
+ return GMPYInteger(a.numerator)
61
+
62
+ def from_FF_gmpy(K1, a, K0):
63
+ """Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
64
+ return K0.to_int(a)
65
+
66
+ def from_ZZ_gmpy(K1, a, K0):
67
+ """Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
68
+ return a
69
+
70
+ def from_QQ_gmpy(K1, a, K0):
71
+ """Convert GMPY ``mpq`` to GMPY's ``mpz``. """
72
+ if a.denominator == 1:
73
+ return a.numerator
74
+
75
+ def from_RealField(K1, a, K0):
76
+ """Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
77
+ p, q = K0.to_rational(a)
78
+
79
+ if q == 1:
80
+ return GMPYInteger(p)
81
+
82
+ def from_GaussianIntegerRing(K1, a, K0):
83
+ if a.y == 0:
84
+ return a.x
85
+
86
+ def gcdex(self, a, b):
87
+ """Compute extended GCD of ``a`` and ``b``. """
88
+ h, s, t = gmpy_gcdex(a, b)
89
+ return s, t, h
90
+
91
+ def gcd(self, a, b):
92
+ """Compute GCD of ``a`` and ``b``. """
93
+ return gmpy_gcd(a, b)
94
+
95
+ def lcm(self, a, b):
96
+ """Compute LCM of ``a`` and ``b``. """
97
+ return gmpy_lcm(a, b)
98
+
99
+ def sqrt(self, a):
100
+ """Compute square root of ``a``. """
101
+ return gmpy_sqrt(a)
102
+
103
+ def factorial(self, a):
104
+ """Compute factorial of ``a``. """
105
+ return gmpy_factorial(a)
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`GMPYRationalField` class. """
2
+
3
+
4
+ from sympy.polys.domains.groundtypes import (
5
+ GMPYRational, SymPyRational,
6
+ gmpy_numer, gmpy_denom, factorial as gmpy_factorial,
7
+ )
8
+ from sympy.polys.domains.rationalfield import RationalField
9
+ from sympy.polys.polyerrors import CoercionFailed
10
+ from sympy.utilities import public
11
+
12
+ @public
13
+ class GMPYRationalField(RationalField):
14
+ """Rational field based on GMPY's ``mpq`` type.
15
+
16
+ This will be the implementation of :ref:`QQ` if ``gmpy`` or ``gmpy2`` is
17
+ installed. Elements will be of type ``gmpy.mpq``.
18
+ """
19
+
20
+ dtype = GMPYRational
21
+ zero = dtype(0)
22
+ one = dtype(1)
23
+ tp = type(one)
24
+ alias = 'QQ_gmpy'
25
+
26
+ def __init__(self):
27
+ pass
28
+
29
+ def get_ring(self):
30
+ """Returns ring associated with ``self``. """
31
+ from sympy.polys.domains import GMPYIntegerRing
32
+ return GMPYIntegerRing()
33
+
34
+ def to_sympy(self, a):
35
+ """Convert ``a`` to a SymPy object. """
36
+ return SymPyRational(int(gmpy_numer(a)),
37
+ int(gmpy_denom(a)))
38
+
39
+ def from_sympy(self, a):
40
+ """Convert SymPy's Integer to ``dtype``. """
41
+ if a.is_Rational:
42
+ return GMPYRational(a.p, a.q)
43
+ elif a.is_Float:
44
+ from sympy.polys.domains import RR
45
+ return GMPYRational(*map(int, RR.to_rational(a)))
46
+ else:
47
+ raise CoercionFailed("expected ``Rational`` object, got %s" % a)
48
+
49
+ def from_ZZ_python(K1, a, K0):
50
+ """Convert a Python ``int`` object to ``dtype``. """
51
+ return GMPYRational(a)
52
+
53
+ def from_QQ_python(K1, a, K0):
54
+ """Convert a Python ``Fraction`` object to ``dtype``. """
55
+ return GMPYRational(a.numerator, a.denominator)
56
+
57
+ def from_ZZ_gmpy(K1, a, K0):
58
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
59
+ return GMPYRational(a)
60
+
61
+ def from_QQ_gmpy(K1, a, K0):
62
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
63
+ return a
64
+
65
+ def from_GaussianRationalField(K1, a, K0):
66
+ """Convert a ``GaussianElement`` object to ``dtype``. """
67
+ if a.y == 0:
68
+ return GMPYRational(a.x)
69
+
70
+ def from_RealField(K1, a, K0):
71
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
72
+ return GMPYRational(*map(int, K0.to_rational(a)))
73
+
74
+ def exquo(self, a, b):
75
+ """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
76
+ return GMPYRational(a) / GMPYRational(b)
77
+
78
+ def quo(self, a, b):
79
+ """Quotient of ``a`` and ``b``, implies ``__truediv__``. """
80
+ return GMPYRational(a) / GMPYRational(b)
81
+
82
+ def rem(self, a, b):
83
+ """Remainder of ``a`` and ``b``, implies nothing. """
84
+ return self.zero
85
+
86
+ def div(self, a, b):
87
+ """Division of ``a`` and ``b``, implies ``__truediv__``. """
88
+ return GMPYRational(a) / GMPYRational(b), self.zero
89
+
90
+ def numer(self, a):
91
+ """Returns numerator of ``a``. """
92
+ return a.numerator
93
+
94
+ def denom(self, a):
95
+ """Returns denominator of ``a``. """
96
+ return a.denominator
97
+
98
+ def factorial(self, a):
99
+ """Returns factorial of ``a``. """
100
+ return GMPYRational(gmpy_factorial(int(a)))
.venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Ground types for various mathematical domains in SymPy. """
2
+
3
+ import builtins
4
+ from sympy.external.gmpy import GROUND_TYPES, factorial, sqrt, is_square, sqrtrem
5
+
6
+ PythonInteger = builtins.int
7
+ PythonReal = builtins.float
8
+ PythonComplex = builtins.complex
9
+
10
+ from .pythonrational import PythonRational
11
+
12
+ from sympy.core.intfunc import (
13
+ igcdex as python_gcdex,
14
+ igcd2 as python_gcd,
15
+ ilcm as python_lcm,
16
+ )
17
+
18
+ from sympy.core.numbers import (Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational)
19
+
20
+
21
+ class _GMPYInteger:
22
+ def __init__(self, obj):
23
+ pass
24
+
25
+ class _GMPYRational:
26
+ def __init__(self, obj):
27
+ pass
28
+
29
+
30
+ if GROUND_TYPES == 'gmpy':
31
+
32
+ from gmpy2 import (
33
+ mpz as GMPYInteger,
34
+ mpq as GMPYRational,
35
+ numer as gmpy_numer,
36
+ denom as gmpy_denom,
37
+ gcdext as gmpy_gcdex,
38
+ gcd as gmpy_gcd,
39
+ lcm as gmpy_lcm,
40
+ qdiv as gmpy_qdiv,
41
+ )
42
+ gcdex = gmpy_gcdex
43
+ gcd = gmpy_gcd
44
+ lcm = gmpy_lcm
45
+
46
+ elif GROUND_TYPES == 'flint':
47
+
48
+ from flint import fmpz as _fmpz
49
+
50
+ GMPYInteger = _GMPYInteger
51
+ GMPYRational = _GMPYRational
52
+ gmpy_numer = None
53
+ gmpy_denom = None
54
+ gmpy_gcdex = None
55
+ gmpy_gcd = None
56
+ gmpy_lcm = None
57
+ gmpy_qdiv = None
58
+
59
+ def gcd(a, b):
60
+ return a.gcd(b)
61
+
62
+ def gcdex(a, b):
63
+ x, y, g = python_gcdex(a, b)
64
+ return _fmpz(x), _fmpz(y), _fmpz(g)
65
+
66
+ def lcm(a, b):
67
+ return a.lcm(b)
68
+
69
+ else:
70
+ GMPYInteger = _GMPYInteger
71
+ GMPYRational = _GMPYRational
72
+ gmpy_numer = None
73
+ gmpy_denom = None
74
+ gmpy_gcdex = None
75
+ gmpy_gcd = None
76
+ gmpy_lcm = None
77
+ gmpy_qdiv = None
78
+ gcdex = python_gcdex
79
+ gcd = python_gcd
80
+ lcm = python_lcm
81
+
82
+
83
+ __all__ = [
84
+ 'PythonInteger', 'PythonReal', 'PythonComplex',
85
+
86
+ 'PythonRational',
87
+
88
+ 'python_gcdex', 'python_gcd', 'python_lcm',
89
+
90
+ 'SymPyReal', 'SymPyInteger', 'SymPyRational',
91
+
92
+ 'GMPYInteger', 'GMPYRational', 'gmpy_numer',
93
+ 'gmpy_denom', 'gmpy_gcdex', 'gmpy_gcd', 'gmpy_lcm',
94
+ 'gmpy_qdiv',
95
+
96
+ 'factorial', 'sqrt', 'is_square', 'sqrtrem',
97
+
98
+ 'GMPYInteger', 'GMPYRational',
99
+ ]
.venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`IntegerRing` class. """
2
+
3
+ from sympy.external.gmpy import MPZ, GROUND_TYPES
4
+
5
+ from sympy.core.numbers import int_valued
6
+ from sympy.polys.domains.groundtypes import (
7
+ SymPyInteger,
8
+ factorial,
9
+ gcdex, gcd, lcm, sqrt, is_square, sqrtrem,
10
+ )
11
+
12
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
13
+ from sympy.polys.domains.ring import Ring
14
+ from sympy.polys.domains.simpledomain import SimpleDomain
15
+ from sympy.polys.polyerrors import CoercionFailed
16
+ from sympy.utilities import public
17
+
18
+ import math
19
+
20
+ @public
21
+ class IntegerRing(Ring, CharacteristicZero, SimpleDomain):
22
+ r"""The domain ``ZZ`` representing the integers `\mathbb{Z}`.
23
+
24
+ The :py:class:`IntegerRing` class represents the ring of integers as a
25
+ :py:class:`~.Domain` in the domain system. :py:class:`IntegerRing` is a
26
+ super class of :py:class:`PythonIntegerRing` and
27
+ :py:class:`GMPYIntegerRing` one of which will be the implementation for
28
+ :ref:`ZZ` depending on whether or not ``gmpy`` or ``gmpy2`` is installed.
29
+
30
+ See also
31
+ ========
32
+
33
+ Domain
34
+ """
35
+
36
+ rep = 'ZZ'
37
+ alias = 'ZZ'
38
+ dtype = MPZ
39
+ zero = dtype(0)
40
+ one = dtype(1)
41
+ tp = type(one)
42
+
43
+
44
+ is_IntegerRing = is_ZZ = True
45
+ is_Numerical = True
46
+ is_PID = True
47
+
48
+ has_assoc_Ring = True
49
+ has_assoc_Field = True
50
+
51
+ def __init__(self):
52
+ """Allow instantiation of this domain. """
53
+
54
+ def __eq__(self, other):
55
+ """Returns ``True`` if two domains are equivalent. """
56
+ if isinstance(other, IntegerRing):
57
+ return True
58
+ else:
59
+ return NotImplemented
60
+
61
+ def __hash__(self):
62
+ """Compute a hash value for this domain. """
63
+ return hash('ZZ')
64
+
65
+ def to_sympy(self, a):
66
+ """Convert ``a`` to a SymPy object. """
67
+ return SymPyInteger(int(a))
68
+
69
+ def from_sympy(self, a):
70
+ """Convert SymPy's Integer to ``dtype``. """
71
+ if a.is_Integer:
72
+ return MPZ(a.p)
73
+ elif int_valued(a):
74
+ return MPZ(int(a))
75
+ else:
76
+ raise CoercionFailed("expected an integer, got %s" % a)
77
+
78
+ def get_field(self):
79
+ r"""Return the associated field of fractions :ref:`QQ`
80
+
81
+ Returns
82
+ =======
83
+
84
+ :ref:`QQ`:
85
+ The associated field of fractions :ref:`QQ`, a
86
+ :py:class:`~.Domain` representing the rational numbers
87
+ `\mathbb{Q}`.
88
+
89
+ Examples
90
+ ========
91
+
92
+ >>> from sympy import ZZ
93
+ >>> ZZ.get_field()
94
+ QQ
95
+ """
96
+ from sympy.polys.domains import QQ
97
+ return QQ
98
+
99
+ def algebraic_field(self, *extension, alias=None):
100
+ r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
101
+
102
+ Parameters
103
+ ==========
104
+
105
+ *extension : One or more :py:class:`~.Expr`.
106
+ Generators of the extension. These should be expressions that are
107
+ algebraic over `\mathbb{Q}`.
108
+
109
+ alias : str, :py:class:`~.Symbol`, None, optional (default=None)
110
+ If provided, this will be used as the alias symbol for the
111
+ primitive element of the returned :py:class:`~.AlgebraicField`.
112
+
113
+ Returns
114
+ =======
115
+
116
+ :py:class:`~.AlgebraicField`
117
+ A :py:class:`~.Domain` representing the algebraic field extension.
118
+
119
+ Examples
120
+ ========
121
+
122
+ >>> from sympy import ZZ, sqrt
123
+ >>> ZZ.algebraic_field(sqrt(2))
124
+ QQ<sqrt(2)>
125
+ """
126
+ return self.get_field().algebraic_field(*extension, alias=alias)
127
+
128
+ def from_AlgebraicField(K1, a, K0):
129
+ """Convert a :py:class:`~.ANP` object to :ref:`ZZ`.
130
+
131
+ See :py:meth:`~.Domain.convert`.
132
+ """
133
+ if a.is_ground:
134
+ return K1.convert(a.LC(), K0.dom)
135
+
136
+ def log(self, a, b):
137
+ r"""Logarithm of *a* to the base *b*.
138
+
139
+ Parameters
140
+ ==========
141
+
142
+ a: number
143
+ b: number
144
+
145
+ Returns
146
+ =======
147
+
148
+ $\\lfloor\log(a, b)\\rfloor$:
149
+ Floor of the logarithm of *a* to the base *b*
150
+
151
+ Examples
152
+ ========
153
+
154
+ >>> from sympy import ZZ
155
+ >>> ZZ.log(ZZ(8), ZZ(2))
156
+ 3
157
+ >>> ZZ.log(ZZ(9), ZZ(2))
158
+ 3
159
+
160
+ Notes
161
+ =====
162
+
163
+ This function uses ``math.log`` which is based on ``float`` so it will
164
+ fail for large integer arguments.
165
+ """
166
+ return self.dtype(int(math.log(int(a), b)))
167
+
168
+ def from_FF(K1, a, K0):
169
+ """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
170
+ return MPZ(K0.to_int(a))
171
+
172
+ def from_FF_python(K1, a, K0):
173
+ """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
174
+ return MPZ(K0.to_int(a))
175
+
176
+ def from_ZZ(K1, a, K0):
177
+ """Convert Python's ``int`` to GMPY's ``mpz``. """
178
+ return MPZ(a)
179
+
180
+ def from_ZZ_python(K1, a, K0):
181
+ """Convert Python's ``int`` to GMPY's ``mpz``. """
182
+ return MPZ(a)
183
+
184
+ def from_QQ(K1, a, K0):
185
+ """Convert Python's ``Fraction`` to GMPY's ``mpz``. """
186
+ if a.denominator == 1:
187
+ return MPZ(a.numerator)
188
+
189
+ def from_QQ_python(K1, a, K0):
190
+ """Convert Python's ``Fraction`` to GMPY's ``mpz``. """
191
+ if a.denominator == 1:
192
+ return MPZ(a.numerator)
193
+
194
+ def from_FF_gmpy(K1, a, K0):
195
+ """Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
196
+ return MPZ(K0.to_int(a))
197
+
198
+ def from_ZZ_gmpy(K1, a, K0):
199
+ """Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
200
+ return a
201
+
202
+ def from_QQ_gmpy(K1, a, K0):
203
+ """Convert GMPY ``mpq`` to GMPY's ``mpz``. """
204
+ if a.denominator == 1:
205
+ return a.numerator
206
+
207
+ def from_RealField(K1, a, K0):
208
+ """Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
209
+ p, q = K0.to_rational(a)
210
+
211
+ if q == 1:
212
+ # XXX: If MPZ is flint.fmpz and p is a gmpy2.mpz, then we need
213
+ # to convert via int because fmpz and mpz do not know about each
214
+ # other.
215
+ return MPZ(int(p))
216
+
217
+ def from_GaussianIntegerRing(K1, a, K0):
218
+ if a.y == 0:
219
+ return a.x
220
+
221
+ def from_EX(K1, a, K0):
222
+ """Convert ``Expression`` to GMPY's ``mpz``. """
223
+ if a.is_Integer:
224
+ return K1.from_sympy(a)
225
+
226
+ def gcdex(self, a, b):
227
+ """Compute extended GCD of ``a`` and ``b``. """
228
+ h, s, t = gcdex(a, b)
229
+ # XXX: This conditional logic should be handled somewhere else.
230
+ if GROUND_TYPES == 'gmpy':
231
+ return s, t, h
232
+ else:
233
+ return h, s, t
234
+
235
+ def gcd(self, a, b):
236
+ """Compute GCD of ``a`` and ``b``. """
237
+ return gcd(a, b)
238
+
239
+ def lcm(self, a, b):
240
+ """Compute LCM of ``a`` and ``b``. """
241
+ return lcm(a, b)
242
+
243
+ def sqrt(self, a):
244
+ """Compute square root of ``a``. """
245
+ return sqrt(a)
246
+
247
+ def is_square(self, a):
248
+ """Return ``True`` if ``a`` is a square.
249
+
250
+ Explanation
251
+ ===========
252
+ An integer is a square if and only if there exists an integer
253
+ ``b`` such that ``b * b == a``.
254
+ """
255
+ return is_square(a)
256
+
257
+ def exsqrt(self, a):
258
+ """Non-negative square root of ``a`` if ``a`` is a square.
259
+
260
+ See also
261
+ ========
262
+ is_square
263
+ """
264
+ if a < 0:
265
+ return None
266
+ root, rem = sqrtrem(a)
267
+ if rem != 0:
268
+ return None
269
+ return root
270
+
271
+ def factorial(self, a):
272
+ """Compute factorial of ``a``. """
273
+ return factorial(a)
274
+
275
+
276
+ ZZ = IntegerRing()
.venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`ModularInteger` class. """
2
+
3
+ from __future__ import annotations
4
+ from typing import Any
5
+
6
+ import operator
7
+
8
+ from sympy.polys.polyutils import PicklableWithSlots
9
+ from sympy.polys.polyerrors import CoercionFailed
10
+ from sympy.polys.domains.domainelement import DomainElement
11
+
12
+ from sympy.utilities import public
13
+ from sympy.utilities.exceptions import sympy_deprecation_warning
14
+
15
+ @public
16
+ class ModularInteger(PicklableWithSlots, DomainElement):
17
+ """A class representing a modular integer. """
18
+
19
+ mod, dom, sym, _parent = None, None, None, None
20
+
21
+ __slots__ = ('val',)
22
+
23
+ def parent(self):
24
+ return self._parent
25
+
26
+ def __init__(self, val):
27
+ if isinstance(val, self.__class__):
28
+ self.val = val.val % self.mod
29
+ else:
30
+ self.val = self.dom.convert(val) % self.mod
31
+
32
+ def modulus(self):
33
+ return self.mod
34
+
35
+ def __hash__(self):
36
+ return hash((self.val, self.mod))
37
+
38
+ def __repr__(self):
39
+ return "%s(%s)" % (self.__class__.__name__, self.val)
40
+
41
+ def __str__(self):
42
+ return "%s mod %s" % (self.val, self.mod)
43
+
44
+ def __int__(self):
45
+ return int(self.val)
46
+
47
+ def to_int(self):
48
+
49
+ sympy_deprecation_warning(
50
+ """ModularInteger.to_int() is deprecated.
51
+
52
+ Use int(a) or K = GF(p) and K.to_int(a) instead of a.to_int().
53
+ """,
54
+ deprecated_since_version="1.13",
55
+ active_deprecations_target="modularinteger-to-int",
56
+ )
57
+
58
+ if self.sym:
59
+ if self.val <= self.mod // 2:
60
+ return self.val
61
+ else:
62
+ return self.val - self.mod
63
+ else:
64
+ return self.val
65
+
66
+ def __pos__(self):
67
+ return self
68
+
69
+ def __neg__(self):
70
+ return self.__class__(-self.val)
71
+
72
+ @classmethod
73
+ def _get_val(cls, other):
74
+ if isinstance(other, cls):
75
+ return other.val
76
+ else:
77
+ try:
78
+ return cls.dom.convert(other)
79
+ except CoercionFailed:
80
+ return None
81
+
82
+ def __add__(self, other):
83
+ val = self._get_val(other)
84
+
85
+ if val is not None:
86
+ return self.__class__(self.val + val)
87
+ else:
88
+ return NotImplemented
89
+
90
+ def __radd__(self, other):
91
+ return self.__add__(other)
92
+
93
+ def __sub__(self, other):
94
+ val = self._get_val(other)
95
+
96
+ if val is not None:
97
+ return self.__class__(self.val - val)
98
+ else:
99
+ return NotImplemented
100
+
101
+ def __rsub__(self, other):
102
+ return (-self).__add__(other)
103
+
104
+ def __mul__(self, other):
105
+ val = self._get_val(other)
106
+
107
+ if val is not None:
108
+ return self.__class__(self.val * val)
109
+ else:
110
+ return NotImplemented
111
+
112
+ def __rmul__(self, other):
113
+ return self.__mul__(other)
114
+
115
+ def __truediv__(self, other):
116
+ val = self._get_val(other)
117
+
118
+ if val is not None:
119
+ return self.__class__(self.val * self._invert(val))
120
+ else:
121
+ return NotImplemented
122
+
123
+ def __rtruediv__(self, other):
124
+ return self.invert().__mul__(other)
125
+
126
+ def __mod__(self, other):
127
+ val = self._get_val(other)
128
+
129
+ if val is not None:
130
+ return self.__class__(self.val % val)
131
+ else:
132
+ return NotImplemented
133
+
134
+ def __rmod__(self, other):
135
+ val = self._get_val(other)
136
+
137
+ if val is not None:
138
+ return self.__class__(val % self.val)
139
+ else:
140
+ return NotImplemented
141
+
142
+ def __pow__(self, exp):
143
+ if not exp:
144
+ return self.__class__(self.dom.one)
145
+
146
+ if exp < 0:
147
+ val, exp = self.invert().val, -exp
148
+ else:
149
+ val = self.val
150
+
151
+ return self.__class__(pow(val, int(exp), self.mod))
152
+
153
+ def _compare(self, other, op):
154
+ val = self._get_val(other)
155
+
156
+ if val is None:
157
+ return NotImplemented
158
+
159
+ return op(self.val, val % self.mod)
160
+
161
+ def _compare_deprecated(self, other, op):
162
+ val = self._get_val(other)
163
+
164
+ if val is None:
165
+ return NotImplemented
166
+
167
+ sympy_deprecation_warning(
168
+ """Ordered comparisons with modular integers are deprecated.
169
+
170
+ Use e.g. int(a) < int(b) instead of a < b.
171
+ """,
172
+ deprecated_since_version="1.13",
173
+ active_deprecations_target="modularinteger-compare",
174
+ stacklevel=4,
175
+ )
176
+
177
+ return op(self.val, val % self.mod)
178
+
179
+ def __eq__(self, other):
180
+ return self._compare(other, operator.eq)
181
+
182
+ def __ne__(self, other):
183
+ return self._compare(other, operator.ne)
184
+
185
+ def __lt__(self, other):
186
+ return self._compare_deprecated(other, operator.lt)
187
+
188
+ def __le__(self, other):
189
+ return self._compare_deprecated(other, operator.le)
190
+
191
+ def __gt__(self, other):
192
+ return self._compare_deprecated(other, operator.gt)
193
+
194
+ def __ge__(self, other):
195
+ return self._compare_deprecated(other, operator.ge)
196
+
197
+ def __bool__(self):
198
+ return bool(self.val)
199
+
200
+ @classmethod
201
+ def _invert(cls, value):
202
+ return cls.dom.invert(value, cls.mod)
203
+
204
+ def invert(self):
205
+ return self.__class__(self._invert(self.val))
206
+
207
+ _modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {}
208
+
209
+ def ModularIntegerFactory(_mod, _dom, _sym, parent):
210
+ """Create custom class for specific integer modulus."""
211
+ try:
212
+ _mod = _dom.convert(_mod)
213
+ except CoercionFailed:
214
+ ok = False
215
+ else:
216
+ ok = True
217
+
218
+ if not ok or _mod < 1:
219
+ raise ValueError("modulus must be a positive integer, got %s" % _mod)
220
+
221
+ key = _mod, _dom, _sym
222
+
223
+ try:
224
+ cls = _modular_integer_cache[key]
225
+ except KeyError:
226
+ class cls(ModularInteger):
227
+ mod, dom, sym = _mod, _dom, _sym
228
+ _parent = parent
229
+
230
+ if _sym:
231
+ cls.__name__ = "SymmetricModularIntegerMod%s" % _mod
232
+ else:
233
+ cls.__name__ = "ModularIntegerMod%s" % _mod
234
+
235
+ _modular_integer_cache[key] = cls
236
+
237
+ return cls
.venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # This module is deprecated and should not be used any more. The actual
3
+ # implementation of RR and CC now uses mpmath's mpf and mpc types directly.
4
+ #
5
+ """Real and complex elements. """
6
+
7
+
8
+ from sympy.external.gmpy import MPQ
9
+ from sympy.polys.domains.domainelement import DomainElement
10
+ from sympy.utilities import public
11
+
12
+ from mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant
13
+ from mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan,
14
+ round_nearest, mpf_mul, repr_dps, int_types,
15
+ from_int, from_float, from_str, to_rational)
16
+
17
+
18
+ @public
19
+ class RealElement(_mpf, DomainElement):
20
+ """An element of a real domain. """
21
+
22
+ __slots__ = ('__mpf__',)
23
+
24
+ def _set_mpf(self, val):
25
+ self.__mpf__ = val
26
+
27
+ _mpf_ = property(lambda self: self.__mpf__, _set_mpf)
28
+
29
+ def parent(self):
30
+ return self.context._parent
31
+
32
+ @public
33
+ class ComplexElement(_mpc, DomainElement):
34
+ """An element of a complex domain. """
35
+
36
+ __slots__ = ('__mpc__',)
37
+
38
+ def _set_mpc(self, val):
39
+ self.__mpc__ = val
40
+
41
+ _mpc_ = property(lambda self: self.__mpc__, _set_mpc)
42
+
43
+ def parent(self):
44
+ return self.context._parent
45
+
46
+ new = object.__new__
47
+
48
+ @public
49
+ class MPContext(PythonMPContext):
50
+
51
+ def __init__(ctx, prec=53, dps=None, tol=None, real=False):
52
+ ctx._prec_rounding = [prec, round_nearest]
53
+
54
+ if dps is None:
55
+ ctx._set_prec(prec)
56
+ else:
57
+ ctx._set_dps(dps)
58
+
59
+ ctx.mpf = RealElement
60
+ ctx.mpc = ComplexElement
61
+ ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
62
+ ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding]
63
+
64
+ if real:
65
+ ctx.mpf.context = ctx
66
+ else:
67
+ ctx.mpc.context = ctx
68
+
69
+ ctx.constant = _constant
70
+ ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
71
+ ctx.constant.context = ctx
72
+
73
+ ctx.types = [ctx.mpf, ctx.mpc, ctx.constant]
74
+ ctx.trap_complex = True
75
+ ctx.pretty = True
76
+
77
+ if tol is None:
78
+ ctx.tol = ctx._make_tol()
79
+ elif tol is False:
80
+ ctx.tol = fzero
81
+ else:
82
+ ctx.tol = ctx._convert_tol(tol)
83
+
84
+ ctx.tolerance = ctx.make_mpf(ctx.tol)
85
+
86
+ if not ctx.tolerance:
87
+ ctx.max_denom = 1000000
88
+ else:
89
+ ctx.max_denom = int(1/ctx.tolerance)
90
+
91
+ ctx.zero = ctx.make_mpf(fzero)
92
+ ctx.one = ctx.make_mpf(fone)
93
+ ctx.j = ctx.make_mpc((fzero, fone))
94
+ ctx.inf = ctx.make_mpf(finf)
95
+ ctx.ninf = ctx.make_mpf(fninf)
96
+ ctx.nan = ctx.make_mpf(fnan)
97
+
98
+ def _make_tol(ctx):
99
+ hundred = (0, 25, 2, 5)
100
+ eps = (0, MPZ_ONE, 1-ctx.prec, 1)
101
+ return mpf_mul(hundred, eps)
102
+
103
+ def make_tol(ctx):
104
+ return ctx.make_mpf(ctx._make_tol())
105
+
106
+ def _convert_tol(ctx, tol):
107
+ if isinstance(tol, int_types):
108
+ return from_int(tol)
109
+ if isinstance(tol, float):
110
+ return from_float(tol)
111
+ if hasattr(tol, "_mpf_"):
112
+ return tol._mpf_
113
+ prec, rounding = ctx._prec_rounding
114
+ if isinstance(tol, str):
115
+ return from_str(tol, prec, rounding)
116
+ raise ValueError("expected a real number, got %s" % tol)
117
+
118
+ def _convert_fallback(ctx, x, strings):
119
+ raise TypeError("cannot create mpf from " + repr(x))
120
+
121
+ @property
122
+ def _repr_digits(ctx):
123
+ return repr_dps(ctx._prec)
124
+
125
+ @property
126
+ def _str_digits(ctx):
127
+ return ctx._dps
128
+
129
+ def to_rational(ctx, s, limit=True):
130
+ p, q = to_rational(s._mpf_)
131
+
132
+ # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
133
+ # to_rational() function returns a gmpy2.mpz instance and if MPQ is
134
+ # flint.fmpq then MPQ(p, q) will fail.
135
+ p = int(p)
136
+
137
+ if not limit or q <= ctx.max_denom:
138
+ return p, q
139
+
140
+ p0, q0, p1, q1 = 0, 1, 1, 0
141
+ n, d = p, q
142
+
143
+ while True:
144
+ a = n//d
145
+ q2 = q0 + a*q1
146
+ if q2 > ctx.max_denom:
147
+ break
148
+ p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
149
+ n, d = d, n - a*d
150
+
151
+ k = (ctx.max_denom - q0)//q1
152
+
153
+ number = MPQ(p, q)
154
+ bound1 = MPQ(p0 + k*p1, q0 + k*q1)
155
+ bound2 = MPQ(p1, q1)
156
+
157
+ if not bound2 or not bound1:
158
+ return p, q
159
+ elif abs(bound2 - number) <= abs(bound1 - number):
160
+ return bound2.numerator, bound2.denominator
161
+ else:
162
+ return bound1.numerator, bound1.denominator
163
+
164
+ def almosteq(ctx, s, t, rel_eps=None, abs_eps=None):
165
+ t = ctx.convert(t)
166
+ if abs_eps is None and rel_eps is None:
167
+ rel_eps = abs_eps = ctx.tolerance or ctx.make_tol()
168
+ if abs_eps is None:
169
+ abs_eps = ctx.convert(rel_eps)
170
+ elif rel_eps is None:
171
+ rel_eps = ctx.convert(abs_eps)
172
+ diff = abs(s-t)
173
+ if diff <= abs_eps:
174
+ return True
175
+ abss = abs(s)
176
+ abst = abs(t)
177
+ if abss < abst:
178
+ err = diff/abst
179
+ else:
180
+ err = diff/abss
181
+ return err <= rel_eps
.venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`FractionField` class. """
2
+
3
+
4
+ from sympy.polys.domains.field import Field
5
+ from sympy.polys.domains.compositedomain import CompositeDomain
6
+ from sympy.polys.polyclasses import DMF
7
+ from sympy.polys.polyerrors import GeneratorsNeeded
8
+ from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
9
+ from sympy.utilities import public
10
+
11
+ @public
12
+ class FractionField(Field, CompositeDomain):
13
+ """A class for representing rational function fields. """
14
+
15
+ dtype = DMF
16
+ is_FractionField = is_Frac = True
17
+
18
+ has_assoc_Ring = True
19
+ has_assoc_Field = True
20
+
21
+ def __init__(self, dom, *gens):
22
+ if not gens:
23
+ raise GeneratorsNeeded("generators not specified")
24
+
25
+ lev = len(gens) - 1
26
+ self.ngens = len(gens)
27
+
28
+ self.zero = self.dtype.zero(lev, dom)
29
+ self.one = self.dtype.one(lev, dom)
30
+
31
+ self.domain = self.dom = dom
32
+ self.symbols = self.gens = gens
33
+
34
+ def set_domain(self, dom):
35
+ """Make a new fraction field with given domain. """
36
+ return self.__class__(dom, *self.gens)
37
+
38
+ def new(self, element):
39
+ return self.dtype(element, self.dom, len(self.gens) - 1)
40
+
41
+ def __str__(self):
42
+ return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')'
43
+
44
+ def __hash__(self):
45
+ return hash((self.__class__.__name__, self.dtype, self.dom, self.gens))
46
+
47
+ def __eq__(self, other):
48
+ """Returns ``True`` if two domains are equivalent. """
49
+ return isinstance(other, FractionField) and \
50
+ self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens
51
+
52
+ def to_sympy(self, a):
53
+ """Convert ``a`` to a SymPy object. """
54
+ return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
55
+ basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
56
+
57
+ def from_sympy(self, a):
58
+ """Convert SymPy's expression to ``dtype``. """
59
+ p, q = a.as_numer_denom()
60
+
61
+ num, _ = dict_from_basic(p, gens=self.gens)
62
+ den, _ = dict_from_basic(q, gens=self.gens)
63
+
64
+ for k, v in num.items():
65
+ num[k] = self.dom.from_sympy(v)
66
+
67
+ for k, v in den.items():
68
+ den[k] = self.dom.from_sympy(v)
69
+
70
+ return self((num, den)).cancel()
71
+
72
+ def from_ZZ(K1, a, K0):
73
+ """Convert a Python ``int`` object to ``dtype``. """
74
+ return K1(K1.dom.convert(a, K0))
75
+
76
+ def from_ZZ_python(K1, a, K0):
77
+ """Convert a Python ``int`` object to ``dtype``. """
78
+ return K1(K1.dom.convert(a, K0))
79
+
80
+ def from_QQ_python(K1, a, K0):
81
+ """Convert a Python ``Fraction`` object to ``dtype``. """
82
+ return K1(K1.dom.convert(a, K0))
83
+
84
+ def from_ZZ_gmpy(K1, a, K0):
85
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
86
+ return K1(K1.dom.convert(a, K0))
87
+
88
+ def from_QQ_gmpy(K1, a, K0):
89
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
90
+ return K1(K1.dom.convert(a, K0))
91
+
92
+ def from_RealField(K1, a, K0):
93
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
94
+ return K1(K1.dom.convert(a, K0))
95
+
96
+ def from_GlobalPolynomialRing(K1, a, K0):
97
+ """Convert a ``DMF`` object to ``dtype``. """
98
+ if K1.gens == K0.gens:
99
+ if K1.dom == K0.dom:
100
+ return K1(a.to_list())
101
+ else:
102
+ return K1(a.convert(K1.dom).to_list())
103
+ else:
104
+ monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
105
+
106
+ if K1.dom != K0.dom:
107
+ coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
108
+
109
+ return K1(dict(zip(monoms, coeffs)))
110
+
111
+ def from_FractionField(K1, a, K0):
112
+ """
113
+ Convert a fraction field element to another fraction field.
114
+
115
+ Examples
116
+ ========
117
+
118
+ >>> from sympy.polys.polyclasses import DMF
119
+ >>> from sympy.polys.domains import ZZ, QQ
120
+ >>> from sympy.abc import x
121
+
122
+ >>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ)
123
+
124
+ >>> QQx = QQ.old_frac_field(x)
125
+ >>> ZZx = ZZ.old_frac_field(x)
126
+
127
+ >>> QQx.from_FractionField(f, ZZx)
128
+ DMF([1, 2], [1, 1], QQ)
129
+
130
+ """
131
+ if K1.gens == K0.gens:
132
+ if K1.dom == K0.dom:
133
+ return a
134
+ else:
135
+ return K1((a.numer().convert(K1.dom).to_list(),
136
+ a.denom().convert(K1.dom).to_list()))
137
+ elif set(K0.gens).issubset(K1.gens):
138
+ nmonoms, ncoeffs = _dict_reorder(
139
+ a.numer().to_dict(), K0.gens, K1.gens)
140
+ dmonoms, dcoeffs = _dict_reorder(
141
+ a.denom().to_dict(), K0.gens, K1.gens)
142
+
143
+ if K1.dom != K0.dom:
144
+ ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ]
145
+ dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ]
146
+
147
+ return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs))))
148
+
149
+ def get_ring(self):
150
+ """Returns a ring associated with ``self``. """
151
+ from sympy.polys.domains import PolynomialRing
152
+ return PolynomialRing(self.dom, *self.gens)
153
+
154
+ def poly_ring(self, *gens):
155
+ """Returns a polynomial ring, i.e. `K[X]`. """
156
+ raise NotImplementedError('nested domains not allowed')
157
+
158
+ def frac_field(self, *gens):
159
+ """Returns a fraction field, i.e. `K(X)`. """
160
+ raise NotImplementedError('nested domains not allowed')
161
+
162
+ def is_positive(self, a):
163
+ """Returns True if ``a`` is positive. """
164
+ return self.dom.is_positive(a.numer().LC())
165
+
166
+ def is_negative(self, a):
167
+ """Returns True if ``a`` is negative. """
168
+ return self.dom.is_negative(a.numer().LC())
169
+
170
+ def is_nonpositive(self, a):
171
+ """Returns True if ``a`` is non-positive. """
172
+ return self.dom.is_nonpositive(a.numer().LC())
173
+
174
+ def is_nonnegative(self, a):
175
+ """Returns True if ``a`` is non-negative. """
176
+ return self.dom.is_nonnegative(a.numer().LC())
177
+
178
+ def numer(self, a):
179
+ """Returns numerator of ``a``. """
180
+ return a.numer()
181
+
182
+ def denom(self, a):
183
+ """Returns denominator of ``a``. """
184
+ return a.denom()
185
+
186
+ def factorial(self, a):
187
+ """Returns factorial of ``a``. """
188
+ return self.dtype(self.dom.factorial(a))
.venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`PolynomialRing` class. """
2
+
3
+
4
+ from sympy.polys.agca.modules import FreeModulePolyRing
5
+ from sympy.polys.domains.compositedomain import CompositeDomain
6
+ from sympy.polys.domains.old_fractionfield import FractionField
7
+ from sympy.polys.domains.ring import Ring
8
+ from sympy.polys.orderings import monomial_key, build_product_order
9
+ from sympy.polys.polyclasses import DMP, DMF
10
+ from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError,
11
+ CoercionFailed, ExactQuotientFailed, NotReversible)
12
+ from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
13
+ from sympy.utilities import public
14
+ from sympy.utilities.iterables import iterable
15
+
16
+
17
+ @public
18
+ class PolynomialRingBase(Ring, CompositeDomain):
19
+ """
20
+ Base class for generalized polynomial rings.
21
+
22
+ This base class should be used for uniform access to generalized polynomial
23
+ rings. Subclasses only supply information about the element storage etc.
24
+
25
+ Do not instantiate.
26
+ """
27
+
28
+ has_assoc_Ring = True
29
+ has_assoc_Field = True
30
+
31
+ default_order = "grevlex"
32
+
33
+ def __init__(self, dom, *gens, **opts):
34
+ if not gens:
35
+ raise GeneratorsNeeded("generators not specified")
36
+
37
+ lev = len(gens) - 1
38
+ self.ngens = len(gens)
39
+
40
+ self.zero = self.dtype.zero(lev, dom)
41
+ self.one = self.dtype.one(lev, dom)
42
+
43
+ self.domain = self.dom = dom
44
+ self.symbols = self.gens = gens
45
+ # NOTE 'order' may not be set if inject was called through CompositeDomain
46
+ self.order = opts.get('order', monomial_key(self.default_order))
47
+
48
+ def set_domain(self, dom):
49
+ """Return a new polynomial ring with given domain. """
50
+ return self.__class__(dom, *self.gens, order=self.order)
51
+
52
+ def new(self, element):
53
+ return self.dtype(element, self.dom, len(self.gens) - 1)
54
+
55
+ def _ground_new(self, element):
56
+ return self.one.ground_new(element)
57
+
58
+ def _from_dict(self, element):
59
+ return DMP.from_dict(element, len(self.gens) - 1, self.dom)
60
+
61
+ def __str__(self):
62
+ s_order = str(self.order)
63
+ orderstr = (
64
+ " order=" + s_order) if s_order != self.default_order else ""
65
+ return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']'
66
+
67
+ def __hash__(self):
68
+ return hash((self.__class__.__name__, self.dtype, self.dom,
69
+ self.gens, self.order))
70
+
71
+ def __eq__(self, other):
72
+ """Returns ``True`` if two domains are equivalent. """
73
+ return isinstance(other, PolynomialRingBase) and \
74
+ self.dtype == other.dtype and self.dom == other.dom and \
75
+ self.gens == other.gens and self.order == other.order
76
+
77
+ def from_ZZ(K1, a, K0):
78
+ """Convert a Python ``int`` object to ``dtype``. """
79
+ return K1._ground_new(K1.dom.convert(a, K0))
80
+
81
+ def from_ZZ_python(K1, a, K0):
82
+ """Convert a Python ``int`` object to ``dtype``. """
83
+ return K1._ground_new(K1.dom.convert(a, K0))
84
+
85
+ def from_QQ(K1, a, K0):
86
+ """Convert a Python ``Fraction`` object to ``dtype``. """
87
+ return K1._ground_new(K1.dom.convert(a, K0))
88
+
89
+ def from_QQ_python(K1, a, K0):
90
+ """Convert a Python ``Fraction`` object to ``dtype``. """
91
+ return K1._ground_new(K1.dom.convert(a, K0))
92
+
93
+ def from_ZZ_gmpy(K1, a, K0):
94
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
95
+ return K1._ground_new(K1.dom.convert(a, K0))
96
+
97
+ def from_QQ_gmpy(K1, a, K0):
98
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
99
+ return K1._ground_new(K1.dom.convert(a, K0))
100
+
101
+ def from_RealField(K1, a, K0):
102
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
103
+ return K1._ground_new(K1.dom.convert(a, K0))
104
+
105
+ def from_AlgebraicField(K1, a, K0):
106
+ """Convert a ``ANP`` object to ``dtype``. """
107
+ if K1.dom == K0:
108
+ return K1._ground_new(a)
109
+
110
+ def from_PolynomialRing(K1, a, K0):
111
+ """Convert a ``PolyElement`` object to ``dtype``. """
112
+ if K1.gens == K0.symbols:
113
+ if K1.dom == K0.dom:
114
+ return K1(dict(a)) # set the correct ring
115
+ else:
116
+ convert_dom = lambda c: K1.dom.convert_from(c, K0.dom)
117
+ return K1._from_dict({m: convert_dom(c) for m, c in a.items()})
118
+ else:
119
+ monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens)
120
+
121
+ if K1.dom != K0.dom:
122
+ coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
123
+
124
+ return K1._from_dict(dict(zip(monoms, coeffs)))
125
+
126
+ def from_GlobalPolynomialRing(K1, a, K0):
127
+ """Convert a ``DMP`` object to ``dtype``. """
128
+ if K1.gens == K0.gens:
129
+ if K1.dom != K0.dom:
130
+ a = a.convert(K1.dom)
131
+ return K1(a.to_list())
132
+ else:
133
+ monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
134
+
135
+ if K1.dom != K0.dom:
136
+ coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
137
+
138
+ return K1(dict(zip(monoms, coeffs)))
139
+
140
+ def get_field(self):
141
+ """Returns a field associated with ``self``. """
142
+ return FractionField(self.dom, *self.gens)
143
+
144
+ def poly_ring(self, *gens):
145
+ """Returns a polynomial ring, i.e. ``K[X]``. """
146
+ raise NotImplementedError('nested domains not allowed')
147
+
148
+ def frac_field(self, *gens):
149
+ """Returns a fraction field, i.e. ``K(X)``. """
150
+ raise NotImplementedError('nested domains not allowed')
151
+
152
+ def revert(self, a):
153
+ try:
154
+ return self.exquo(self.one, a)
155
+ except (ExactQuotientFailed, ZeroDivisionError):
156
+ raise NotReversible('%s is not a unit' % a)
157
+
158
+ def gcdex(self, a, b):
159
+ """Extended GCD of ``a`` and ``b``. """
160
+ return a.gcdex(b)
161
+
162
+ def gcd(self, a, b):
163
+ """Returns GCD of ``a`` and ``b``. """
164
+ return a.gcd(b)
165
+
166
+ def lcm(self, a, b):
167
+ """Returns LCM of ``a`` and ``b``. """
168
+ return a.lcm(b)
169
+
170
+ def factorial(self, a):
171
+ """Returns factorial of ``a``. """
172
+ return self.dtype(self.dom.factorial(a))
173
+
174
+ def _vector_to_sdm(self, v, order):
175
+ """
176
+ For internal use by the modules class.
177
+
178
+ Convert an iterable of elements of this ring into a sparse distributed
179
+ module element.
180
+ """
181
+ raise NotImplementedError
182
+
183
+ def _sdm_to_dics(self, s, n):
184
+ """Helper for _sdm_to_vector."""
185
+ from sympy.polys.distributedmodules import sdm_to_dict
186
+ dic = sdm_to_dict(s)
187
+ res = [{} for _ in range(n)]
188
+ for k, v in dic.items():
189
+ res[k[0]][k[1:]] = v
190
+ return res
191
+
192
+ def _sdm_to_vector(self, s, n):
193
+ """
194
+ For internal use by the modules class.
195
+
196
+ Convert a sparse distributed module into a list of length ``n``.
197
+
198
+ Examples
199
+ ========
200
+
201
+ >>> from sympy import QQ, ilex
202
+ >>> from sympy.abc import x, y
203
+ >>> R = QQ.old_poly_ring(x, y, order=ilex)
204
+ >>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))]
205
+ >>> R._sdm_to_vector(L, 2)
206
+ [DMF([[1], [2, 0]], [[1]], QQ), DMF([[1, 0], []], [[1]], QQ)]
207
+ """
208
+ dics = self._sdm_to_dics(s, n)
209
+ # NOTE this works for global and local rings!
210
+ return [self(x) for x in dics]
211
+
212
+ def free_module(self, rank):
213
+ """
214
+ Generate a free module of rank ``rank`` over ``self``.
215
+
216
+ Examples
217
+ ========
218
+
219
+ >>> from sympy.abc import x
220
+ >>> from sympy import QQ
221
+ >>> QQ.old_poly_ring(x).free_module(2)
222
+ QQ[x]**2
223
+ """
224
+ return FreeModulePolyRing(self, rank)
225
+
226
+
227
+ def _vector_to_sdm_helper(v, order):
228
+ """Helper method for common code in Global and Local poly rings."""
229
+ from sympy.polys.distributedmodules import sdm_from_dict
230
+ d = {}
231
+ for i, e in enumerate(v):
232
+ for key, value in e.to_dict().items():
233
+ d[(i,) + key] = value
234
+ return sdm_from_dict(d, order)
235
+
236
+
237
+ @public
238
+ class GlobalPolynomialRing(PolynomialRingBase):
239
+ """A true polynomial ring, with objects DMP. """
240
+
241
+ is_PolynomialRing = is_Poly = True
242
+ dtype = DMP
243
+
244
+ def new(self, element):
245
+ if isinstance(element, dict):
246
+ return DMP.from_dict(element, len(self.gens) - 1, self.dom)
247
+ elif element in self.dom:
248
+ return self._ground_new(self.dom.convert(element))
249
+ else:
250
+ return self.dtype(element, self.dom, len(self.gens) - 1)
251
+
252
+ def from_FractionField(K1, a, K0):
253
+ """
254
+ Convert a ``DMF`` object to ``DMP``.
255
+
256
+ Examples
257
+ ========
258
+
259
+ >>> from sympy.polys.polyclasses import DMP, DMF
260
+ >>> from sympy.polys.domains import ZZ
261
+ >>> from sympy.abc import x
262
+
263
+ >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ)
264
+ >>> K = ZZ.old_frac_field(x)
265
+
266
+ >>> F = ZZ.old_poly_ring(x).from_FractionField(f, K)
267
+
268
+ >>> F == DMP([ZZ(1), ZZ(1)], ZZ)
269
+ True
270
+ >>> type(F) # doctest: +SKIP
271
+ <class 'sympy.polys.polyclasses.DMP_Python'>
272
+
273
+ """
274
+ if a.denom().is_one:
275
+ return K1.from_GlobalPolynomialRing(a.numer(), K0)
276
+
277
+ def to_sympy(self, a):
278
+ """Convert ``a`` to a SymPy object. """
279
+ return basic_from_dict(a.to_sympy_dict(), *self.gens)
280
+
281
+ def from_sympy(self, a):
282
+ """Convert SymPy's expression to ``dtype``. """
283
+ try:
284
+ rep, _ = dict_from_basic(a, gens=self.gens)
285
+ except PolynomialError:
286
+ raise CoercionFailed("Cannot convert %s to type %s" % (a, self))
287
+
288
+ for k, v in rep.items():
289
+ rep[k] = self.dom.from_sympy(v)
290
+
291
+ return DMP.from_dict(rep, self.ngens - 1, self.dom)
292
+
293
+ def is_positive(self, a):
294
+ """Returns True if ``LC(a)`` is positive. """
295
+ return self.dom.is_positive(a.LC())
296
+
297
+ def is_negative(self, a):
298
+ """Returns True if ``LC(a)`` is negative. """
299
+ return self.dom.is_negative(a.LC())
300
+
301
+ def is_nonpositive(self, a):
302
+ """Returns True if ``LC(a)`` is non-positive. """
303
+ return self.dom.is_nonpositive(a.LC())
304
+
305
+ def is_nonnegative(self, a):
306
+ """Returns True if ``LC(a)`` is non-negative. """
307
+ return self.dom.is_nonnegative(a.LC())
308
+
309
+ def _vector_to_sdm(self, v, order):
310
+ """
311
+ Examples
312
+ ========
313
+
314
+ >>> from sympy import lex, QQ
315
+ >>> from sympy.abc import x, y
316
+ >>> R = QQ.old_poly_ring(x, y)
317
+ >>> f = R.convert(x + 2*y)
318
+ >>> g = R.convert(x * y)
319
+ >>> R._vector_to_sdm([f, g], lex)
320
+ [((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)]
321
+ """
322
+ return _vector_to_sdm_helper(v, order)
323
+
324
+
325
+ class GeneralizedPolynomialRing(PolynomialRingBase):
326
+ """A generalized polynomial ring, with objects DMF. """
327
+
328
+ dtype = DMF
329
+
330
+ def new(self, a):
331
+ """Construct an element of ``self`` domain from ``a``. """
332
+ res = self.dtype(a, self.dom, len(self.gens) - 1)
333
+
334
+ # make sure res is actually in our ring
335
+ if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens):
336
+ from sympy.printing.str import sstr
337
+ raise CoercionFailed("denominator %s not allowed in %s"
338
+ % (sstr(res), self))
339
+ return res
340
+
341
+ def __contains__(self, a):
342
+ try:
343
+ a = self.convert(a)
344
+ except CoercionFailed:
345
+ return False
346
+ return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens)
347
+
348
+ def to_sympy(self, a):
349
+ """Convert ``a`` to a SymPy object. """
350
+ return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
351
+ basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
352
+
353
+ def from_sympy(self, a):
354
+ """Convert SymPy's expression to ``dtype``. """
355
+ p, q = a.as_numer_denom()
356
+
357
+ num, _ = dict_from_basic(p, gens=self.gens)
358
+ den, _ = dict_from_basic(q, gens=self.gens)
359
+
360
+ for k, v in num.items():
361
+ num[k] = self.dom.from_sympy(v)
362
+
363
+ for k, v in den.items():
364
+ den[k] = self.dom.from_sympy(v)
365
+
366
+ return self((num, den)).cancel()
367
+
368
+ def exquo(self, a, b):
369
+ """Exact quotient of ``a`` and ``b``. """
370
+ # Elements are DMF that will always divide (except 0). The result is
371
+ # not guaranteed to be in this ring, so we have to check that.
372
+ r = a / b
373
+
374
+ try:
375
+ r = self.new((r.num, r.den))
376
+ except CoercionFailed:
377
+ raise ExactQuotientFailed(a, b, self)
378
+
379
+ return r
380
+
381
+ def from_FractionField(K1, a, K0):
382
+ dmf = K1.get_field().from_FractionField(a, K0)
383
+ return K1((dmf.num, dmf.den))
384
+
385
+ def _vector_to_sdm(self, v, order):
386
+ """
387
+ Turn an iterable into a sparse distributed module.
388
+
389
+ Note that the vector is multiplied by a unit first to make all entries
390
+ polynomials.
391
+
392
+ Examples
393
+ ========
394
+
395
+ >>> from sympy import ilex, QQ
396
+ >>> from sympy.abc import x, y
397
+ >>> R = QQ.old_poly_ring(x, y, order=ilex)
398
+ >>> f = R.convert((x + 2*y) / (1 + x))
399
+ >>> g = R.convert(x * y)
400
+ >>> R._vector_to_sdm([f, g], ilex)
401
+ [((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1,
402
+ 2, 1), 1)]
403
+ """
404
+ # NOTE this is quite inefficient...
405
+ u = self.one.numer()
406
+ for x in v:
407
+ u *= x.denom()
408
+ return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order)
409
+
410
+
411
+ @public
412
+ def PolynomialRing(dom, *gens, **opts):
413
+ r"""
414
+ Create a generalized multivariate polynomial ring.
415
+
416
+ A generalized polynomial ring is defined by a ground field `K`, a set
417
+ of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`.
418
+ The monomial order can be global, local or mixed. In any case it induces
419
+ a total ordering on the monomials, and there exists for every (non-zero)
420
+ polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial"
421
+ `LM(f) = LM(f, >)`. One can then define a multiplicative subset
422
+ `S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized
423
+ polynomial ring corresponding to the monomial order is
424
+ `R = S^{-1}K[x_1, \ldots, x_n]`.
425
+
426
+ If `>` is a so-called global order, that is `1` is the smallest monomial,
427
+ then we just have `S = K` and `R = K[x_1, \ldots, x_n]`.
428
+
429
+ Examples
430
+ ========
431
+
432
+ A few examples may make this clearer.
433
+
434
+ >>> from sympy.abc import x, y
435
+ >>> from sympy import QQ
436
+
437
+ Our first ring uses global lexicographic order.
438
+
439
+ >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),))
440
+
441
+ The second ring uses local lexicographic order. Note that when using a
442
+ single (non-product) order, you can just specify the name and omit the
443
+ variables:
444
+
445
+ >>> R2 = QQ.old_poly_ring(x, y, order="ilex")
446
+
447
+ The third and fourth rings use a mixed orders:
448
+
449
+ >>> o1 = (("ilex", x), ("lex", y))
450
+ >>> o2 = (("lex", x), ("ilex", y))
451
+ >>> R3 = QQ.old_poly_ring(x, y, order=o1)
452
+ >>> R4 = QQ.old_poly_ring(x, y, order=o2)
453
+
454
+ We will investigate what elements of `K(x, y)` are contained in the various
455
+ rings.
456
+
457
+ >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)]
458
+ >>> test = lambda R: [f in R for f in L]
459
+
460
+ The first ring is just `K[x, y]`:
461
+
462
+ >>> test(R1)
463
+ [True, False, False, False, False]
464
+
465
+ The second ring is R1 localised at the maximal ideal (x, y):
466
+
467
+ >>> test(R2)
468
+ [True, False, True, True, True]
469
+
470
+ The third ring is R1 localised at the prime ideal (x):
471
+
472
+ >>> test(R3)
473
+ [True, False, True, False, True]
474
+
475
+ Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`:
476
+
477
+ >>> test(R4)
478
+ [True, False, False, True, False]
479
+ """
480
+
481
+ order = opts.get("order", GeneralizedPolynomialRing.default_order)
482
+ if iterable(order):
483
+ order = build_product_order(order, gens)
484
+ order = monomial_key(order)
485
+ opts['order'] = order
486
+
487
+ if order.is_global:
488
+ return GlobalPolynomialRing(dom, *gens, **opts)
489
+ else:
490
+ return GeneralizedPolynomialRing(dom, *gens, **opts)
.venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`PolynomialRing` class. """
2
+
3
+
4
+ from sympy.polys.domains.ring import Ring
5
+ from sympy.polys.domains.compositedomain import CompositeDomain
6
+
7
+ from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
8
+ from sympy.utilities import public
9
+
10
+ @public
11
+ class PolynomialRing(Ring, CompositeDomain):
12
+ """A class for representing multivariate polynomial rings. """
13
+
14
+ is_PolynomialRing = is_Poly = True
15
+
16
+ has_assoc_Ring = True
17
+ has_assoc_Field = True
18
+
19
+ def __init__(self, domain_or_ring, symbols=None, order=None):
20
+ from sympy.polys.rings import PolyRing
21
+
22
+ if isinstance(domain_or_ring, PolyRing) and symbols is None and order is None:
23
+ ring = domain_or_ring
24
+ else:
25
+ ring = PolyRing(symbols, domain_or_ring, order)
26
+
27
+ self.ring = ring
28
+ self.dtype = ring.dtype
29
+
30
+ self.gens = ring.gens
31
+ self.ngens = ring.ngens
32
+ self.symbols = ring.symbols
33
+ self.domain = ring.domain
34
+
35
+
36
+ if symbols:
37
+ if ring.domain.is_Field and ring.domain.is_Exact and len(symbols)==1:
38
+ self.is_PID = True
39
+
40
+ # TODO: remove this
41
+ self.dom = self.domain
42
+
43
+ def new(self, element):
44
+ return self.ring.ring_new(element)
45
+
46
+ def of_type(self, element):
47
+ """Check if ``a`` is of type ``dtype``. """
48
+ return self.ring.is_element(element)
49
+
50
+ @property
51
+ def zero(self):
52
+ return self.ring.zero
53
+
54
+ @property
55
+ def one(self):
56
+ return self.ring.one
57
+
58
+ @property
59
+ def order(self):
60
+ return self.ring.order
61
+
62
+ def __str__(self):
63
+ return str(self.domain) + '[' + ','.join(map(str, self.symbols)) + ']'
64
+
65
+ def __hash__(self):
66
+ return hash((self.__class__.__name__, self.ring, self.domain, self.symbols))
67
+
68
+ def __eq__(self, other):
69
+ """Returns `True` if two domains are equivalent. """
70
+ if not isinstance(other, PolynomialRing):
71
+ return NotImplemented
72
+ return self.ring == other.ring
73
+
74
+ def is_unit(self, a):
75
+ """Returns ``True`` if ``a`` is a unit of ``self``"""
76
+ if not a.is_ground:
77
+ return False
78
+ K = self.domain
79
+ return K.is_unit(K.convert_from(a, self))
80
+
81
+ def canonical_unit(self, a):
82
+ u = self.domain.canonical_unit(a.LC)
83
+ return self.ring.ground_new(u)
84
+
85
+ def to_sympy(self, a):
86
+ """Convert `a` to a SymPy object. """
87
+ return a.as_expr()
88
+
89
+ def from_sympy(self, a):
90
+ """Convert SymPy's expression to `dtype`. """
91
+ return self.ring.from_expr(a)
92
+
93
+ def from_ZZ(K1, a, K0):
94
+ """Convert a Python `int` object to `dtype`. """
95
+ return K1(K1.domain.convert(a, K0))
96
+
97
+ def from_ZZ_python(K1, a, K0):
98
+ """Convert a Python `int` object to `dtype`. """
99
+ return K1(K1.domain.convert(a, K0))
100
+
101
+ def from_QQ(K1, a, K0):
102
+ """Convert a Python `Fraction` object to `dtype`. """
103
+ return K1(K1.domain.convert(a, K0))
104
+
105
+ def from_QQ_python(K1, a, K0):
106
+ """Convert a Python `Fraction` object to `dtype`. """
107
+ return K1(K1.domain.convert(a, K0))
108
+
109
+ def from_ZZ_gmpy(K1, a, K0):
110
+ """Convert a GMPY `mpz` object to `dtype`. """
111
+ return K1(K1.domain.convert(a, K0))
112
+
113
+ def from_QQ_gmpy(K1, a, K0):
114
+ """Convert a GMPY `mpq` object to `dtype`. """
115
+ return K1(K1.domain.convert(a, K0))
116
+
117
+ def from_GaussianIntegerRing(K1, a, K0):
118
+ """Convert a `GaussianInteger` object to `dtype`. """
119
+ return K1(K1.domain.convert(a, K0))
120
+
121
+ def from_GaussianRationalField(K1, a, K0):
122
+ """Convert a `GaussianRational` object to `dtype`. """
123
+ return K1(K1.domain.convert(a, K0))
124
+
125
+ def from_RealField(K1, a, K0):
126
+ """Convert a mpmath `mpf` object to `dtype`. """
127
+ return K1(K1.domain.convert(a, K0))
128
+
129
+ def from_ComplexField(K1, a, K0):
130
+ """Convert a mpmath `mpf` object to `dtype`. """
131
+ return K1(K1.domain.convert(a, K0))
132
+
133
+ def from_AlgebraicField(K1, a, K0):
134
+ """Convert an algebraic number to ``dtype``. """
135
+ if K1.domain != K0:
136
+ a = K1.domain.convert_from(a, K0)
137
+ if a is not None:
138
+ return K1.new(a)
139
+
140
+ def from_PolynomialRing(K1, a, K0):
141
+ """Convert a polynomial to ``dtype``. """
142
+ try:
143
+ return a.set_ring(K1.ring)
144
+ except (CoercionFailed, GeneratorsError):
145
+ return None
146
+
147
+ def from_FractionField(K1, a, K0):
148
+ """Convert a rational function to ``dtype``. """
149
+ if K1.domain == K0:
150
+ return K1.ring.from_list([a])
151
+
152
+ q, r = K0.numer(a).div(K0.denom(a))
153
+
154
+ if r.is_zero:
155
+ return K1.from_PolynomialRing(q, K0.field.ring.to_domain())
156
+ else:
157
+ return None
158
+
159
+ def from_GlobalPolynomialRing(K1, a, K0):
160
+ """Convert from old poly ring to ``dtype``. """
161
+ if K1.symbols == K0.gens:
162
+ ad = a.to_dict()
163
+ if K1.domain != K0.domain:
164
+ ad = {m: K1.domain.convert(c) for m, c in ad.items()}
165
+ return K1(ad)
166
+ elif a.is_ground and K0.domain == K1:
167
+ return K1.convert_from(a.to_list()[0], K0.domain)
168
+
169
+ def get_field(self):
170
+ """Returns a field associated with `self`. """
171
+ return self.ring.to_field().to_domain()
172
+
173
+ def is_positive(self, a):
174
+ """Returns True if `LC(a)` is positive. """
175
+ return self.domain.is_positive(a.LC)
176
+
177
+ def is_negative(self, a):
178
+ """Returns True if `LC(a)` is negative. """
179
+ return self.domain.is_negative(a.LC)
180
+
181
+ def is_nonpositive(self, a):
182
+ """Returns True if `LC(a)` is non-positive. """
183
+ return self.domain.is_nonpositive(a.LC)
184
+
185
+ def is_nonnegative(self, a):
186
+ """Returns True if `LC(a)` is non-negative. """
187
+ return self.domain.is_nonnegative(a.LC)
188
+
189
+ def gcdex(self, a, b):
190
+ """Extended GCD of `a` and `b`. """
191
+ return a.gcdex(b)
192
+
193
+ def gcd(self, a, b):
194
+ """Returns GCD of `a` and `b`. """
195
+ return a.gcd(b)
196
+
197
+ def lcm(self, a, b):
198
+ """Returns LCM of `a` and `b`. """
199
+ return a.lcm(b)
200
+
201
+ def factorial(self, a):
202
+ """Returns factorial of `a`. """
203
+ return self.dtype(self.domain.factorial(a))
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`PythonFiniteField` class. """
2
+
3
+
4
+ from sympy.polys.domains.finitefield import FiniteField
5
+ from sympy.polys.domains.pythonintegerring import PythonIntegerRing
6
+
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class PythonFiniteField(FiniteField):
11
+ """Finite field based on Python's integers. """
12
+
13
+ alias = 'FF_python'
14
+
15
+ def __init__(self, mod, symmetric=True):
16
+ super().__init__(mod, PythonIntegerRing(), symmetric)
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`PythonIntegerRing` class. """
2
+
3
+
4
+ from sympy.core.numbers import int_valued
5
+ from sympy.polys.domains.groundtypes import (
6
+ PythonInteger, SymPyInteger, sqrt as python_sqrt,
7
+ factorial as python_factorial, python_gcdex, python_gcd, python_lcm,
8
+ )
9
+ from sympy.polys.domains.integerring import IntegerRing
10
+ from sympy.polys.polyerrors import CoercionFailed
11
+ from sympy.utilities import public
12
+
13
+ @public
14
+ class PythonIntegerRing(IntegerRing):
15
+ """Integer ring based on Python's ``int`` type.
16
+
17
+ This will be used as :ref:`ZZ` if ``gmpy`` and ``gmpy2`` are not
18
+ installed. Elements are instances of the standard Python ``int`` type.
19
+ """
20
+
21
+ dtype = PythonInteger
22
+ zero = dtype(0)
23
+ one = dtype(1)
24
+ alias = 'ZZ_python'
25
+
26
+ def __init__(self):
27
+ """Allow instantiation of this domain. """
28
+
29
+ def to_sympy(self, a):
30
+ """Convert ``a`` to a SymPy object. """
31
+ return SymPyInteger(a)
32
+
33
+ def from_sympy(self, a):
34
+ """Convert SymPy's Integer to ``dtype``. """
35
+ if a.is_Integer:
36
+ return PythonInteger(a.p)
37
+ elif int_valued(a):
38
+ return PythonInteger(int(a))
39
+ else:
40
+ raise CoercionFailed("expected an integer, got %s" % a)
41
+
42
+ def from_FF_python(K1, a, K0):
43
+ """Convert ``ModularInteger(int)`` to Python's ``int``. """
44
+ return K0.to_int(a)
45
+
46
+ def from_ZZ_python(K1, a, K0):
47
+ """Convert Python's ``int`` to Python's ``int``. """
48
+ return a
49
+
50
+ def from_QQ(K1, a, K0):
51
+ """Convert Python's ``Fraction`` to Python's ``int``. """
52
+ if a.denominator == 1:
53
+ return a.numerator
54
+
55
+ def from_QQ_python(K1, a, K0):
56
+ """Convert Python's ``Fraction`` to Python's ``int``. """
57
+ if a.denominator == 1:
58
+ return a.numerator
59
+
60
+ def from_FF_gmpy(K1, a, K0):
61
+ """Convert ``ModularInteger(mpz)`` to Python's ``int``. """
62
+ return PythonInteger(K0.to_int(a))
63
+
64
+ def from_ZZ_gmpy(K1, a, K0):
65
+ """Convert GMPY's ``mpz`` to Python's ``int``. """
66
+ return PythonInteger(a)
67
+
68
+ def from_QQ_gmpy(K1, a, K0):
69
+ """Convert GMPY's ``mpq`` to Python's ``int``. """
70
+ if a.denom() == 1:
71
+ return PythonInteger(a.numer())
72
+
73
+ def from_RealField(K1, a, K0):
74
+ """Convert mpmath's ``mpf`` to Python's ``int``. """
75
+ p, q = K0.to_rational(a)
76
+
77
+ if q == 1:
78
+ return PythonInteger(p)
79
+
80
+ def gcdex(self, a, b):
81
+ """Compute extended GCD of ``a`` and ``b``. """
82
+ return python_gcdex(a, b)
83
+
84
+ def gcd(self, a, b):
85
+ """Compute GCD of ``a`` and ``b``. """
86
+ return python_gcd(a, b)
87
+
88
+ def lcm(self, a, b):
89
+ """Compute LCM of ``a`` and ``b``. """
90
+ return python_lcm(a, b)
91
+
92
+ def sqrt(self, a):
93
+ """Compute square root of ``a``. """
94
+ return python_sqrt(a)
95
+
96
+ def factorial(self, a):
97
+ """Compute factorial of ``a``. """
98
+ return python_factorial(a)
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Rational number type based on Python integers.
3
+
4
+ The PythonRational class from here has been moved to
5
+ sympy.external.pythonmpq
6
+
7
+ This module is just left here for backwards compatibility.
8
+ """
9
+
10
+
11
+ from sympy.core.numbers import Rational
12
+ from sympy.core.sympify import _sympy_converter
13
+ from sympy.utilities import public
14
+ from sympy.external.pythonmpq import PythonMPQ
15
+
16
+
17
+ PythonRational = public(PythonMPQ)
18
+
19
+
20
+ def sympify_pythonrational(arg):
21
+ return Rational(arg.numerator, arg.denominator)
22
+ _sympy_converter[PythonRational] = sympify_pythonrational
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`PythonRationalField` class. """
2
+
3
+
4
+ from sympy.polys.domains.groundtypes import PythonInteger, PythonRational, SymPyRational
5
+ from sympy.polys.domains.rationalfield import RationalField
6
+ from sympy.polys.polyerrors import CoercionFailed
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class PythonRationalField(RationalField):
11
+ """Rational field based on :ref:`MPQ`.
12
+
13
+ This will be used as :ref:`QQ` if ``gmpy`` and ``gmpy2`` are not
14
+ installed. Elements are instances of :ref:`MPQ`.
15
+ """
16
+
17
+ dtype = PythonRational
18
+ zero = dtype(0)
19
+ one = dtype(1)
20
+ alias = 'QQ_python'
21
+
22
+ def __init__(self):
23
+ pass
24
+
25
+ def get_ring(self):
26
+ """Returns ring associated with ``self``. """
27
+ from sympy.polys.domains import PythonIntegerRing
28
+ return PythonIntegerRing()
29
+
30
+ def to_sympy(self, a):
31
+ """Convert `a` to a SymPy object. """
32
+ return SymPyRational(a.numerator, a.denominator)
33
+
34
+ def from_sympy(self, a):
35
+ """Convert SymPy's Rational to `dtype`. """
36
+ if a.is_Rational:
37
+ return PythonRational(a.p, a.q)
38
+ elif a.is_Float:
39
+ from sympy.polys.domains import RR
40
+ p, q = RR.to_rational(a)
41
+ return PythonRational(int(p), int(q))
42
+ else:
43
+ raise CoercionFailed("expected `Rational` object, got %s" % a)
44
+
45
+ def from_ZZ_python(K1, a, K0):
46
+ """Convert a Python `int` object to `dtype`. """
47
+ return PythonRational(a)
48
+
49
+ def from_QQ_python(K1, a, K0):
50
+ """Convert a Python `Fraction` object to `dtype`. """
51
+ return a
52
+
53
+ def from_ZZ_gmpy(K1, a, K0):
54
+ """Convert a GMPY `mpz` object to `dtype`. """
55
+ return PythonRational(PythonInteger(a))
56
+
57
+ def from_QQ_gmpy(K1, a, K0):
58
+ """Convert a GMPY `mpq` object to `dtype`. """
59
+ return PythonRational(PythonInteger(a.numer()),
60
+ PythonInteger(a.denom()))
61
+
62
+ def from_RealField(K1, a, K0):
63
+ """Convert a mpmath `mpf` object to `dtype`. """
64
+ p, q = K0.to_rational(a)
65
+ return PythonRational(int(p), int(q))
66
+
67
+ def numer(self, a):
68
+ """Returns numerator of `a`. """
69
+ return a.numerator
70
+
71
+ def denom(self, a):
72
+ """Returns denominator of `a`. """
73
+ return a.denominator
.venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`QuotientRing` class."""
2
+
3
+
4
+ from sympy.polys.agca.modules import FreeModuleQuotientRing
5
+ from sympy.polys.domains.ring import Ring
6
+ from sympy.polys.polyerrors import NotReversible, CoercionFailed
7
+ from sympy.utilities import public
8
+
9
+ # TODO
10
+ # - successive quotients (when quotient ideals are implemented)
11
+ # - poly rings over quotients?
12
+ # - division by non-units in integral domains?
13
+
14
+ @public
15
+ class QuotientRingElement:
16
+ """
17
+ Class representing elements of (commutative) quotient rings.
18
+
19
+ Attributes:
20
+
21
+ - ring - containing ring
22
+ - data - element of ring.ring (i.e. base ring) representing self
23
+ """
24
+
25
+ def __init__(self, ring, data):
26
+ self.ring = ring
27
+ self.data = data
28
+
29
+ def __str__(self):
30
+ from sympy.printing.str import sstr
31
+ data = self.ring.ring.to_sympy(self.data)
32
+ return sstr(data) + " + " + str(self.ring.base_ideal)
33
+
34
+ __repr__ = __str__
35
+
36
+ def __bool__(self):
37
+ return not self.ring.is_zero(self)
38
+
39
+ def __add__(self, om):
40
+ if not isinstance(om, self.__class__) or om.ring != self.ring:
41
+ try:
42
+ om = self.ring.convert(om)
43
+ except (NotImplementedError, CoercionFailed):
44
+ return NotImplemented
45
+ return self.ring(self.data + om.data)
46
+
47
+ __radd__ = __add__
48
+
49
+ def __neg__(self):
50
+ return self.ring(self.data*self.ring.ring.convert(-1))
51
+
52
+ def __sub__(self, om):
53
+ return self.__add__(-om)
54
+
55
+ def __rsub__(self, om):
56
+ return (-self).__add__(om)
57
+
58
+ def __mul__(self, o):
59
+ if not isinstance(o, self.__class__):
60
+ try:
61
+ o = self.ring.convert(o)
62
+ except (NotImplementedError, CoercionFailed):
63
+ return NotImplemented
64
+ return self.ring(self.data*o.data)
65
+
66
+ __rmul__ = __mul__
67
+
68
+ def __rtruediv__(self, o):
69
+ return self.ring.revert(self)*o
70
+
71
+ def __truediv__(self, o):
72
+ if not isinstance(o, self.__class__):
73
+ try:
74
+ o = self.ring.convert(o)
75
+ except (NotImplementedError, CoercionFailed):
76
+ return NotImplemented
77
+ return self.ring.revert(o)*self
78
+
79
+ def __pow__(self, oth):
80
+ if oth < 0:
81
+ return self.ring.revert(self) ** -oth
82
+ return self.ring(self.data ** oth)
83
+
84
+ def __eq__(self, om):
85
+ if not isinstance(om, self.__class__) or om.ring != self.ring:
86
+ return False
87
+ return self.ring.is_zero(self - om)
88
+
89
+ def __ne__(self, om):
90
+ return not self == om
91
+
92
+
93
+ class QuotientRing(Ring):
94
+ """
95
+ Class representing (commutative) quotient rings.
96
+
97
+ You should not usually instantiate this by hand, instead use the constructor
98
+ from the base ring in the construction.
99
+
100
+ >>> from sympy.abc import x
101
+ >>> from sympy import QQ
102
+ >>> I = QQ.old_poly_ring(x).ideal(x**3 + 1)
103
+ >>> QQ.old_poly_ring(x).quotient_ring(I)
104
+ QQ[x]/<x**3 + 1>
105
+
106
+ Shorter versions are possible:
107
+
108
+ >>> QQ.old_poly_ring(x)/I
109
+ QQ[x]/<x**3 + 1>
110
+
111
+ >>> QQ.old_poly_ring(x)/[x**3 + 1]
112
+ QQ[x]/<x**3 + 1>
113
+
114
+ Attributes:
115
+
116
+ - ring - the base ring
117
+ - base_ideal - the ideal used to form the quotient
118
+ """
119
+
120
+ has_assoc_Ring = True
121
+ has_assoc_Field = False
122
+ dtype = QuotientRingElement
123
+
124
+ def __init__(self, ring, ideal):
125
+ if not ideal.ring == ring:
126
+ raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal))
127
+ self.ring = ring
128
+ self.base_ideal = ideal
129
+ self.zero = self(self.ring.zero)
130
+ self.one = self(self.ring.one)
131
+
132
+ def __str__(self):
133
+ return str(self.ring) + "/" + str(self.base_ideal)
134
+
135
+ def __hash__(self):
136
+ return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal))
137
+
138
+ def new(self, a):
139
+ """Construct an element of ``self`` domain from ``a``. """
140
+ if not isinstance(a, self.ring.dtype):
141
+ a = self.ring(a)
142
+ # TODO optionally disable reduction?
143
+ return self.dtype(self, self.base_ideal.reduce_element(a))
144
+
145
+ def __eq__(self, other):
146
+ """Returns ``True`` if two domains are equivalent. """
147
+ return isinstance(other, QuotientRing) and \
148
+ self.ring == other.ring and self.base_ideal == other.base_ideal
149
+
150
+ def from_ZZ(K1, a, K0):
151
+ """Convert a Python ``int`` object to ``dtype``. """
152
+ return K1(K1.ring.convert(a, K0))
153
+
154
+ from_ZZ_python = from_ZZ
155
+ from_QQ_python = from_ZZ_python
156
+ from_ZZ_gmpy = from_ZZ_python
157
+ from_QQ_gmpy = from_ZZ_python
158
+ from_RealField = from_ZZ_python
159
+ from_GlobalPolynomialRing = from_ZZ_python
160
+ from_FractionField = from_ZZ_python
161
+
162
+ def from_sympy(self, a):
163
+ return self(self.ring.from_sympy(a))
164
+
165
+ def to_sympy(self, a):
166
+ return self.ring.to_sympy(a.data)
167
+
168
+ def from_QuotientRing(self, a, K0):
169
+ if K0 == self:
170
+ return a
171
+
172
+ def poly_ring(self, *gens):
173
+ """Returns a polynomial ring, i.e. ``K[X]``. """
174
+ raise NotImplementedError('nested domains not allowed')
175
+
176
+ def frac_field(self, *gens):
177
+ """Returns a fraction field, i.e. ``K(X)``. """
178
+ raise NotImplementedError('nested domains not allowed')
179
+
180
+ def revert(self, a):
181
+ """
182
+ Compute a**(-1), if possible.
183
+ """
184
+ I = self.ring.ideal(a.data) + self.base_ideal
185
+ try:
186
+ return self(I.in_terms_of_generators(1)[0])
187
+ except ValueError: # 1 not in I
188
+ raise NotReversible('%s not a unit in %r' % (a, self))
189
+
190
+ def is_zero(self, a):
191
+ return self.base_ideal.contains(a.data)
192
+
193
+ def free_module(self, rank):
194
+ """
195
+ Generate a free module of rank ``rank`` over ``self``.
196
+
197
+ >>> from sympy.abc import x
198
+ >>> from sympy import QQ
199
+ >>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2)
200
+ (QQ[x]/<x**2 + 1>)**2
201
+ """
202
+ return FreeModuleQuotientRing(self, rank)
.venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`RationalField` class. """
2
+
3
+
4
+ from sympy.external.gmpy import MPQ
5
+
6
+ from sympy.polys.domains.groundtypes import SymPyRational, is_square, sqrtrem
7
+
8
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
9
+ from sympy.polys.domains.field import Field
10
+ from sympy.polys.domains.simpledomain import SimpleDomain
11
+ from sympy.polys.polyerrors import CoercionFailed
12
+ from sympy.utilities import public
13
+
14
+ @public
15
+ class RationalField(Field, CharacteristicZero, SimpleDomain):
16
+ r"""Abstract base class for the domain :ref:`QQ`.
17
+
18
+ The :py:class:`RationalField` class represents the field of rational
19
+ numbers $\mathbb{Q}$ as a :py:class:`~.Domain` in the domain system.
20
+ :py:class:`RationalField` is a superclass of
21
+ :py:class:`PythonRationalField` and :py:class:`GMPYRationalField` one of
22
+ which will be the implementation for :ref:`QQ` depending on whether either
23
+ of ``gmpy`` or ``gmpy2`` is installed or not.
24
+
25
+ See also
26
+ ========
27
+
28
+ Domain
29
+ """
30
+
31
+ rep = 'QQ'
32
+ alias = 'QQ'
33
+
34
+ is_RationalField = is_QQ = True
35
+ is_Numerical = True
36
+
37
+ has_assoc_Ring = True
38
+ has_assoc_Field = True
39
+
40
+ dtype = MPQ
41
+ zero = dtype(0)
42
+ one = dtype(1)
43
+ tp = type(one)
44
+
45
+ def __init__(self):
46
+ pass
47
+
48
+ def __eq__(self, other):
49
+ """Returns ``True`` if two domains are equivalent. """
50
+ if isinstance(other, RationalField):
51
+ return True
52
+ else:
53
+ return NotImplemented
54
+
55
+ def __hash__(self):
56
+ """Returns hash code of ``self``. """
57
+ return hash('QQ')
58
+
59
+ def get_ring(self):
60
+ """Returns ring associated with ``self``. """
61
+ from sympy.polys.domains import ZZ
62
+ return ZZ
63
+
64
+ def to_sympy(self, a):
65
+ """Convert ``a`` to a SymPy object. """
66
+ return SymPyRational(int(a.numerator), int(a.denominator))
67
+
68
+ def from_sympy(self, a):
69
+ """Convert SymPy's Integer to ``dtype``. """
70
+ if a.is_Rational:
71
+ return MPQ(a.p, a.q)
72
+ elif a.is_Float:
73
+ from sympy.polys.domains import RR
74
+ return MPQ(*map(int, RR.to_rational(a)))
75
+ else:
76
+ raise CoercionFailed("expected `Rational` object, got %s" % a)
77
+
78
+ def algebraic_field(self, *extension, alias=None):
79
+ r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
80
+
81
+ Parameters
82
+ ==========
83
+
84
+ *extension : One or more :py:class:`~.Expr`
85
+ Generators of the extension. These should be expressions that are
86
+ algebraic over `\mathbb{Q}`.
87
+
88
+ alias : str, :py:class:`~.Symbol`, None, optional (default=None)
89
+ If provided, this will be used as the alias symbol for the
90
+ primitive element of the returned :py:class:`~.AlgebraicField`.
91
+
92
+ Returns
93
+ =======
94
+
95
+ :py:class:`~.AlgebraicField`
96
+ A :py:class:`~.Domain` representing the algebraic field extension.
97
+
98
+ Examples
99
+ ========
100
+
101
+ >>> from sympy import QQ, sqrt
102
+ >>> QQ.algebraic_field(sqrt(2))
103
+ QQ<sqrt(2)>
104
+ """
105
+ from sympy.polys.domains import AlgebraicField
106
+ return AlgebraicField(self, *extension, alias=alias)
107
+
108
+ def from_AlgebraicField(K1, a, K0):
109
+ """Convert a :py:class:`~.ANP` object to :ref:`QQ`.
110
+
111
+ See :py:meth:`~.Domain.convert`
112
+ """
113
+ if a.is_ground:
114
+ return K1.convert(a.LC(), K0.dom)
115
+
116
+ def from_ZZ(K1, a, K0):
117
+ """Convert a Python ``int`` object to ``dtype``. """
118
+ return MPQ(a)
119
+
120
+ def from_ZZ_python(K1, a, K0):
121
+ """Convert a Python ``int`` object to ``dtype``. """
122
+ return MPQ(a)
123
+
124
+ def from_QQ(K1, a, K0):
125
+ """Convert a Python ``Fraction`` object to ``dtype``. """
126
+ return MPQ(a.numerator, a.denominator)
127
+
128
+ def from_QQ_python(K1, a, K0):
129
+ """Convert a Python ``Fraction`` object to ``dtype``. """
130
+ return MPQ(a.numerator, a.denominator)
131
+
132
+ def from_ZZ_gmpy(K1, a, K0):
133
+ """Convert a GMPY ``mpz`` object to ``dtype``. """
134
+ return MPQ(a)
135
+
136
+ def from_QQ_gmpy(K1, a, K0):
137
+ """Convert a GMPY ``mpq`` object to ``dtype``. """
138
+ return a
139
+
140
+ def from_GaussianRationalField(K1, a, K0):
141
+ """Convert a ``GaussianElement`` object to ``dtype``. """
142
+ if a.y == 0:
143
+ return MPQ(a.x)
144
+
145
+ def from_RealField(K1, a, K0):
146
+ """Convert a mpmath ``mpf`` object to ``dtype``. """
147
+ return MPQ(*map(int, K0.to_rational(a)))
148
+
149
+ def exquo(self, a, b):
150
+ """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
151
+ return MPQ(a) / MPQ(b)
152
+
153
+ def quo(self, a, b):
154
+ """Quotient of ``a`` and ``b``, implies ``__truediv__``. """
155
+ return MPQ(a) / MPQ(b)
156
+
157
+ def rem(self, a, b):
158
+ """Remainder of ``a`` and ``b``, implies nothing. """
159
+ return self.zero
160
+
161
+ def div(self, a, b):
162
+ """Division of ``a`` and ``b``, implies ``__truediv__``. """
163
+ return MPQ(a) / MPQ(b), self.zero
164
+
165
+ def numer(self, a):
166
+ """Returns numerator of ``a``. """
167
+ return a.numerator
168
+
169
+ def denom(self, a):
170
+ """Returns denominator of ``a``. """
171
+ return a.denominator
172
+
173
+ def is_square(self, a):
174
+ """Return ``True`` if ``a`` is a square.
175
+
176
+ Explanation
177
+ ===========
178
+ A rational number is a square if and only if there exists
179
+ a rational number ``b`` such that ``b * b == a``.
180
+ """
181
+ return is_square(a.numerator) and is_square(a.denominator)
182
+
183
+ def exsqrt(self, a):
184
+ """Non-negative square root of ``a`` if ``a`` is a square.
185
+
186
+ See also
187
+ ========
188
+ is_square
189
+ """
190
+ if a.numerator < 0: # denominator is always positive
191
+ return None
192
+ p_sqrt, p_rem = sqrtrem(a.numerator)
193
+ if p_rem != 0:
194
+ return None
195
+ q_sqrt, q_rem = sqrtrem(a.denominator)
196
+ if q_rem != 0:
197
+ return None
198
+ return MPQ(p_sqrt, q_sqrt)
199
+
200
+ QQ = RationalField()
.venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`RealField` class. """
2
+
3
+
4
+ from sympy.external.gmpy import SYMPY_INTS, MPQ
5
+ from sympy.core.numbers import Float
6
+ from sympy.polys.domains.field import Field
7
+ from sympy.polys.domains.simpledomain import SimpleDomain
8
+ from sympy.polys.domains.characteristiczero import CharacteristicZero
9
+ from sympy.polys.polyerrors import CoercionFailed
10
+ from sympy.utilities import public
11
+
12
+ from mpmath import MPContext
13
+ from mpmath.libmp import to_rational as _mpmath_to_rational
14
+
15
+
16
+ def to_rational(s, max_denom, limit=True):
17
+
18
+ p, q = _mpmath_to_rational(s._mpf_)
19
+
20
+ # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
21
+ # to_rational() function returns a gmpy2.mpz instance and if MPQ is
22
+ # flint.fmpq then MPQ(p, q) will fail.
23
+ p = int(p)
24
+ q = int(q)
25
+
26
+ if not limit or q <= max_denom:
27
+ return p, q
28
+
29
+ p0, q0, p1, q1 = 0, 1, 1, 0
30
+ n, d = p, q
31
+
32
+ while True:
33
+ a = n//d
34
+ q2 = q0 + a*q1
35
+ if q2 > max_denom:
36
+ break
37
+ p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
38
+ n, d = d, n - a*d
39
+
40
+ k = (max_denom - q0)//q1
41
+
42
+ number = MPQ(p, q)
43
+ bound1 = MPQ(p0 + k*p1, q0 + k*q1)
44
+ bound2 = MPQ(p1, q1)
45
+
46
+ if not bound2 or not bound1:
47
+ return p, q
48
+ elif abs(bound2 - number) <= abs(bound1 - number):
49
+ return bound2.numerator, bound2.denominator
50
+ else:
51
+ return bound1.numerator, bound1.denominator
52
+
53
+
54
+ @public
55
+ class RealField(Field, CharacteristicZero, SimpleDomain):
56
+ """Real numbers up to the given precision. """
57
+
58
+ rep = 'RR'
59
+
60
+ is_RealField = is_RR = True
61
+
62
+ is_Exact = False
63
+ is_Numerical = True
64
+ is_PID = False
65
+
66
+ has_assoc_Ring = False
67
+ has_assoc_Field = True
68
+
69
+ _default_precision = 53
70
+
71
+ @property
72
+ def has_default_precision(self):
73
+ return self.precision == self._default_precision
74
+
75
+ @property
76
+ def precision(self):
77
+ return self._context.prec
78
+
79
+ @property
80
+ def dps(self):
81
+ return self._context.dps
82
+
83
+ @property
84
+ def tolerance(self):
85
+ return self._tolerance
86
+
87
+ def __init__(self, prec=None, dps=None, tol=None):
88
+ # XXX: The tol parameter is ignored but is kept for now for backwards
89
+ # compatibility.
90
+
91
+ context = MPContext()
92
+
93
+ if prec is None and dps is None:
94
+ context.prec = self._default_precision
95
+ elif dps is None:
96
+ context.prec = prec
97
+ elif prec is None:
98
+ context.dps = dps
99
+ else:
100
+ raise TypeError("Cannot set both prec and dps")
101
+
102
+ self._context = context
103
+
104
+ self._dtype = context.mpf
105
+ self.zero = self.dtype(0)
106
+ self.one = self.dtype(1)
107
+
108
+ # Only max_denom here is used for anything and is only used for
109
+ # to_rational.
110
+ self._max_denom = max(2**context.prec // 200, 99)
111
+ self._tolerance = self.one / self._max_denom
112
+
113
+ @property
114
+ def tp(self):
115
+ # XXX: Domain treats tp as an alias of dtype. Here we need to two
116
+ # separate things: dtype is a callable to make/convert instances.
117
+ # We use tp with isinstance to check if an object is an instance
118
+ # of the domain already.
119
+ return self._dtype
120
+
121
+ def dtype(self, arg):
122
+ # XXX: This is needed because mpmath does not recognise fmpz.
123
+ # It might be better to add conversion routines to mpmath and if that
124
+ # happens then this can be removed.
125
+ if isinstance(arg, SYMPY_INTS):
126
+ arg = int(arg)
127
+ return self._dtype(arg)
128
+
129
+ def __eq__(self, other):
130
+ return isinstance(other, RealField) and self.precision == other.precision
131
+
132
+ def __hash__(self):
133
+ return hash((self.__class__.__name__, self._dtype, self.precision))
134
+
135
+ def to_sympy(self, element):
136
+ """Convert ``element`` to SymPy number. """
137
+ return Float(element, self.dps)
138
+
139
+ def from_sympy(self, expr):
140
+ """Convert SymPy's number to ``dtype``. """
141
+ number = expr.evalf(n=self.dps)
142
+
143
+ if number.is_Number:
144
+ return self.dtype(number)
145
+ else:
146
+ raise CoercionFailed("expected real number, got %s" % expr)
147
+
148
+ def from_ZZ(self, element, base):
149
+ return self.dtype(element)
150
+
151
+ def from_ZZ_python(self, element, base):
152
+ return self.dtype(element)
153
+
154
+ def from_ZZ_gmpy(self, element, base):
155
+ return self.dtype(int(element))
156
+
157
+ # XXX: We need to convert the denominators to int here because mpmath does
158
+ # not recognise mpz. Ideally mpmath would handle this and if it changed to
159
+ # do so then the calls to int here could be removed.
160
+
161
+ def from_QQ(self, element, base):
162
+ return self.dtype(element.numerator) / int(element.denominator)
163
+
164
+ def from_QQ_python(self, element, base):
165
+ return self.dtype(element.numerator) / int(element.denominator)
166
+
167
+ def from_QQ_gmpy(self, element, base):
168
+ return self.dtype(int(element.numerator)) / int(element.denominator)
169
+
170
+ def from_AlgebraicField(self, element, base):
171
+ return self.from_sympy(base.to_sympy(element).evalf(self.dps))
172
+
173
+ def from_RealField(self, element, base):
174
+ return self.dtype(element)
175
+
176
+ def from_ComplexField(self, element, base):
177
+ if not element.imag:
178
+ return self.dtype(element.real)
179
+
180
+ def to_rational(self, element, limit=True):
181
+ """Convert a real number to rational number. """
182
+ return to_rational(element, self._max_denom, limit=limit)
183
+
184
+ def get_ring(self):
185
+ """Returns a ring associated with ``self``. """
186
+ return self
187
+
188
+ def get_exact(self):
189
+ """Returns an exact domain associated with ``self``. """
190
+ from sympy.polys.domains import QQ
191
+ return QQ
192
+
193
+ def gcd(self, a, b):
194
+ """Returns GCD of ``a`` and ``b``. """
195
+ return self.one
196
+
197
+ def lcm(self, a, b):
198
+ """Returns LCM of ``a`` and ``b``. """
199
+ return a*b
200
+
201
+ def almosteq(self, a, b, tolerance=None):
202
+ """Check if ``a`` and ``b`` are almost equal. """
203
+ return self._context.almosteq(a, b, tolerance)
204
+
205
+ def is_square(self, a):
206
+ """Returns ``True`` if ``a >= 0`` and ``False`` otherwise. """
207
+ return a >= 0
208
+
209
+ def exsqrt(self, a):
210
+ """Non-negative square root for ``a >= 0`` and ``None`` otherwise.
211
+
212
+ Explanation
213
+ ===========
214
+ The square root may be slightly inaccurate due to floating point
215
+ rounding error.
216
+ """
217
+ return a ** 0.5 if a >= 0 else None
218
+
219
+
220
+ RR = RealField()
.venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`Ring` class. """
2
+
3
+
4
+ from sympy.polys.domains.domain import Domain
5
+ from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible
6
+
7
+ from sympy.utilities import public
8
+
9
+ @public
10
+ class Ring(Domain):
11
+ """Represents a ring domain. """
12
+
13
+ is_Ring = True
14
+
15
+ def get_ring(self):
16
+ """Returns a ring associated with ``self``. """
17
+ return self
18
+
19
+ def exquo(self, a, b):
20
+ """Exact quotient of ``a`` and ``b``, implies ``__floordiv__``. """
21
+ if a % b:
22
+ raise ExactQuotientFailed(a, b, self)
23
+ else:
24
+ return a // b
25
+
26
+ def quo(self, a, b):
27
+ """Quotient of ``a`` and ``b``, implies ``__floordiv__``. """
28
+ return a // b
29
+
30
+ def rem(self, a, b):
31
+ """Remainder of ``a`` and ``b``, implies ``__mod__``. """
32
+ return a % b
33
+
34
+ def div(self, a, b):
35
+ """Division of ``a`` and ``b``, implies ``__divmod__``. """
36
+ return divmod(a, b)
37
+
38
+ def invert(self, a, b):
39
+ """Returns inversion of ``a mod b``. """
40
+ s, t, h = self.gcdex(a, b)
41
+
42
+ if self.is_one(h):
43
+ return s % b
44
+ else:
45
+ raise NotInvertible("zero divisor")
46
+
47
+ def revert(self, a):
48
+ """Returns ``a**(-1)`` if possible. """
49
+ if self.is_one(a) or self.is_one(-a):
50
+ return a
51
+ else:
52
+ raise NotReversible('only units are reversible in a ring')
53
+
54
+ def is_unit(self, a):
55
+ try:
56
+ self.revert(a)
57
+ return True
58
+ except NotReversible:
59
+ return False
60
+
61
+ def numer(self, a):
62
+ """Returns numerator of ``a``. """
63
+ return a
64
+
65
+ def denom(self, a):
66
+ """Returns denominator of `a`. """
67
+ return self.one
68
+
69
+ def free_module(self, rank):
70
+ """
71
+ Generate a free module of rank ``rank`` over self.
72
+
73
+ >>> from sympy.abc import x
74
+ >>> from sympy import QQ
75
+ >>> QQ.old_poly_ring(x).free_module(2)
76
+ QQ[x]**2
77
+ """
78
+ raise NotImplementedError
79
+
80
+ def ideal(self, *gens):
81
+ """
82
+ Generate an ideal of ``self``.
83
+
84
+ >>> from sympy.abc import x
85
+ >>> from sympy import QQ
86
+ >>> QQ.old_poly_ring(x).ideal(x**2)
87
+ <x**2>
88
+ """
89
+ from sympy.polys.agca.ideals import ModuleImplementedIdeal
90
+ return ModuleImplementedIdeal(self, self.free_module(1).submodule(
91
+ *[[x] for x in gens]))
92
+
93
+ def quotient_ring(self, e):
94
+ """
95
+ Form a quotient ring of ``self``.
96
+
97
+ Here ``e`` can be an ideal or an iterable.
98
+
99
+ >>> from sympy.abc import x
100
+ >>> from sympy import QQ
101
+ >>> QQ.old_poly_ring(x).quotient_ring(QQ.old_poly_ring(x).ideal(x**2))
102
+ QQ[x]/<x**2>
103
+ >>> QQ.old_poly_ring(x).quotient_ring([x**2])
104
+ QQ[x]/<x**2>
105
+
106
+ The division operator has been overloaded for this:
107
+
108
+ >>> QQ.old_poly_ring(x)/[x**2]
109
+ QQ[x]/<x**2>
110
+ """
111
+ from sympy.polys.agca.ideals import Ideal
112
+ from sympy.polys.domains.quotientring import QuotientRing
113
+ if not isinstance(e, Ideal):
114
+ e = self.ideal(*e)
115
+ return QuotientRing(self, e)
116
+
117
+ def __truediv__(self, e):
118
+ return self.quotient_ring(e)
.venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Implementation of :class:`SimpleDomain` class. """
2
+
3
+
4
+ from sympy.polys.domains.domain import Domain
5
+ from sympy.utilities import public
6
+
7
+ @public
8
+ class SimpleDomain(Domain):
9
+ """Base class for simple domains, e.g. ZZ, QQ. """
10
+
11
+ is_Simple = True
12
+
13
+ def inject(self, *gens):
14
+ """Inject generators into this domain. """
15
+ return self.poly_ring(*gens)
.venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for efficient functions for generating Appell sequences."""
2
+ from sympy.core.numbers import Rational as Q
3
+ from sympy.polys.polytools import Poly
4
+ from sympy.testing.pytest import raises
5
+ from sympy.polys.appellseqs import (bernoulli_poly, bernoulli_c_poly,
6
+ euler_poly, genocchi_poly, andre_poly)
7
+ from sympy.abc import x
8
+
9
+ def test_bernoulli_poly():
10
+ raises(ValueError, lambda: bernoulli_poly(-1, x))
11
+ assert bernoulli_poly(1, x, polys=True) == Poly(x - Q(1,2))
12
+
13
+ assert bernoulli_poly(0, x) == 1
14
+ assert bernoulli_poly(1, x) == x - Q(1,2)
15
+ assert bernoulli_poly(2, x) == x**2 - x + Q(1,6)
16
+ assert bernoulli_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,2)*x
17
+ assert bernoulli_poly(4, x) == x**4 - 2*x**3 + x**2 - Q(1,30)
18
+ assert bernoulli_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,3)*x**3 - Q(1,6)*x
19
+ assert bernoulli_poly(6, x) == x**6 - 3*x**5 + Q(5,2)*x**4 - Q(1,2)*x**2 + Q(1,42)
20
+
21
+ assert bernoulli_poly(1).dummy_eq(x - Q(1,2))
22
+ assert bernoulli_poly(1, polys=True) == Poly(x - Q(1,2))
23
+
24
+ def test_bernoulli_c_poly():
25
+ raises(ValueError, lambda: bernoulli_c_poly(-1, x))
26
+ assert bernoulli_c_poly(1, x, polys=True) == Poly(x, domain='QQ')
27
+
28
+ assert bernoulli_c_poly(0, x) == 1
29
+ assert bernoulli_c_poly(1, x) == x
30
+ assert bernoulli_c_poly(2, x) == x**2 - Q(1,3)
31
+ assert bernoulli_c_poly(3, x) == x**3 - x
32
+ assert bernoulli_c_poly(4, x) == x**4 - 2*x**2 + Q(7,15)
33
+ assert bernoulli_c_poly(5, x) == x**5 - Q(10,3)*x**3 + Q(7,3)*x
34
+ assert bernoulli_c_poly(6, x) == x**6 - 5*x**4 + 7*x**2 - Q(31,21)
35
+
36
+ assert bernoulli_c_poly(1).dummy_eq(x)
37
+ assert bernoulli_c_poly(1, polys=True) == Poly(x, domain='QQ')
38
+
39
+ assert 2**8 * bernoulli_poly(8, (x+1)/2).expand() == bernoulli_c_poly(8, x)
40
+ assert 2**9 * bernoulli_poly(9, (x+1)/2).expand() == bernoulli_c_poly(9, x)
41
+
42
+ def test_genocchi_poly():
43
+ raises(ValueError, lambda: genocchi_poly(-1, x))
44
+ assert genocchi_poly(2, x, polys=True) == Poly(-2*x + 1)
45
+
46
+ assert genocchi_poly(0, x) == 0
47
+ assert genocchi_poly(1, x) == -1
48
+ assert genocchi_poly(2, x) == 1 - 2*x
49
+ assert genocchi_poly(3, x) == 3*x - 3*x**2
50
+ assert genocchi_poly(4, x) == -1 + 6*x**2 - 4*x**3
51
+ assert genocchi_poly(5, x) == -5*x + 10*x**3 - 5*x**4
52
+ assert genocchi_poly(6, x) == 3 - 15*x**2 + 15*x**4 - 6*x**5
53
+
54
+ assert genocchi_poly(2).dummy_eq(-2*x + 1)
55
+ assert genocchi_poly(2, polys=True) == Poly(-2*x + 1)
56
+
57
+ assert 2 * (bernoulli_poly(8, x) - bernoulli_c_poly(8, x)) == genocchi_poly(8, x)
58
+ assert 2 * (bernoulli_poly(9, x) - bernoulli_c_poly(9, x)) == genocchi_poly(9, x)
59
+
60
+ def test_euler_poly():
61
+ raises(ValueError, lambda: euler_poly(-1, x))
62
+ assert euler_poly(1, x, polys=True) == Poly(x - Q(1,2))
63
+
64
+ assert euler_poly(0, x) == 1
65
+ assert euler_poly(1, x) == x - Q(1,2)
66
+ assert euler_poly(2, x) == x**2 - x
67
+ assert euler_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,4)
68
+ assert euler_poly(4, x) == x**4 - 2*x**3 + x
69
+ assert euler_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,2)*x**2 - Q(1,2)
70
+ assert euler_poly(6, x) == x**6 - 3*x**5 + 5*x**3 - 3*x
71
+
72
+ assert euler_poly(1).dummy_eq(x - Q(1,2))
73
+ assert euler_poly(1, polys=True) == Poly(x - Q(1,2))
74
+
75
+ assert genocchi_poly(9, x) == euler_poly(8, x) * -9
76
+ assert genocchi_poly(10, x) == euler_poly(9, x) * -10
77
+
78
+ def test_andre_poly():
79
+ raises(ValueError, lambda: andre_poly(-1, x))
80
+ assert andre_poly(1, x, polys=True) == Poly(x)
81
+
82
+ assert andre_poly(0, x) == 1
83
+ assert andre_poly(1, x) == x
84
+ assert andre_poly(2, x) == x**2 - 1
85
+ assert andre_poly(3, x) == x**3 - 3*x
86
+ assert andre_poly(4, x) == x**4 - 6*x**2 + 5
87
+ assert andre_poly(5, x) == x**5 - 10*x**3 + 25*x
88
+ assert andre_poly(6, x) == x**6 - 15*x**4 + 75*x**2 - 61
89
+
90
+ assert andre_poly(1).dummy_eq(x)
91
+ assert andre_poly(1, polys=True) == Poly(x)
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for tools for constructing domains for expressions. """
2
+
3
+ from sympy.testing.pytest import tooslow
4
+
5
+ from sympy.polys.constructor import construct_domain
6
+ from sympy.polys.domains import ZZ, QQ, ZZ_I, QQ_I, RR, CC, EX
7
+ from sympy.polys.domains.realfield import RealField
8
+ from sympy.polys.domains.complexfield import ComplexField
9
+
10
+ from sympy.core import (Catalan, GoldenRatio)
11
+ from sympy.core.numbers import (E, Float, I, Rational, pi)
12
+ from sympy.core.singleton import S
13
+ from sympy.functions.elementary.exponential import exp
14
+ from sympy.functions.elementary.miscellaneous import sqrt
15
+ from sympy.functions.elementary.trigonometric import sin
16
+ from sympy import rootof
17
+
18
+ from sympy.abc import x, y
19
+
20
+
21
+ def test_construct_domain():
22
+
23
+ assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)])
24
+ assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)])
25
+
26
+ assert construct_domain([S.One, S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)])
27
+ assert construct_domain([S.One, S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)])
28
+
29
+ assert construct_domain([S.Half, S(2)]) == (QQ, [QQ(1, 2), QQ(2)])
30
+ result = construct_domain([3.14, 1, S.Half])
31
+ assert isinstance(result[0], RealField)
32
+ assert result[1] == [RR(3.14), RR(1.0), RR(0.5)]
33
+
34
+ result = construct_domain([3.14, I, S.Half])
35
+ assert isinstance(result[0], ComplexField)
36
+ assert result[1] == [CC(3.14), CC(1.0j), CC(0.5)]
37
+
38
+ assert construct_domain([1.0+I]) == (CC, [CC(1.0, 1.0)])
39
+ assert construct_domain([2.0+3.0*I]) == (CC, [CC(2.0, 3.0)])
40
+
41
+ assert construct_domain([1, I]) == (ZZ_I, [ZZ_I(1, 0), ZZ_I(0, 1)])
42
+ assert construct_domain([1, I/2]) == (QQ_I, [QQ_I(1, 0), QQ_I(0, S.Half)])
43
+
44
+ assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))])
45
+ assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))])
46
+
47
+ assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))])
48
+
49
+ assert construct_domain([x, sqrt(x)]) == (EX, [EX(x), EX(sqrt(x))])
50
+ assert construct_domain([x, sqrt(x), sqrt(y)]) == (EX, [EX(x), EX(sqrt(x)), EX(sqrt(y))])
51
+
52
+ alg = QQ.algebraic_field(sqrt(2))
53
+
54
+ assert construct_domain([7, S.Half, sqrt(2)], extension=True) == \
55
+ (alg, [alg.convert(7), alg.convert(S.Half), alg.convert(sqrt(2))])
56
+
57
+ alg = QQ.algebraic_field(sqrt(2) + sqrt(3))
58
+
59
+ assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \
60
+ (alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))])
61
+
62
+ dom = ZZ[x]
63
+
64
+ assert construct_domain([2*x, 3]) == \
65
+ (dom, [dom.convert(2*x), dom.convert(3)])
66
+
67
+ dom = ZZ[x, y]
68
+
69
+ assert construct_domain([2*x, 3*y]) == \
70
+ (dom, [dom.convert(2*x), dom.convert(3*y)])
71
+
72
+ dom = QQ[x]
73
+
74
+ assert construct_domain([x/2, 3]) == \
75
+ (dom, [dom.convert(x/2), dom.convert(3)])
76
+
77
+ dom = QQ[x, y]
78
+
79
+ assert construct_domain([x/2, 3*y]) == \
80
+ (dom, [dom.convert(x/2), dom.convert(3*y)])
81
+
82
+ dom = ZZ_I[x]
83
+
84
+ assert construct_domain([2*x, I]) == \
85
+ (dom, [dom.convert(2*x), dom.convert(I)])
86
+
87
+ dom = ZZ_I[x, y]
88
+
89
+ assert construct_domain([2*x, I*y]) == \
90
+ (dom, [dom.convert(2*x), dom.convert(I*y)])
91
+
92
+ dom = QQ_I[x]
93
+
94
+ assert construct_domain([x/2, I]) == \
95
+ (dom, [dom.convert(x/2), dom.convert(I)])
96
+
97
+ dom = QQ_I[x, y]
98
+
99
+ assert construct_domain([x/2, I*y]) == \
100
+ (dom, [dom.convert(x/2), dom.convert(I*y)])
101
+
102
+ dom = RR[x]
103
+
104
+ assert construct_domain([x/2, 3.5]) == \
105
+ (dom, [dom.convert(x/2), dom.convert(3.5)])
106
+
107
+ dom = RR[x, y]
108
+
109
+ assert construct_domain([x/2, 3.5*y]) == \
110
+ (dom, [dom.convert(x/2), dom.convert(3.5*y)])
111
+
112
+ dom = CC[x]
113
+
114
+ assert construct_domain([I*x/2, 3.5]) == \
115
+ (dom, [dom.convert(I*x/2), dom.convert(3.5)])
116
+
117
+ dom = CC[x, y]
118
+
119
+ assert construct_domain([I*x/2, 3.5*y]) == \
120
+ (dom, [dom.convert(I*x/2), dom.convert(3.5*y)])
121
+
122
+ dom = CC[x]
123
+
124
+ assert construct_domain([x/2, I*3.5]) == \
125
+ (dom, [dom.convert(x/2), dom.convert(I*3.5)])
126
+
127
+ dom = CC[x, y]
128
+
129
+ assert construct_domain([x/2, I*3.5*y]) == \
130
+ (dom, [dom.convert(x/2), dom.convert(I*3.5*y)])
131
+
132
+ dom = ZZ.frac_field(x)
133
+
134
+ assert construct_domain([2/x, 3]) == \
135
+ (dom, [dom.convert(2/x), dom.convert(3)])
136
+
137
+ dom = ZZ.frac_field(x, y)
138
+
139
+ assert construct_domain([2/x, 3*y]) == \
140
+ (dom, [dom.convert(2/x), dom.convert(3*y)])
141
+
142
+ dom = RR.frac_field(x)
143
+
144
+ assert construct_domain([2/x, 3.5]) == \
145
+ (dom, [dom.convert(2/x), dom.convert(3.5)])
146
+
147
+ dom = RR.frac_field(x, y)
148
+
149
+ assert construct_domain([2/x, 3.5*y]) == \
150
+ (dom, [dom.convert(2/x), dom.convert(3.5*y)])
151
+
152
+ dom = RealField(prec=336)[x]
153
+
154
+ assert construct_domain([pi.evalf(100)*x]) == \
155
+ (dom, [dom.convert(pi.evalf(100)*x)])
156
+
157
+ assert construct_domain(2) == (ZZ, ZZ(2))
158
+ assert construct_domain(S(2)/3) == (QQ, QQ(2, 3))
159
+ assert construct_domain(Rational(2, 3)) == (QQ, QQ(2, 3))
160
+
161
+ assert construct_domain({}) == (ZZ, {})
162
+
163
+
164
+ def test_complex_exponential():
165
+ w = exp(-I*2*pi/3, evaluate=False)
166
+ alg = QQ.algebraic_field(w)
167
+ assert construct_domain([w**2, w, 1], extension=True) == (
168
+ alg,
169
+ [alg.convert(w**2),
170
+ alg.convert(w),
171
+ alg.convert(1)]
172
+ )
173
+
174
+
175
+ def test_rootof():
176
+ r1 = rootof(x**3 + x + 1, 0)
177
+ r2 = rootof(x**3 + x + 1, 1)
178
+ K1 = QQ.algebraic_field(r1)
179
+ K2 = QQ.algebraic_field(r2)
180
+ assert construct_domain([r1]) == (EX, [EX(r1)])
181
+ assert construct_domain([r2]) == (EX, [EX(r2)])
182
+ assert construct_domain([r1, r2]) == (EX, [EX(r1), EX(r2)])
183
+
184
+ assert construct_domain([r1], extension=True) == (
185
+ K1, [K1.from_sympy(r1)])
186
+ assert construct_domain([r2], extension=True) == (
187
+ K2, [K2.from_sympy(r2)])
188
+
189
+
190
+ @tooslow
191
+ def test_rootof_primitive_element():
192
+ r1 = rootof(x**3 + x + 1, 0)
193
+ r2 = rootof(x**3 + x + 1, 1)
194
+ K12 = QQ.algebraic_field(r1 + r2)
195
+ assert construct_domain([r1, r2], extension=True) == (
196
+ K12, [K12.from_sympy(r1), K12.from_sympy(r2)])
197
+
198
+
199
+ def test_composite_option():
200
+ assert construct_domain({(1,): sin(y)}, composite=False) == \
201
+ (EX, {(1,): EX(sin(y))})
202
+
203
+ assert construct_domain({(1,): y}, composite=False) == \
204
+ (EX, {(1,): EX(y)})
205
+
206
+ assert construct_domain({(1, 1): 1}, composite=False) == \
207
+ (ZZ, {(1, 1): 1})
208
+
209
+ assert construct_domain({(1, 0): y}, composite=False) == \
210
+ (EX, {(1, 0): EX(y)})
211
+
212
+
213
+ def test_precision():
214
+ f1 = Float("1.01")
215
+ f2 = Float("1.0000000000000000000001")
216
+ for u in [1, 1e-2, 1e-6, 1e-13, 1e-14, 1e-16, 1e-20, 1e-100, 1e-300,
217
+ f1, f2]:
218
+ result = construct_domain([u])
219
+ v = float(result[1][0])
220
+ assert abs(u - v) / u < 1e-14 # Test relative accuracy
221
+
222
+ result = construct_domain([f1])
223
+ y = result[1][0]
224
+ assert y-1 > 1e-50
225
+
226
+ result = construct_domain([f2])
227
+ y = result[1][0]
228
+ assert y-1 > 1e-50
229
+
230
+
231
+ def test_issue_11538():
232
+ for n in [E, pi, Catalan]:
233
+ assert construct_domain(n)[0] == ZZ[n]
234
+ assert construct_domain(x + n)[0] == ZZ[x, n]
235
+ assert construct_domain(GoldenRatio)[0] == EX
236
+ assert construct_domain(x + GoldenRatio)[0] == EX
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py ADDED
@@ -0,0 +1,1007 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for dense recursive polynomials' arithmetics. """
2
+
3
+ from sympy.external.gmpy import GROUND_TYPES
4
+
5
+ from sympy.polys.densebasic import (
6
+ dup_normal, dmp_normal,
7
+ )
8
+
9
+ from sympy.polys.densearith import (
10
+ dup_add_term, dmp_add_term,
11
+ dup_sub_term, dmp_sub_term,
12
+ dup_mul_term, dmp_mul_term,
13
+ dup_add_ground, dmp_add_ground,
14
+ dup_sub_ground, dmp_sub_ground,
15
+ dup_mul_ground, dmp_mul_ground,
16
+ dup_quo_ground, dmp_quo_ground,
17
+ dup_exquo_ground, dmp_exquo_ground,
18
+ dup_lshift, dup_rshift,
19
+ dup_abs, dmp_abs,
20
+ dup_neg, dmp_neg,
21
+ dup_add, dmp_add,
22
+ dup_sub, dmp_sub,
23
+ dup_mul, dmp_mul,
24
+ dup_sqr, dmp_sqr,
25
+ dup_pow, dmp_pow,
26
+ dup_add_mul, dmp_add_mul,
27
+ dup_sub_mul, dmp_sub_mul,
28
+ dup_pdiv, dup_prem, dup_pquo, dup_pexquo,
29
+ dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo,
30
+ dup_rr_div, dmp_rr_div,
31
+ dup_ff_div, dmp_ff_div,
32
+ dup_div, dup_rem, dup_quo, dup_exquo,
33
+ dmp_div, dmp_rem, dmp_quo, dmp_exquo,
34
+ dup_max_norm, dmp_max_norm,
35
+ dup_l1_norm, dmp_l1_norm,
36
+ dup_l2_norm_squared, dmp_l2_norm_squared,
37
+ dup_expand, dmp_expand,
38
+ )
39
+
40
+ from sympy.polys.polyerrors import (
41
+ ExactQuotientFailed,
42
+ )
43
+
44
+ from sympy.polys.specialpolys import f_polys, Symbol, Poly
45
+ from sympy.polys.domains import FF, ZZ, QQ, CC
46
+
47
+ from sympy.testing.pytest import raises
48
+
49
+ x = Symbol('x')
50
+
51
+ f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
52
+ F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1, 7), 2, QQ)
53
+
54
+ def test_dup_add_term():
55
+ f = dup_normal([], ZZ)
56
+
57
+ assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ)
58
+
59
+ assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ)
60
+ assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ)
61
+ assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ)
62
+
63
+ f = dup_normal([1, 1, 1], ZZ)
64
+
65
+ assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ)
66
+ assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ)
67
+ assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ)
68
+
69
+ assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ)
70
+ assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ)
71
+ assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ)
72
+ assert dup_add_term(
73
+ f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ)
74
+
75
+ assert dup_add_term(f, ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ)
76
+
77
+
78
+ def test_dmp_add_term():
79
+ assert dmp_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \
80
+ dup_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ)
81
+ assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0
82
+ assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0
83
+
84
+
85
+ def test_dup_sub_term():
86
+ f = dup_normal([], ZZ)
87
+
88
+ assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ)
89
+
90
+ assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ)
91
+ assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ)
92
+ assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ)
93
+
94
+ f = dup_normal([1, 1, 1], ZZ)
95
+
96
+ assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1, -1], ZZ)
97
+ assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1, -1, 1], ZZ)
98
+ assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ)
99
+
100
+ assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ)
101
+ assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ)
102
+ assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ)
103
+ assert dup_sub_term(
104
+ f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ)
105
+
106
+ assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ)
107
+
108
+
109
+ def test_dmp_sub_term():
110
+ assert dmp_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \
111
+ dup_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ)
112
+ assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0
113
+ assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0
114
+
115
+
116
+ def test_dup_mul_term():
117
+ f = dup_normal([], ZZ)
118
+
119
+ assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ)
120
+
121
+ f = dup_normal([1, 1], ZZ)
122
+
123
+ assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ)
124
+
125
+ f = dup_normal([1, 2, 3], ZZ)
126
+
127
+ assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2, 4, 6], ZZ)
128
+ assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2, 4, 6, 0], ZZ)
129
+ assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2, 4, 6, 0, 0], ZZ)
130
+ assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2, 4, 6, 0, 0, 0], ZZ)
131
+
132
+
133
+ def test_dmp_mul_term():
134
+ assert dmp_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, 0, ZZ) == \
135
+ dup_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, ZZ)
136
+
137
+ assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]]
138
+ assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]]
139
+
140
+ assert dmp_mul_term([[ZZ(1), ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \
141
+ [[ZZ(2), ZZ(4)], [ZZ(6)], [], []]
142
+
143
+ assert dmp_mul_term([[]], [QQ(2, 3)], 3, 1, QQ) == [[]]
144
+ assert dmp_mul_term([[QQ(1, 2)]], [], 3, 1, QQ) == [[]]
145
+
146
+ assert dmp_mul_term([[QQ(1, 5), QQ(2, 5)], [QQ(3, 5)]], [QQ(2, 3)], 2, 1, QQ) == \
147
+ [[QQ(2, 15), QQ(4, 15)], [QQ(6, 15)], [], []]
148
+
149
+
150
+ def test_dup_add_ground():
151
+ f = ZZ.map([1, 2, 3, 4])
152
+ g = ZZ.map([1, 2, 3, 8])
153
+
154
+ assert dup_add_ground(f, ZZ(4), ZZ) == g
155
+
156
+
157
+ def test_dmp_add_ground():
158
+ f = ZZ.map([[1], [2], [3], [4]])
159
+ g = ZZ.map([[1], [2], [3], [8]])
160
+
161
+ assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g
162
+
163
+
164
+ def test_dup_sub_ground():
165
+ f = ZZ.map([1, 2, 3, 4])
166
+ g = ZZ.map([1, 2, 3, 0])
167
+
168
+ assert dup_sub_ground(f, ZZ(4), ZZ) == g
169
+
170
+
171
+ def test_dmp_sub_ground():
172
+ f = ZZ.map([[1], [2], [3], [4]])
173
+ g = ZZ.map([[1], [2], [3], []])
174
+
175
+ assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g
176
+
177
+
178
+ def test_dup_mul_ground():
179
+ f = dup_normal([], ZZ)
180
+
181
+ assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ)
182
+
183
+ f = dup_normal([1, 2, 3], ZZ)
184
+
185
+ assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ)
186
+ assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2, 4, 6], ZZ)
187
+
188
+
189
+ def test_dmp_mul_ground():
190
+ assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [
191
+ [[ZZ(2), ZZ(4), ZZ(6)], [ZZ(4)]],
192
+ [[ZZ(6)]],
193
+ [[ZZ(8), ZZ(10), ZZ(12)], [ZZ(2), ZZ(4), ZZ(2)], [ZZ(2)]]
194
+ ]
195
+
196
+ assert dmp_mul_ground(F_0, QQ(1, 2), 2, QQ) == [
197
+ [[QQ(1, 14), QQ(2, 14), QQ(3, 14)], [QQ(2, 14)]],
198
+ [[QQ(3, 14)]],
199
+ [[QQ(4, 14), QQ(5, 14), QQ(6, 14)], [QQ(1, 14), QQ(2, 14),
200
+ QQ(1, 14)], [QQ(1, 14)]]
201
+ ]
202
+
203
+
204
+ def test_dup_quo_ground():
205
+ raises(ZeroDivisionError, lambda: dup_quo_ground(dup_normal([1, 2,
206
+ 3], ZZ), ZZ(0), ZZ))
207
+
208
+ f = dup_normal([], ZZ)
209
+
210
+ assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ)
211
+
212
+ f = dup_normal([6, 2, 8], ZZ)
213
+
214
+ assert dup_quo_ground(f, ZZ(1), ZZ) == f
215
+ assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ)
216
+
217
+ assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2, 0, 2], ZZ)
218
+
219
+ f = dup_normal([6, 2, 8], QQ)
220
+
221
+ assert dup_quo_ground(f, QQ(1), QQ) == f
222
+ assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)]
223
+ assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)]
224
+
225
+
226
+ def test_dup_exquo_ground():
227
+ raises(ZeroDivisionError, lambda: dup_exquo_ground(dup_normal([1,
228
+ 2, 3], ZZ), ZZ(0), ZZ))
229
+ raises(ExactQuotientFailed, lambda: dup_exquo_ground(dup_normal([1,
230
+ 2, 3], ZZ), ZZ(3), ZZ))
231
+
232
+ f = dup_normal([], ZZ)
233
+
234
+ assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ)
235
+
236
+ f = dup_normal([6, 2, 8], ZZ)
237
+
238
+ assert dup_exquo_ground(f, ZZ(1), ZZ) == f
239
+ assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ)
240
+
241
+ f = dup_normal([6, 2, 8], QQ)
242
+
243
+ assert dup_exquo_ground(f, QQ(1), QQ) == f
244
+ assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)]
245
+ assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)]
246
+
247
+
248
+ def test_dmp_quo_ground():
249
+ f = dmp_normal([[6], [2], [8]], 1, ZZ)
250
+
251
+ assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f
252
+ assert dmp_quo_ground(
253
+ f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ)
254
+
255
+ assert dmp_normal(dmp_quo_ground(
256
+ f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2], [], [2]], 1, ZZ)
257
+
258
+
259
+ def test_dmp_exquo_ground():
260
+ f = dmp_normal([[6], [2], [8]], 1, ZZ)
261
+
262
+ assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f
263
+ assert dmp_exquo_ground(
264
+ f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ)
265
+
266
+
267
+ def test_dup_lshift():
268
+ assert dup_lshift([], 3, ZZ) == []
269
+ assert dup_lshift([1], 3, ZZ) == [1, 0, 0, 0]
270
+
271
+
272
+ def test_dup_rshift():
273
+ assert dup_rshift([], 3, ZZ) == []
274
+ assert dup_rshift([1, 0, 0, 0], 3, ZZ) == [1]
275
+
276
+
277
+ def test_dup_abs():
278
+ assert dup_abs([], ZZ) == []
279
+ assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)]
280
+ assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)]
281
+ assert dup_abs([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(2), ZZ(3)]
282
+
283
+ assert dup_abs([], QQ) == []
284
+ assert dup_abs([QQ( 1, 2)], QQ) == [QQ(1, 2)]
285
+ assert dup_abs([QQ(-7, 3)], QQ) == [QQ(7, 3)]
286
+ assert dup_abs(
287
+ [QQ(-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(2, 7), QQ(3, 7)]
288
+
289
+
290
+ def test_dmp_abs():
291
+ assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)]
292
+ assert dmp_abs([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)]
293
+
294
+ assert dmp_abs([[[]]], 2, ZZ) == [[[]]]
295
+ assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
296
+ assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]]
297
+
298
+ assert dmp_abs([[[]]], 2, QQ) == [[[]]]
299
+ assert dmp_abs([[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]]
300
+ assert dmp_abs([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]]
301
+
302
+
303
+ def test_dup_neg():
304
+ assert dup_neg([], ZZ) == []
305
+ assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)]
306
+ assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)]
307
+ assert dup_neg([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(-2), ZZ(-3)]
308
+
309
+ assert dup_neg([], QQ) == []
310
+ assert dup_neg([QQ(1, 2)], QQ) == [QQ(-1, 2)]
311
+ assert dup_neg([QQ(-7, 9)], QQ) == [QQ(7, 9)]
312
+ assert dup_neg([QQ(
313
+ -1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(-2, 7), QQ(-3, 7)]
314
+
315
+
316
+ def test_dmp_neg():
317
+ assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)]
318
+ assert dmp_neg([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)]
319
+
320
+ assert dmp_neg([[[]]], 2, ZZ) == [[[]]]
321
+ assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]]
322
+ assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]]
323
+
324
+ assert dmp_neg([[[]]], 2, QQ) == [[[]]]
325
+ assert dmp_neg([[[QQ(1, 9)]]], 2, QQ) == [[[QQ(-1, 9)]]]
326
+ assert dmp_neg([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]]
327
+
328
+
329
+ def test_dup_add():
330
+ assert dup_add([], [], ZZ) == []
331
+ assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)]
332
+ assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)]
333
+ assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)]
334
+ assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)]
335
+
336
+ assert dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(3)]
337
+ assert dup_add([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(3)]
338
+
339
+ assert dup_add([ZZ(1), ZZ(
340
+ 2), ZZ(3)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(9), ZZ(11), ZZ(13)]
341
+
342
+ assert dup_add([], [], QQ) == []
343
+ assert dup_add([QQ(1, 2)], [], QQ) == [QQ(1, 2)]
344
+ assert dup_add([], [QQ(1, 2)], QQ) == [QQ(1, 2)]
345
+ assert dup_add([QQ(1, 4)], [QQ(1, 4)], QQ) == [QQ(1, 2)]
346
+ assert dup_add([QQ(1, 4)], [QQ(1, 2)], QQ) == [QQ(3, 4)]
347
+
348
+ assert dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) == [QQ(1, 2), QQ(5, 3)]
349
+ assert dup_add([QQ(1)], [QQ(1, 2), QQ(2, 3)], QQ) == [QQ(1, 2), QQ(5, 3)]
350
+
351
+ assert dup_add([QQ(1, 7), QQ(2, 7), QQ(3, 7)], [QQ(
352
+ 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(9, 7), QQ(11, 7), QQ(13, 7)]
353
+
354
+
355
+ def test_dmp_add():
356
+ assert dmp_add([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \
357
+ dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ)
358
+ assert dmp_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \
359
+ dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ)
360
+
361
+ assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]]
362
+ assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]]
363
+ assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
364
+ assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]]
365
+ assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]]
366
+
367
+ assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]]
368
+ assert dmp_add([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]]
369
+ assert dmp_add([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]]
370
+ assert dmp_add([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(3, 7)]]]
371
+ assert dmp_add([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(3, 7)]]]
372
+
373
+
374
+ def test_dup_sub():
375
+ assert dup_sub([], [], ZZ) == []
376
+ assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)]
377
+ assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)]
378
+ assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == []
379
+ assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)]
380
+
381
+ assert dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(1)]
382
+ assert dup_sub([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-1), ZZ(-1)]
383
+
384
+ assert dup_sub([ZZ(3), ZZ(
385
+ 2), ZZ(1)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(-5), ZZ(-7), ZZ(-9)]
386
+
387
+ assert dup_sub([], [], QQ) == []
388
+ assert dup_sub([QQ(1, 2)], [], QQ) == [QQ(1, 2)]
389
+ assert dup_sub([], [QQ(1, 2)], QQ) == [QQ(-1, 2)]
390
+ assert dup_sub([QQ(1, 3)], [QQ(1, 3)], QQ) == []
391
+ assert dup_sub([QQ(1, 3)], [QQ(2, 3)], QQ) == [QQ(-1, 3)]
392
+
393
+ assert dup_sub([QQ(1, 7), QQ(2, 7)], [QQ(1)], QQ) == [QQ(1, 7), QQ(-5, 7)]
394
+ assert dup_sub([QQ(1)], [QQ(1, 7), QQ(2, 7)], QQ) == [QQ(-1, 7), QQ(5, 7)]
395
+
396
+ assert dup_sub([QQ(3, 7), QQ(2, 7), QQ(1, 7)], [QQ(
397
+ 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(-5, 7), QQ(-7, 7), QQ(-9, 7)]
398
+
399
+
400
+ def test_dmp_sub():
401
+ assert dmp_sub([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \
402
+ dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ)
403
+ assert dmp_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \
404
+ dup_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ)
405
+
406
+ assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]]
407
+ assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]]
408
+ assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]]
409
+ assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
410
+ assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]]
411
+
412
+ assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]]
413
+ assert dmp_sub([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]]
414
+ assert dmp_sub([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(-1, 2)]]]
415
+ assert dmp_sub([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(1, 7)]]]
416
+ assert dmp_sub([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(-1, 7)]]]
417
+
418
+
419
+ def test_dup_add_mul():
420
+ assert dup_add_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)],
421
+ [ZZ(1), ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)]
422
+ assert dmp_add_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]],
423
+ [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]]
424
+
425
+
426
+ def test_dup_sub_mul():
427
+ assert dup_sub_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)],
428
+ [ZZ(1), ZZ(2)], ZZ) == [ZZ(-3), ZZ(-7), ZZ(-3), ZZ(1)]
429
+ assert dmp_sub_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]],
430
+ [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]]
431
+
432
+
433
+ def test_dup_mul():
434
+ assert dup_mul([], [], ZZ) == []
435
+ assert dup_mul([], [ZZ(1)], ZZ) == []
436
+ assert dup_mul([ZZ(1)], [], ZZ) == []
437
+ assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)]
438
+ assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)]
439
+
440
+ assert dup_mul([], [], QQ) == []
441
+ assert dup_mul([], [QQ(1, 2)], QQ) == []
442
+ assert dup_mul([QQ(1, 2)], [], QQ) == []
443
+ assert dup_mul([QQ(1, 2)], [QQ(4, 7)], QQ) == [QQ(2, 7)]
444
+ assert dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) == [QQ(15, 49)]
445
+
446
+ f = dup_normal([3, 0, 0, 6, 1, 2], ZZ)
447
+ g = dup_normal([4, 0, 1, 0], ZZ)
448
+ h = dup_normal([12, 0, 3, 24, 4, 14, 1, 2, 0], ZZ)
449
+
450
+ assert dup_mul(f, g, ZZ) == h
451
+ assert dup_mul(g, f, ZZ) == h
452
+
453
+ f = dup_normal([2, 0, 0, 1, 7], ZZ)
454
+ h = dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
455
+
456
+ assert dup_mul(f, f, ZZ) == h
457
+
458
+ K = FF(6)
459
+
460
+ assert dup_mul([K(2), K(1)], [K(3), K(4)], K) == [K(5), K(4)]
461
+
462
+ p1 = dup_normal([79, -1, 78, -94, -10, 11, 32, -19, 78, 2, -89, 30, 73, 42,
463
+ 85, 77, 83, -30, -34, -2, 95, -81, 37, -49, -46, -58, -16, 37, 35, -11,
464
+ -57, -15, -31, 67, -20, 27, 76, 2, 70, 67, -65, 65, -26, -93, -44, -12,
465
+ -92, 57, -90, -57, -11, -67, -98, -69, 97, -41, 89, 33, 89, -50, 81,
466
+ -31, 60, -27, 43, 29, -77, 44, 21, -91, 32, -57, 33, 3, 53, -51, -38,
467
+ -99, -84, 23, -50, 66, -100, 1, -75, -25, 27, -60, 98, -51, -87, 6, 8,
468
+ 78, -28, -95, -88, 12, -35, 26, -9, 16, -92, 55, -7, -86, 68, -39, -46,
469
+ 84, 94, 45, 60, 92, 68, -75, -74, -19, 8, 75, 78, 91, 57, 34, 14, -3,
470
+ -49, 65, 78, -18, 6, -29, -80, -98, 17, 13, 58, 21, 20, 9, 37, 7, -30,
471
+ -53, -20, 34, 67, -42, 89, -22, 73, 43, -6, 5, 51, -8, -15, -52, -22,
472
+ -58, -72, -3, 43, -92, 82, 83, -2, -13, -23, -60, 16, -94, -8, -28,
473
+ -95, -72, 63, -90, 76, 6, -43, -100, -59, 76, 3, 3, 46, -85, 75, 62,
474
+ -71, -76, 88, 97, -72, -1, 30, -64, 72, -48, 14, -78, 58, 63, -91, 24,
475
+ -87, -27, -80, -100, -44, 98, 70, 100, -29, -38, 11, 77, 100, 52, 86,
476
+ 65, -5, -42, -81, -38, -42, 43, -2, -70, -63, -52], ZZ)
477
+ p2 = dup_normal([65, -19, -47, 1, 90, 81, -15, -34, 25, -75, 9, -83, 50, -5,
478
+ -44, 31, 1, 70, -7, 78, 74, 80, 85, 65, 21, 41, 66, 19, -40, 63, -21,
479
+ -27, 32, 69, 83, 34, -35, 14, 81, 57, -75, 32, -67, -89, -100, -61, 46,
480
+ 84, -78, -29, -50, -94, -24, -32, -68, -16, 100, -7, -72, -89, 35, 82,
481
+ 58, 81, -92, 62, 5, -47, -39, -58, -72, -13, 84, 44, 55, -25, 48, -54,
482
+ -31, -56, -11, -50, -84, 10, 67, 17, 13, -14, 61, 76, -64, -44, -40,
483
+ -96, 11, -11, -94, 2, 6, 27, -6, 68, -54, 66, -74, -14, -1, -24, -73,
484
+ 96, 89, -11, -89, 56, -53, 72, -43, 96, 25, 63, -31, 29, 68, 83, 91,
485
+ -93, -19, -38, -40, 40, -12, -19, -79, 44, 100, -66, -29, -77, 62, 39,
486
+ -8, 11, -97, 14, 87, 64, 21, -18, 13, 15, -59, -75, -99, -88, 57, 54,
487
+ 56, -67, 6, -63, -59, -14, 28, 87, -20, -39, 84, -91, -2, 49, -75, 11,
488
+ -24, -95, 36, 66, 5, 25, -72, -40, 86, 90, 37, -33, 57, -35, 29, -18,
489
+ 4, -79, 64, -17, -27, 21, 29, -5, -44, -87, -24, 52, 78, 11, -23, -53,
490
+ 36, 42, 21, -68, 94, -91, -51, -21, 51, -76, 72, 31, 24, -48, -80, -9,
491
+ 37, -47, -6, -8, -63, -91, 79, -79, -100, 38, -20, 38, 100, 83, -90,
492
+ 87, 63, -36, 82, -19, 18, -98, -38, 26, 98, -70, 79, 92, 12, 12, 70,
493
+ 74, 36, 48, -13, 31, 31, -47, -71, -12, -64, 36, -42, 32, -86, 60, 83,
494
+ 70, 55, 0, 1, 29, -35, 8, -82, 8, -73, -46, -50, 43, 48, -5, -86, -72,
495
+ 44, -90, 19, 19, 5, -20, 97, -13, -66, -5, 5, -69, 64, -30, 41, 51, 36,
496
+ 13, -99, -61, 94, -12, 74, 98, 68, 24, 46, -97, -87, -6, -27, 82, 62,
497
+ -11, -77, 86, 66, -47, -49, -50, 13, 18, 89, -89, 46, -80, 13, 98, -35,
498
+ -36, -25, 12, 20, 26, -52, 79, 27, 79, 100, 8, 62, -58, -28, 37], ZZ)
499
+ res = dup_normal([5135, -1566, 1376, -7466, 4579, 11710, 8001, -7183,
500
+ -3737, -7439, 345, -10084, 24522, -1201, 1070, -10245, 9582, 9264,
501
+ 1903, 23312, 18953, 10037, -15268, -5450, 6442, -6243, -3777, 5110,
502
+ 10936, -16649, -6022, 16255, 31300, 24818, 31922, 32760, 7854, 27080,
503
+ 15766, 29596, 7139, 31945, -19810, 465, -38026, -3971, 9641, 465,
504
+ -19375, 5524, -30112, -11960, -12813, 13535, 30670, 5925, -43725,
505
+ -14089, 11503, -22782, 6371, 43881, 37465, -33529, -33590, -39798,
506
+ -37854, -18466, -7908, -35825, -26020, -36923, -11332, -5699, 25166,
507
+ -3147, 19885, 12962, -20659, -1642, 27723, -56331, -24580, -11010,
508
+ -20206, 20087, -23772, -16038, 38580, 20901, -50731, 32037, -4299,
509
+ 26508, 18038, -28357, 31846, -7405, -20172, -15894, 2096, 25110,
510
+ -45786, 45918, -55333, -31928, -49428, -29824, -58796, -24609, -15408,
511
+ 69, -35415, -18439, 10123, -20360, -65949, 33356, -20333, 26476,
512
+ -32073, 33621, 930, 28803, -42791, 44716, 38164, 12302, -1739, 11421,
513
+ 73385, -7613, 14297, 38155, -414, 77587, 24338, -21415, 29367, 42639,
514
+ 13901, -288, 51027, -11827, 91260, 43407, 88521, -15186, 70572, -12049,
515
+ 5090, -12208, -56374, 15520, -623, -7742, 50825, 11199, -14894, 40892,
516
+ 59591, -31356, -28696, -57842, -87751, -33744, -28436, -28945, -40287,
517
+ 37957, -35638, 33401, -61534, 14870, 40292, 70366, -10803, 102290,
518
+ -71719, -85251, 7902, -22409, 75009, 99927, 35298, -1175, -762, -34744,
519
+ -10587, -47574, -62629, -19581, -43659, -54369, -32250, -39545, 15225,
520
+ -24454, 11241, -67308, -30148, 39929, 37639, 14383, -73475, -77636,
521
+ -81048, -35992, 41601, -90143, 76937, -8112, 56588, 9124, -40094,
522
+ -32340, 13253, 10898, -51639, 36390, 12086, -1885, 100714, -28561,
523
+ -23784, -18735, 18916, 16286, 10742, -87360, -13697, 10689, -19477,
524
+ -29770, 5060, 20189, -8297, 112407, 47071, 47743, 45519, -4109, 17468,
525
+ -68831, 78325, -6481, -21641, -19459, 30919, 96115, 8607, 53341, 32105,
526
+ -16211, 23538, 57259, -76272, -40583, 62093, 38511, -34255, -40665,
527
+ -40604, -37606, -15274, 33156, -13885, 103636, 118678, -14101, -92682,
528
+ -100791, 2634, 63791, 98266, 19286, -34590, -21067, -71130, 25380,
529
+ -40839, -27614, -26060, 52358, -15537, 27138, -6749, 36269, -33306,
530
+ 13207, -91084, -5540, -57116, 69548, 44169, -57742, -41234, -103327,
531
+ -62904, -8566, 41149, -12866, 71188, 23980, 1838, 58230, 73950, 5594,
532
+ 43113, -8159, -15925, 6911, 85598, -75016, -16214, -62726, -39016,
533
+ 8618, -63882, -4299, 23182, 49959, 49342, -3238, -24913, -37138, 78361,
534
+ 32451, 6337, -11438, -36241, -37737, 8169, -3077, -24829, 57953, 53016,
535
+ -31511, -91168, 12599, -41849, 41576, 55275, -62539, 47814, -62319,
536
+ 12300, -32076, -55137, -84881, -27546, 4312, -3433, -54382, 113288,
537
+ -30157, 74469, 18219, 79880, -2124, 98911, 17655, -33499, -32861,
538
+ 47242, -37393, 99765, 14831, -44483, 10800, -31617, -52710, 37406,
539
+ 22105, 29704, -20050, 13778, 43683, 36628, 8494, 60964, -22644, 31550,
540
+ -17693, 33805, -124879, -12302, 19343, 20400, -30937, -21574, -34037,
541
+ -33380, 56539, -24993, -75513, -1527, 53563, 65407, -101, 53577, 37991,
542
+ 18717, -23795, -8090, -47987, -94717, 41967, 5170, -14815, -94311,
543
+ 17896, -17734, -57718, -774, -38410, 24830, 29682, 76480, 58802,
544
+ -46416, -20348, -61353, -68225, -68306, 23822, -31598, 42972, 36327,
545
+ 28968, -65638, -21638, 24354, -8356, 26777, 52982, -11783, -44051,
546
+ -26467, -44721, -28435, -53265, -25574, -2669, 44155, 22946, -18454,
547
+ -30718, -11252, 58420, 8711, 67447, 4425, 41749, 67543, 43162, 11793,
548
+ -41907, 20477, -13080, 6559, -6104, -13244, 42853, 42935, 29793, 36730,
549
+ -28087, 28657, 17946, 7503, 7204, 21491, -27450, -24241, -98156,
550
+ -18082, -42613, -24928, 10775, -14842, -44127, 55910, 14777, 31151, -2194,
551
+ 39206, -2100, -4211, 11827, -8918, -19471, 72567, 36447, -65590, -34861,
552
+ -17147, -45303, 9025, -7333, -35473, 11101, 11638, 3441, 6626, -41800,
553
+ 9416, 13679, 33508, 40502, -60542, 16358, 8392, -43242, -35864, -34127,
554
+ -48721, 35878, 30598, 28630, 20279, -19983, -14638, -24455, -1851, -11344,
555
+ 45150, 42051, 26034, -28889, -32382, -3527, -14532, 22564, -22346, 477,
556
+ 11706, 28338, -25972, -9185, -22867, -12522, 32120, -4424, 11339, -33913,
557
+ -7184, 5101, -23552, -17115, -31401, -6104, 21906, 25708, 8406, 6317,
558
+ -7525, 5014, 20750, 20179, 22724, 11692, 13297, 2493, -253, -16841, -17339,
559
+ -6753, -4808, 2976, -10881, -10228, -13816, -12686, 1385, 2316, 2190, -875,
560
+ -1924], ZZ)
561
+
562
+ assert dup_mul(p1, p2, ZZ) == res
563
+
564
+ p1 = dup_normal([83, -61, -86, -24, 12, 43, -88, -9, 42, 55, -66, 74, 95,
565
+ -25, -12, 68, -99, 4, 45, 6, -15, -19, 78, 65, -55, 47, -13, 17, 86,
566
+ 81, -58, -27, 50, -40, -24, 39, -41, -92, 75, 90, -1, 40, -15, -27,
567
+ -35, 68, 70, -64, -40, 78, -88, -58, -39, 69, 46, 12, 28, -94, -37,
568
+ -50, -80, -96, -61, 25, 1, 71, 4, 12, 48, 4, 34, -47, -75, 5, 48, 82,
569
+ 88, 23, 98, 35, 17, -10, 48, -61, -95, 47, 65, -19, -66, -57, -6, -51,
570
+ -42, -89, 66, -13, 18, 37, 90, -23, 72, 96, -53, 0, 40, -73, -52, -68,
571
+ 32, -25, -53, 79, -52, 18, 44, 73, -81, 31, -90, 70, 3, 36, 48, 76,
572
+ -24, -44, 23, 98, -4, 73, 69, 88, -70, 14, -68, 94, -78, -15, -64, -97,
573
+ -70, -35, 65, 88, 49, -53, -7, 12, -45, -7, 59, -94, 99, -2, 67, -60,
574
+ -71, 29, -62, -77, 1, 51, 17, 80, -20, -47, -19, 24, -9, 39, -23, 21,
575
+ -84, 10, 84, 56, -17, -21, -66, 85, 70, 46, -51, -22, -95, 78, -60,
576
+ -96, -97, -45, 72, 35, 30, -61, -92, -93, -60, -61, 4, -4, -81, -73,
577
+ 46, 53, -11, 26, 94, 45, 14, -78, 55, 84, -68, 98, 60, 23, 100, -63,
578
+ 68, 96, -16, 3, 56, 21, -58, 62, -67, 66, 85, 41, -79, -22, 97, -67,
579
+ 82, 82, -96, -20, -7, 48, -67, 48, -9, -39, 78], ZZ)
580
+ p2 = dup_normal([52, 88, 76, 66, 9, -64, 46, -20, -28, 69, 60, 96, -36,
581
+ -92, -30, -11, -35, 35, 55, 63, -92, -7, 25, -58, 74, 55, -6, 4, 47,
582
+ -92, -65, 67, -45, 74, -76, 59, -6, 69, 39, 24, -71, -7, 39, -45, 60,
583
+ -68, 98, 97, -79, 17, 4, 94, -64, 68, -100, -96, -2, 3, 22, 96, 54,
584
+ -77, -86, 67, 6, 57, 37, 40, 89, -78, 64, -94, -45, -92, 57, 87, -26,
585
+ 36, 19, 97, 25, 77, -87, 24, 43, -5, 35, 57, 83, 71, 35, 63, 61, 96,
586
+ -22, 8, -1, 96, 43, 45, 94, -93, 36, 71, -41, -99, 85, -48, 59, 52,
587
+ -17, 5, 87, -16, -68, -54, 76, -18, 100, 91, -42, -70, -66, -88, -12,
588
+ 1, 95, -82, 52, 43, -29, 3, 12, 72, -99, -43, -32, -93, -51, 16, -20,
589
+ -12, -11, 5, 33, -38, 93, -5, -74, 25, 74, -58, 93, 59, -63, -86, 63,
590
+ -20, -4, -74, -73, -95, 29, -28, 93, -91, -2, -38, -62, 77, -58, -85,
591
+ -28, 95, 38, 19, -69, 86, 94, 25, -2, -4, 47, 34, -59, 35, -48, 29,
592
+ -63, -53, 34, 29, 66, 73, 6, 92, -84, 89, 15, 81, 93, 97, 51, -72, -78,
593
+ 25, 60, 90, -45, 39, 67, -84, -62, 57, 26, -32, -56, -14, -83, 76, 5,
594
+ -2, 99, -100, 28, 46, 94, -7, 53, -25, 16, -23, -36, 89, -78, -63, 31,
595
+ 1, 84, -99, -52, 76, 48, 90, -76, 44, -19, 54, -36, -9, -73, -100, -69,
596
+ 31, 42, 25, -39, 76, -26, -8, -14, 51, 3, 37, 45, 2, -54, 13, -34, -92,
597
+ 17, -25, -65, 53, -63, 30, 4, -70, -67, 90, 52, 51, 18, -3, 31, -45,
598
+ -9, 59, 63, -87, 22, -32, 29, -38, 21, 36, -82, 27, -11], ZZ)
599
+ res = dup_normal([4316, 4132, -3532, -7974, -11303, -10069, 5484, -3330,
600
+ -5874, 7734, 4673, 11327, -9884, -8031, 17343, 21035, -10570, -9285,
601
+ 15893, 3780, -14083, 8819, 17592, 10159, 7174, -11587, 8598, -16479,
602
+ 3602, 25596, 9781, 12163, 150, 18749, -21782, -12307, 27578, -2757,
603
+ -12573, 12565, 6345, -18956, 19503, -15617, 1443, -16778, 36851, 23588,
604
+ -28474, 5749, 40695, -7521, -53669, -2497, -18530, 6770, 57038, 3926,
605
+ -6927, -15399, 1848, -64649, -27728, 3644, 49608, 15187, -8902, -9480,
606
+ -7398, -40425, 4824, 23767, -7594, -6905, 33089, 18786, 12192, 24670,
607
+ 31114, 35334, -4501, -14676, 7107, -59018, -21352, 20777, 19661, 20653,
608
+ 33754, -885, -43758, 6269, 51897, -28719, -97488, -9527, 13746, 11644,
609
+ 17644, -21720, 23782, -10481, 47867, 20752, 33810, -1875, 39918, -7710,
610
+ -40840, 19808, -47075, 23066, 46616, 25201, 9287, 35436, -1602, 9645,
611
+ -11978, 13273, 15544, 33465, 20063, 44539, 11687, 27314, -6538, -37467,
612
+ 14031, 32970, -27086, 41323, 29551, 65910, -39027, -37800, -22232,
613
+ 8212, 46316, -28981, -55282, 50417, -44929, -44062, 73879, 37573,
614
+ -2596, -10877, -21893, -133218, -33707, -25753, -9531, 17530, 61126,
615
+ 2748, -56235, 43874, -10872, -90459, -30387, 115267, -7264, -44452,
616
+ 122626, 14839, -599, 10337, 57166, -67467, -54957, 63669, 1202, 18488,
617
+ 52594, 7205, -97822, 612, 78069, -5403, -63562, 47236, 36873, -154827,
618
+ -26188, 82427, -39521, 5628, 7416, 5276, -53095, 47050, 26121, -42207,
619
+ 79021, -13035, 2499, -66943, 29040, -72355, -23480, 23416, -12885,
620
+ -44225, -42688, -4224, 19858, 55299, 15735, 11465, 101876, -39169,
621
+ 51786, 14723, 43280, -68697, 16410, 92295, 56767, 7183, 111850, 4550,
622
+ 115451, -38443, -19642, -35058, 10230, 93829, 8925, 63047, 3146, 29250,
623
+ 8530, 5255, -98117, -115517, -76817, -8724, 41044, 1312, -35974, 79333,
624
+ -28567, 7547, -10580, -24559, -16238, 10794, -3867, 24848, 57770,
625
+ -51536, -35040, 71033, 29853, 62029, -7125, -125585, -32169, -47907,
626
+ 156811, -65176, -58006, -15757, -57861, 11963, 30225, -41901, -41681,
627
+ 31310, 27982, 18613, 61760, 60746, -59096, 33499, 30097, -17997, 24032,
628
+ 56442, -83042, 23747, -20931, -21978, -158752, -9883, -73598, -7987,
629
+ -7333, -125403, -116329, 30585, 53281, 51018, -29193, 88575, 8264,
630
+ -40147, -16289, 113088, 12810, -6508, 101552, -13037, 34440, -41840,
631
+ 101643, 24263, 80532, 61748, 65574, 6423, -20672, 6591, -10834, -71716,
632
+ 86919, -92626, 39161, 28490, 81319, 46676, 106720, 43530, 26998, 57456,
633
+ -8862, 60989, 13982, 3119, -2224, 14743, 55415, -49093, -29303, 28999,
634
+ 1789, 55953, -84043, -7780, -65013, 57129, -47251, 61484, 61994,
635
+ -78361, -82778, 22487, -26894, 9756, -74637, -15519, -4360, 30115,
636
+ 42433, 35475, 15286, 69768, 21509, -20214, 78675, -21163, 13596, 11443,
637
+ -10698, -53621, -53867, -24155, 64500, -42784, -33077, -16500, 873,
638
+ -52788, 14546, -38011, 36974, -39849, -34029, -94311, 83068, -50437,
639
+ -26169, -46746, 59185, 42259, -101379, -12943, 30089, -59086, 36271,
640
+ 22723, -30253, -52472, -70826, -23289, 3331, -31687, 14183, -857,
641
+ -28627, 35246, -51284, 5636, -6933, 66539, 36654, 50927, 24783, 3457,
642
+ 33276, 45281, 45650, -4938, -9968, -22590, 47995, 69229, 5214, -58365,
643
+ -17907, -14651, 18668, 18009, 12649, -11851, -13387, 20339, 52472,
644
+ -1087, -21458, -68647, 52295, 15849, 40608, 15323, 25164, -29368,
645
+ 10352, -7055, 7159, 21695, -5373, -54849, 101103, -24963, -10511,
646
+ 33227, 7659, 41042, -69588, 26718, -20515, 6441, 38135, -63, 24088,
647
+ -35364, -12785, -18709, 47843, 48533, -48575, 17251, -19394, 32878,
648
+ -9010, -9050, 504, -12407, 28076, -3429, 25324, -4210, -26119, 752,
649
+ -29203, 28251, -11324, -32140, -3366, -25135, 18702, -31588, -7047,
650
+ -24267, 49987, -14975, -33169, 37744, -7720, -9035, 16964, -2807, -421,
651
+ 14114, -17097, -13662, 40628, -12139, -9427, 5369, 17551, -13232, -16211,
652
+ 9804, -7422, 2677, 28635, -8280, -4906, 2908, -22558, 5604, 12459, 8756,
653
+ -3980, -4745, -18525, 7913, 5970, -16457, 20230, -6247, -13812, 2505,
654
+ 11899, 1409, -15094, 22540, -18863, 137, 11123, -4516, 2290, -8594, 12150,
655
+ -10380, 3005, 5235, -7350, 2535, -858], ZZ)
656
+
657
+ assert dup_mul(p1, p2, ZZ) == res
658
+
659
+
660
+ def test_dmp_mul():
661
+ assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \
662
+ dup_mul([ZZ(5)], [ZZ(7)], ZZ)
663
+ assert dmp_mul([QQ(5, 7)], [QQ(3, 7)], 0, QQ) == \
664
+ dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ)
665
+
666
+ assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]]
667
+ assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]]
668
+ assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]]
669
+ assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]]
670
+ assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]]
671
+
672
+ assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]]
673
+ assert dmp_mul([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[]]]
674
+ assert dmp_mul([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[]]]
675
+ assert dmp_mul([[[QQ(2, 7)]]], [[[QQ(1, 3)]]], 2, QQ) == [[[QQ(2, 21)]]]
676
+ assert dmp_mul([[[QQ(1, 7)]]], [[[QQ(2, 3)]]], 2, QQ) == [[[QQ(2, 21)]]]
677
+
678
+ K = FF(6)
679
+
680
+ assert dmp_mul(
681
+ [[K(2)], [K(1)]], [[K(3)], [K(4)]], 1, K) == [[K(5)], [K(4)]]
682
+
683
+
684
+ def test_dup_sqr():
685
+ assert dup_sqr([], ZZ) == []
686
+ assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)]
687
+ assert dup_sqr([ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(4), ZZ(4)]
688
+
689
+ assert dup_sqr([], QQ) == []
690
+ assert dup_sqr([QQ(2, 3)], QQ) == [QQ(4, 9)]
691
+ assert dup_sqr([QQ(1, 3), QQ(2, 3)], QQ) == [QQ(1, 9), QQ(4, 9), QQ(4, 9)]
692
+
693
+ f = dup_normal([2, 0, 0, 1, 7], ZZ)
694
+
695
+ assert dup_sqr(f, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
696
+
697
+ K = FF(9)
698
+
699
+ assert dup_sqr([K(3), K(4)], K) == [K(6), K(7)]
700
+
701
+
702
+ def test_dmp_sqr():
703
+ assert dmp_sqr([ZZ(1), ZZ(2)], 0, ZZ) == \
704
+ dup_sqr([ZZ(1), ZZ(2)], ZZ)
705
+
706
+ assert dmp_sqr([[[]]], 2, ZZ) == [[[]]]
707
+ assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]]
708
+
709
+ assert dmp_sqr([[[]]], 2, QQ) == [[[]]]
710
+ assert dmp_sqr([[[QQ(2, 3)]]], 2, QQ) == [[[QQ(4, 9)]]]
711
+
712
+ K = FF(9)
713
+
714
+ assert dmp_sqr([[K(3)], [K(4)]], 1, K) == [[K(6)], [K(7)]]
715
+
716
+
717
+ def test_dup_pow():
718
+ assert dup_pow([], 0, ZZ) == [ZZ(1)]
719
+ assert dup_pow([], 0, QQ) == [QQ(1)]
720
+
721
+ assert dup_pow([], 1, ZZ) == []
722
+ assert dup_pow([], 7, ZZ) == []
723
+
724
+ assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)]
725
+ assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)]
726
+ assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)]
727
+
728
+ assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)]
729
+ assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)]
730
+ assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)]
731
+
732
+ assert dup_pow([QQ(1, 1)], 0, QQ) == [QQ(1, 1)]
733
+ assert dup_pow([QQ(1, 1)], 1, QQ) == [QQ(1, 1)]
734
+ assert dup_pow([QQ(1, 1)], 7, QQ) == [QQ(1, 1)]
735
+
736
+ assert dup_pow([QQ(3, 7)], 0, QQ) == [QQ(1, 1)]
737
+ assert dup_pow([QQ(3, 7)], 1, QQ) == [QQ(3, 7)]
738
+ assert dup_pow([QQ(3, 7)], 7, QQ) == [QQ(2187, 823543)]
739
+
740
+ f = dup_normal([2, 0, 0, 1, 7], ZZ)
741
+
742
+ assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ)
743
+ assert dup_pow(f, 1, ZZ) == dup_normal([2, 0, 0, 1, 7], ZZ)
744
+ assert dup_pow(f, 2, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
745
+ assert dup_pow(f, 3, ZZ) == dup_normal(
746
+ [8, 0, 0, 12, 84, 0, 6, 84, 294, 1, 21, 147, 343], ZZ)
747
+
748
+
749
+ def test_dmp_pow():
750
+ assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]]
751
+ assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]]
752
+
753
+ assert dmp_pow([[]], 1, 1, ZZ) == [[]]
754
+ assert dmp_pow([[]], 7, 1, ZZ) == [[]]
755
+
756
+ assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]]
757
+ assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]]
758
+ assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]]
759
+
760
+ assert dmp_pow([[QQ(3, 7)]], 0, 1, QQ) == [[QQ(1, 1)]]
761
+ assert dmp_pow([[QQ(3, 7)]], 1, 1, QQ) == [[QQ(3, 7)]]
762
+ assert dmp_pow([[QQ(3, 7)]], 7, 1, QQ) == [[QQ(2187, 823543)]]
763
+
764
+ f = dup_normal([2, 0, 0, 1, 7], ZZ)
765
+
766
+ assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ)
767
+
768
+
769
+ def test_dup_pdiv():
770
+ f = dup_normal([3, 1, 1, 5], ZZ)
771
+ g = dup_normal([5, -3, 1], ZZ)
772
+
773
+ q = dup_normal([15, 14], ZZ)
774
+ r = dup_normal([52, 111], ZZ)
775
+
776
+ assert dup_pdiv(f, g, ZZ) == (q, r)
777
+ assert dup_pquo(f, g, ZZ) == q
778
+ assert dup_prem(f, g, ZZ) == r
779
+
780
+ raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, ZZ))
781
+
782
+ f = dup_normal([3, 1, 1, 5], QQ)
783
+ g = dup_normal([5, -3, 1], QQ)
784
+
785
+ q = dup_normal([15, 14], QQ)
786
+ r = dup_normal([52, 111], QQ)
787
+
788
+ assert dup_pdiv(f, g, QQ) == (q, r)
789
+ assert dup_pquo(f, g, QQ) == q
790
+ assert dup_prem(f, g, QQ) == r
791
+
792
+ raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, QQ))
793
+
794
+
795
+ def test_dmp_pdiv():
796
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
797
+ g = dmp_normal([[1], [-1, 0]], 1, ZZ)
798
+
799
+ q = dmp_normal([[1], [1, 0]], 1, ZZ)
800
+ r = dmp_normal([[2, 0, 0]], 1, ZZ)
801
+
802
+ assert dmp_pdiv(f, g, 1, ZZ) == (q, r)
803
+ assert dmp_pquo(f, g, 1, ZZ) == q
804
+ assert dmp_prem(f, g, 1, ZZ) == r
805
+
806
+ raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ))
807
+
808
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
809
+ g = dmp_normal([[2], [-2, 0]], 1, ZZ)
810
+
811
+ q = dmp_normal([[2], [2, 0]], 1, ZZ)
812
+ r = dmp_normal([[8, 0, 0]], 1, ZZ)
813
+
814
+ assert dmp_pdiv(f, g, 1, ZZ) == (q, r)
815
+ assert dmp_pquo(f, g, 1, ZZ) == q
816
+ assert dmp_prem(f, g, 1, ZZ) == r
817
+
818
+ raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ))
819
+
820
+
821
+ def test_dup_rr_div():
822
+ raises(ZeroDivisionError, lambda: dup_rr_div([1, 2, 3], [], ZZ))
823
+
824
+ f = dup_normal([3, 1, 1, 5], ZZ)
825
+ g = dup_normal([5, -3, 1], ZZ)
826
+
827
+ q, r = [], f
828
+
829
+ assert dup_rr_div(f, g, ZZ) == (q, r)
830
+
831
+
832
+ def test_dmp_rr_div():
833
+ raises(ZeroDivisionError, lambda: dmp_rr_div([[1, 2], [3]], [[]], 1, ZZ))
834
+
835
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
836
+ g = dmp_normal([[1], [-1, 0]], 1, ZZ)
837
+
838
+ q = dmp_normal([[1], [1, 0]], 1, ZZ)
839
+ r = dmp_normal([[2, 0, 0]], 1, ZZ)
840
+
841
+ assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
842
+
843
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
844
+ g = dmp_normal([[-1], [1, 0]], 1, ZZ)
845
+
846
+ q = dmp_normal([[-1], [-1, 0]], 1, ZZ)
847
+ r = dmp_normal([[2, 0, 0]], 1, ZZ)
848
+
849
+ assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
850
+
851
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
852
+ g = dmp_normal([[2], [-2, 0]], 1, ZZ)
853
+
854
+ q, r = [[]], f
855
+
856
+ assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
857
+
858
+
859
+ def test_dup_ff_div():
860
+ raises(ZeroDivisionError, lambda: dup_ff_div([1, 2, 3], [], QQ))
861
+
862
+ f = dup_normal([3, 1, 1, 5], QQ)
863
+ g = dup_normal([5, -3, 1], QQ)
864
+
865
+ q = [QQ(3, 5), QQ(14, 25)]
866
+ r = [QQ(52, 25), QQ(111, 25)]
867
+
868
+ assert dup_ff_div(f, g, QQ) == (q, r)
869
+
870
+ def test_dup_ff_div_gmpy2():
871
+ if GROUND_TYPES != 'gmpy2':
872
+ return
873
+
874
+ from gmpy2 import mpq
875
+ from sympy.polys.domains import GMPYRationalField
876
+ K = GMPYRationalField()
877
+
878
+ f = [mpq(1,3), mpq(3,2)]
879
+ g = [mpq(2,1)]
880
+ assert dmp_ff_div(f, g, 0, K) == ([mpq(1,6), mpq(3,4)], [])
881
+
882
+ f = [mpq(1,2), mpq(1,3), mpq(1,4), mpq(1,5)]
883
+ g = [mpq(-1,1), mpq(1,1), mpq(-1,1)]
884
+ assert dmp_ff_div(f, g, 0, K) == ([mpq(-1,2), mpq(-5,6)], [mpq(7,12), mpq(-19,30)])
885
+
886
+ def test_dmp_ff_div():
887
+ raises(ZeroDivisionError, lambda: dmp_ff_div([[1, 2], [3]], [[]], 1, QQ))
888
+
889
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
890
+ g = dmp_normal([[1], [-1, 0]], 1, QQ)
891
+
892
+ q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]]
893
+ r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
894
+
895
+ assert dmp_ff_div(f, g, 1, QQ) == (q, r)
896
+
897
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
898
+ g = dmp_normal([[-1], [1, 0]], 1, QQ)
899
+
900
+ q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]]
901
+ r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
902
+
903
+ assert dmp_ff_div(f, g, 1, QQ) == (q, r)
904
+
905
+ f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
906
+ g = dmp_normal([[2], [-2, 0]], 1, QQ)
907
+
908
+ q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]]
909
+ r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
910
+
911
+ assert dmp_ff_div(f, g, 1, QQ) == (q, r)
912
+
913
+
914
+ def test_dup_div():
915
+ f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1]
916
+
917
+ assert dup_div(f, g, ZZ) == (q, r)
918
+ assert dup_quo(f, g, ZZ) == q
919
+ assert dup_rem(f, g, ZZ) == r
920
+
921
+ raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ))
922
+
923
+ f, g, q, r = [5, 4, 3, 2, 1, 0], [1, 2, 0, 0, 9], [5, -6], [15, 2, -44, 54]
924
+
925
+ assert dup_div(f, g, ZZ) == (q, r)
926
+ assert dup_quo(f, g, ZZ) == q
927
+ assert dup_rem(f, g, ZZ) == r
928
+
929
+ raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ))
930
+
931
+
932
+ def test_dmp_div():
933
+ f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1]
934
+
935
+ assert dmp_div(f, g, 0, ZZ) == (q, r)
936
+ assert dmp_quo(f, g, 0, ZZ) == q
937
+ assert dmp_rem(f, g, 0, ZZ) == r
938
+
939
+ raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 0, ZZ))
940
+
941
+ f, g, q, r = [[[1]]], [[[2]], [1]], [[[]]], [[[1]]]
942
+
943
+ assert dmp_div(f, g, 2, ZZ) == (q, r)
944
+ assert dmp_quo(f, g, 2, ZZ) == q
945
+ assert dmp_rem(f, g, 2, ZZ) == r
946
+
947
+ raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 2, ZZ))
948
+
949
+
950
+ def test_dup_max_norm():
951
+ assert dup_max_norm([], ZZ) == 0
952
+ assert dup_max_norm([1], ZZ) == 1
953
+
954
+ assert dup_max_norm([1, 4, 2, 3], ZZ) == 4
955
+
956
+
957
+ def test_dmp_max_norm():
958
+ assert dmp_max_norm([[[]]], 2, ZZ) == 0
959
+ assert dmp_max_norm([[[1]]], 2, ZZ) == 1
960
+
961
+ assert dmp_max_norm(f_0, 2, ZZ) == 6
962
+
963
+
964
+ def test_dup_l1_norm():
965
+ assert dup_l1_norm([], ZZ) == 0
966
+ assert dup_l1_norm([1], ZZ) == 1
967
+ assert dup_l1_norm([1, 4, 2, 3], ZZ) == 10
968
+
969
+
970
+ def test_dmp_l1_norm():
971
+ assert dmp_l1_norm([[[]]], 2, ZZ) == 0
972
+ assert dmp_l1_norm([[[1]]], 2, ZZ) == 1
973
+
974
+ assert dmp_l1_norm(f_0, 2, ZZ) == 31
975
+
976
+
977
+ def test_dup_l2_norm_squared():
978
+ assert dup_l2_norm_squared([], ZZ) == 0
979
+ assert dup_l2_norm_squared([1], ZZ) == 1
980
+ assert dup_l2_norm_squared([1, 4, 2, 3], ZZ) == 30
981
+
982
+
983
+ def test_dmp_l2_norm_squared():
984
+ assert dmp_l2_norm_squared([[[]]], 2, ZZ) == 0
985
+ assert dmp_l2_norm_squared([[[1]]], 2, ZZ) == 1
986
+ assert dmp_l2_norm_squared(f_0, 2, ZZ) == 111
987
+
988
+
989
+ def test_dup_expand():
990
+ assert dup_expand((), ZZ) == [1]
991
+ assert dup_expand(([1, 2, 3], [1, 2], [7, 5, 4, 3]), ZZ) == \
992
+ dup_mul([1, 2, 3], dup_mul([1, 2], [7, 5, 4, 3], ZZ), ZZ)
993
+
994
+
995
+ def test_dmp_expand():
996
+ assert dmp_expand((), 1, ZZ) == [[1]]
997
+ assert dmp_expand(([[1], [2], [3]], [[1], [2]], [[7], [5], [4], [3]]), 1, ZZ) == \
998
+ dmp_mul([[1], [2], [3]], dmp_mul([[1], [2]], [[7], [5], [
999
+ 4], [3]], 1, ZZ), 1, ZZ)
1000
+
1001
+ def test_dup_mul_poly():
1002
+ p = Poly(18786186952704.0*x**165 + 9.31746684052255e+31*x**82, x, domain='RR')
1003
+ px = Poly(18786186952704.0*x**166 + 9.31746684052255e+31*x**83, x, domain='RR')
1004
+
1005
+ assert p * x == px
1006
+ assert p.set_domain(QQ) * x == px.set_domain(QQ)
1007
+ assert p.set_domain(CC) * x == px.set_domain(CC)
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for dense recursive polynomials' basic tools. """
2
+
3
+ from sympy.polys.densebasic import (
4
+ ninf,
5
+ dup_LC, dmp_LC,
6
+ dup_TC, dmp_TC,
7
+ dmp_ground_LC, dmp_ground_TC,
8
+ dmp_true_LT,
9
+ dup_degree, dmp_degree,
10
+ dmp_degree_in, dmp_degree_list,
11
+ dup_strip, dmp_strip,
12
+ dmp_validate,
13
+ dup_reverse,
14
+ dup_copy, dmp_copy,
15
+ dup_normal, dmp_normal,
16
+ dup_convert, dmp_convert,
17
+ dup_from_sympy, dmp_from_sympy,
18
+ dup_nth, dmp_nth, dmp_ground_nth,
19
+ dmp_zero_p, dmp_zero,
20
+ dmp_one_p, dmp_one,
21
+ dmp_ground_p, dmp_ground,
22
+ dmp_negative_p, dmp_positive_p,
23
+ dmp_zeros, dmp_grounds,
24
+ dup_from_dict, dup_from_raw_dict,
25
+ dup_to_dict, dup_to_raw_dict,
26
+ dmp_from_dict, dmp_to_dict,
27
+ dmp_swap, dmp_permute,
28
+ dmp_nest, dmp_raise,
29
+ dup_deflate, dmp_deflate,
30
+ dup_multi_deflate, dmp_multi_deflate,
31
+ dup_inflate, dmp_inflate,
32
+ dmp_exclude, dmp_include,
33
+ dmp_inject, dmp_eject,
34
+ dup_terms_gcd, dmp_terms_gcd,
35
+ dmp_list_terms, dmp_apply_pairs,
36
+ dup_slice,
37
+ dup_random,
38
+ )
39
+
40
+ from sympy.polys.specialpolys import f_polys
41
+ from sympy.polys.domains import ZZ, QQ
42
+ from sympy.polys.rings import ring
43
+
44
+ from sympy.core.singleton import S
45
+ from sympy.testing.pytest import raises
46
+
47
+ from sympy.core.numbers import oo
48
+
49
+ f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
50
+
51
+ def test_dup_LC():
52
+ assert dup_LC([], ZZ) == 0
53
+ assert dup_LC([2, 3, 4, 5], ZZ) == 2
54
+
55
+
56
+ def test_dup_TC():
57
+ assert dup_TC([], ZZ) == 0
58
+ assert dup_TC([2, 3, 4, 5], ZZ) == 5
59
+
60
+
61
+ def test_dmp_LC():
62
+ assert dmp_LC([[]], ZZ) == []
63
+ assert dmp_LC([[2, 3, 4], [5]], ZZ) == [2, 3, 4]
64
+ assert dmp_LC([[[]]], ZZ) == [[]]
65
+ assert dmp_LC([[[2], [3, 4]], [[5]]], ZZ) == [[2], [3, 4]]
66
+
67
+
68
+ def test_dmp_TC():
69
+ assert dmp_TC([[]], ZZ) == []
70
+ assert dmp_TC([[2, 3, 4], [5]], ZZ) == [5]
71
+ assert dmp_TC([[[]]], ZZ) == [[]]
72
+ assert dmp_TC([[[2], [3, 4]], [[5]]], ZZ) == [[5]]
73
+
74
+
75
+ def test_dmp_ground_LC():
76
+ assert dmp_ground_LC([[]], 1, ZZ) == 0
77
+ assert dmp_ground_LC([[2, 3, 4], [5]], 1, ZZ) == 2
78
+ assert dmp_ground_LC([[[]]], 2, ZZ) == 0
79
+ assert dmp_ground_LC([[[2], [3, 4]], [[5]]], 2, ZZ) == 2
80
+
81
+
82
+ def test_dmp_ground_TC():
83
+ assert dmp_ground_TC([[]], 1, ZZ) == 0
84
+ assert dmp_ground_TC([[2, 3, 4], [5]], 1, ZZ) == 5
85
+ assert dmp_ground_TC([[[]]], 2, ZZ) == 0
86
+ assert dmp_ground_TC([[[2], [3, 4]], [[5]]], 2, ZZ) == 5
87
+
88
+
89
+ def test_dmp_true_LT():
90
+ assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0)
91
+ assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7)
92
+
93
+ assert dmp_true_LT([[1, 0]], 1, ZZ) == ((0, 1), 1)
94
+ assert dmp_true_LT([[1], []], 1, ZZ) == ((1, 0), 1)
95
+ assert dmp_true_LT([[1, 0], []], 1, ZZ) == ((1, 1), 1)
96
+
97
+
98
+ def test_dup_degree():
99
+ assert ninf == float('-inf')
100
+ assert dup_degree([]) is ninf
101
+ assert dup_degree([1]) == 0
102
+ assert dup_degree([1, 0]) == 1
103
+ assert dup_degree([1, 0, 0, 0, 1]) == 4
104
+
105
+
106
+ def test_dmp_degree():
107
+ assert dmp_degree([[]], 1) is ninf
108
+ assert dmp_degree([[[]]], 2) is ninf
109
+
110
+ assert dmp_degree([[1]], 1) == 0
111
+ assert dmp_degree([[2], [1]], 1) == 1
112
+
113
+
114
+ def test_dmp_degree_in():
115
+ assert dmp_degree_in([[[]]], 0, 2) is ninf
116
+ assert dmp_degree_in([[[]]], 1, 2) is ninf
117
+ assert dmp_degree_in([[[]]], 2, 2) is ninf
118
+
119
+ assert dmp_degree_in([[[1]]], 0, 2) == 0
120
+ assert dmp_degree_in([[[1]]], 1, 2) == 0
121
+ assert dmp_degree_in([[[1]]], 2, 2) == 0
122
+
123
+ assert dmp_degree_in(f_4, 0, 2) == 9
124
+ assert dmp_degree_in(f_4, 1, 2) == 12
125
+ assert dmp_degree_in(f_4, 2, 2) == 8
126
+
127
+ assert dmp_degree_in(f_6, 0, 2) == 4
128
+ assert dmp_degree_in(f_6, 1, 2) == 4
129
+ assert dmp_degree_in(f_6, 2, 2) == 6
130
+ assert dmp_degree_in(f_6, 3, 3) == 3
131
+
132
+ raises(IndexError, lambda: dmp_degree_in([[1]], -5, 1))
133
+
134
+
135
+ def test_dmp_degree_list():
136
+ assert dmp_degree_list([[[[ ]]]], 3) == (-oo, -oo, -oo, -oo)
137
+ assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0)
138
+
139
+ assert dmp_degree_list(f_0, 2) == (2, 2, 2)
140
+ assert dmp_degree_list(f_1, 2) == (3, 3, 3)
141
+ assert dmp_degree_list(f_2, 2) == (5, 3, 3)
142
+ assert dmp_degree_list(f_3, 2) == (5, 4, 7)
143
+ assert dmp_degree_list(f_4, 2) == (9, 12, 8)
144
+ assert dmp_degree_list(f_5, 2) == (3, 3, 3)
145
+ assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3)
146
+
147
+
148
+ def test_dup_strip():
149
+ assert dup_strip([]) == []
150
+ assert dup_strip([0]) == []
151
+ assert dup_strip([0, 0, 0]) == []
152
+
153
+ assert dup_strip([1]) == [1]
154
+ assert dup_strip([0, 1]) == [1]
155
+ assert dup_strip([0, 0, 0, 1]) == [1]
156
+
157
+ assert dup_strip([1, 2, 0]) == [1, 2, 0]
158
+ assert dup_strip([0, 1, 2, 0]) == [1, 2, 0]
159
+ assert dup_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0]
160
+
161
+
162
+ def test_dmp_strip():
163
+ assert dmp_strip([0, 1, 0], 0) == [1, 0]
164
+
165
+ assert dmp_strip([[]], 1) == [[]]
166
+ assert dmp_strip([[], []], 1) == [[]]
167
+ assert dmp_strip([[], [], []], 1) == [[]]
168
+
169
+ assert dmp_strip([[[]]], 2) == [[[]]]
170
+ assert dmp_strip([[[]], [[]]], 2) == [[[]]]
171
+ assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]]
172
+
173
+ assert dmp_strip([[[1]]], 2) == [[[1]]]
174
+ assert dmp_strip([[[]], [[1]]], 2) == [[[1]]]
175
+ assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]]
176
+
177
+
178
+ def test_dmp_validate():
179
+ assert dmp_validate([]) == ([], 0)
180
+ assert dmp_validate([0, 0, 0, 1, 0]) == ([1, 0], 0)
181
+
182
+ assert dmp_validate([[[]]]) == ([[[]]], 2)
183
+ assert dmp_validate([[0], [], [0], [1], [0]]) == ([[1], []], 1)
184
+
185
+ raises(ValueError, lambda: dmp_validate([[0], 0, [0], [1], [0]]))
186
+
187
+
188
+ def test_dup_reverse():
189
+ assert dup_reverse([1, 2, 0, 3]) == [3, 0, 2, 1]
190
+ assert dup_reverse([1, 2, 3, 0]) == [3, 2, 1]
191
+
192
+
193
+ def test_dup_copy():
194
+ f = [ZZ(1), ZZ(0), ZZ(2)]
195
+ g = dup_copy(f)
196
+
197
+ g[0], g[2] = ZZ(7), ZZ(0)
198
+
199
+ assert f != g
200
+
201
+
202
+ def test_dmp_copy():
203
+ f = [[ZZ(1)], [ZZ(2), ZZ(0)]]
204
+ g = dmp_copy(f, 1)
205
+
206
+ g[0][0], g[1][1] = ZZ(7), ZZ(1)
207
+
208
+ assert f != g
209
+
210
+
211
+ def test_dup_normal():
212
+ assert dup_normal([0, 0, 2, 1, 0, 11, 0], ZZ) == \
213
+ [ZZ(2), ZZ(1), ZZ(0), ZZ(11), ZZ(0)]
214
+
215
+
216
+ def test_dmp_normal():
217
+ assert dmp_normal([[0], [], [0, 2, 1], [0], [11], []], 1, ZZ) == \
218
+ [[ZZ(2), ZZ(1)], [], [ZZ(11)], []]
219
+
220
+
221
+ def test_dup_convert():
222
+ K0, K1 = ZZ['x'], ZZ
223
+
224
+ f = [K0(1), K0(2), K0(0), K0(3)]
225
+
226
+ assert dup_convert(f, K0, K1) == \
227
+ [ZZ(1), ZZ(2), ZZ(0), ZZ(3)]
228
+
229
+
230
+ def test_dmp_convert():
231
+ K0, K1 = ZZ['x'], ZZ
232
+
233
+ f = [[K0(1)], [K0(2)], [], [K0(3)]]
234
+
235
+ assert dmp_convert(f, 1, K0, K1) == \
236
+ [[ZZ(1)], [ZZ(2)], [], [ZZ(3)]]
237
+
238
+
239
+ def test_dup_from_sympy():
240
+ assert dup_from_sympy([S.One, S(2)], ZZ) == \
241
+ [ZZ(1), ZZ(2)]
242
+ assert dup_from_sympy([S.Half, S(3)], QQ) == \
243
+ [QQ(1, 2), QQ(3, 1)]
244
+
245
+
246
+ def test_dmp_from_sympy():
247
+ assert dmp_from_sympy([[S.One, S(2)], [S.Zero]], 1, ZZ) == \
248
+ [[ZZ(1), ZZ(2)], []]
249
+ assert dmp_from_sympy([[S.Half, S(2)]], 1, QQ) == \
250
+ [[QQ(1, 2), QQ(2, 1)]]
251
+
252
+
253
+ def test_dup_nth():
254
+ assert dup_nth([1, 2, 3], 0, ZZ) == 3
255
+ assert dup_nth([1, 2, 3], 1, ZZ) == 2
256
+ assert dup_nth([1, 2, 3], 2, ZZ) == 1
257
+
258
+ assert dup_nth([1, 2, 3], 9, ZZ) == 0
259
+
260
+ raises(IndexError, lambda: dup_nth([3, 4, 5], -1, ZZ))
261
+
262
+
263
+ def test_dmp_nth():
264
+ assert dmp_nth([[1], [2], [3]], 0, 1, ZZ) == [3]
265
+ assert dmp_nth([[1], [2], [3]], 1, 1, ZZ) == [2]
266
+ assert dmp_nth([[1], [2], [3]], 2, 1, ZZ) == [1]
267
+
268
+ assert dmp_nth([[1], [2], [3]], 9, 1, ZZ) == []
269
+
270
+ raises(IndexError, lambda: dmp_nth([[3], [4], [5]], -1, 1, ZZ))
271
+
272
+
273
+ def test_dmp_ground_nth():
274
+ assert dmp_ground_nth([[]], (0, 0), 1, ZZ) == 0
275
+ assert dmp_ground_nth([[1], [2], [3]], (0, 0), 1, ZZ) == 3
276
+ assert dmp_ground_nth([[1], [2], [3]], (1, 0), 1, ZZ) == 2
277
+ assert dmp_ground_nth([[1], [2], [3]], (2, 0), 1, ZZ) == 1
278
+
279
+ assert dmp_ground_nth([[1], [2], [3]], (2, 1), 1, ZZ) == 0
280
+ assert dmp_ground_nth([[1], [2], [3]], (3, 0), 1, ZZ) == 0
281
+
282
+ raises(IndexError, lambda: dmp_ground_nth([[3], [4], [5]], (2, -1), 1, ZZ))
283
+
284
+
285
+ def test_dmp_zero_p():
286
+ assert dmp_zero_p([], 0) is True
287
+ assert dmp_zero_p([[]], 1) is True
288
+
289
+ assert dmp_zero_p([[[]]], 2) is True
290
+ assert dmp_zero_p([[[1]]], 2) is False
291
+
292
+
293
+ def test_dmp_zero():
294
+ assert dmp_zero(0) == []
295
+ assert dmp_zero(2) == [[[]]]
296
+
297
+
298
+ def test_dmp_one_p():
299
+ assert dmp_one_p([1], 0, ZZ) is True
300
+ assert dmp_one_p([[1]], 1, ZZ) is True
301
+ assert dmp_one_p([[[1]]], 2, ZZ) is True
302
+ assert dmp_one_p([[[12]]], 2, ZZ) is False
303
+
304
+
305
+ def test_dmp_one():
306
+ assert dmp_one(0, ZZ) == [ZZ(1)]
307
+ assert dmp_one(2, ZZ) == [[[ZZ(1)]]]
308
+
309
+
310
+ def test_dmp_ground_p():
311
+ assert dmp_ground_p([], 0, 0) is True
312
+ assert dmp_ground_p([[]], 0, 1) is True
313
+ assert dmp_ground_p([[]], 1, 1) is False
314
+
315
+ assert dmp_ground_p([[ZZ(1)]], 1, 1) is True
316
+ assert dmp_ground_p([[[ZZ(2)]]], 2, 2) is True
317
+
318
+ assert dmp_ground_p([[[ZZ(2)]]], 3, 2) is False
319
+ assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) is False
320
+
321
+ assert dmp_ground_p([], None, 0) is True
322
+ assert dmp_ground_p([[]], None, 1) is True
323
+
324
+ assert dmp_ground_p([ZZ(1)], None, 0) is True
325
+ assert dmp_ground_p([[[ZZ(1)]]], None, 2) is True
326
+
327
+ assert dmp_ground_p([[[ZZ(3)], []]], None, 2) is False
328
+
329
+
330
+ def test_dmp_ground():
331
+ assert dmp_ground(ZZ(0), 2) == [[[]]]
332
+
333
+ assert dmp_ground(ZZ(7), -1) == ZZ(7)
334
+ assert dmp_ground(ZZ(7), 0) == [ZZ(7)]
335
+ assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]]
336
+
337
+
338
+ def test_dmp_zeros():
339
+ assert dmp_zeros(4, 0, ZZ) == [[], [], [], []]
340
+
341
+ assert dmp_zeros(0, 2, ZZ) == []
342
+ assert dmp_zeros(1, 2, ZZ) == [[[[]]]]
343
+ assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]]
344
+ assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]]
345
+
346
+ assert dmp_zeros(3, -1, ZZ) == [0, 0, 0]
347
+
348
+
349
+ def test_dmp_grounds():
350
+ assert dmp_grounds(ZZ(7), 0, 2) == []
351
+
352
+ assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]]
353
+ assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]]
354
+ assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]]
355
+
356
+ assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7]
357
+
358
+
359
+ def test_dmp_negative_p():
360
+ assert dmp_negative_p([[[]]], 2, ZZ) is False
361
+ assert dmp_negative_p([[[1], [2]]], 2, ZZ) is False
362
+ assert dmp_negative_p([[[-1], [2]]], 2, ZZ) is True
363
+
364
+
365
+ def test_dmp_positive_p():
366
+ assert dmp_positive_p([[[]]], 2, ZZ) is False
367
+ assert dmp_positive_p([[[1], [2]]], 2, ZZ) is True
368
+ assert dmp_positive_p([[[-1], [2]]], 2, ZZ) is False
369
+
370
+
371
+ def test_dup_from_to_dict():
372
+ assert dup_from_raw_dict({}, ZZ) == []
373
+ assert dup_from_dict({}, ZZ) == []
374
+
375
+ assert dup_to_raw_dict([]) == {}
376
+ assert dup_to_dict([]) == {}
377
+
378
+ assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)}
379
+ assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)}
380
+
381
+ f = [3, 0, 0, 2, 0, 0, 0, 0, 8]
382
+ g = {8: 3, 5: 2, 0: 8}
383
+ h = {(8,): 3, (5,): 2, (0,): 8}
384
+
385
+ assert dup_from_raw_dict(g, ZZ) == f
386
+ assert dup_from_dict(h, ZZ) == f
387
+
388
+ assert dup_to_raw_dict(f) == g
389
+ assert dup_to_dict(f) == h
390
+
391
+ R, x,y = ring("x,y", ZZ)
392
+ K = R.to_domain()
393
+
394
+ f = [R(3), R(0), R(2), R(0), R(0), R(8)]
395
+ g = {5: R(3), 3: R(2), 0: R(8)}
396
+ h = {(5,): R(3), (3,): R(2), (0,): R(8)}
397
+
398
+ assert dup_from_raw_dict(g, K) == f
399
+ assert dup_from_dict(h, K) == f
400
+
401
+ assert dup_to_raw_dict(f) == g
402
+ assert dup_to_dict(f) == h
403
+
404
+
405
+ def test_dmp_from_to_dict():
406
+ assert dmp_from_dict({}, 1, ZZ) == [[]]
407
+ assert dmp_to_dict([[]], 1) == {}
408
+
409
+ assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)}
410
+ assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0, 0): ZZ(0)}
411
+
412
+ f = [[3], [], [], [2], [], [], [], [], [8]]
413
+ g = {(8, 0): 3, (5, 0): 2, (0, 0): 8}
414
+
415
+ assert dmp_from_dict(g, 1, ZZ) == f
416
+ assert dmp_to_dict(f, 1) == g
417
+
418
+
419
+ def test_dmp_swap():
420
+ f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ)
421
+ g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ)
422
+
423
+ assert dmp_swap(f, 1, 1, 1, ZZ) == f
424
+
425
+ assert dmp_swap(f, 0, 1, 1, ZZ) == g
426
+ assert dmp_swap(g, 0, 1, 1, ZZ) == f
427
+
428
+ raises(IndexError, lambda: dmp_swap(f, -1, -7, 1, ZZ))
429
+
430
+
431
+ def test_dmp_permute():
432
+ f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ)
433
+ g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ)
434
+
435
+ assert dmp_permute(f, [0, 1], 1, ZZ) == f
436
+ assert dmp_permute(g, [0, 1], 1, ZZ) == g
437
+
438
+ assert dmp_permute(f, [1, 0], 1, ZZ) == g
439
+ assert dmp_permute(g, [1, 0], 1, ZZ) == f
440
+
441
+
442
+ def test_dmp_nest():
443
+ assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]]
444
+
445
+ assert dmp_nest([[1]], 0, ZZ) == [[1]]
446
+ assert dmp_nest([[1]], 1, ZZ) == [[[1]]]
447
+ assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]]
448
+
449
+
450
+ def test_dmp_raise():
451
+ assert dmp_raise([], 2, 0, ZZ) == [[[]]]
452
+ assert dmp_raise([[1]], 0, 1, ZZ) == [[1]]
453
+
454
+ assert dmp_raise([[1, 2, 3], [], [2, 3]], 2, 1, ZZ) == \
455
+ [[[[1]], [[2]], [[3]]], [[[]]], [[[2]], [[3]]]]
456
+
457
+
458
+ def test_dup_deflate():
459
+ assert dup_deflate([], ZZ) == (1, [])
460
+ assert dup_deflate([2], ZZ) == (1, [2])
461
+ assert dup_deflate([1, 2, 3], ZZ) == (1, [1, 2, 3])
462
+ assert dup_deflate([1, 0, 2, 0, 3], ZZ) == (2, [1, 2, 3])
463
+
464
+ assert dup_deflate(dup_from_raw_dict({7: 1, 1: 1}, ZZ), ZZ) == \
465
+ (1, [1, 0, 0, 0, 0, 0, 1, 0])
466
+ assert dup_deflate(dup_from_raw_dict({7: 1, 0: 1}, ZZ), ZZ) == \
467
+ (7, [1, 1])
468
+ assert dup_deflate(dup_from_raw_dict({7: 1, 3: 1}, ZZ), ZZ) == \
469
+ (1, [1, 0, 0, 0, 1, 0, 0, 0])
470
+
471
+ assert dup_deflate(dup_from_raw_dict({7: 1, 4: 1}, ZZ), ZZ) == \
472
+ (1, [1, 0, 0, 1, 0, 0, 0, 0])
473
+ assert dup_deflate(dup_from_raw_dict({8: 1, 4: 1}, ZZ), ZZ) == \
474
+ (4, [1, 1, 0])
475
+
476
+ assert dup_deflate(dup_from_raw_dict({8: 1}, ZZ), ZZ) == \
477
+ (8, [1, 0])
478
+ assert dup_deflate(dup_from_raw_dict({7: 1}, ZZ), ZZ) == \
479
+ (7, [1, 0])
480
+ assert dup_deflate(dup_from_raw_dict({1: 1}, ZZ), ZZ) == \
481
+ (1, [1, 0])
482
+
483
+
484
+ def test_dmp_deflate():
485
+ assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]])
486
+ assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]])
487
+
488
+ f = [[1, 0, 0], [], [1, 0], [], [1]]
489
+
490
+ assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]])
491
+
492
+
493
+ def test_dup_multi_deflate():
494
+ assert dup_multi_deflate(([2],), ZZ) == (1, ([2],))
495
+ assert dup_multi_deflate(([], []), ZZ) == (1, ([], []))
496
+
497
+ assert dup_multi_deflate(([1, 2, 3],), ZZ) == (1, ([1, 2, 3],))
498
+ assert dup_multi_deflate(([1, 0, 2, 0, 3],), ZZ) == (2, ([1, 2, 3],))
499
+
500
+ assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 0, 0]), ZZ) == \
501
+ (2, ([1, 2, 3], [2, 0]))
502
+ assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 1, 0]), ZZ) == \
503
+ (1, ([1, 0, 2, 0, 3], [2, 1, 0]))
504
+
505
+
506
+ def test_dmp_multi_deflate():
507
+ assert dmp_multi_deflate(([[]],), 1, ZZ) == \
508
+ ((1, 1), ([[]],))
509
+ assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \
510
+ ((1, 1), ([[]], [[]]))
511
+
512
+ assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \
513
+ ((1, 1), ([[1]], [[]]))
514
+ assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \
515
+ ((1, 1), ([[1]], [[2]]))
516
+ assert dmp_multi_deflate(([[1]], [[2, 0]]), 1, ZZ) == \
517
+ ((1, 1), ([[1]], [[2, 0]]))
518
+
519
+ assert dmp_multi_deflate(([[2, 0]], [[2, 0]]), 1, ZZ) == \
520
+ ((1, 1), ([[2, 0]], [[2, 0]]))
521
+
522
+ assert dmp_multi_deflate(
523
+ ([[2]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]]))
524
+ assert dmp_multi_deflate(
525
+ ([[2, 0, 0]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]]))
526
+
527
+ assert dmp_multi_deflate(([2, 0, 0], [1, 0, 4, 0, 1]), 0, ZZ) == \
528
+ ((2,), ([2, 0], [1, 4, 1]))
529
+
530
+ f = [[1, 0, 0], [], [1, 0], [], [1]]
531
+ g = [[1, 0, 1, 0], [], [1]]
532
+
533
+ assert dmp_multi_deflate((f,), 1, ZZ) == \
534
+ ((2, 1), ([[1, 0, 0], [1, 0], [1]],))
535
+
536
+ assert dmp_multi_deflate((f, g), 1, ZZ) == \
537
+ ((2, 1), ([[1, 0, 0], [1, 0], [1]],
538
+ [[1, 0, 1, 0], [1]]))
539
+
540
+
541
+ def test_dup_inflate():
542
+ assert dup_inflate([], 17, ZZ) == []
543
+
544
+ assert dup_inflate([1, 2, 3], 1, ZZ) == [1, 2, 3]
545
+ assert dup_inflate([1, 2, 3], 2, ZZ) == [1, 0, 2, 0, 3]
546
+ assert dup_inflate([1, 2, 3], 3, ZZ) == [1, 0, 0, 2, 0, 0, 3]
547
+ assert dup_inflate([1, 2, 3], 4, ZZ) == [1, 0, 0, 0, 2, 0, 0, 0, 3]
548
+
549
+ raises(IndexError, lambda: dup_inflate([1, 2, 3], 0, ZZ))
550
+
551
+
552
+ def test_dmp_inflate():
553
+ assert dmp_inflate([1], (3,), 0, ZZ) == [1]
554
+
555
+ assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]]
556
+ assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]]
557
+
558
+ assert dmp_inflate([[2, 0]], (1, 1), 1, ZZ) == [[2, 0]]
559
+ assert dmp_inflate([[2, 0]], (1, 2), 1, ZZ) == [[2, 0, 0]]
560
+ assert dmp_inflate([[2, 0]], (1, 3), 1, ZZ) == [[2, 0, 0, 0]]
561
+
562
+ assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \
563
+ [[1, 0, 0], [], [1], [], [1, 0]]
564
+
565
+ raises(IndexError, lambda: dmp_inflate([[]], (-3, 7), 1, ZZ))
566
+
567
+
568
+ def test_dmp_exclude():
569
+ assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2)
570
+ assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2)
571
+
572
+ assert dmp_exclude([1, 2, 3], 0, ZZ) == ([], [1, 2, 3], 0)
573
+ assert dmp_exclude([[1], [2, 3]], 1, ZZ) == ([], [[1], [2, 3]], 1)
574
+
575
+ assert dmp_exclude([[1, 2, 3]], 1, ZZ) == ([0], [1, 2, 3], 0)
576
+ assert dmp_exclude([[1], [2], [3]], 1, ZZ) == ([1], [1, 2, 3], 0)
577
+
578
+ assert dmp_exclude([[[1, 2, 3]]], 2, ZZ) == ([0, 1], [1, 2, 3], 0)
579
+ assert dmp_exclude([[[1]], [[2]], [[3]]], 2, ZZ) == ([1, 2], [1, 2, 3], 0)
580
+
581
+
582
+ def test_dmp_include():
583
+ assert dmp_include([1, 2, 3], [], 0, ZZ) == [1, 2, 3]
584
+
585
+ assert dmp_include([1, 2, 3], [0], 0, ZZ) == [[1, 2, 3]]
586
+ assert dmp_include([1, 2, 3], [1], 0, ZZ) == [[1], [2], [3]]
587
+
588
+ assert dmp_include([1, 2, 3], [0, 1], 0, ZZ) == [[[1, 2, 3]]]
589
+ assert dmp_include([1, 2, 3], [1, 2], 0, ZZ) == [[[1]], [[2]], [[3]]]
590
+
591
+
592
+ def test_dmp_inject():
593
+ R, x,y = ring("x,y", ZZ)
594
+ K = R.to_domain()
595
+
596
+ assert dmp_inject([], 0, K) == ([[[]]], 2)
597
+ assert dmp_inject([[]], 1, K) == ([[[[]]]], 3)
598
+
599
+ assert dmp_inject([R(1)], 0, K) == ([[[1]]], 2)
600
+ assert dmp_inject([[R(1)]], 1, K) == ([[[[1]]]], 3)
601
+
602
+ assert dmp_inject([R(1), 2*x + 3*y + 4], 0, K) == ([[[1]], [[2], [3, 4]]], 2)
603
+
604
+ f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11]
605
+ g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]]
606
+
607
+ assert dmp_inject(f, 0, K) == (g, 2)
608
+
609
+
610
+ def test_dmp_eject():
611
+ R, x,y = ring("x,y", ZZ)
612
+ K = R.to_domain()
613
+
614
+ assert dmp_eject([[[]]], 2, K) == []
615
+ assert dmp_eject([[[[]]]], 3, K) == [[]]
616
+
617
+ assert dmp_eject([[[1]]], 2, K) == [R(1)]
618
+ assert dmp_eject([[[[1]]]], 3, K) == [[R(1)]]
619
+
620
+ assert dmp_eject([[[1]], [[2], [3, 4]]], 2, K) == [R(1), 2*x + 3*y + 4]
621
+
622
+ f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11]
623
+ g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]]
624
+
625
+ assert dmp_eject(g, 2, K) == f
626
+
627
+
628
+ def test_dup_terms_gcd():
629
+ assert dup_terms_gcd([], ZZ) == (0, [])
630
+ assert dup_terms_gcd([1, 0, 1], ZZ) == (0, [1, 0, 1])
631
+ assert dup_terms_gcd([1, 0, 1, 0], ZZ) == (1, [1, 0, 1])
632
+
633
+
634
+ def test_dmp_terms_gcd():
635
+ assert dmp_terms_gcd([[]], 1, ZZ) == ((0, 0), [[]])
636
+
637
+ assert dmp_terms_gcd([1, 0, 1, 0], 0, ZZ) == ((1,), [1, 0, 1])
638
+ assert dmp_terms_gcd([[1], [], [1], []], 1, ZZ) == ((1, 0), [[1], [], [1]])
639
+
640
+ assert dmp_terms_gcd(
641
+ [[1, 0], [], [1]], 1, ZZ) == ((0, 0), [[1, 0], [], [1]])
642
+ assert dmp_terms_gcd(
643
+ [[1, 0], [1, 0, 0], [], []], 1, ZZ) == ((2, 1), [[1], [1, 0]])
644
+
645
+
646
+ def test_dmp_list_terms():
647
+ assert dmp_list_terms([[[]]], 2, ZZ) == [((0, 0, 0), 0)]
648
+ assert dmp_list_terms([[[1]]], 2, ZZ) == [((0, 0, 0), 1)]
649
+
650
+ assert dmp_list_terms([1, 2, 4, 3, 5], 0, ZZ) == \
651
+ [((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)]
652
+
653
+ assert dmp_list_terms([[1], [2, 4], [3, 5, 0]], 1, ZZ) == \
654
+ [((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)]
655
+
656
+ f = [[2, 0, 0, 0], [1, 0, 0], []]
657
+
658
+ assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)]
659
+ assert dmp_list_terms(
660
+ f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)]
661
+
662
+ f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []]
663
+
664
+ assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)]
665
+ assert dmp_list_terms(
666
+ f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)]
667
+
668
+
669
+ def test_dmp_apply_pairs():
670
+ h = lambda a, b: a*b
671
+
672
+ assert dmp_apply_pairs([1, 2, 3], [4, 5, 6], h, [], 0, ZZ) == [4, 10, 18]
673
+
674
+ assert dmp_apply_pairs([2, 3], [4, 5, 6], h, [], 0, ZZ) == [10, 18]
675
+ assert dmp_apply_pairs([1, 2, 3], [5, 6], h, [], 0, ZZ) == [10, 18]
676
+
677
+ assert dmp_apply_pairs(
678
+ [[1, 2], [3]], [[4, 5], [6]], h, [], 1, ZZ) == [[4, 10], [18]]
679
+
680
+ assert dmp_apply_pairs(
681
+ [[1, 2], [3]], [[4], [5, 6]], h, [], 1, ZZ) == [[8], [18]]
682
+ assert dmp_apply_pairs(
683
+ [[1], [2, 3]], [[4, 5], [6]], h, [], 1, ZZ) == [[5], [18]]
684
+
685
+
686
+ def test_dup_slice():
687
+ f = [1, 2, 3, 4]
688
+
689
+ assert dup_slice(f, 0, 0, ZZ) == []
690
+ assert dup_slice(f, 0, 1, ZZ) == [4]
691
+ assert dup_slice(f, 0, 2, ZZ) == [3, 4]
692
+ assert dup_slice(f, 0, 3, ZZ) == [2, 3, 4]
693
+ assert dup_slice(f, 0, 4, ZZ) == [1, 2, 3, 4]
694
+
695
+ assert dup_slice(f, 0, 4, ZZ) == f
696
+ assert dup_slice(f, 0, 9, ZZ) == f
697
+
698
+ assert dup_slice(f, 1, 0, ZZ) == []
699
+ assert dup_slice(f, 1, 1, ZZ) == []
700
+ assert dup_slice(f, 1, 2, ZZ) == [3, 0]
701
+ assert dup_slice(f, 1, 3, ZZ) == [2, 3, 0]
702
+ assert dup_slice(f, 1, 4, ZZ) == [1, 2, 3, 0]
703
+
704
+ assert dup_slice([1, 2], 0, 3, ZZ) == [1, 2]
705
+
706
+ g = [1, 0, 0, 2]
707
+
708
+ assert dup_slice(g, 0, 3, ZZ) == [2]
709
+
710
+
711
+ def test_dup_random():
712
+ f = dup_random(0, -10, 10, ZZ)
713
+
714
+ assert dup_degree(f) == 0
715
+ assert all(-10 <= c <= 10 for c in f)
716
+
717
+ f = dup_random(1, -20, 20, ZZ)
718
+
719
+ assert dup_degree(f) == 1
720
+ assert all(-20 <= c <= 20 for c in f)
721
+
722
+ f = dup_random(2, -30, 30, ZZ)
723
+
724
+ assert dup_degree(f) == 2
725
+ assert all(-30 <= c <= 30 for c in f)
726
+
727
+ f = dup_random(3, -40, 40, ZZ)
728
+
729
+ assert dup_degree(f) == 3
730
+ assert all(-40 <= c <= 40 for c in f)
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py ADDED
@@ -0,0 +1,714 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for dense recursive polynomials' tools. """
2
+
3
+ from sympy.polys.densebasic import (
4
+ dup_normal, dmp_normal,
5
+ dup_from_raw_dict,
6
+ dmp_convert, dmp_swap,
7
+ )
8
+
9
+ from sympy.polys.densearith import dmp_mul_ground
10
+
11
+ from sympy.polys.densetools import (
12
+ dup_clear_denoms, dmp_clear_denoms,
13
+ dup_integrate, dmp_integrate, dmp_integrate_in,
14
+ dup_diff, dmp_diff, dmp_diff_in,
15
+ dup_eval, dmp_eval, dmp_eval_in,
16
+ dmp_eval_tail, dmp_diff_eval_in,
17
+ dup_trunc, dmp_trunc, dmp_ground_trunc,
18
+ dup_monic, dmp_ground_monic,
19
+ dup_content, dmp_ground_content,
20
+ dup_primitive, dmp_ground_primitive,
21
+ dup_extract, dmp_ground_extract,
22
+ dup_real_imag,
23
+ dup_mirror, dup_scale, dup_shift, dmp_shift,
24
+ dup_transform,
25
+ dup_compose, dmp_compose,
26
+ dup_decompose,
27
+ dmp_lift,
28
+ dup_sign_variations,
29
+ dup_revert, dmp_revert,
30
+ )
31
+ from sympy.polys.polyclasses import ANP
32
+
33
+ from sympy.polys.polyerrors import (
34
+ MultivariatePolynomialError,
35
+ ExactQuotientFailed,
36
+ NotReversible,
37
+ DomainError,
38
+ )
39
+
40
+ from sympy.polys.specialpolys import f_polys
41
+
42
+ from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, EX, RR
43
+ from sympy.polys.rings import ring
44
+
45
+ from sympy.core.numbers import I
46
+ from sympy.core.singleton import S
47
+ from sympy.functions.elementary.trigonometric import sin
48
+
49
+ from sympy.abc import x
50
+ from sympy.testing.pytest import raises
51
+
52
+ f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
53
+
54
+ def test_dup_integrate():
55
+ assert dup_integrate([], 1, QQ) == []
56
+ assert dup_integrate([], 2, QQ) == []
57
+
58
+ assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1), QQ(0)]
59
+ assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1, 2), QQ(0), QQ(0)]
60
+
61
+ assert dup_integrate([QQ(1), QQ(2), QQ(3)], 0, QQ) == \
62
+ [QQ(1), QQ(2), QQ(3)]
63
+ assert dup_integrate([QQ(1), QQ(2), QQ(3)], 1, QQ) == \
64
+ [QQ(1, 3), QQ(1), QQ(3), QQ(0)]
65
+ assert dup_integrate([QQ(1), QQ(2), QQ(3)], 2, QQ) == \
66
+ [QQ(1, 12), QQ(1, 3), QQ(3, 2), QQ(0), QQ(0)]
67
+ assert dup_integrate([QQ(1), QQ(2), QQ(3)], 3, QQ) == \
68
+ [QQ(1, 60), QQ(1, 12), QQ(1, 2), QQ(0), QQ(0), QQ(0)]
69
+
70
+ assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \
71
+ dup_from_raw_dict({32: QQ(17, 29760)}, QQ)
72
+
73
+ assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1, 2)}, QQ), 3, QQ) == \
74
+ dup_from_raw_dict({32: QQ(17, 29760), 8: QQ(1, 672)}, QQ)
75
+
76
+
77
+ def test_dmp_integrate():
78
+ assert dmp_integrate([QQ(1)], 2, 0, QQ) == [QQ(1, 2), QQ(0), QQ(0)]
79
+
80
+ assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]]
81
+ assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]]
82
+
83
+ assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]], [[]]]
84
+ assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1, 2)]], [[]], [[]]]
85
+
86
+ assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 0, 1, QQ) == \
87
+ [[QQ(1)], [QQ(2)], [QQ(3)]]
88
+ assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 1, 1, QQ) == \
89
+ [[QQ(1, 3)], [QQ(1)], [QQ(3)], []]
90
+ assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 2, 1, QQ) == \
91
+ [[QQ(1, 12)], [QQ(1, 3)], [QQ(3, 2)], [], []]
92
+ assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 3, 1, QQ) == \
93
+ [[QQ(1, 60)], [QQ(1, 12)], [QQ(1, 2)], [], [], []]
94
+
95
+
96
+ def test_dmp_integrate_in():
97
+ f = dmp_convert(f_6, 3, ZZ, QQ)
98
+
99
+ assert dmp_integrate_in(f, 2, 1, 3, QQ) == \
100
+ dmp_swap(
101
+ dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ)
102
+ assert dmp_integrate_in(f, 3, 1, 3, QQ) == \
103
+ dmp_swap(
104
+ dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ)
105
+ assert dmp_integrate_in(f, 2, 2, 3, QQ) == \
106
+ dmp_swap(
107
+ dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ)
108
+ assert dmp_integrate_in(f, 3, 2, 3, QQ) == \
109
+ dmp_swap(
110
+ dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ)
111
+
112
+ raises(IndexError, lambda: dmp_integrate_in(f, 1, -1, 3, QQ))
113
+ raises(IndexError, lambda: dmp_integrate_in(f, 1, 4, 3, QQ))
114
+
115
+
116
+ def test_dup_diff():
117
+ assert dup_diff([], 1, ZZ) == []
118
+ assert dup_diff([7], 1, ZZ) == []
119
+ assert dup_diff([2, 7], 1, ZZ) == [2]
120
+ assert dup_diff([1, 2, 1], 1, ZZ) == [2, 2]
121
+ assert dup_diff([1, 2, 3, 4], 1, ZZ) == [3, 4, 3]
122
+ assert dup_diff([1, -1, 0, 0, 2], 1, ZZ) == [4, -3, 0, 0]
123
+
124
+ f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], ZZ)
125
+
126
+ assert dup_diff(f, 0, ZZ) == f
127
+ assert dup_diff(f, 1, ZZ) == [170, 306, 448, -2415, 138, 380, 0, 0, 24, 3]
128
+ assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ)
129
+ assert dup_diff(
130
+ f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ)
131
+
132
+ K = FF(3)
133
+ f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], K)
134
+
135
+ assert dup_diff(f, 1, K) == dup_normal([2, 0, 1, 0, 0, 2, 0, 0, 0, 0], K)
136
+ assert dup_diff(f, 2, K) == dup_normal([1, 0, 0, 2, 0, 0, 0], K)
137
+ assert dup_diff(f, 3, K) == dup_normal([], K)
138
+
139
+ assert dup_diff(f, 0, K) == f
140
+ assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K)
141
+ assert dup_diff(
142
+ f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K)
143
+
144
+
145
+ def test_dmp_diff():
146
+ assert dmp_diff([], 1, 0, ZZ) == []
147
+ assert dmp_diff([[]], 1, 1, ZZ) == [[]]
148
+ assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]]
149
+
150
+ assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]]
151
+
152
+ assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]]
153
+ assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]]
154
+
155
+ assert dmp_diff([1, -1, 0, 0, 2], 1, 0, ZZ) == \
156
+ dup_diff([1, -1, 0, 0, 2], 1, ZZ)
157
+
158
+ assert dmp_diff(f_6, 0, 3, ZZ) == f_6
159
+ assert dmp_diff(f_6, 1, 3, ZZ) == [[[[8460]], [[]]],
160
+ [[[135, 0, 0], [], [], [-135, 0, 0]]],
161
+ [[[]]],
162
+ [[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]]]
163
+ assert dmp_diff(
164
+ f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ)
165
+ assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff(
166
+ dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ)
167
+
168
+ K = FF(23)
169
+ F_6 = dmp_normal(f_6, 3, K)
170
+
171
+ assert dmp_diff(F_6, 0, 3, K) == F_6
172
+ assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K)
173
+ assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K)
174
+ assert dmp_diff(F_6, 3, 3, K) == dmp_diff(
175
+ dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K)
176
+
177
+
178
+ def test_dmp_diff_in():
179
+ assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \
180
+ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ)
181
+ assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \
182
+ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ)
183
+ assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \
184
+ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ)
185
+ assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \
186
+ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ)
187
+
188
+ raises(IndexError, lambda: dmp_diff_in(f_6, 1, -1, 3, ZZ))
189
+ raises(IndexError, lambda: dmp_diff_in(f_6, 1, 4, 3, ZZ))
190
+
191
+ def test_dup_eval():
192
+ assert dup_eval([], 7, ZZ) == 0
193
+ assert dup_eval([1, 2], 0, ZZ) == 2
194
+ assert dup_eval([1, 2, 3], 7, ZZ) == 66
195
+
196
+
197
+ def test_dmp_eval():
198
+ assert dmp_eval([], 3, 0, ZZ) == 0
199
+
200
+ assert dmp_eval([[]], 3, 1, ZZ) == []
201
+ assert dmp_eval([[[]]], 3, 2, ZZ) == [[]]
202
+
203
+ assert dmp_eval([[1, 2]], 0, 1, ZZ) == [1, 2]
204
+
205
+ assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]]
206
+ assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]]
207
+
208
+ assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8]
209
+ assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]]
210
+
211
+
212
+ def test_dmp_eval_in():
213
+ assert dmp_eval_in(
214
+ f_6, -2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), -2, 3, ZZ)
215
+ assert dmp_eval_in(
216
+ f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ)
217
+ assert dmp_eval_in(f_6, -2, 2, 3, ZZ) == dmp_swap(
218
+ dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), -2, 3, ZZ), 0, 1, 2, ZZ)
219
+ assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap(
220
+ dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ)
221
+
222
+ f = [[[int(45)]], [[]], [[]], [[int(-9)], [-1], [], [int(3), int(0), int(10), int(0)]]]
223
+
224
+ assert dmp_eval_in(f, -2, 2, 2, ZZ) == \
225
+ [[45], [], [], [-9, -1, 0, -44]]
226
+
227
+ raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), -1, 3, ZZ))
228
+ raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), 4, 3, ZZ))
229
+
230
+
231
+ def test_dmp_eval_tail():
232
+ assert dmp_eval_tail([[]], [1], 1, ZZ) == []
233
+ assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]]
234
+ assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == []
235
+
236
+ assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0
237
+
238
+ assert dmp_eval_tail(f_0, [1, -17, 8], 2, ZZ) == 84496
239
+ assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902]
240
+ assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]]
241
+
242
+ assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144]
243
+
244
+ assert dmp_eval_tail(
245
+ f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624]
246
+ assert dmp_eval_tail(
247
+ f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540]
248
+
249
+ assert dmp_eval_tail(
250
+ f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750,
251
+ -3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520]
252
+ assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576]
253
+
254
+ assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480]
255
+
256
+
257
+ def test_dmp_diff_eval_in():
258
+ assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \
259
+ dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ)
260
+
261
+ assert dmp_diff_eval_in(f_6, 2, 7, 0, 3, ZZ) == \
262
+ dmp_eval(dmp_diff(f_6, 2, 3, ZZ), 7, 3, ZZ)
263
+
264
+ raises(IndexError, lambda: dmp_diff_eval_in(f_6, 1, ZZ(1), 4, 3, ZZ))
265
+
266
+
267
+ def test_dup_revert():
268
+ f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)]
269
+ g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)]
270
+
271
+ assert dup_revert(f, 8, QQ) == g
272
+
273
+ raises(NotReversible, lambda: dup_revert([QQ(1), QQ(0)], 3, QQ))
274
+
275
+
276
+ def test_dmp_revert():
277
+ f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)]
278
+ g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)]
279
+
280
+ assert dmp_revert(f, 8, 0, QQ) == g
281
+
282
+ raises(MultivariatePolynomialError, lambda: dmp_revert([[1]], 2, 1, QQ))
283
+
284
+
285
+ def test_dup_trunc():
286
+ assert dup_trunc([1, 2, 3, 4, 5, 6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0]
287
+ assert dup_trunc([6, 5, 4, 3, 2, 1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1]
288
+
289
+ R = ZZ_I
290
+ assert dup_trunc([R(3), R(4), R(5)], R(3), R) == [R(1), R(-1)]
291
+
292
+ K = FF(5)
293
+ assert dup_trunc([K(3), K(4), K(5)], K(3), K) == [K(1), K(0)]
294
+
295
+
296
+ def test_dmp_trunc():
297
+ assert dmp_trunc([[]], [1, 2], 2, ZZ) == [[]]
298
+ assert dmp_trunc([[1, 2], [1, 4, 1], [1]], [1, 2], 1, ZZ) == [[-3], [1]]
299
+
300
+
301
+ def test_dmp_ground_trunc():
302
+ assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \
303
+ dmp_normal(
304
+ [[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ)
305
+
306
+
307
+ def test_dup_monic():
308
+ assert dup_monic([3, 6, 9], ZZ) == [1, 2, 3]
309
+
310
+ raises(ExactQuotientFailed, lambda: dup_monic([3, 4, 5], ZZ))
311
+
312
+ assert dup_monic([], QQ) == []
313
+ assert dup_monic([QQ(1)], QQ) == [QQ(1)]
314
+ assert dup_monic([QQ(7), QQ(1), QQ(21)], QQ) == [QQ(1), QQ(1, 7), QQ(3)]
315
+
316
+
317
+ def test_dmp_ground_monic():
318
+ assert dmp_ground_monic([3, 6, 9], 0, ZZ) == [1, 2, 3]
319
+
320
+ assert dmp_ground_monic([[3], [6], [9]], 1, ZZ) == [[1], [2], [3]]
321
+
322
+ raises(
323
+ ExactQuotientFailed, lambda: dmp_ground_monic([[3], [4], [5]], 1, ZZ))
324
+
325
+ assert dmp_ground_monic([[]], 1, QQ) == [[]]
326
+ assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]]
327
+ assert dmp_ground_monic(
328
+ [[QQ(7)], [QQ(1)], [QQ(21)]], 1, QQ) == [[QQ(1)], [QQ(1, 7)], [QQ(3)]]
329
+
330
+
331
+ def test_dup_content():
332
+ assert dup_content([], ZZ) == ZZ(0)
333
+ assert dup_content([1], ZZ) == ZZ(1)
334
+ assert dup_content([-1], ZZ) == ZZ(1)
335
+ assert dup_content([1, 1], ZZ) == ZZ(1)
336
+ assert dup_content([2, 2], ZZ) == ZZ(2)
337
+ assert dup_content([1, 2, 1], ZZ) == ZZ(1)
338
+ assert dup_content([2, 4, 2], ZZ) == ZZ(2)
339
+
340
+ assert dup_content([QQ(2, 3), QQ(4, 9)], QQ) == QQ(2, 9)
341
+ assert dup_content([QQ(2, 3), QQ(4, 5)], QQ) == QQ(2, 15)
342
+
343
+
344
+ def test_dmp_ground_content():
345
+ assert dmp_ground_content([[]], 1, ZZ) == ZZ(0)
346
+ assert dmp_ground_content([[]], 1, QQ) == QQ(0)
347
+ assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1)
348
+ assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1)
349
+ assert dmp_ground_content([[1], [1]], 1, ZZ) == ZZ(1)
350
+ assert dmp_ground_content([[2], [2]], 1, ZZ) == ZZ(2)
351
+ assert dmp_ground_content([[1], [2], [1]], 1, ZZ) == ZZ(1)
352
+ assert dmp_ground_content([[2], [4], [2]], 1, ZZ) == ZZ(2)
353
+
354
+ assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == QQ(2, 9)
355
+ assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == QQ(2, 15)
356
+
357
+ assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1)
358
+ assert dmp_ground_content(
359
+ dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2)
360
+
361
+ assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1)
362
+ assert dmp_ground_content(
363
+ dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3)
364
+
365
+ assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1)
366
+ assert dmp_ground_content(
367
+ dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4)
368
+
369
+ assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1)
370
+ assert dmp_ground_content(
371
+ dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5)
372
+
373
+ assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1)
374
+ assert dmp_ground_content(
375
+ dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6)
376
+
377
+ assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1)
378
+ assert dmp_ground_content(
379
+ dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7)
380
+
381
+ assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1)
382
+ assert dmp_ground_content(
383
+ dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8)
384
+
385
+
386
+ def test_dup_primitive():
387
+ assert dup_primitive([], ZZ) == (ZZ(0), [])
388
+ assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)])
389
+ assert dup_primitive([ZZ(1), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(1)])
390
+ assert dup_primitive([ZZ(2), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(1)])
391
+ assert dup_primitive(
392
+ [ZZ(1), ZZ(2), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(2), ZZ(1)])
393
+ assert dup_primitive(
394
+ [ZZ(2), ZZ(4), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(2), ZZ(1)])
395
+
396
+ assert dup_primitive([], QQ) == (QQ(0), [])
397
+ assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)])
398
+ assert dup_primitive([QQ(1), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(1)])
399
+ assert dup_primitive([QQ(2), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(1)])
400
+ assert dup_primitive(
401
+ [QQ(1), QQ(2), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(2), QQ(1)])
402
+ assert dup_primitive(
403
+ [QQ(2), QQ(4), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(2), QQ(1)])
404
+
405
+ assert dup_primitive(
406
+ [QQ(2, 3), QQ(4, 9)], QQ) == (QQ(2, 9), [QQ(3), QQ(2)])
407
+ assert dup_primitive(
408
+ [QQ(2, 3), QQ(4, 5)], QQ) == (QQ(2, 15), [QQ(5), QQ(6)])
409
+
410
+
411
+ def test_dmp_ground_primitive():
412
+ assert dmp_ground_primitive([ZZ(1)], 0, ZZ) == (ZZ(1), [ZZ(1)])
413
+
414
+ assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]])
415
+
416
+ assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0)
417
+ assert dmp_ground_primitive(
418
+ dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0)
419
+
420
+ assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1)
421
+ assert dmp_ground_primitive(
422
+ dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1)
423
+
424
+ assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2)
425
+ assert dmp_ground_primitive(
426
+ dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2)
427
+
428
+ assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3)
429
+ assert dmp_ground_primitive(
430
+ dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3)
431
+
432
+ assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4)
433
+ assert dmp_ground_primitive(
434
+ dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4)
435
+
436
+ assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5)
437
+ assert dmp_ground_primitive(
438
+ dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5)
439
+
440
+ assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6)
441
+ assert dmp_ground_primitive(
442
+ dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6)
443
+
444
+ assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]])
445
+ assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(2), [[QQ(1)]])
446
+
447
+ assert dmp_ground_primitive(
448
+ [[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == (QQ(2, 9), [[QQ(3)], [QQ(2)]])
449
+ assert dmp_ground_primitive(
450
+ [[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == (QQ(2, 15), [[QQ(5)], [QQ(6)]])
451
+
452
+
453
+ def test_dup_extract():
454
+ f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ)
455
+ g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ)
456
+
457
+ F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ)
458
+ G = dup_normal([384, 0, 192, 0, 24, 0], ZZ)
459
+
460
+ assert dup_extract(f, g, ZZ) == (45796, F, G)
461
+
462
+
463
+ def test_dmp_ground_extract():
464
+ f = dmp_normal(
465
+ [[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ)
466
+ g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ)
467
+
468
+ F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ)
469
+ G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ)
470
+
471
+ assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G)
472
+
473
+
474
+ def test_dup_real_imag():
475
+ assert dup_real_imag([], ZZ) == ([[]], [[]])
476
+ assert dup_real_imag([1], ZZ) == ([[1]], [[]])
477
+
478
+ assert dup_real_imag([1, 1], ZZ) == ([[1], [1]], [[1, 0]])
479
+ assert dup_real_imag([1, 2], ZZ) == ([[1], [2]], [[1, 0]])
480
+
481
+ assert dup_real_imag(
482
+ [1, 2, 3], ZZ) == ([[1], [2], [-1, 0, 3]], [[2, 0], [2, 0]])
483
+
484
+ assert dup_real_imag([ZZ(1), ZZ(0), ZZ(1), ZZ(3)], ZZ) == (
485
+ [[ZZ(1)], [], [ZZ(-3), ZZ(0), ZZ(1)], [ZZ(3)]],
486
+ [[ZZ(3), ZZ(0)], [], [ZZ(-1), ZZ(0), ZZ(1), ZZ(0)]]
487
+ )
488
+
489
+ raises(DomainError, lambda: dup_real_imag([EX(1), EX(2)], EX))
490
+
491
+
492
+
493
+ def test_dup_mirror():
494
+ assert dup_mirror([], ZZ) == []
495
+ assert dup_mirror([1], ZZ) == [1]
496
+
497
+ assert dup_mirror([1, 2, 3, 4, 5], ZZ) == [1, -2, 3, -4, 5]
498
+ assert dup_mirror([1, 2, 3, 4, 5, 6], ZZ) == [-1, 2, -3, 4, -5, 6]
499
+
500
+
501
+ def test_dup_scale():
502
+ assert dup_scale([], -1, ZZ) == []
503
+ assert dup_scale([1], -1, ZZ) == [1]
504
+
505
+ assert dup_scale([1, 2, 3, 4, 5], -1, ZZ) == [1, -2, 3, -4, 5]
506
+ assert dup_scale([1, 2, 3, 4, 5], -7, ZZ) == [2401, -686, 147, -28, 5]
507
+
508
+
509
+ def test_dup_shift():
510
+ assert dup_shift([], 1, ZZ) == []
511
+ assert dup_shift([1], 1, ZZ) == [1]
512
+
513
+ assert dup_shift([1, 2, 3, 4, 5], 1, ZZ) == [1, 6, 15, 20, 15]
514
+ assert dup_shift([1, 2, 3, 4, 5], 7, ZZ) == [1, 30, 339, 1712, 3267]
515
+
516
+
517
+ def test_dmp_shift():
518
+ assert dmp_shift([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == [ZZ(1), ZZ(3)]
519
+
520
+ assert dmp_shift([[]], [ZZ(1), ZZ(2)], 1, ZZ) == [[]]
521
+
522
+ xy = [[ZZ(1), ZZ(0)], []] # x*y
523
+ x1y2 = [[ZZ(1), ZZ(2)], [ZZ(1), ZZ(2)]] # (x+1)*(y+2)
524
+ assert dmp_shift(xy, [ZZ(1), ZZ(2)], 1, ZZ) == x1y2
525
+
526
+
527
+ def test_dup_transform():
528
+ assert dup_transform([], [], [1, 1], ZZ) == []
529
+ assert dup_transform([], [1], [1, 1], ZZ) == []
530
+ assert dup_transform([], [1, 2], [1, 1], ZZ) == []
531
+
532
+ assert dup_transform([6, -5, 4, -3, 17], [1, -3, 4], [2, -3], ZZ) == \
533
+ [6, -82, 541, -2205, 6277, -12723, 17191, -13603, 4773]
534
+
535
+
536
+ def test_dup_compose():
537
+ assert dup_compose([], [], ZZ) == []
538
+ assert dup_compose([], [1], ZZ) == []
539
+ assert dup_compose([], [1, 2], ZZ) == []
540
+
541
+ assert dup_compose([1], [], ZZ) == [1]
542
+
543
+ assert dup_compose([1, 2, 0], [], ZZ) == []
544
+ assert dup_compose([1, 2, 1], [], ZZ) == [1]
545
+
546
+ assert dup_compose([1, 2, 1], [1], ZZ) == [4]
547
+ assert dup_compose([1, 2, 1], [7], ZZ) == [64]
548
+
549
+ assert dup_compose([1, 2, 1], [1, -1], ZZ) == [1, 0, 0]
550
+ assert dup_compose([1, 2, 1], [1, 1], ZZ) == [1, 4, 4]
551
+ assert dup_compose([1, 2, 1], [1, 2, 1], ZZ) == [1, 4, 8, 8, 4]
552
+
553
+
554
+ def test_dmp_compose():
555
+ assert dmp_compose([1, 2, 1], [1, 2, 1], 0, ZZ) == [1, 4, 8, 8, 4]
556
+
557
+ assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]]
558
+ assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]]
559
+ assert dmp_compose([[[]]], [[[1]], [[2]]], 2, ZZ) == [[[]]]
560
+
561
+ assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]]
562
+
563
+ assert dmp_compose([[1], [2], [ ]], [[]], 1, ZZ) == [[]]
564
+ assert dmp_compose([[1], [2], [1]], [[]], 1, ZZ) == [[1]]
565
+
566
+ assert dmp_compose([[1], [2], [1]], [[1]], 1, ZZ) == [[4]]
567
+ assert dmp_compose([[1], [2], [1]], [[7]], 1, ZZ) == [[64]]
568
+
569
+ assert dmp_compose([[1], [2], [1]], [[1], [-1]], 1, ZZ) == [[1], [ ], [ ]]
570
+ assert dmp_compose([[1], [2], [1]], [[1], [ 1]], 1, ZZ) == [[1], [4], [4]]
571
+
572
+ assert dmp_compose(
573
+ [[1], [2], [1]], [[1], [2], [1]], 1, ZZ) == [[1], [4], [8], [8], [4]]
574
+
575
+
576
+ def test_dup_decompose():
577
+ assert dup_decompose([1], ZZ) == [[1]]
578
+
579
+ assert dup_decompose([1, 0], ZZ) == [[1, 0]]
580
+ assert dup_decompose([1, 0, 0, 0], ZZ) == [[1, 0, 0, 0]]
581
+
582
+ assert dup_decompose([1, 0, 0, 0, 0], ZZ) == [[1, 0, 0], [1, 0, 0]]
583
+ assert dup_decompose(
584
+ [1, 0, 0, 0, 0, 0, 0], ZZ) == [[1, 0, 0, 0], [1, 0, 0]]
585
+
586
+ assert dup_decompose([7, 0, 0, 0, 1], ZZ) == [[7, 0, 1], [1, 0, 0]]
587
+ assert dup_decompose([4, 0, 3, 0, 2], ZZ) == [[4, 3, 2], [1, 0, 0]]
588
+
589
+ f = [1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9]
590
+
591
+ assert dup_decompose(f, ZZ) == [[1, 0, 0, -2, 9], [1, 0, 5, 0]]
592
+
593
+ f = [2, 0, 40, 0, 300, 0, 1000, 0, 1250, -4, 0, -20, 18]
594
+
595
+ assert dup_decompose(f, ZZ) == [[2, 0, 0, -4, 18], [1, 0, 5, 0]]
596
+
597
+ f = [1, 0, 20, -8, 150, -120, 524, -600, 865, -1034, 600, -170, 29]
598
+
599
+ assert dup_decompose(f, ZZ) == [[1, -8, 24, -34, 29], [1, 0, 5, 0]]
600
+
601
+ R, t = ring("t", ZZ)
602
+ f = [6*t**2 - 42,
603
+ 48*t**2 + 96,
604
+ 144*t**2 + 648*t + 288,
605
+ 624*t**2 + 864*t + 384,
606
+ 108*t**3 + 312*t**2 + 432*t + 192]
607
+
608
+ assert dup_decompose(f, R.to_domain()) == [f]
609
+
610
+
611
+ def test_dmp_lift():
612
+ q = [QQ(1, 1), QQ(0, 1), QQ(1, 1)]
613
+
614
+ f_a = [ANP([QQ(1, 1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ),
615
+ ANP([QQ(1, 1), QQ(0, 1)], q, QQ), ANP([QQ(17, 1), QQ(0, 1)], q, QQ)]
616
+
617
+ f_lift = QQ.map([1, 0, 0, 0, 0, 0, 1, 34, 289])
618
+
619
+ assert dmp_lift(f_a, 0, QQ.algebraic_field(I)) == f_lift
620
+
621
+ f_g = [QQ_I(1), QQ_I(0), QQ_I(0), QQ_I(0, 1), QQ_I(0, 17)]
622
+
623
+ assert dmp_lift(f_g, 0, QQ_I) == f_lift
624
+
625
+ raises(DomainError, lambda: dmp_lift([EX(1), EX(2)], 0, EX))
626
+
627
+
628
+ def test_dup_sign_variations():
629
+ assert dup_sign_variations([], ZZ) == 0
630
+ assert dup_sign_variations([1, 0], ZZ) == 0
631
+ assert dup_sign_variations([1, 0, 2], ZZ) == 0
632
+ assert dup_sign_variations([1, 0, 3, 0], ZZ) == 0
633
+ assert dup_sign_variations([1, 0, 4, 0, 5], ZZ) == 0
634
+
635
+ assert dup_sign_variations([-1, 0, 2], ZZ) == 1
636
+ assert dup_sign_variations([-1, 0, 3, 0], ZZ) == 1
637
+ assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1
638
+
639
+ assert dup_sign_variations([-1, -4, -5], ZZ) == 0
640
+ assert dup_sign_variations([ 1, -4, -5], ZZ) == 1
641
+ assert dup_sign_variations([ 1, 4, -5], ZZ) == 1
642
+ assert dup_sign_variations([ 1, -4, 5], ZZ) == 2
643
+ assert dup_sign_variations([-1, 4, -5], ZZ) == 2
644
+ assert dup_sign_variations([-1, 4, 5], ZZ) == 1
645
+ assert dup_sign_variations([-1, -4, 5], ZZ) == 1
646
+ assert dup_sign_variations([ 1, 4, 5], ZZ) == 0
647
+
648
+ assert dup_sign_variations([-1, 0, -4, 0, -5], ZZ) == 0
649
+ assert dup_sign_variations([ 1, 0, -4, 0, -5], ZZ) == 1
650
+ assert dup_sign_variations([ 1, 0, 4, 0, -5], ZZ) == 1
651
+ assert dup_sign_variations([ 1, 0, -4, 0, 5], ZZ) == 2
652
+ assert dup_sign_variations([-1, 0, 4, 0, -5], ZZ) == 2
653
+ assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1
654
+ assert dup_sign_variations([-1, 0, -4, 0, 5], ZZ) == 1
655
+ assert dup_sign_variations([ 1, 0, 4, 0, 5], ZZ) == 0
656
+
657
+
658
+ def test_dup_clear_denoms():
659
+ assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), [])
660
+
661
+ assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)])
662
+ assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)])
663
+
664
+ assert dup_clear_denoms([QQ(7, 3)], QQ) == (ZZ(3), [QQ(7)])
665
+ assert dup_clear_denoms([QQ(7, 3)], QQ, ZZ) == (ZZ(3), [QQ(7)])
666
+
667
+ assert dup_clear_denoms(
668
+ [QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)])
669
+ assert dup_clear_denoms(
670
+ [QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)])
671
+
672
+ assert dup_clear_denoms([QQ(3), QQ(
673
+ 1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
674
+ assert dup_clear_denoms([QQ(1), QQ(
675
+ 1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
676
+
677
+ assert dup_clear_denoms(
678
+ [EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)])
679
+
680
+ assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)])
681
+ assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)])
682
+
683
+ F = RR.frac_field(x)
684
+ result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
685
+ assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])"
686
+
687
+ def test_dmp_clear_denoms():
688
+ assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]])
689
+
690
+ assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]])
691
+ assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]])
692
+
693
+ assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ) == (ZZ(3), [[QQ(7)]])
694
+ assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]])
695
+
696
+ assert dmp_clear_denoms(
697
+ [[QQ(3)], [QQ(1)], []], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)], [QQ(1)], []])
698
+ assert dmp_clear_denoms([[QQ(
699
+ 1)], [QQ(1, 2)], []], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)], [QQ(1)], []])
700
+
701
+ assert dmp_clear_denoms([QQ(3), QQ(
702
+ 1), QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
703
+ assert dmp_clear_denoms([QQ(1), QQ(1, 2), QQ(
704
+ 0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
705
+
706
+ assert dmp_clear_denoms([[QQ(3)], [QQ(
707
+ 1)], []], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)], [QQ(1)], []])
708
+ assert dmp_clear_denoms([[QQ(1)], [QQ(1, 2)], []], 1, QQ, ZZ,
709
+ convert=True) == (ZZ(2), [[QQ(2)], [QQ(1)], []])
710
+
711
+ assert dmp_clear_denoms(
712
+ [[EX(S(3)/2)], [EX(S(9)/4)]], 1, EX) == (EX(4), [[EX(6)], [EX(9)]])
713
+ assert dmp_clear_denoms([[EX(7)]], 1, EX) == (EX(1), [[EX(7)]])
714
+ assert dmp_clear_denoms([[EX(sin(x)/x), EX(0)]], 1, EX) == (EX(x), [[EX(sin(x)), EX(0)]])
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Symbol, S, oo
2
+ from sympy.functions.elementary.miscellaneous import sqrt
3
+ from sympy.polys import poly
4
+ from sympy.polys.dispersion import dispersion, dispersionset
5
+
6
+
7
+ def test_dispersion():
8
+ x = Symbol("x")
9
+ a = Symbol("a")
10
+
11
+ fp = poly(S.Zero, x)
12
+ assert sorted(dispersionset(fp)) == [0]
13
+
14
+ fp = poly(S(2), x)
15
+ assert sorted(dispersionset(fp)) == [0]
16
+
17
+ fp = poly(x + 1, x)
18
+ assert sorted(dispersionset(fp)) == [0]
19
+ assert dispersion(fp) == 0
20
+
21
+ fp = poly((x + 1)*(x + 2), x)
22
+ assert sorted(dispersionset(fp)) == [0, 1]
23
+ assert dispersion(fp) == 1
24
+
25
+ fp = poly(x*(x + 3), x)
26
+ assert sorted(dispersionset(fp)) == [0, 3]
27
+ assert dispersion(fp) == 3
28
+
29
+ fp = poly((x - 3)*(x + 3), x)
30
+ assert sorted(dispersionset(fp)) == [0, 6]
31
+ assert dispersion(fp) == 6
32
+
33
+ fp = poly(x**4 - 3*x**2 + 1, x)
34
+ gp = fp.shift(-3)
35
+ assert sorted(dispersionset(fp, gp)) == [2, 3, 4]
36
+ assert dispersion(fp, gp) == 4
37
+ assert sorted(dispersionset(gp, fp)) == []
38
+ assert dispersion(gp, fp) is -oo
39
+
40
+ fp = poly(x*(3*x**2+a)*(x-2536)*(x**3+a), x)
41
+ gp = fp.as_expr().subs(x, x-345).as_poly(x)
42
+ assert sorted(dispersionset(fp, gp)) == [345, 2881]
43
+ assert sorted(dispersionset(gp, fp)) == [2191]
44
+
45
+ gp = poly((x-2)**2*(x-3)**3*(x-5)**3, x)
46
+ assert sorted(dispersionset(gp)) == [0, 1, 2, 3]
47
+ assert sorted(dispersionset(gp, (gp+4)**2)) == [1, 2]
48
+
49
+ fp = poly(x*(x+2)*(x-1), x)
50
+ assert sorted(dispersionset(fp)) == [0, 1, 2, 3]
51
+
52
+ fp = poly(x**2 + sqrt(5)*x - 1, x, domain='QQ<sqrt(5)>')
53
+ gp = poly(x**2 + (2 + sqrt(5))*x + sqrt(5), x, domain='QQ<sqrt(5)>')
54
+ assert sorted(dispersionset(fp, gp)) == [2]
55
+ assert sorted(dispersionset(gp, fp)) == [1, 4]
56
+
57
+ # There are some difficulties if we compute over Z[a]
58
+ # and alpha happens to lie in Z[a] instead of simply Z.
59
+ # Hence we can not decide if alpha is indeed integral
60
+ # in general.
61
+
62
+ fp = poly(4*x**4 + (4*a + 8)*x**3 + (a**2 + 6*a + 4)*x**2 + (a**2 + 2*a)*x, x)
63
+ assert sorted(dispersionset(fp)) == [0, 1]
64
+
65
+ # For any specific value of a, the dispersion is 3*a
66
+ # but the algorithm can not find this in general.
67
+ # This is the point where the resultant based Ansatz
68
+ # is superior to the current one.
69
+ fp = poly(a**2*x**3 + (a**3 + a**2 + a + 1)*x, x)
70
+ gp = fp.as_expr().subs(x, x - 3*a).as_poly(x)
71
+ assert sorted(dispersionset(fp, gp)) == []
72
+
73
+ fpa = fp.as_expr().subs(a, 2).as_poly(x)
74
+ gpa = gp.as_expr().subs(a, 2).as_poly(x)
75
+ assert sorted(dispersionset(fpa, gpa)) == [6]
76
+
77
+ # Work with Expr instead of Poly
78
+ f = (x + 1)*(x + 2)
79
+ assert sorted(dispersionset(f)) == [0, 1]
80
+ assert dispersion(f) == 1
81
+
82
+ f = x**4 - 3*x**2 + 1
83
+ g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55
84
+ assert sorted(dispersionset(f, g)) == [2, 3, 4]
85
+ assert dispersion(f, g) == 4
86
+
87
+ # Work with Expr and specify a generator
88
+ f = (x + 1)*(x + 2)
89
+ assert sorted(dispersionset(f, None, x)) == [0, 1]
90
+ assert dispersion(f, None, x) == 1
91
+
92
+ f = x**4 - 3*x**2 + 1
93
+ g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55
94
+ assert sorted(dispersionset(f, g, x)) == [2, 3, 4]
95
+ assert dispersion(f, g, x) == 4
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for sparse distributed modules. """
2
+
3
+ from sympy.polys.distributedmodules import (
4
+ sdm_monomial_mul, sdm_monomial_deg, sdm_monomial_divides,
5
+ sdm_add, sdm_LM, sdm_LT, sdm_mul_term, sdm_zero, sdm_deg,
6
+ sdm_LC, sdm_from_dict,
7
+ sdm_spoly, sdm_ecart, sdm_nf_mora, sdm_groebner,
8
+ sdm_from_vector, sdm_to_vector, sdm_monomial_lcm
9
+ )
10
+
11
+ from sympy.polys.orderings import lex, grlex, InverseOrder
12
+ from sympy.polys.domains import QQ
13
+
14
+ from sympy.abc import x, y, z
15
+
16
+
17
+ def test_sdm_monomial_mul():
18
+ assert sdm_monomial_mul((1, 1, 0), (1, 3)) == (1, 2, 3)
19
+
20
+
21
+ def test_sdm_monomial_deg():
22
+ assert sdm_monomial_deg((5, 2, 1)) == 3
23
+
24
+
25
+ def test_sdm_monomial_lcm():
26
+ assert sdm_monomial_lcm((1, 2, 3), (1, 5, 0)) == (1, 5, 3)
27
+
28
+
29
+ def test_sdm_monomial_divides():
30
+ assert sdm_monomial_divides((1, 0, 0), (1, 0, 0)) is True
31
+ assert sdm_monomial_divides((1, 0, 0), (1, 2, 1)) is True
32
+ assert sdm_monomial_divides((5, 1, 1), (5, 2, 1)) is True
33
+
34
+ assert sdm_monomial_divides((1, 0, 0), (2, 0, 0)) is False
35
+ assert sdm_monomial_divides((1, 1, 0), (1, 0, 0)) is False
36
+ assert sdm_monomial_divides((5, 1, 2), (5, 0, 1)) is False
37
+
38
+
39
+ def test_sdm_LC():
40
+ assert sdm_LC([((1, 2, 3), QQ(5))], QQ) == QQ(5)
41
+
42
+
43
+ def test_sdm_from_dict():
44
+ dic = {(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1),
45
+ (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)}
46
+ assert sdm_from_dict(dic, grlex) == \
47
+ [((1, 2, 1, 1), QQ(1)), ((1, 1, 2, 1), QQ(1)),
48
+ ((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))]
49
+
50
+ # TODO test to_dict?
51
+
52
+
53
+ def test_sdm_add():
54
+ assert sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) == \
55
+ [((2, 0, 0), QQ(1)), ((1, 1, 1), QQ(1))]
56
+ assert sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) == []
57
+ assert sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) == \
58
+ [((1, 0, 0), QQ(3))]
59
+ assert sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) == \
60
+ [((1, 1, 0), QQ(1)), ((1, 0, 1), QQ(1))]
61
+
62
+
63
+ def test_sdm_LM():
64
+ dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)}
65
+ assert sdm_LM(sdm_from_dict(dic, lex)) == (4, 0, 1)
66
+
67
+
68
+ def test_sdm_LT():
69
+ dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)}
70
+ assert sdm_LT(sdm_from_dict(dic, lex)) == ((4, 0, 1), QQ(3))
71
+
72
+
73
+ def test_sdm_mul_term():
74
+ assert sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) == []
75
+ assert sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) == []
76
+ assert sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) == \
77
+ [((1, 1, 0), QQ(1))]
78
+ f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))]
79
+ assert sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) == \
80
+ [((2, 1, 2), QQ(8)), ((1, 2, 1), QQ(6))]
81
+
82
+
83
+ def test_sdm_zero():
84
+ assert sdm_zero() == []
85
+
86
+
87
+ def test_sdm_deg():
88
+ assert sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) == 7
89
+
90
+
91
+ def test_sdm_spoly():
92
+ f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))]
93
+ g = [((2, 3, 0), QQ(1))]
94
+ h = [((1, 2, 3), QQ(1))]
95
+ assert sdm_spoly(f, h, lex, QQ) == []
96
+ assert sdm_spoly(f, g, lex, QQ) == [((1, 2, 1), QQ(1))]
97
+
98
+
99
+ def test_sdm_ecart():
100
+ assert sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) == 0
101
+ assert sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) == 3
102
+
103
+
104
+ def test_sdm_nf_mora():
105
+ f = sdm_from_dict({(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1),
106
+ (1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)},
107
+ grlex)
108
+ f1 = sdm_from_dict({(1, 1, 1, 0): QQ(1), (1, 0, 2, 0): QQ(1),
109
+ (1, 0, 0, 0): QQ(-1)}, grlex)
110
+ f2 = sdm_from_dict({(1, 1, 1, 0): QQ(1)}, grlex)
111
+ (id0, id1, id2) = [sdm_from_dict({(i, 0, 0, 0): QQ(1)}, grlex)
112
+ for i in range(3)]
113
+
114
+ assert sdm_nf_mora(f, [f1, f2], grlex, QQ, phantom=(id0, [id1, id2])) == \
115
+ ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1)),
116
+ ((1, 1, 0, 1), QQ(1))],
117
+ [((1, 1, 0, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))])
118
+ assert sdm_nf_mora(f, [f2, f1], grlex, QQ, phantom=(id0, [id2, id1])) == \
119
+ ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))],
120
+ [((2, 1, 0, 1), QQ(-1)), ((2, 0, 1, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))])
121
+
122
+ f = sdm_from_vector([x*z, y**2 + y*z - z, y], lex, QQ, gens=[x, y, z])
123
+ f1 = sdm_from_vector([x, y, 1], lex, QQ, gens=[x, y, z])
124
+ f2 = sdm_from_vector([x*y, z, z**2], lex, QQ, gens=[x, y, z])
125
+ assert sdm_nf_mora(f, [f1, f2], lex, QQ) == \
126
+ sdm_nf_mora(f, [f2, f1], lex, QQ) == \
127
+ [((1, 0, 1, 1), QQ(1)), ((1, 0, 0, 1), QQ(-1)), ((0, 1, 1, 0), QQ(-1)),
128
+ ((0, 1, 0, 1), QQ(1))]
129
+
130
+
131
+ def test_conversion():
132
+ f = [x**2 + y**2, 2*z]
133
+ g = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))]
134
+ assert sdm_to_vector(g, [x, y, z], QQ) == f
135
+ assert sdm_from_vector(f, lex, QQ) == g
136
+ assert sdm_from_vector(
137
+ [x, 1], lex, QQ) == [((1, 0), QQ(1)), ((0, 1), QQ(1))]
138
+ assert sdm_to_vector([((1, 1, 0, 0), 1)], [x, y, z], QQ, n=3) == [0, x, 0]
139
+ assert sdm_from_vector([0, 0], lex, QQ, gens=[x, y]) == sdm_zero()
140
+
141
+
142
+ def test_nontrivial():
143
+ gens = [x, y, z]
144
+
145
+ def contains(I, f):
146
+ S = [sdm_from_vector([g], lex, QQ, gens=gens) for g in I]
147
+ G = sdm_groebner(S, sdm_nf_mora, lex, QQ)
148
+ return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens),
149
+ G, lex, QQ) == sdm_zero()
150
+
151
+ assert contains([x, y], x)
152
+ assert contains([x, y], x + y)
153
+ assert not contains([x, y], 1)
154
+ assert not contains([x, y], z)
155
+ assert contains([x**2 + y, x**2 + x], x - y)
156
+ assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
157
+ assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3)
158
+ assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4)
159
+ assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2)
160
+ assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x)
161
+ assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z)
162
+ assert contains([x, 1 + x + y, 5 - 7*y], 1)
163
+ assert contains(
164
+ [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
165
+ x**3)
166
+ assert not contains(
167
+ [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
168
+ x**2 + y**2)
169
+
170
+ # compare local order
171
+ assert not contains([x*(1 + x + y), y*(1 + z)], x)
172
+ assert not contains([x*(1 + x + y), y*(1 + z)], x + y)
173
+
174
+
175
+ def test_local():
176
+ igrlex = InverseOrder(grlex)
177
+ gens = [x, y, z]
178
+
179
+ def contains(I, f):
180
+ S = [sdm_from_vector([g], igrlex, QQ, gens=gens) for g in I]
181
+ G = sdm_groebner(S, sdm_nf_mora, igrlex, QQ)
182
+ return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens),
183
+ G, lex, QQ) == sdm_zero()
184
+ assert contains([x, y], x)
185
+ assert contains([x, y], x + y)
186
+ assert not contains([x, y], 1)
187
+ assert not contains([x, y], z)
188
+ assert contains([x**2 + y, x**2 + x], x - y)
189
+ assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
190
+ assert contains([x*(1 + x + y), y*(1 + z)], x)
191
+ assert contains([x*(1 + x + y), y*(1 + z)], x + y)
192
+
193
+
194
+ def test_uncovered_line():
195
+ gens = [x, y]
196
+ f1 = sdm_zero()
197
+ f2 = sdm_from_vector([x, 0], lex, QQ, gens=gens)
198
+ f3 = sdm_from_vector([0, y], lex, QQ, gens=gens)
199
+
200
+ assert sdm_spoly(f1, f2, lex, QQ) == sdm_zero()
201
+ assert sdm_spoly(f3, f2, lex, QQ) == sdm_zero()
202
+
203
+
204
+ def test_chain_criterion():
205
+ gens = [x]
206
+ f1 = sdm_from_vector([1, x], grlex, QQ, gens=gens)
207
+ f2 = sdm_from_vector([0, x - 2], grlex, QQ, gens=gens)
208
+ assert len(sdm_groebner([f1, f2], sdm_nf_mora, grlex, QQ)) == 2
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py ADDED
@@ -0,0 +1,712 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """
2
+
3
+ from sympy.polys.rings import ring
4
+ from sympy.polys.domains import ZZ, QQ, RR
5
+
6
+ from sympy.polys.specialpolys import (
7
+ f_polys,
8
+ dmp_fateman_poly_F_1,
9
+ dmp_fateman_poly_F_2,
10
+ dmp_fateman_poly_F_3)
11
+
12
+ f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys()
13
+
14
+ def test_dup_gcdex():
15
+ R, x = ring("x", QQ)
16
+
17
+ f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15
18
+ g = x**3 + x**2 - 4*x - 4
19
+
20
+ s = -QQ(1,5)*x + QQ(3,5)
21
+ t = QQ(1,5)*x**2 - QQ(6,5)*x + 2
22
+ h = x + 1
23
+
24
+ assert R.dup_half_gcdex(f, g) == (s, h)
25
+ assert R.dup_gcdex(f, g) == (s, t, h)
26
+
27
+ f = x**4 + 4*x**3 - x + 1
28
+ g = x**3 - x + 1
29
+
30
+ s, t, h = R.dup_gcdex(f, g)
31
+ S, T, H = R.dup_gcdex(g, f)
32
+
33
+ assert R.dup_add(R.dup_mul(s, f),
34
+ R.dup_mul(t, g)) == h
35
+ assert R.dup_add(R.dup_mul(S, g),
36
+ R.dup_mul(T, f)) == H
37
+
38
+ f = 2*x
39
+ g = x**2 - 16
40
+
41
+ s = QQ(1,32)*x
42
+ t = -QQ(1,16)
43
+ h = 1
44
+
45
+ assert R.dup_half_gcdex(f, g) == (s, h)
46
+ assert R.dup_gcdex(f, g) == (s, t, h)
47
+
48
+
49
+ def test_dup_invert():
50
+ R, x = ring("x", QQ)
51
+ assert R.dup_invert(2*x, x**2 - 16) == QQ(1,32)*x
52
+
53
+
54
+ def test_dup_euclidean_prs():
55
+ R, x = ring("x", QQ)
56
+
57
+ f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
58
+ g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
59
+
60
+ assert R.dup_euclidean_prs(f, g) == [
61
+ f,
62
+ g,
63
+ -QQ(5,9)*x**4 + QQ(1,9)*x**2 - QQ(1,3),
64
+ -QQ(117,25)*x**2 - 9*x + QQ(441,25),
65
+ QQ(233150,19773)*x - QQ(102500,6591),
66
+ -QQ(1288744821,543589225)]
67
+
68
+
69
+ def test_dup_primitive_prs():
70
+ R, x = ring("x", ZZ)
71
+
72
+ f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
73
+ g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
74
+
75
+ assert R.dup_primitive_prs(f, g) == [
76
+ f,
77
+ g,
78
+ -5*x**4 + x**2 - 3,
79
+ 13*x**2 + 25*x - 49,
80
+ 4663*x - 6150,
81
+ 1]
82
+
83
+
84
+ def test_dup_subresultants():
85
+ R, x = ring("x", ZZ)
86
+
87
+ assert R.dup_resultant(0, 0) == 0
88
+
89
+ assert R.dup_resultant(1, 0) == 0
90
+ assert R.dup_resultant(0, 1) == 0
91
+
92
+ f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
93
+ g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
94
+
95
+ a = 15*x**4 - 3*x**2 + 9
96
+ b = 65*x**2 + 125*x - 245
97
+ c = 9326*x - 12300
98
+ d = 260708
99
+
100
+ assert R.dup_subresultants(f, g) == [f, g, a, b, c, d]
101
+ assert R.dup_resultant(f, g) == R.dup_LC(d)
102
+
103
+ f = x**2 - 2*x + 1
104
+ g = x**2 - 1
105
+
106
+ a = 2*x - 2
107
+
108
+ assert R.dup_subresultants(f, g) == [f, g, a]
109
+ assert R.dup_resultant(f, g) == 0
110
+
111
+ f = x**2 + 1
112
+ g = x**2 - 1
113
+
114
+ a = -2
115
+
116
+ assert R.dup_subresultants(f, g) == [f, g, a]
117
+ assert R.dup_resultant(f, g) == 4
118
+
119
+ f = x**2 - 1
120
+ g = x**3 - x**2 + 2
121
+
122
+ assert R.dup_resultant(f, g) == 0
123
+
124
+ f = 3*x**3 - x
125
+ g = 5*x**2 + 1
126
+
127
+ assert R.dup_resultant(f, g) == 64
128
+
129
+ f = x**2 - 2*x + 7
130
+ g = x**3 - x + 5
131
+
132
+ assert R.dup_resultant(f, g) == 265
133
+
134
+ f = x**3 - 6*x**2 + 11*x - 6
135
+ g = x**3 - 15*x**2 + 74*x - 120
136
+
137
+ assert R.dup_resultant(f, g) == -8640
138
+
139
+ f = x**3 - 6*x**2 + 11*x - 6
140
+ g = x**3 - 10*x**2 + 29*x - 20
141
+
142
+ assert R.dup_resultant(f, g) == 0
143
+
144
+ f = x**3 - 1
145
+ g = x**3 + 2*x**2 + 2*x - 1
146
+
147
+ assert R.dup_resultant(f, g) == 16
148
+
149
+ f = x**8 - 2
150
+ g = x - 1
151
+
152
+ assert R.dup_resultant(f, g) == -1
153
+
154
+
155
+ def test_dmp_subresultants():
156
+ R, x, y = ring("x,y", ZZ)
157
+
158
+ assert R.dmp_resultant(0, 0) == 0
159
+ assert R.dmp_prs_resultant(0, 0)[0] == 0
160
+ assert R.dmp_zz_collins_resultant(0, 0) == 0
161
+ assert R.dmp_qq_collins_resultant(0, 0) == 0
162
+
163
+ assert R.dmp_resultant(1, 0) == 0
164
+ assert R.dmp_resultant(1, 0) == 0
165
+ assert R.dmp_resultant(1, 0) == 0
166
+
167
+ assert R.dmp_resultant(0, 1) == 0
168
+ assert R.dmp_prs_resultant(0, 1)[0] == 0
169
+ assert R.dmp_zz_collins_resultant(0, 1) == 0
170
+ assert R.dmp_qq_collins_resultant(0, 1) == 0
171
+
172
+ f = 3*x**2*y - y**3 - 4
173
+ g = x**2 + x*y**3 - 9
174
+
175
+ a = 3*x*y**4 + y**3 - 27*y + 4
176
+ b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16
177
+
178
+ r = R.dmp_LC(b)
179
+
180
+ assert R.dmp_subresultants(f, g) == [f, g, a, b]
181
+
182
+ assert R.dmp_resultant(f, g) == r
183
+ assert R.dmp_prs_resultant(f, g)[0] == r
184
+ assert R.dmp_zz_collins_resultant(f, g) == r
185
+ assert R.dmp_qq_collins_resultant(f, g) == r
186
+
187
+ f = -x**3 + 5
188
+ g = 3*x**2*y + x**2
189
+
190
+ a = 45*y**2 + 30*y + 5
191
+ b = 675*y**3 + 675*y**2 + 225*y + 25
192
+
193
+ r = R.dmp_LC(b)
194
+
195
+ assert R.dmp_subresultants(f, g) == [f, g, a]
196
+ assert R.dmp_resultant(f, g) == r
197
+ assert R.dmp_prs_resultant(f, g)[0] == r
198
+ assert R.dmp_zz_collins_resultant(f, g) == r
199
+ assert R.dmp_qq_collins_resultant(f, g) == r
200
+
201
+ R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
202
+
203
+ f = 6*x**2 - 3*x*y - 2*x*z + y*z
204
+ g = x**2 - x*u - x*v + u*v
205
+
206
+ r = y**2*z**2 - 3*y**2*z*u - 3*y**2*z*v + 9*y**2*u*v - 2*y*z**2*u \
207
+ - 2*y*z**2*v + 6*y*z*u**2 + 12*y*z*u*v + 6*y*z*v**2 - 18*y*u**2*v \
208
+ - 18*y*u*v**2 + 4*z**2*u*v - 12*z*u**2*v - 12*z*u*v**2 + 36*u**2*v**2
209
+
210
+ assert R.dmp_zz_collins_resultant(f, g) == r.drop(x)
211
+
212
+ R, x, y, z, u, v = ring("x,y,z,u,v", QQ)
213
+
214
+ f = x**2 - QQ(1,2)*x*y - QQ(1,3)*x*z + QQ(1,6)*y*z
215
+ g = x**2 - x*u - x*v + u*v
216
+
217
+ r = QQ(1,36)*y**2*z**2 - QQ(1,12)*y**2*z*u - QQ(1,12)*y**2*z*v + QQ(1,4)*y**2*u*v \
218
+ - QQ(1,18)*y*z**2*u - QQ(1,18)*y*z**2*v + QQ(1,6)*y*z*u**2 + QQ(1,3)*y*z*u*v \
219
+ + QQ(1,6)*y*z*v**2 - QQ(1,2)*y*u**2*v - QQ(1,2)*y*u*v**2 + QQ(1,9)*z**2*u*v \
220
+ - QQ(1,3)*z*u**2*v - QQ(1,3)*z*u*v**2 + u**2*v**2
221
+
222
+ assert R.dmp_qq_collins_resultant(f, g) == r.drop(x)
223
+
224
+ Rt, t = ring("t", ZZ)
225
+ Rx, x = ring("x", Rt)
226
+
227
+ f = x**6 - 5*x**4 + 5*x**2 + 4
228
+ g = -6*t*x**5 + x**4 + 20*t*x**3 - 3*x**2 - 10*t*x + 6
229
+
230
+ assert Rx.dup_resultant(f, g) == 2930944*t**6 + 2198208*t**4 + 549552*t**2 + 45796
231
+
232
+
233
+ def test_dup_discriminant():
234
+ R, x = ring("x", ZZ)
235
+
236
+ assert R.dup_discriminant(0) == 0
237
+ assert R.dup_discriminant(x) == 1
238
+
239
+ assert R.dup_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664
240
+ assert R.dup_discriminant(5*x**5 + x**3 + 2) == 31252160
241
+ assert R.dup_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0
242
+ assert R.dup_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112
243
+
244
+
245
+ def test_dmp_discriminant():
246
+ R, x = ring("x", ZZ)
247
+
248
+ assert R.dmp_discriminant(0) == 0
249
+
250
+ R, x, y = ring("x,y", ZZ)
251
+
252
+ assert R.dmp_discriminant(0) == 0
253
+ assert R.dmp_discriminant(y) == 0
254
+
255
+ assert R.dmp_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664
256
+ assert R.dmp_discriminant(5*x**5 + x**3 + 2) == 31252160
257
+ assert R.dmp_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0
258
+ assert R.dmp_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112
259
+
260
+ assert R.dmp_discriminant(x**2*y + 2*y) == (-8*y**2).drop(x)
261
+ assert R.dmp_discriminant(x*y**2 + 2*x) == 1
262
+
263
+ R, x, y, z = ring("x,y,z", ZZ)
264
+ assert R.dmp_discriminant(x*y + z) == 1
265
+
266
+ R, x, y, z, u = ring("x,y,z,u", ZZ)
267
+ assert R.dmp_discriminant(x**2*y + x*z + u) == (-4*y*u + z**2).drop(x)
268
+
269
+ R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
270
+ assert R.dmp_discriminant(x**3*y + x**2*z + x*u + v) == \
271
+ (-27*y**2*v**2 + 18*y*z*u*v - 4*y*u**3 - 4*z**3*v + z**2*u**2).drop(x)
272
+
273
+
274
+ def test_dup_gcd():
275
+ R, x = ring("x", ZZ)
276
+
277
+ f, g = 0, 0
278
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (0, 0, 0)
279
+
280
+ f, g = 2, 0
281
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 0)
282
+
283
+ f, g = -2, 0
284
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 0)
285
+
286
+ f, g = 0, -2
287
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 0, -1)
288
+
289
+ f, g = 0, 2*x + 4
290
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 0, 1)
291
+
292
+ f, g = 2*x + 4, 0
293
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 1, 0)
294
+
295
+ f, g = 2, 2
296
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 1)
297
+
298
+ f, g = -2, 2
299
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 1)
300
+
301
+ f, g = 2, -2
302
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, -1)
303
+
304
+ f, g = -2, -2
305
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, -1)
306
+
307
+ f, g = x**2 + 2*x + 1, 1
308
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1)
309
+
310
+ f, g = x**2 + 2*x + 1, 2
311
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2)
312
+
313
+ f, g = 2*x**2 + 4*x + 2, 2
314
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1)
315
+
316
+ f, g = 2, 2*x**2 + 4*x + 2
317
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1)
318
+
319
+ f, g = 2*x**2 + 4*x + 2, x + 1
320
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1)
321
+
322
+ f, g = x + 1, 2*x**2 + 4*x + 2
323
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2)
324
+
325
+ f, g = x - 31, x
326
+ assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, f, g)
327
+
328
+ f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8
329
+ g = x**3 + 6*x**2 + 11*x + 6
330
+
331
+ h = x**2 + 3*x + 2
332
+
333
+ cff = x**2 + 5*x + 4
334
+ cfg = x + 3
335
+
336
+ assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
337
+ assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
338
+
339
+ f = x**4 - 4
340
+ g = x**4 + 4*x**2 + 4
341
+
342
+ h = x**2 + 2
343
+
344
+ cff = x**2 - 2
345
+ cfg = x**2 + 2
346
+
347
+ assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
348
+ assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
349
+
350
+ f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
351
+ g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
352
+
353
+ h = 1
354
+
355
+ cff = f
356
+ cfg = g
357
+
358
+ assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
359
+ assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
360
+
361
+ R, x = ring("x", QQ)
362
+
363
+ f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
364
+ g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
365
+
366
+ h = 1
367
+
368
+ cff = f
369
+ cfg = g
370
+
371
+ assert R.dup_qq_heu_gcd(f, g) == (h, cff, cfg)
372
+ assert R.dup_ff_prs_gcd(f, g) == (h, cff, cfg)
373
+
374
+ R, x = ring("x", ZZ)
375
+
376
+ f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \
377
+ + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \
378
+ + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \
379
+ + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \
380
+ - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \
381
+ + 289127344604779611146960547954288113529690984687482920704*x**14 \
382
+ + 19007977035740498977629742919480623972236450681*x**7 \
383
+ + 311973482284542371301330321821976049
384
+
385
+ g = 365431878023781158602430064717380211405897160759702125019136*x**21 \
386
+ + 197599133478719444145775798221171663643171734081650688*x**14 \
387
+ - 9504116979659010018253915765478924103928886144*x**7 \
388
+ - 311973482284542371301330321821976049
389
+
390
+ assert R.dup_zz_heu_gcd(f, R.dup_diff(f, 1))[0] == g
391
+ assert R.dup_rr_prs_gcd(f, R.dup_diff(f, 1))[0] == g
392
+
393
+ R, x = ring("x", QQ)
394
+
395
+ f = QQ(1,2)*x**2 + x + QQ(1,2)
396
+ g = QQ(1,2)*x + QQ(1,2)
397
+
398
+ h = x + 1
399
+
400
+ assert R.dup_qq_heu_gcd(f, g) == (h, g, QQ(1,2))
401
+ assert R.dup_ff_prs_gcd(f, g) == (h, g, QQ(1,2))
402
+
403
+ R, x = ring("x", ZZ)
404
+
405
+ f = 1317378933230047068160*x + 2945748836994210856960
406
+ g = 120352542776360960*x + 269116466014453760
407
+
408
+ h = 120352542776360960*x + 269116466014453760
409
+ cff = 10946
410
+ cfg = 1
411
+
412
+ assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
413
+
414
+
415
+ def test_dmp_gcd():
416
+ R, x, y = ring("x,y", ZZ)
417
+
418
+ f, g = 0, 0
419
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (0, 0, 0)
420
+
421
+ f, g = 2, 0
422
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 0)
423
+
424
+ f, g = -2, 0
425
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 0)
426
+
427
+ f, g = 0, -2
428
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 0, -1)
429
+
430
+ f, g = 0, 2*x + 4
431
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 0, 1)
432
+
433
+ f, g = 2*x + 4, 0
434
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 1, 0)
435
+
436
+ f, g = 2, 2
437
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 1)
438
+
439
+ f, g = -2, 2
440
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 1)
441
+
442
+ f, g = 2, -2
443
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, -1)
444
+
445
+ f, g = -2, -2
446
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, -1)
447
+
448
+ f, g = x**2 + 2*x + 1, 1
449
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1)
450
+
451
+ f, g = x**2 + 2*x + 1, 2
452
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2)
453
+
454
+ f, g = 2*x**2 + 4*x + 2, 2
455
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1)
456
+
457
+ f, g = 2, 2*x**2 + 4*x + 2
458
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1)
459
+
460
+ f, g = 2*x**2 + 4*x + 2, x + 1
461
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1)
462
+
463
+ f, g = x + 1, 2*x**2 + 4*x + 2
464
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2)
465
+
466
+ R, x, y, z, u = ring("x,y,z,u", ZZ)
467
+
468
+ f, g = u**2 + 2*u + 1, 2*u + 2
469
+ assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (u + 1, u + 1, 2)
470
+
471
+ f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1
472
+ h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1
473
+
474
+ assert R.dmp_zz_heu_gcd(f, g) == (h, cff, cfg)
475
+ assert R.dmp_rr_prs_gcd(f, g) == (h, cff, cfg)
476
+
477
+ assert R.dmp_zz_heu_gcd(g, f) == (h, cfg, cff)
478
+ assert R.dmp_rr_prs_gcd(g, f) == (h, cfg, cff)
479
+
480
+ R, x, y, z = ring("x,y,z", ZZ)
481
+
482
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(2, ZZ))
483
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
484
+
485
+ assert H == h and R.dmp_mul(H, cff) == f \
486
+ and R.dmp_mul(H, cfg) == g
487
+
488
+ H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
489
+
490
+ assert H == h and R.dmp_mul(H, cff) == f \
491
+ and R.dmp_mul(H, cfg) == g
492
+
493
+ R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
494
+
495
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(4, ZZ))
496
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
497
+
498
+ assert H == h and R.dmp_mul(H, cff) == f \
499
+ and R.dmp_mul(H, cfg) == g
500
+
501
+ R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ)
502
+
503
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(6, ZZ))
504
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
505
+
506
+ assert H == h and R.dmp_mul(H, cff) == f \
507
+ and R.dmp_mul(H, cfg) == g
508
+
509
+ R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ)
510
+
511
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(8, ZZ))
512
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
513
+
514
+ assert H == h and R.dmp_mul(H, cff) == f \
515
+ and R.dmp_mul(H, cfg) == g
516
+
517
+ R, x, y, z = ring("x,y,z", ZZ)
518
+
519
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_2(2, ZZ))
520
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
521
+
522
+ assert H == h and R.dmp_mul(H, cff) == f \
523
+ and R.dmp_mul(H, cfg) == g
524
+
525
+ H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
526
+
527
+ assert H == h and R.dmp_mul(H, cff) == f \
528
+ and R.dmp_mul(H, cfg) == g
529
+
530
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(2, ZZ))
531
+ H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
532
+
533
+ assert H == h and R.dmp_mul(H, cff) == f \
534
+ and R.dmp_mul(H, cfg) == g
535
+
536
+ H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
537
+
538
+ assert H == h and R.dmp_mul(H, cff) == f \
539
+ and R.dmp_mul(H, cfg) == g
540
+
541
+ R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
542
+
543
+ f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(4, ZZ))
544
+ H, cff, cfg = R.dmp_inner_gcd(f, g)
545
+
546
+ assert H == h and R.dmp_mul(H, cff) == f \
547
+ and R.dmp_mul(H, cfg) == g
548
+
549
+ R, x, y = ring("x,y", QQ)
550
+
551
+ f = QQ(1,2)*x**2 + x + QQ(1,2)
552
+ g = QQ(1,2)*x + QQ(1,2)
553
+
554
+ h = x + 1
555
+
556
+ assert R.dmp_qq_heu_gcd(f, g) == (h, g, QQ(1,2))
557
+ assert R.dmp_ff_prs_gcd(f, g) == (h, g, QQ(1,2))
558
+
559
+ R, x, y = ring("x,y", RR)
560
+
561
+ f = 2.1*x*y**2 - 2.2*x*y + 2.1*x
562
+ g = 1.0*x**3
563
+
564
+ assert R.dmp_ff_prs_gcd(f, g) == \
565
+ (1.0*x, 2.1*y**2 - 2.2*y + 2.1, 1.0*x**2)
566
+
567
+
568
+ def test_dup_lcm():
569
+ R, x = ring("x", ZZ)
570
+
571
+ assert R.dup_lcm(2, 6) == 6
572
+
573
+ assert R.dup_lcm(2*x**3, 6*x) == 6*x**3
574
+ assert R.dup_lcm(2*x**3, 3*x) == 6*x**3
575
+
576
+ assert R.dup_lcm(x**2 + x, x) == x**2 + x
577
+ assert R.dup_lcm(x**2 + x, 2*x) == 2*x**2 + 2*x
578
+ assert R.dup_lcm(x**2 + 2*x, x) == x**2 + 2*x
579
+ assert R.dup_lcm(2*x**2 + x, x) == 2*x**2 + x
580
+ assert R.dup_lcm(2*x**2 + x, 2*x) == 4*x**2 + 2*x
581
+
582
+
583
+ def test_dmp_lcm():
584
+ R, x, y = ring("x,y", ZZ)
585
+
586
+ assert R.dmp_lcm(2, 6) == 6
587
+ assert R.dmp_lcm(x, y) == x*y
588
+
589
+ assert R.dmp_lcm(2*x**3, 6*x*y**2) == 6*x**3*y**2
590
+ assert R.dmp_lcm(2*x**3, 3*x*y**2) == 6*x**3*y**2
591
+
592
+ assert R.dmp_lcm(x**2*y, x*y**2) == x**2*y**2
593
+
594
+ f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2
595
+ g = y**5 - 2*y**3 + y
596
+ h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2
597
+
598
+ assert R.dmp_lcm(f, g) == h
599
+
600
+ f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3
601
+ g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4
602
+ h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5
603
+
604
+ assert R.dmp_lcm(f, g) == h
605
+
606
+
607
+ def test_dmp_content():
608
+ R, x,y = ring("x,y", ZZ)
609
+
610
+ assert R.dmp_content(-2) == 2
611
+
612
+ f, g, F = 3*y**2 + 2*y + 1, 1, 0
613
+
614
+ for i in range(0, 5):
615
+ g *= f
616
+ F += x**i*g
617
+
618
+ assert R.dmp_content(F) == f.drop(x)
619
+
620
+ R, x,y,z = ring("x,y,z", ZZ)
621
+
622
+ assert R.dmp_content(f_4) == 1
623
+ assert R.dmp_content(f_5) == 1
624
+
625
+ R, x,y,z,t = ring("x,y,z,t", ZZ)
626
+ assert R.dmp_content(f_6) == 1
627
+
628
+
629
+ def test_dmp_primitive():
630
+ R, x,y = ring("x,y", ZZ)
631
+
632
+ assert R.dmp_primitive(0) == (0, 0)
633
+ assert R.dmp_primitive(1) == (1, 1)
634
+
635
+ f, g, F = 3*y**2 + 2*y + 1, 1, 0
636
+
637
+ for i in range(0, 5):
638
+ g *= f
639
+ F += x**i*g
640
+
641
+ assert R.dmp_primitive(F) == (f.drop(x), F / f)
642
+
643
+ R, x,y,z = ring("x,y,z", ZZ)
644
+
645
+ cont, f = R.dmp_primitive(f_4)
646
+ assert cont == 1 and f == f_4
647
+ cont, f = R.dmp_primitive(f_5)
648
+ assert cont == 1 and f == f_5
649
+
650
+ R, x,y,z,t = ring("x,y,z,t", ZZ)
651
+
652
+ cont, f = R.dmp_primitive(f_6)
653
+ assert cont == 1 and f == f_6
654
+
655
+
656
+ def test_dup_cancel():
657
+ R, x = ring("x", ZZ)
658
+
659
+ f = 2*x**2 - 2
660
+ g = x**2 - 2*x + 1
661
+
662
+ p = 2*x + 2
663
+ q = x - 1
664
+
665
+ assert R.dup_cancel(f, g) == (p, q)
666
+ assert R.dup_cancel(f, g, include=False) == (1, 1, p, q)
667
+
668
+ f = -x - 2
669
+ g = 3*x - 4
670
+
671
+ F = x + 2
672
+ G = -3*x + 4
673
+
674
+ assert R.dup_cancel(f, g) == (f, g)
675
+ assert R.dup_cancel(F, G) == (f, g)
676
+
677
+ assert R.dup_cancel(0, 0) == (0, 0)
678
+ assert R.dup_cancel(0, 0, include=False) == (1, 1, 0, 0)
679
+
680
+ assert R.dup_cancel(x, 0) == (1, 0)
681
+ assert R.dup_cancel(x, 0, include=False) == (1, 1, 1, 0)
682
+
683
+ assert R.dup_cancel(0, x) == (0, 1)
684
+ assert R.dup_cancel(0, x, include=False) == (1, 1, 0, 1)
685
+
686
+ f = 0
687
+ g = x
688
+ one = 1
689
+
690
+ assert R.dup_cancel(f, g, include=True) == (f, one)
691
+
692
+
693
+ def test_dmp_cancel():
694
+ R, x, y = ring("x,y", ZZ)
695
+
696
+ f = 2*x**2 - 2
697
+ g = x**2 - 2*x + 1
698
+
699
+ p = 2*x + 2
700
+ q = x - 1
701
+
702
+ assert R.dmp_cancel(f, g) == (p, q)
703
+ assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q)
704
+
705
+ assert R.dmp_cancel(0, 0) == (0, 0)
706
+ assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0)
707
+
708
+ assert R.dmp_cancel(y, 0) == (1, 0)
709
+ assert R.dmp_cancel(y, 0, include=False) == (1, 1, 1, 0)
710
+
711
+ assert R.dmp_cancel(0, y) == (0, 1)
712
+ assert R.dmp_cancel(0, y, include=False) == (1, 1, 0, 1)