@@ -2090,6 +2090,162 @@ def irreducible_element(self, n, algorithm=None):
2090
2090
else :
2091
2091
raise ValueError ("no such algorithm for finding an irreducible polynomial: %s" % algorithm )
2092
2092
2093
+ def _roth_ruckenstein (self , Q , degree_bound , precision ):
2094
+ r"""
2095
+ Returns all polynomials which are a solution to the, possibly modular,
2096
+ root-finding problem.
2097
+
2098
+ This is the core of Roth-Ruckenstein's algorithm where all conversions,
2099
+ checks and parent-extraction have been done.
2100
+
2101
+ INPUT:
2102
+
2103
+ - ``Q`` -- a nonzero polynomial over ``F[x][y]``, represented as an ``F[x]``
2104
+ list. The polynomial ``Q`` should be first truncated to ``precision``
2105
+
2106
+ - ``degree_bound`` -- a bound on the degree of the roots of ``Q`` that
2107
+ the algorithm computes
2108
+
2109
+ - ``precision`` -- a non-negative integer or `None`. If given, it is the
2110
+ sought precision for modular roots of `Q`. Otherwise, the algorithm
2111
+ computes unconditional roots.
2112
+
2113
+ OUTPUT:
2114
+
2115
+ - a list containing all `F[x]` roots of `Q(y)`, possibly modular. If
2116
+ ``precision`` is given, the algorithm returns a list of pairs `(f, h)`,
2117
+ where `f` is a polynomial and `h` is a non-negative integer such that
2118
+ `Q(f + x^{h}*g) \equiv 0 \mod x^{d}` for any `g \in F[x]`, where `d` is
2119
+ ``precision``.
2120
+
2121
+ EXAMPLES::
2122
+
2123
+ sage: F = GF(17)
2124
+ sage: Px.<x> = F[]
2125
+ sage: Pxy.<y> = Px[]
2126
+ sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16))
2127
+ sage: Q = p.coefficients()
2128
+ sage: Px._roth_ruckenstein(Q, 3, None)
2129
+ [x^3 + 4*x + 16, x^2 + x + 1]
2130
+ sage: Px._roth_ruckenstein(Q, 2, None)
2131
+ [x^2 + x + 1]
2132
+ sage: Px._roth_ruckenstein(Q, 1, 2)
2133
+ [(4*x + 16, 2), (2*x + 13, 2), (15*x + 4, 2), (x + 1, 2)]
2134
+ """
2135
+ def roth_rec (Q , lam , k ):
2136
+ r"""
2137
+ Recursive core method for Roth-Ruckenstein algorithm.
2138
+
2139
+ INPUT:
2140
+
2141
+ - ``Q`` -- the current value of the polynomial
2142
+ - ``lam`` -- is the power of x whose coefficient is being computed
2143
+ - ``k`` -- the remaining precision to handle (if ``precision`` is given)
2144
+ """
2145
+ def pascal_triangle (n , gamma ):
2146
+ """
2147
+ Compute a modified Pascal triangle, where the entry ``(i,j)``
2148
+ equals ``binomial(j,i) * gamma^(j-i)``.
2149
+ """
2150
+ res = [[F .one ()]]
2151
+ for _ in range (1 , n + 1 ):
2152
+ res .append ([gamma * res [- 1 ][0 ]])
2153
+ for i in range (1 , n + 1 ):
2154
+ for j in range (1 , i ):
2155
+ res [i ].append (res [i - 1 ][j - 1 ] + gamma * res [i - 1 ][j ])
2156
+ res [i ].append (F .one ())
2157
+ return res
2158
+
2159
+ if precision and k <= 0 :
2160
+ solutions .append ((self (g [:lam ]), lam ))
2161
+ return
2162
+ val = min (c .valuation () for c in Q )
2163
+ T = [c .shift (- val ) for c in Q ]
2164
+ if precision :
2165
+ k = k - val
2166
+ Ty = self ([ p [0 ] for p in T ])
2167
+ if Ty .is_zero () or (precision and k <= 0 ):
2168
+ if precision :
2169
+ solutions .append ((self (g [:lam ]), lam ))
2170
+ else :
2171
+ solutions .append (self (g [:lam ]))
2172
+ return
2173
+ roots = Ty .roots (multiplicities = False )
2174
+ for gamma in roots :
2175
+ g [lam ] = gamma
2176
+ if lam < degree_bound :
2177
+ # Construct T(y=x*y + gamma)
2178
+ ell = len (T )- 1
2179
+ yc = pascal_triangle (ell + 1 , gamma )
2180
+ Tg = []
2181
+ for t in range (ell + 1 ):
2182
+ Tg .append (sum (yc [s ][t ] * T [s ] for s in range (t , ell + 1 )).shift (t ))
2183
+ roth_rec (Tg , lam + 1 , k )
2184
+ else :
2185
+ if precision :
2186
+ solutions .append ((self (g [:lam + 1 ]), lam + 1 ))
2187
+ elif sum ( Q [t ] * gamma ** t for t in range (len (Q )) ).is_zero ():
2188
+ solutions .append (self (g [:lam + 1 ]))
2189
+ return
2190
+
2191
+ x = self .gen ()
2192
+ F = self .base_ring ()
2193
+ solutions = []
2194
+ g = [F .zero ()] * (degree_bound + 1 )
2195
+
2196
+ roth_rec (Q , 0 , precision )
2197
+ return solutions
2198
+
2199
+ def _roots_univariate_polynomial (self , p , ring = None , multiplicities = False , algorithm = None , degree_bound = None ):
2200
+ """
2201
+ Return the list of roots of ``p``.
2202
+
2203
+ INPUT:
2204
+
2205
+ - ``p`` -- the polynomial whose roots are computed
2206
+ - ``ring`` -- the ring to find roots (default is the base ring of ``p``)
2207
+ - ``multiplicities`` -- bool (default: True): currently, only roots
2208
+ without multiplicities are computed.
2209
+ - ``algorithm`` -- the algorithm to use; currently, the only supported
2210
+ algorithm is ``"Roth-Ruckenstein"``
2211
+ - ``degree_bound``-- if not ``None``, return only roots of degree at
2212
+ most ``degree_bound``
2213
+
2214
+ EXAMPLES::
2215
+
2216
+ sage: R.<x> = GF(13)[]
2217
+ sage: S.<y> = R[]
2218
+ sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1
2219
+ sage: p.roots(multiplicities=False)
2220
+ [x^2 + 11*x + 1, x + 1]
2221
+ sage: p.roots(multiplicities=False, degree_bound=1)
2222
+ [x + 1]
2223
+ """
2224
+ if multiplicities :
2225
+ raise NotImplementedError ("Use multiplicities=False" )
2226
+
2227
+ Q = p .list ()
2228
+
2229
+ if degree_bound is None :
2230
+ degree_bound = p .degree () # TODO; replace this by next lines once fixed
2231
+ #l = len(Q) - 1
2232
+ #dl = Q[l].degree()
2233
+ #degree_bound = min((Q[i].degree() - dl)//(l - i) for i in range(l) if Q[i])
2234
+
2235
+ if algorithm is None :
2236
+ algorithm = "Roth-Ruckenstein"
2237
+
2238
+ if algorithm == "Roth-Ruckenstein" :
2239
+ Q = p .list ()
2240
+ return self ._roth_ruckenstein (Q , degree_bound , None )
2241
+
2242
+ elif algorithm == "Alekhnovich" :
2243
+ raise NotImplementedError ("Alekhnovich algorithm is not implemented yet" )
2244
+
2245
+ else :
2246
+ raise ValueError ("unknown algorithm '{}'" .format (algorithm ))
2247
+
2248
+
2093
2249
2094
2250
class PolynomialRing_cdvr (PolynomialRing_integral_domain ):
2095
2251
r"""
@@ -2520,7 +2676,7 @@ def irreducible_element(self, n, algorithm=None):
2520
2676
x^33 + x^10 + 1
2521
2677
2522
2678
In degree 1::
2523
-
2679
+
2524
2680
sage: GF(97)['x'].irreducible_element(1)
2525
2681
x + 96
2526
2682
sage: GF(97)['x'].irreducible_element(1, algorithm="conway")
0 commit comments