def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ self._kwargs = {} if repr not in ['int', 'log', 'poly']: raise ValueError, "Unknown representation %s" % repr q = Integer(q) if q < 2: raise ValueError, "q must be a prime power" F = q.factor() if len(F) > 1: raise ValueError, "q must be a prime power" p = F[0][0] k = F[0][1] if q >= 1 << 16: raise ValueError, "q must be < 2^16" import constructor FiniteField.__init__(self, constructor.FiniteField(p), name, normalize=False) self._kwargs['repr'] = repr self._kwargs['cache'] = cache self._is_conway = False if modulus is None or modulus == 'conway': if k == 1: modulus = 'random' # this will use the gfq_factory_pk function. elif ConwayPolynomials().has_polynomial(p, k): from sage.rings.finite_rings.constructor import conway_polynomial modulus = conway_polynomial(p, k) self._is_conway = True elif modulus is None: modulus = 'random' else: raise ValueError, "Conway polynomial not found" from sage.rings.polynomial.all import is_Polynomial if is_Polynomial(modulus): modulus = modulus.list() self._cache = Cache_givaro(self, p, k, modulus, repr, cache)
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ self._kwargs = {} if repr not in ["int", "log", "poly"]: raise ValueError, "Unknown representation %s" % repr q = Integer(q) if q < 2: raise ValueError, "q must be a prime power" F = q.factor() if len(F) > 1: raise ValueError, "q must be a prime power" p = F[0][0] k = F[0][1] if q >= 1 << 16: raise ValueError, "q must be < 2^16" import constructor FiniteField.__init__(self, constructor.FiniteField(p), name, normalize=False) self._kwargs["repr"] = repr self._kwargs["cache"] = cache self._is_conway = False if modulus is None or modulus == "conway": if k == 1: modulus = "random" # this will use the gfq_factory_pk function. elif ConwayPolynomials().has_polynomial(p, k): from sage.rings.finite_rings.constructor import conway_polynomial modulus = conway_polynomial(p, k) self._is_conway = True elif modulus is None: modulus = "random" else: raise ValueError, "Conway polynomial not found" from sage.rings.polynomial.all import is_Polynomial if is_Polynomial(modulus): modulus = modulus.list() self._cache = Cache_givaro(self, p, k, modulus, repr, cache)
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ self._kwargs = {} if repr not in ["int", "log", "poly"]: raise ValueError("Unknown representation %s" % repr) q = Integer(q) if q < 2: raise ValueError("q must be a prime power") F = q.factor() if len(F) > 1: raise ValueError("q must be a prime power") p = F[0][0] k = F[0][1] if q >= 1 << 16: raise ValueError("q must be < 2^16") from .finite_field_constructor import GF FiniteField.__init__(self, GF(p), name, normalize=False) self._kwargs["repr"] = repr self._kwargs["cache"] = cache from sage.rings.polynomial.polynomial_element import is_Polynomial if not is_Polynomial(modulus): from sage.misc.superseded import deprecation deprecation( 16930, "constructing a FiniteField_givaro without giving a polynomial as modulus is deprecated, use the more general FiniteField constructor instead", ) R = GF(p)["x"] if modulus is None or isinstance(modulus, str): modulus = R.irreducible_element(k, algorithm=modulus) else: modulus = R(modulus) self._cache = Cache_givaro(self, p, k, modulus, repr, cache) self._modulus = modulus
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ self._kwargs = {} if repr not in ['int', 'log', 'poly']: raise ValueError("Unknown representation %s"%repr) q = Integer(q) if q < 2: raise ValueError("q must be a prime power") F = q.factor() if len(F) > 1: raise ValueError("q must be a prime power") p = F[0][0] k = F[0][1] if q >= 1<<16: raise ValueError("q must be < 2^16") import constructor FiniteField.__init__(self, constructor.FiniteField(p), name, normalize=False) self._kwargs['repr'] = repr self._kwargs['cache'] = cache if modulus is None or modulus == 'conway': if k == 1: modulus = 'random' # this will use the gfq_factory_pk function. elif ConwayPolynomials().has_polynomial(p, k): from sage.rings.finite_rings.conway_polynomials import conway_polynomial modulus = conway_polynomial(p, k) elif modulus is None: modulus = 'random' else: raise ValueError("Conway polynomial not found") from sage.rings.polynomial.polynomial_element import is_Polynomial if is_Polynomial(modulus): modulus = modulus.list() self._cache = Cache_givaro(self, p, k, modulus, repr, cache)
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ self._kwargs = {} if repr not in ['int', 'log', 'poly']: raise ValueError("Unknown representation %s" % repr) q = Integer(q) if q < 2: raise ValueError("q must be a prime power") F = q.factor() if len(F) > 1: raise ValueError("q must be a prime power") p = F[0][0] k = F[0][1] if q >= 1 << 16: raise ValueError("q must be < 2^16") from .finite_field_constructor import GF FiniteField.__init__(self, GF(p), name, normalize=False) self._kwargs['repr'] = repr self._kwargs['cache'] = cache from sage.rings.polynomial.polynomial_element import is_Polynomial if not is_Polynomial(modulus): from sage.misc.superseded import deprecation deprecation( 16930, "constructing a FiniteField_givaro without giving a polynomial as modulus is deprecated, use the more general FiniteField constructor instead" ) R = GF(p)['x'] if modulus is None or isinstance(modulus, str): modulus = R.irreducible_element(k, algorithm=modulus) else: modulus = R(modulus) self._cache = Cache_givaro(self, p, k, modulus, repr, cache) self._modulus = modulus
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Initialize ``self``. EXAMPLES:: sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(GF(2^3, 'a')).run() """ if repr not in ['int', 'log', 'poly']: raise ValueError("Unknown representation %s"%repr) q = Integer(q) if q < 2: raise ValueError("q must be a prime power") F = q.factor() if len(F) > 1: raise ValueError("q must be a prime power") p = F[0][0] k = F[0][1] if q >= 1<<16: raise ValueError("q must be < 2^16") from .finite_field_constructor import GF FiniteField.__init__(self, GF(p), name, normalize=False) from sage.rings.polynomial.polynomial_element import is_Polynomial if not is_Polynomial(modulus): raise TypeError("modulus must be a polynomial") self._cache = Cache_givaro(self, p, k, modulus, repr, cache) self._modulus = modulus
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): r""" Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: from sage.misc.verbose import set_verbose sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import cputime from sage.misc.verbose import verbose degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s" % Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree // absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s" % [s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd( factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac // 2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq // 2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([ 4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0, -a2, 1 ]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq // 3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort( absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]" % (degree_divisor, degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s" % (f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol * (denom**Mpol.poldegree())).subst( "x", pari([0, 1 / denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0]) - 1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod( Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL / Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s" % Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm // f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k * KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd( f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol / Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): """ Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import verbose, cputime degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s"%Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree//absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s"%[s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd(factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac//2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq//2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q/q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([4*a0*a2 - a1*a1 -a0*a3*a3, a1*a3 - 4*a0, -a2, 1]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq//3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s"%[s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]"%(degree_divisor,degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s"%(f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol*(denom**Mpol.poldegree())).subst("x", pari([0,1/denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s"%Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0])-1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL/Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s"%Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm//f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k*KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd(f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol/Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def carmichael_lambda(n): r""" Return the Carmichael function of a positive integer ``n``. The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. INPUT: - ``n`` -- a positive integer. OUTPUT: - The Carmichael function of ``n``. ALGORITHM: If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd prime and let `k` be a positive integer. Then `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the prime factorization of `n`. Then .. MATH:: \lambda(n) = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) EXAMPLES: The Carmichael function of all positive integers up to and including 10:: sage: from sage.crypto.util import carmichael_lambda sage: list(map(carmichael_lambda, [1..10])) [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] The Carmichael function of the first ten primes:: sage: list(map(carmichael_lambda, primes_first_n(10))) [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] Cases where the Carmichael function is equivalent to the Euler phi function:: sage: carmichael_lambda(2) == euler_phi(2) True sage: carmichael_lambda(4) == euler_phi(4) True sage: p = random_prime(1000, lbound=3, proof=True) sage: k = randint(1, 1000) sage: carmichael_lambda(p^k) == euler_phi(p^k) True A case where `\lambda(n) \neq \varphi(n)`:: sage: k = randint(1, 1000) sage: carmichael_lambda(2^k) == 2^(k - 2) True sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) False Verifying the current implementation of the Carmichael function using another implementation. The other implementation that we use for verification is an exhaustive search for the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 500) sage: c = carmichael_lambda(n) sage: def coprime(n): ....: return [i for i in range(n) if gcd(i, n) == 1] sage: def znpower(n, k): ....: L = coprime(n) ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) sage: def my_carmichael(n): ....: for k in range(1, n): ....: L = znpower(n, k) ....: ones = [1] * len(L) ....: T = [L[i] == ones[i] for i in range(len(L))] ....: if all(T): ....: return k sage: c == my_carmichael(n) True Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. Here, we verify Carmichael's theorem. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 1000) sage: c = carmichael_lambda(n) sage: ZnZ = IntegerModRing(n) sage: M = ZnZ.list_of_elements_of_multiplicative_group() sage: ones = [1] * len(M) sage: P = [power_mod(a, c, n) for a in M] sage: P == ones True TESTS: The input ``n`` must be a positive integer:: sage: from sage.crypto.util import carmichael_lambda sage: carmichael_lambda(0) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. sage: carmichael_lambda(randint(-10, 0)) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. Bug reported in :trac:`8283`:: sage: from sage.crypto.util import carmichael_lambda sage: type(carmichael_lambda(16)) <type 'sage.rings.integer.Integer'> REFERENCES: - :wikipedia:`Carmichael_function` """ n = Integer(n) # sanity check if n < 1: raise ValueError("Input n must be a positive integer.") L = n.factor() t = [] # first get rid of the prime factor 2 if n & 1 == 0: e = L[0][1] L = L[1:] # now, n = 2**e * L.value() if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) e = e - 1 else: # for k >= 3, lambda(2**k) = 2**(k - 2) e = e - 2 t.append(1 << e) # 2**e # then other prime factors t += [p**(k - 1) * (p - 1) for p, k in L] # finish the job return lcm(t)
def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parameters=False): r""" Return ``True`` if the parameters ``(v,k,lmbda)`` are the one of hyperplanes in a (finite Desarguesian) projective space. In other words, test whether there exists a prime power ``q`` and an integer ``d`` greater than two such that: - `v = (q^{d+1}-1)/(q-1) = q^d + q^{d-1} + ... + 1` - `k = (q^d - 1)/(q-1) = q^{d-1} + q^{d-2} + ... + 1` - `lmbda = (q^{d-1}-1)/(q-1) = q^{d-2} + q^{d-3} + ... + 1` If it exists, such a pair ``(q,d)`` is unique. INPUT: - ``v,k,lmbda`` (integers) OUTPUT: - a boolean or, if ``return_parameters`` is set to ``True`` a pair ``(True, (q,d))`` or ``(False, (None,None))``. EXAMPLES:: sage: from sage.combinat.designs.block_design import are_hyperplanes_in_projective_geometry_parameters sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4) True sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4,return_parameters=True) (True, (3, 3)) sage: PG = designs.ProjectiveGeometryDesign(3,2,GF(3)) sage: PG.is_t_design(return_parameters=True) (True, (2, 40, 13, 4)) sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1) False sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1,return_parameters=True) (False, (None, None)) TESTS:: sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) sage: for q in [3,4,5,7,8,9,11]: ....: for d in [2,3,4,5]: ....: v,k,l = sgp(q,d) ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l,True) == (True, (q,d)) ....: assert are_hyperplanes_in_projective_geometry_parameters(v+1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v-1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k+1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k-1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l+1) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l-1) is False """ import sage.arith.all as arith q1 = Integer(v - k) q2 = Integer(k - lmbda) if (lmbda <= 0 or q1 < 4 or q2 < 2 or not q1.is_prime_power() or not q2.is_prime_power()): return (False,(None,None)) if return_parameters else False p1,e1 = q1.factor()[0] p2,e2 = q2.factor()[0] k = arith.gcd(e1,e2) d = e1//k q = p1**k if e2//k != d-1 or lmbda != (q**(d-1)-1)//(q-1): return (False,(None,None)) if return_parameters else False return (True, (q,d)) if return_parameters else True
def singer_difference_set(q,d): r""" Return a difference set associated to the set of hyperplanes in a projective space of dimension `d` over `GF(q)`. A Singer difference set has parameters: .. MATH:: v = \frac{q^{d+1}-1}{q-1}, \quad k = \frac{q^d-1}{q-1}, \quad \lambda = \frac{q^{d-1}-1}{q-1}. The idea of the construction is as follows. One consider the finite field `GF(q^{d+1})` as a vector space of dimension `d+1` over `GF(q)`. The set of `GF(q)`-lines in `GF(q^{d+1})` is a projective plane and its set of hyperplanes form a balanced incomplete block design. Now, considering a multiplicative generator `z` of `GF(q^{d+1})`, we get a transitive action of a cyclic group on our projective plane from which it is possible to build a difference set. The construction is given in details in [Stinson2004]_, section 3.3. EXAMPLES:: sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family sage: G,D = singer_difference_set(3,2) sage: is_difference_family(G,D,verbose=True) It is a (13,4,1)-difference family True sage: G,D = singer_difference_set(4,2) sage: is_difference_family(G,D,verbose=True) It is a (21,5,1)-difference family True sage: G,D = singer_difference_set(3,3) sage: is_difference_family(G,D,verbose=True) It is a (40,13,4)-difference family True sage: G,D = singer_difference_set(9,3) sage: is_difference_family(G,D,verbose=True) It is a (820,91,10)-difference family True """ q = Integer(q) assert q.is_prime_power() assert d >= 2 from sage.rings.finite_rings.constructor import GF from sage.rings.finite_rings.conway_polynomials import conway_polynomial from sage.rings.finite_rings.integer_mod_ring import Zmod # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a # GF(q**(d+1)) and such that x is a multiplicative generator. p,e = q.factor()[0] c = conway_polynomial(p,e*(d+1)) if e != 1: # i.e. q is not a prime, so we factorize c over GF(q) and pick # one of its factor K = GF(q,'z') c = c.change_ring(K).factor()[0][0] else: K = GF(q) z = c.parent().gen() # Now we consider the GF(q)-subspace V spanned by (1,z,z^2,...,z^(d-1)) inside # GF(q^(d+1)). The multiplication by z is an automorphism of the # GF(q)-projective space built from GF(q^(d+1)). The difference family is # obtained by taking the integers i such that z^i belong to V. powers = [0] i = 1 x = z k = (q**d-1)//(q-1) while len(powers) < k: if x.degree() <= (d-1): powers.append(i) x = (x*z).mod(c) i += 1 return Zmod((q**(d+1)-1)//(q-1)), [powers]
def singer_difference_set(q, d): r""" Return a difference set associated to the set of hyperplanes in a projective space of dimension `d` over `GF(q)`. A Singer difference set has parameters: .. MATH:: v = \frac{q^{d+1}-1}{q-1}, \quad k = \frac{q^d-1}{q-1}, \quad \lambda = \frac{q^{d-1}-1}{q-1}. The idea of the construction is as follows. One consider the finite field `GF(q^{d+1})` as a vector space of dimension `d+1` over `GF(q)`. The set of `GF(q)`-lines in `GF(q^{d+1})` is a projective plane and its set of hyperplanes form a balanced incomplete block design. Now, considering a multiplicative generator `z` of `GF(q^{d+1})`, we get a transitive action of a cyclic group on our projective plane from which it is possible to build a difference set. The construction is given in details in [Stinson2004]_, section 3.3. EXAMPLES:: sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family sage: G,D = singer_difference_set(3,2) sage: is_difference_family(G,D,verbose=True) It is a (13,4,1)-difference family True sage: G,D = singer_difference_set(4,2) sage: is_difference_family(G,D,verbose=True) It is a (21,5,1)-difference family True sage: G,D = singer_difference_set(3,3) sage: is_difference_family(G,D,verbose=True) It is a (40,13,4)-difference family True sage: G,D = singer_difference_set(9,3) sage: is_difference_family(G,D,verbose=True) It is a (820,91,10)-difference family True """ q = Integer(q) assert q.is_prime_power() assert d >= 2 from sage.rings.finite_rings.constructor import GF from sage.rings.finite_rings.conway_polynomials import conway_polynomial from sage.rings.finite_rings.integer_mod_ring import Zmod # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a # GF(q**(d+1)) and such that x is a multiplicative generator. p, e = q.factor()[0] c = conway_polynomial(p, e * (d + 1)) if e != 1: # i.e. q is not a prime, so we factorize c over GF(q) and pick # one of its factor K = GF(q, 'z') c = c.change_ring(K).factor()[0][0] else: K = GF(q) z = c.parent().gen() # Now we consider the GF(q)-subspace V spanned by (1,z,z^2,...,z^(d-1)) inside # GF(q^(d+1)). The multiplication by z is an automorphism of the # GF(q)-projective space built from GF(q^(d+1)). The difference family is # obtained by taking the integers i such that z^i belong to V. powers = [0] i = 1 x = z k = (q**d - 1) // (q - 1) while len(powers) < k: if x.degree() <= (d - 1): powers.append(i) x = (x * z).mod(c) i += 1 return Zmod((q**(d + 1) - 1) // (q - 1)), [powers]
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Finite Field. These are implemented using Zech logs and the cardinality must be < 2^16. By default conway polynomials are used as minimal polynomial. INPUT: q -- p^n (must be prime power) name -- variable used for poly_repr (default: 'a') modulus -- you may provide a minimal polynomial to use for reduction or 'random' to force a random irreducible polynomial. (default: None, a conway polynomial is used if found. Otherwise a random polynomial is used) repr -- controls the way elements are printed to the user: (default: 'poly') 'log': repr is element.log_repr() 'int': repr is element.int_repr() 'poly': repr is element.poly_repr() cache -- if True a cache of all elements of this field is created. Thus, arithmetic does not create new elements which speeds calculations up. Also, if many elements are needed during a calculation this cache reduces the memory requirement as at most self.order() elements are created. (default: False) OUTPUT: Givaro finite field with characteristic p and cardinality p^n. EXAMPLES: By default conway polynomials are used: sage: k.<a> = GF(2**8) sage: -a ^ k.degree() a^4 + a^3 + a^2 + 1 sage: f = k.modulus(); f x^8 + x^4 + x^3 + x^2 + 1 You may enforce a modulus: sage: P.<x> = PolynomialRing(GF(2)) sage: f = x^8 + x^4 + x^3 + x + 1 # Rijndael Polynomial sage: k.<a> = GF(2^8, modulus=f) sage: k.modulus() x^8 + x^4 + x^3 + x + 1 sage: a^(2^8) a You may enforce a random modulus: sage: k = GF(3**5, 'a', modulus='random') sage: k.modulus() # random polynomial x^5 + 2*x^4 + 2*x^3 + x^2 + 2 Three different representations are possible: sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='poly').gen() a sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='int').gen() 3 sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='log').gen() 5 sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(j).run() """ self._kwargs = {} if repr not in ['int', 'log', 'poly']: raise ValueError, "Unknown representation %s" % repr q = Integer(q) if q < 2: raise ValueError, "q must be a prime power" F = q.factor() if len(F) > 1: raise ValueError, "q must be a prime power" p = F[0][0] k = F[0][1] if q >= 1 << 16: raise ValueError, "q must be < 2^16" import constructor FiniteField.__init__(self, constructor.FiniteField(p), name, normalize=False) self._kwargs['repr'] = repr self._kwargs['cache'] = cache self._is_conway = False if modulus is None or modulus == 'conway': if k == 1: modulus = 'random' # this will use the gfq_factory_pk function. elif ConwayPolynomials().has_polynomial(p, k): from sage.rings.finite_rings.constructor import conway_polynomial modulus = conway_polynomial(p, k) self._is_conway = True elif modulus is None: modulus = 'random' else: raise ValueError, "Conway polynomial not found" from sage.rings.polynomial.all import is_Polynomial if is_Polynomial(modulus): modulus = modulus.list() self._cache = Cache_givaro(self, p, k, modulus, repr, cache)
def carmichael_lambda(n): r""" Return the Carmichael function of a positive integer ``n``. The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. INPUT: - ``n`` -- a positive integer. OUTPUT: - The Carmichael function of ``n``. ALGORITHM: If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd prime and let `k` be a positive integer. Then `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the prime factorization of `n`. Then .. MATH:: \lambda(n) = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) EXAMPLES: The Carmichael function of all positive integers up to and including 10:: sage: from sage.crypto.util import carmichael_lambda sage: list(map(carmichael_lambda, [1..10])) [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] The Carmichael function of the first ten primes:: sage: list(map(carmichael_lambda, primes_first_n(10))) [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] Cases where the Carmichael function is equivalent to the Euler phi function:: sage: carmichael_lambda(2) == euler_phi(2) True sage: carmichael_lambda(4) == euler_phi(4) True sage: p = random_prime(1000, lbound=3, proof=True) sage: k = randint(1, 1000) sage: carmichael_lambda(p^k) == euler_phi(p^k) True A case where `\lambda(n) \neq \varphi(n)`:: sage: k = randint(1, 1000) sage: carmichael_lambda(2^k) == 2^(k - 2) True sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) False Verifying the current implementation of the Carmichael function using another implementation. The other implementation that we use for verification is an exhaustive search for the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 500) sage: c = carmichael_lambda(n) sage: def coprime(n): ....: return [i for i in range(n) if gcd(i, n) == 1] sage: def znpower(n, k): ....: L = coprime(n) ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) sage: def my_carmichael(n): ....: for k in range(1, n): ....: L = znpower(n, k) ....: ones = [1] * len(L) ....: T = [L[i] == ones[i] for i in xrange(len(L))] ....: if all(T): ....: return k sage: c == my_carmichael(n) True Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. Here, we verify Carmichael's theorem. :: sage: from sage.crypto.util import carmichael_lambda sage: n = randint(1, 1000) sage: c = carmichael_lambda(n) sage: ZnZ = IntegerModRing(n) sage: M = ZnZ.list_of_elements_of_multiplicative_group() sage: ones = [1] * len(M) sage: P = [power_mod(a, c, n) for a in M] sage: P == ones True TESTS: The input ``n`` must be a positive integer:: sage: from sage.crypto.util import carmichael_lambda sage: carmichael_lambda(0) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. sage: carmichael_lambda(randint(-10, 0)) Traceback (most recent call last): ... ValueError: Input n must be a positive integer. Bug reported in :trac:`8283`:: sage: from sage.crypto.util import carmichael_lambda sage: type(carmichael_lambda(16)) <type 'sage.rings.integer.Integer'> REFERENCES: .. [Carmichael2010] Carmichael function, http://en.wikipedia.org/wiki/Carmichael_function """ n = Integer(n) # sanity check if n < 1: raise ValueError("Input n must be a positive integer.") L = n.factor() t = [] # first get rid of the prime factor 2 if n & 1 == 0: e = L[0][1] L = L[1:] # now, n = 2**e * L.value() if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) e = e - 1 else: # for k >= 3, lambda(2**k) = 2**(k - 2) e = e - 2 t.append(1 << e) # 2**e # then other prime factors t += [p**(k - 1) * (p - 1) for p, k in L] # finish the job return lcm(t)
def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): """ Finite Field. These are implemented using Zech logs and the cardinality must be < 2^16. By default conway polynomials are used as minimal polynomial. INPUT: q -- p^n (must be prime power) name -- variable used for poly_repr (default: 'a') modulus -- you may provide a minimal polynomial to use for reduction or 'random' to force a random irreducible polynomial. (default: None, a conway polynomial is used if found. Otherwise a random polynomial is used) repr -- controls the way elements are printed to the user: (default: 'poly') 'log': repr is element.log_repr() 'int': repr is element.int_repr() 'poly': repr is element.poly_repr() cache -- if True a cache of all elements of this field is created. Thus, arithmetic does not create new elements which speeds calculations up. Also, if many elements are needed during a calculation this cache reduces the memory requirement as at most self.order() elements are created. (default: False) OUTPUT: Givaro finite field with characteristic p and cardinality p^n. EXAMPLES: By default conway polynomials are used: sage: k.<a> = GF(2**8) sage: -a ^ k.degree() a^4 + a^3 + a^2 + 1 sage: f = k.modulus(); f x^8 + x^4 + x^3 + x^2 + 1 You may enforce a modulus: sage: P.<x> = PolynomialRing(GF(2)) sage: f = x^8 + x^4 + x^3 + x + 1 # Rijndael Polynomial sage: k.<a> = GF(2^8, modulus=f) sage: k.modulus() x^8 + x^4 + x^3 + x + 1 sage: a^(2^8) a You may enforce a random modulus: sage: k = GF(3**5, 'a', modulus='random') sage: k.modulus() # random polynomial x^5 + 2*x^4 + 2*x^3 + x^2 + 2 Three different representations are possible: sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='poly').gen() a sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='int').gen() 3 sage: sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro(9,repr='log').gen() 5 sage: k.<a> = GF(2^3) sage: j.<b> = GF(3^4) sage: k == j False sage: GF(2^3,'a') == copy(GF(2^3,'a')) True sage: TestSuite(j).run() """ self._kwargs = {} if repr not in ['int', 'log', 'poly']: raise ValueError, "Unknown representation %s"%repr q = Integer(q) if q < 2: raise ValueError, "q must be a prime power" F = q.factor() if len(F) > 1: raise ValueError, "q must be a prime power" p = F[0][0] k = F[0][1] if q >= 1<<16: raise ValueError, "q must be < 2^16" import constructor FiniteField.__init__(self, constructor.FiniteField(p), name, normalize=False) self._kwargs['repr'] = repr self._kwargs['cache'] = cache self._is_conway = False if modulus is None or modulus == 'conway': if k == 1: modulus = 'random' # this will use the gfq_factory_pk function. elif ConwayPolynomials().has_polynomial(p, k): from sage.rings.finite_rings.constructor import conway_polynomial modulus = conway_polynomial(p, k) self._is_conway = True elif modulus is None: modulus = 'random' else: raise ValueError, "Conway polynomial not found" from sage.rings.polynomial.all import is_Polynomial if is_Polynomial(modulus): modulus = modulus.list() self._cache = Cache_givaro(self, p, k, modulus, repr, cache)
def create_key_and_extra_args( self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, **kwds ): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) sage: GF.create_key_and_extra_args(9, 'a', foo='value') ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() with WithProof("arithmetic", proof): order = Integer(order) if order <= 1: raise ValueError("the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = "modn" name = ("x",) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False elif order.is_prime_power(): if names is not None: name = names if name is not None: name = normalize_names(1, name) p, n = order.factor()[0] if name is None: if "prefix" not in kwds: kwds["prefix"] = "z" name = kwds["prefix"] + str(n) if modulus is not None: raise ValueError("no modulus may be specified if variable name not given") if "conway" in kwds: del kwds["conway"] from sage.misc.superseded import deprecation deprecation( 17569, "the 'conway' argument is deprecated, pseudo-conway polynomials are now used by default if no variable name is given", ) # Fpbar will have a strong reference, since algebraic_closure caches its results, # and the coefficients of modulus lie in GF(p) Fpbar = GF(p).algebraic_closure(kwds.get("prefix", "z")) # This will give a Conway polynomial if p,n is small enough to be in the database # and a pseudo-Conway polynomial if it's not. modulus = Fpbar._get_polynomial(n) check_irreducible = False if impl is None: if order < zech_log_bound: impl = "givaro" elif p == 2: impl = "ntl" else: impl = "pari_ffelt" else: raise ValueError("the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != "modn": R = PolynomialRing(FiniteField(p), "x") if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus == "default": from sage.misc.superseded import deprecation deprecation( 16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)", ) modulus = None modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial(modulus): modulus = modulus.change_variable_name("x") modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError("the degree of the modulus does not equal the degree of the field") if check_irreducible and not modulus.is_irreducible(): raise ValueError("finite field modulus must be irreducible but it is not") # If modulus is x - 1 for impl="modn", set it to None if impl == "modn" and modulus[0] == -1: modulus = None return (order, name, modulus, impl, str(kwds), p, n, proof), kwds
def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, **kwds): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) sage: GF.create_key_and_extra_args(9, 'a', foo='value') ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() with WithProof('arithmetic', proof): order = Integer(order) if order <= 1: raise ValueError( "the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = 'modn' name = ('x', ) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False elif order.is_prime_power(): if names is not None: name = names if name is not None: name = normalize_names(1, name) p, n = order.factor()[0] if name is None: if 'prefix' not in kwds: kwds['prefix'] = 'z' name = kwds['prefix'] + str(n) if modulus is not None: raise ValueError( "no modulus may be specified if variable name not given" ) if 'conway' in kwds: del kwds['conway'] from sage.misc.superseded import deprecation deprecation( 17569, "the 'conway' argument is deprecated, pseudo-conway polynomials are now used by default if no variable name is given" ) # Fpbar will have a strong reference, since algebraic_closure caches its results, # and the coefficients of modulus lie in GF(p) Fpbar = GF(p).algebraic_closure(kwds.get('prefix', 'z')) # This will give a Conway polynomial if p,n is small enough to be in the database # and a pseudo-Conway polynomial if it's not. modulus = Fpbar._get_polynomial(n) check_irreducible = False if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' else: raise ValueError( "the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus == "default": from sage.misc.superseded import deprecation deprecation( 16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)" ) modulus = None modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial( modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError( "the degree of the modulus does not equal the degree of the field" ) if check_irreducible and not modulus.is_irreducible(): raise ValueError( "finite field modulus must be irreducible but it is not" ) # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None return (order, name, modulus, impl, str(kwds), p, n, proof), kwds
def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, **kwds): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) sage: GF.create_key_and_extra_args(9, 'a', foo='value') ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) """ import sage.rings.arith from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() with WithProof('arithmetic', proof): order = Integer(order) if order <= 1: raise ValueError("the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = 'modn' name = ('x',) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False # This check should be replaced by order.is_prime_power() # if Trac #16878 is fixed. elif sage.rings.arith.is_prime_power(order): if not names is None: name = names name = normalize_names(1,name) p, n = order.factor()[0] # The following is a temporary solution that allows us # to construct compatible systems of finite fields # until algebraic closures of finite fields are # implemented in Sage. It requires the user to # specify two parameters: # # - `conway` -- boolean; if True, this field is # constructed to fit in a compatible system using # a Conway polynomial. # - `prefix` -- a string used to generate names for # automatically constructed finite fields # # See the docstring of FiniteFieldFactory for examples. # # Once algebraic closures of finite fields are # implemented, this syntax should be superseded by # something like the following: # # sage: Fpbar = GF(5).algebraic_closure('z') # sage: F, e = Fpbar.subfield(3) # e is the embedding into Fpbar # sage: F # Finite field in z3 of size 5^3 # # This temporary solution only uses actual Conway # polynomials (no pseudo-Conway polynomials), since # pseudo-Conway polynomials are not unique, and until # we have algebraic closures of finite fields, there # is no good place to store a specific choice of # pseudo-Conway polynomials. if name is None: if not ('conway' in kwds and kwds['conway']): raise ValueError("parameter 'conway' is required if no name given") if 'prefix' not in kwds: raise ValueError("parameter 'prefix' is required if no name given") name = kwds['prefix'] + str(n) if 'conway' in kwds and kwds['conway']: from conway_polynomials import conway_polynomial if 'prefix' not in kwds: raise ValueError("a prefix must be specified if conway=True") if modulus is not None: raise ValueError("no modulus may be specified if conway=True") # The following raises a RuntimeError if no polynomial is found. modulus = conway_polynomial(p, n) if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' else: raise ValueError("the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus == "default": from sage.misc.superseded import deprecation deprecation(16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)") modulus = None modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial(modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError("the degree of the modulus does not equal the degree of the field") if check_irreducible and not modulus.is_irreducible(): raise ValueError("finite field modulus must be irreducible but it is not") # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None return (order, name, modulus, impl, str(kwds), p, n, proof), kwds
def are_hyperplanes_in_projective_geometry_parameters(v, k, lmbda, return_parameters=False): r""" Return ``True`` if the parameters ``(v,k,lmbda)`` are the one of hyperplanes in a (finite Desarguesian) projective space. In other words, test whether there exists a prime power ``q`` and an integer ``d`` greater than two such that: - `v = (q^{d+1}-1)/(q-1) = q^d + q^{d-1} + ... + 1` - `k = (q^d - 1)/(q-1) = q^{d-1} + q^{d-2} + ... + 1` - `lmbda = (q^{d-1}-1)/(q-1) = q^{d-2} + q^{d-3} + ... + 1` If it exists, such a pair ``(q,d)`` is unique. INPUT: - ``v,k,lmbda`` (integers) OUTPUT: - a boolean or, if ``return_parameters`` is set to ``True`` a pair ``(True, (q,d))`` or ``(False, (None,None))``. EXAMPLES:: sage: from sage.combinat.designs.block_design import are_hyperplanes_in_projective_geometry_parameters sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4) True sage: are_hyperplanes_in_projective_geometry_parameters(40,13,4,return_parameters=True) (True, (3, 3)) sage: PG = designs.ProjectiveGeometryDesign(3,2,GF(3)) sage: PG.is_t_design(return_parameters=True) (True, (2, 40, 13, 4)) sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1) False sage: are_hyperplanes_in_projective_geometry_parameters(15,3,1,return_parameters=True) (False, (None, None)) TESTS:: sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) sage: for q in [3,4,5,7,8,9,11]: ....: for d in [2,3,4,5]: ....: v,k,l = sgp(q,d) ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l,True) == (True, (q,d)) ....: assert are_hyperplanes_in_projective_geometry_parameters(v+1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v-1,k,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k+1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k-1,l) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l+1) is False ....: assert are_hyperplanes_in_projective_geometry_parameters(v,k,l-1) is False """ import sage.arith.all as arith q1 = Integer(v - k) q2 = Integer(k - lmbda) if (lmbda <= 0 or q1 < 4 or q2 < 2 or not q1.is_prime_power() or not q2.is_prime_power()): return (False, (None, None)) if return_parameters else False p1, e1 = q1.factor()[0] p2, e2 = q2.factor()[0] k = arith.gcd(e1, e2) d = e1 // k q = p1**k if e2 // k != d - 1 or lmbda != (q**(d - 1) - 1) // (q - 1): return (False, (None, None)) if return_parameters else False return (True, (q, d)) if return_parameters else True
def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, **kwds): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) sage: GF.create_key_and_extra_args(9, 'a', foo='value') ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() with WithProof('arithmetic', proof): order = Integer(order) if order <= 1: raise ValueError( "the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = 'modn' name = ('x', ) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False elif order.is_prime_power(): if names is not None: name = names if name is not None: name = normalize_names(1, name) p, n = order.factor()[0] # The following is a temporary solution that allows us # to construct compatible systems of finite fields # until algebraic closures of finite fields are # implemented in Sage. It requires the user to # specify two parameters: # # - `conway` -- boolean; if True, this field is # constructed to fit in a compatible system using # a Conway polynomial. # - `prefix` -- a string used to generate names for # automatically constructed finite fields # # See the docstring of FiniteFieldFactory for examples. # # Once algebraic closures of finite fields are # implemented, this syntax should be superseded by # something like the following: # # sage: Fpbar = GF(5).algebraic_closure('z') # sage: F, e = Fpbar.subfield(3) # e is the embedding into Fpbar # sage: F # Finite field in z3 of size 5^3 # # This temporary solution only uses actual Conway # polynomials (no pseudo-Conway polynomials), since # pseudo-Conway polynomials are not unique, and until # we have algebraic closures of finite fields, there # is no good place to store a specific choice of # pseudo-Conway polynomials. if name is None: if not ('conway' in kwds and kwds['conway']): raise ValueError( "parameter 'conway' is required if no name given") if 'prefix' not in kwds: raise ValueError( "parameter 'prefix' is required if no name given") name = kwds['prefix'] + str(n) if 'conway' in kwds and kwds['conway']: from conway_polynomials import conway_polynomial if 'prefix' not in kwds: raise ValueError( "a prefix must be specified if conway=True") if modulus is not None: raise ValueError( "no modulus may be specified if conway=True") # The following raises a RuntimeError if no polynomial is found. modulus = conway_polynomial(p, n) if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' else: raise ValueError( "the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus == "default": from sage.misc.superseded import deprecation deprecation( 16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)" ) modulus = None modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial( modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError( "the degree of the modulus does not equal the degree of the field" ) if check_irreducible and not modulus.is_irreducible(): raise ValueError( "finite field modulus must be irreducible but it is not" ) # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None return (order, name, modulus, impl, str(kwds), p, n, proof), kwds
def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, prefix=None, repr=None, elem_cache=None, **kwds): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) We do not take invalid keyword arguments and raise a value error to better ensure uniqueness:: sage: GF.create_key_and_extra_args(9, 'a', foo='value') Traceback (most recent call last): ... TypeError: create_key_and_extra_args() got an unexpected keyword argument 'foo' Moreover, ``repr`` and ``elem_cache`` are ignored when not using givaro:: sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', repr='poly') ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', elem_cache=False) ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF(16, impl='ntl') is GF(16, impl='ntl', repr='foo') True We handle extra arguments for the givaro finite field and create unique objects for their defaults:: sage: GF(25, impl='givaro') is GF(25, impl='givaro', repr='poly') True sage: GF(25, impl='givaro') is GF(25, impl='givaro', elem_cache=True) True sage: GF(625, impl='givaro') is GF(625, impl='givaro', elem_cache=False) True We explicitly take ``structure``, ``implementation`` and ``prec`` attributes for compatibility with :class:`~sage.categories.pushout.AlgebraicExtensionFunctor` but we ignore them as they are not used, see :trac:`21433`:: sage: GF.create_key_and_extra_args(9, 'a', structure=None) ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() for key, val in kwds.items(): if key not in ['structure', 'implementation', 'prec', 'embedding']: raise TypeError( "create_key_and_extra_args() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError( "ring extension with prescribed %s is not implemented" % key) with WithProof('arithmetic', proof): order = Integer(order) if order <= 1: raise ValueError( "the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = 'modn' name = ('x', ) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False elif order.is_prime_power(): if names is not None: name = names if name is not None: name = normalize_names(1, name) p, n = order.factor()[0] if name is None: if prefix is None: prefix = 'z' name = prefix + str(n) if modulus is not None: raise ValueError( "no modulus may be specified if variable name not given" ) # Fpbar will have a strong reference, since algebraic_closure caches its results, # and the coefficients of modulus lie in GF(p) Fpbar = GF(p).algebraic_closure(prefix) # This will give a Conway polynomial if p,n is small enough to be in the database # and a pseudo-Conway polynomial if it's not. modulus = Fpbar._get_polynomial(n) check_irreducible = False if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' else: raise ValueError( "the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial( modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError( "the degree of the modulus does not equal the degree of the field" ) if check_irreducible and not modulus.is_irreducible(): raise ValueError( "finite field modulus must be irreducible but it is not" ) # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None # Check extra arguments for givaro and setup their defaults # TODO: ntl takes a repr, but ignores it if impl == 'givaro': if repr is None: repr = 'poly' if elem_cache is None: elem_cache = (order < 500) else: # This has the effect of ignoring these keywords repr = None elem_cache = None return (order, name, modulus, impl, p, n, proof, prefix, repr, elem_cache), {}
def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, impl=None, proof=None, check_irreducible=True, prefix=None, repr=None, elem_cache=None, structure=None): """ EXAMPLES:: sage: GF.create_key_and_extra_args(9, 'a') ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) We do not take invalid keyword arguments and raise a value error to better ensure uniqueness:: sage: GF.create_key_and_extra_args(9, 'a', foo='value') Traceback (most recent call last): ... TypeError: create_key_and_extra_args() got an unexpected keyword argument 'foo' Moreover, ``repr`` and ``elem_cache`` are ignored when not using givaro:: sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', repr='poly') ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF.create_key_and_extra_args(16, 'a', impl='ntl', elem_cache=False) ((16, ('a',), x^4 + x + 1, 'ntl', 2, 4, True, None, None, None), {}) sage: GF(16, impl='ntl') is GF(16, impl='ntl', repr='foo') True We handle extra arguments for the givaro finite field and create unique objects for their defaults:: sage: GF(25, impl='givaro') is GF(25, impl='givaro', repr='poly') True sage: GF(25, impl='givaro') is GF(25, impl='givaro', elem_cache=True) True sage: GF(625, impl='givaro') is GF(625, impl='givaro', elem_cache=False) True We explicitly take a ``structure`` attribute for compatibility with :class:`~sage.categories.pushout.AlgebraicExtensionFunctor` but we ignore it as it is not used, see :trac:`21433`:: sage: GF.create_key_and_extra_args(9, 'a', structure=None) ((9, ('a',), x^2 + 2*x + 2, 'givaro', 3, 2, True, None, 'poly', True), {}) """ import sage.arith.all from sage.structure.proof.all import WithProof, arithmetic if proof is None: proof = arithmetic() with WithProof('arithmetic', proof): order = Integer(order) if order <= 1: raise ValueError("the order of a finite field must be at least 2") if order.is_prime(): p = order n = Integer(1) if impl is None: impl = 'modn' name = ('x',) # Ignore name # Every polynomial of degree 1 is irreducible check_irreducible = False elif order.is_prime_power(): if names is not None: name = names if name is not None: name = normalize_names(1, name) p, n = order.factor()[0] if name is None: if prefix is None: prefix = 'z' name = prefix + str(n) if modulus is not None: raise ValueError("no modulus may be specified if variable name not given") # Fpbar will have a strong reference, since algebraic_closure caches its results, # and the coefficients of modulus lie in GF(p) Fpbar = GF(p).algebraic_closure(prefix) # This will give a Conway polynomial if p,n is small enough to be in the database # and a pseudo-Conway polynomial if it's not. modulus = Fpbar._get_polynomial(n) check_irreducible = False if impl is None: if order < zech_log_bound: impl = 'givaro' elif p == 2: impl = 'ntl' else: impl = 'pari_ffelt' else: raise ValueError("the order of a finite field must be a prime power") # Determine modulus. # For the 'modn' implementation, we use the following # optimization which we also need to avoid an infinite loop: # a modulus of None is a shorthand for x-1. if modulus is not None or impl != 'modn': R = PolynomialRing(FiniteField(p), 'x') if modulus is None: modulus = R.irreducible_element(n) if isinstance(modulus, str): # A string specifies an algorithm to find a suitable modulus. if modulus == "default": from sage.misc.superseded import deprecation deprecation(16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)") modulus = None modulus = R.irreducible_element(n, algorithm=modulus) else: if sage.rings.polynomial.polynomial_element.is_Polynomial(modulus): modulus = modulus.change_variable_name('x') modulus = R(modulus).monic() if modulus.degree() != n: raise ValueError("the degree of the modulus does not equal the degree of the field") if check_irreducible and not modulus.is_irreducible(): raise ValueError("finite field modulus must be irreducible but it is not") # If modulus is x - 1 for impl="modn", set it to None if impl == 'modn' and modulus[0] == -1: modulus = None # Check extra arguments for givaro and setup their defaults # TODO: ntl takes a repr, but ignores it if impl == 'givaro': if repr is None: repr = 'poly' if elem_cache is None: elem_cache = (order < 500) else: # This has the effect of ignoring these keywords repr = None elem_cache = None return (order, name, modulus, impl, p, n, proof, prefix, repr, elem_cache), {}