Miscellaneous generic functions¶
A collection of functions implementing generic algorithms in arbitrary groups, including additive and multiplicative groups.
In all cases the group operation is specified by a parameter
operation
, which is a string either one of the set of
multiplication_names
or addition_names
specified below, or other.
In the latter case, the caller must provide an identity, inverse()
and
op()
functions.
multiplication_names = ('multiplication', 'times', 'product', '*')
addition_names = ('addition', 'plus', 'sum', '+')
Also included are a generic function for computing multiples (or powers), and an iterator for general multiples and powers.
EXAMPLES:
Some examples in the multiplicative group of a finite field:
Discrete logs:
sage: # needs sage.rings.finite_rings sage: K = GF(3^6,'b') sage: b = K.gen() sage: a = b^210 sage: discrete_log(a, b, K.order() - 1) 210
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> K = GF(Integer(3)**Integer(6),'b') >>> b = K.gen() >>> a = b**Integer(210) >>> discrete_log(a, b, K.order() - Integer(1)) 210
Linear relation finder:
sage: # needs sage.rings.finite_rings sage: F.<a> = GF(3^6,'a') sage: a.multiplicative_order().factor() 2^3 * 7 * 13 sage: b = a^7 sage: c = a^13 sage: linear_relation(b,c,'*') (13, 7) sage: b^13 == c^7 True
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> F = GF(Integer(3)**Integer(6),'a', names=('a',)); (a,) = F._first_ngens(1) >>> a.multiplicative_order().factor() 2^3 * 7 * 13 >>> b = a**Integer(7) >>> c = a**Integer(13) >>> linear_relation(b,c,'*') (13, 7) >>> b**Integer(13) == c**Integer(7) True
Orders of elements:
sage: # needs sage.rings.finite_rings sage: from sage.groups.generic import order_from_multiple, order_from_bounds sage: k.<a> = GF(5^5) sage: b = a^4 sage: order_from_multiple(b, 5^5 - 1, operation='*') 781 sage: order_from_bounds(b, (5^4, 5^5), operation='*') 781
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> from sage.groups.generic import order_from_multiple, order_from_bounds >>> k = GF(Integer(5)**Integer(5), names=('a',)); (a,) = k._first_ngens(1) >>> b = a**Integer(4) >>> order_from_multiple(b, Integer(5)**Integer(5) - Integer(1), operation='*') 781 >>> order_from_bounds(b, (Integer(5)**Integer(4), Integer(5)**Integer(5)), operation='*') 781
Some examples in the group of points of an elliptic curve over a finite field:
Discrete logs:
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: F = GF(37^2,'a') sage: E = EllipticCurve(F,[1,1]) sage: F.<a> = GF(37^2,'a') sage: E = EllipticCurve(F,[1,1]) sage: P = E(25*a + 16 , 15*a + 7 ) sage: P.order() 672 sage: Q = 39*P; Q (36*a + 32 : 5*a + 12 : 1) sage: discrete_log(Q, P, P.order(), operation='+') 39
>>> from sage.all import * >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(2),'a') >>> E = EllipticCurve(F,[Integer(1),Integer(1)]) >>> F = GF(Integer(37)**Integer(2),'a', names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F,[Integer(1),Integer(1)]) >>> P = E(Integer(25)*a + Integer(16) , Integer(15)*a + Integer(7) ) >>> P.order() 672 >>> Q = Integer(39)*P; Q (36*a + 32 : 5*a + 12 : 1) >>> discrete_log(Q, P, P.order(), operation='+') 39
Linear relation finder:
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: F.<a> = GF(3^6,'a') sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1]) sage: P = E(a^5 + a^4 + a^3 + a^2 + a + 2 , 0) sage: Q = E(2*a^3 + 2*a^2 + 2*a , a^3 + 2*a^2 + 1) sage: linear_relation(P,Q,'+') (1, 2) sage: P == 2*Q True
>>> from sage.all import * >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> F = GF(Integer(3)**Integer(6),'a', names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve([a**Integer(5) + Integer(2)*a**Integer(3) + Integer(2)*a**Integer(2) + Integer(2)*a, a**Integer(4) + a**Integer(3) + Integer(2)*a + Integer(1)]) >>> P = E(a**Integer(5) + a**Integer(4) + a**Integer(3) + a**Integer(2) + a + Integer(2) , Integer(0)) >>> Q = E(Integer(2)*a**Integer(3) + Integer(2)*a**Integer(2) + Integer(2)*a , a**Integer(3) + Integer(2)*a**Integer(2) + Integer(1)) >>> linear_relation(P,Q,'+') (1, 2) >>> P == Integer(2)*Q True
Orders of elements:
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: from sage.groups.generic import order_from_multiple, order_from_bounds sage: k.<a> = GF(5^5) sage: E = EllipticCurve(k,[2,4]) sage: P = E(3*a^4 + 3*a, 2*a + 1) sage: M = E.cardinality(); M 3227 sage: plist = M.prime_factors() sage: order_from_multiple(P, M, plist, operation='+') 3227 sage: Q = E(0,2) sage: order_from_multiple(Q, M, plist, operation='+') 7 sage: order_from_bounds(Q, Hasse_bounds(5^5), operation='+') 7
>>> from sage.all import * >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> from sage.groups.generic import order_from_multiple, order_from_bounds >>> k = GF(Integer(5)**Integer(5), names=('a',)); (a,) = k._first_ngens(1) >>> E = EllipticCurve(k,[Integer(2),Integer(4)]) >>> P = E(Integer(3)*a**Integer(4) + Integer(3)*a, Integer(2)*a + Integer(1)) >>> M = E.cardinality(); M 3227 >>> plist = M.prime_factors() >>> order_from_multiple(P, M, plist, operation='+') 3227 >>> Q = E(Integer(0),Integer(2)) >>> order_from_multiple(Q, M, plist, operation='+') 7 >>> order_from_bounds(Q, Hasse_bounds(Integer(5)**Integer(5)), operation='+') 7
- sage.groups.generic.bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None)[source]¶
Totally generic discrete baby-step giant-step function.
Solves \(na=b\) (or \(a^n=b\)) with \(lb\le n\le ub\) where
bounds==(lb,ub)
, raising an error if no such \(n\) exists.\(a\) and \(b\) must be elements of some group with given identity, inverse of
x
given byinverse(x)
, and group operation onx
,y
byop(x,y)
.If operation is ‘*’ or ‘+’ then the other arguments are provided automatically; otherwise they must be provided by the caller.
INPUT:
a
– group elementb
– group elementbounds
– a 2-tuple of integers(lower,upper)
with0<=lower<=upper
operation
– string:'*'
,'+'
, otheridentity
– the identity element of the groupinverse
– function of 1 argumentx
, returning inverse ofx
op
– function of 2 argumentsx
,y
returningx*y
in the group
OUTPUT:
An integer \(n\) such that \(a^n = b\) (or \(na = b\)). If no such \(n\) exists, this function raises a
ValueError
exception.Note
This is a generalization of discrete logarithm. One situation where this version is useful is to find the order of an element in a group where we only have bounds on the group order (see the elliptic curve example below).
ALGORITHM: Baby step giant step. Time and space are soft \(O(\sqrt{n})\) where \(n\) is the difference between upper and lower bounds.
EXAMPLES:
sage: from sage.groups.generic import bsgs sage: b = Mod(2,37); a = b^20 sage: bsgs(b, a, (0, 36)) 20 sage: p = next_prime(10^20) # needs sage.libs.pari sage: a = Mod(2,p); b = a^(10^25) # needs sage.libs.pari sage: bsgs(a, b, (10^25 - 10^6, 10^25 + 10^6)) == 10^25 # needs sage.libs.pari True sage: # needs sage.rings.finite_rings sage: K = GF(3^6,'b') sage: a = K.gen() sage: b = a^210 sage: bsgs(a, b, (0, K.order() - 1)) 210 sage: K.<z> = CyclotomicField(230) # needs sage.rings.number_field sage: w = z^500 # needs sage.rings.number_field sage: bsgs(z, w, (0, 229)) # needs sage.rings.number_field 40
>>> from sage.all import * >>> from sage.groups.generic import bsgs >>> b = Mod(Integer(2),Integer(37)); a = b**Integer(20) >>> bsgs(b, a, (Integer(0), Integer(36))) 20 >>> p = next_prime(Integer(10)**Integer(20)) # needs sage.libs.pari >>> a = Mod(Integer(2),p); b = a**(Integer(10)**Integer(25)) # needs sage.libs.pari >>> bsgs(a, b, (Integer(10)**Integer(25) - Integer(10)**Integer(6), Integer(10)**Integer(25) + Integer(10)**Integer(6))) == Integer(10)**Integer(25) # needs sage.libs.pari True >>> # needs sage.rings.finite_rings >>> K = GF(Integer(3)**Integer(6),'b') >>> a = K.gen() >>> b = a**Integer(210) >>> bsgs(a, b, (Integer(0), K.order() - Integer(1))) 210 >>> K = CyclotomicField(Integer(230), names=('z',)); (z,) = K._first_ngens(1)# needs sage.rings.number_field >>> w = z**Integer(500) # needs sage.rings.number_field >>> bsgs(z, w, (Integer(0), Integer(229))) # needs sage.rings.number_field 40
An additive example in an elliptic curve group:
sage: # needs sage.rings.finite_rings sage.schemes sage: F.<a> = GF(37^5) sage: E = EllipticCurve(F, [1,1]) sage: P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
>>> from sage.all import * >>> # needs sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(5), names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
This will return a multiple of the order of P:
sage: bsgs(P, P.parent().zero(), Hasse_bounds(F.order()), operation='+') # needs sage.rings.finite_rings sage.schemes 69327408
>>> from sage.all import * >>> bsgs(P, P.parent().zero(), Hasse_bounds(F.order()), operation='+') # needs sage.rings.finite_rings sage.schemes 69327408
AUTHOR:
John Cremona (2008-03-15)
- sage.groups.generic.discrete_log(a, base, ord, bounds=None, operation=None, identity='*', inverse=None, op=None, algorithm=None, verify='bsgs')[source]¶
Totally generic discrete log function.
INPUT:
a
– group elementbase
– group element (the base)ord
– integer (multiple of order of base, orNone
)bounds
– a priori bounds on the logoperation
– string:'*'
,'+'
, otheridentity
– the group’s identityinverse
– function of 1 argumentx
, returning inverse ofx
op
– function of 2 argumentsx
,y
, returningx*y
in the groupalgorithm
– string denoting what algorithm to use for prime-order logarithms:'bsgs'
,'rho'
,'lambda'
verify
– boolean (default:True
); whether to verify that output is correct before returning it.
a
andbase
must be elements of some group with identity given byidentity
, inverse ofx
byinverse(x)
, and group operation onx
,y
byop(x,y)
.If operation is
'*'
or'+'
, then the other arguments are provided automatically; otherwise they must be provided by the caller.OUTPUT:
This returns an integer \(n\) such that \(b^n = a\) (or \(nb = a\)), assuming that
ord
is a multiple of the order of the base \(b\). Iford
is not specified, an attempt is made to compute it.If no such \(n\) exists, this function raises a
ValueError
exception.Warning
If
x
has alog
method, it is likely to be vastly faster than using this function. E.g., ifx
is an integer modulo \(n\), use itslog
method instead!ALGORITHM: Pohlig-Hellman, Baby step giant step, Pollard’s lambda/kangaroo, and Pollard’s rho.
EXAMPLES:
sage: b = Mod(2,37); a = b^20 sage: discrete_log(a, b) 20 sage: b = Mod(3,2017); a = b^20 sage: discrete_log(a, b, bounds=(10, 100)) 20 sage: # needs sage.rings.finite_rings sage: K = GF(3^6, 'b') sage: b = K.gen() sage: a = b^210 sage: discrete_log(a, b, K.order() - 1) 210 sage: b = Mod(1,37); x = Mod(2,37) sage: discrete_log(x, b) Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 sage: b = Mod(1,997); x = Mod(2,997) sage: discrete_log(x, b) Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1
>>> from sage.all import * >>> b = Mod(Integer(2),Integer(37)); a = b**Integer(20) >>> discrete_log(a, b) 20 >>> b = Mod(Integer(3),Integer(2017)); a = b**Integer(20) >>> discrete_log(a, b, bounds=(Integer(10), Integer(100))) 20 >>> # needs sage.rings.finite_rings >>> K = GF(Integer(3)**Integer(6), 'b') >>> b = K.gen() >>> a = b**Integer(210) >>> discrete_log(a, b, K.order() - Integer(1)) 210 >>> b = Mod(Integer(1),Integer(37)); x = Mod(Integer(2),Integer(37)) >>> discrete_log(x, b) Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 >>> b = Mod(Integer(1),Integer(997)); x = Mod(Integer(2),Integer(997)) >>> discrete_log(x, b) Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1
See Issue #2356:
sage: F.<w> = GF(121) # needs sage.rings.finite_rings sage: v = w^120 # needs sage.rings.finite_rings sage: v.log(w) # needs sage.rings.finite_rings 0 sage: K.<z> = CyclotomicField(230) # needs sage.rings.number_field sage: w = z^50 # needs sage.rings.number_field sage: discrete_log(w, z) # needs sage.rings.number_field 50
>>> from sage.all import * >>> F = GF(Integer(121), names=('w',)); (w,) = F._first_ngens(1)# needs sage.rings.finite_rings >>> v = w**Integer(120) # needs sage.rings.finite_rings >>> v.log(w) # needs sage.rings.finite_rings 0 >>> K = CyclotomicField(Integer(230), names=('z',)); (z,) = K._first_ngens(1)# needs sage.rings.number_field >>> w = z**Integer(50) # needs sage.rings.number_field >>> discrete_log(w, z) # needs sage.rings.number_field 50
An example where the order is infinite: note that we must give an upper bound here:
sage: # needs sage.rings.number_field sage: K.<a> = QuadraticField(23) sage: eps = 5*a - 24 # a fundamental unit sage: eps.multiplicative_order() +Infinity sage: eta = eps^100 sage: discrete_log(eta, eps, bounds=(0,1000)) 100
>>> from sage.all import * >>> # needs sage.rings.number_field >>> K = QuadraticField(Integer(23), names=('a',)); (a,) = K._first_ngens(1) >>> eps = Integer(5)*a - Integer(24) # a fundamental unit >>> eps.multiplicative_order() +Infinity >>> eta = eps**Integer(100) >>> discrete_log(eta, eps, bounds=(Integer(0),Integer(1000))) 100
In this case we cannot detect negative powers:
sage: eta = eps^(-3) # needs sage.rings.number_field sage: discrete_log(eta,eps,bounds=(0,100)) # needs sage.rings.number_field Traceback (most recent call last): ... ValueError: no discrete log of -11515*a - 55224 found to base 5*a - 24 with bounds (0, 100)
>>> from sage.all import * >>> eta = eps**(-Integer(3)) # needs sage.rings.number_field >>> discrete_log(eta,eps,bounds=(Integer(0),Integer(100))) # needs sage.rings.number_field Traceback (most recent call last): ... ValueError: no discrete log of -11515*a - 55224 found to base 5*a - 24 with bounds (0, 100)
But we can invert the base (and negate the result) instead:
sage: -discrete_log(eta^-1, eps, bounds=(0,100)) # needs sage.rings.number_field -3
>>> from sage.all import * >>> -discrete_log(eta**-Integer(1), eps, bounds=(Integer(0),Integer(100))) # needs sage.rings.number_field -3
An additive example: elliptic curve DLOG:
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: F = GF(37^2,'a') sage: E = EllipticCurve(F, [1,1]) sage: F.<a> = GF(37^2,'a') sage: E = EllipticCurve(F, [1,1]) sage: P = E(25*a + 16, 15*a + 7) sage: P.order() 672 sage: Q = 39*P; Q (36*a + 32 : 5*a + 12 : 1) sage: discrete_log(Q, P, P.order(), operation='+') 39
>>> from sage.all import * >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(2),'a') >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> F = GF(Integer(37)**Integer(2),'a', names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> P = E(Integer(25)*a + Integer(16), Integer(15)*a + Integer(7)) >>> P.order() 672 >>> Q = Integer(39)*P; Q (36*a + 32 : 5*a + 12 : 1) >>> discrete_log(Q, P, P.order(), operation='+') 39
An example of big smooth group:
sage: # needs sage.rings.finite_rings sage: F.<a> = GF(2^63) sage: g = F.gen() sage: u = g**123456789 sage: discrete_log(u,g) 123456789
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> F = GF(Integer(2)**Integer(63), names=('a',)); (a,) = F._first_ngens(1) >>> g = F.gen() >>> u = g**Integer(123456789) >>> discrete_log(u,g) 123456789
The above examples also work when the
'rho'
and'lambda'
algorithms are used:sage: b = Mod(2,37); a = b^20 sage: discrete_log(a, b, algorithm='rho') 20 sage: b = Mod(3,2017); a = b^20 sage: discrete_log(a, b, algorithm='lambda', bounds=(10, 100)) 20 sage: # needs sage.rings.finite_rings sage: K = GF(3^6,'b') sage: b = K.gen() sage: a = b^210 sage: discrete_log(a, b, K.order()-1, algorithm='rho') 210 sage: b = Mod(1,37); x = Mod(2,37) sage: discrete_log(x, b, algorithm='lambda') Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 sage: b = Mod(1,997); x = Mod(2,997) sage: discrete_log(x, b, algorithm='rho') Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: F = GF(37^2,'a') sage: E = EllipticCurve(F, [1,1]) sage: F.<a> = GF(37^2,'a') sage: E = EllipticCurve(F, [1,1]) sage: P = E(25*a + 16, 15*a + 7) sage: P.order() 672 sage: Q = 39*P; Q (36*a + 32 : 5*a + 12 : 1) sage: discrete_log(Q, P, P.order(), operation='+', algorithm='lambda') 39 sage: # needs sage.rings.finite_rings sage: F.<a> = GF(2^63) sage: g = F.gen() sage: u = g**123456789 sage: discrete_log(u, g, algorithm='rho') 123456789
>>> from sage.all import * >>> b = Mod(Integer(2),Integer(37)); a = b**Integer(20) >>> discrete_log(a, b, algorithm='rho') 20 >>> b = Mod(Integer(3),Integer(2017)); a = b**Integer(20) >>> discrete_log(a, b, algorithm='lambda', bounds=(Integer(10), Integer(100))) 20 >>> # needs sage.rings.finite_rings >>> K = GF(Integer(3)**Integer(6),'b') >>> b = K.gen() >>> a = b**Integer(210) >>> discrete_log(a, b, K.order()-Integer(1), algorithm='rho') 210 >>> b = Mod(Integer(1),Integer(37)); x = Mod(Integer(2),Integer(37)) >>> discrete_log(x, b, algorithm='lambda') Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 >>> b = Mod(Integer(1),Integer(997)); x = Mod(Integer(2),Integer(997)) >>> discrete_log(x, b, algorithm='rho') Traceback (most recent call last): ... ValueError: no discrete log of 2 found to base 1 >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(2),'a') >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> F = GF(Integer(37)**Integer(2),'a', names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> P = E(Integer(25)*a + Integer(16), Integer(15)*a + Integer(7)) >>> P.order() 672 >>> Q = Integer(39)*P; Q (36*a + 32 : 5*a + 12 : 1) >>> discrete_log(Q, P, P.order(), operation='+', algorithm='lambda') 39 >>> # needs sage.rings.finite_rings >>> F = GF(Integer(2)**Integer(63), names=('a',)); (a,) = F._first_ngens(1) >>> g = F.gen() >>> u = g**Integer(123456789) >>> discrete_log(u, g, algorithm='rho') 123456789
AUTHORS:
William Stein and David Joyner (2005-01-05)
John Cremona (2008-02-29) rewrite using
dict()
and make genericJulien Grijalva (2022-08-09) rewrite to make more generic, more algorithm options, and more effective use of bounds
- sage.groups.generic.discrete_log_generic(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs')[source]¶
Alias for
discrete_log
.
- sage.groups.generic.discrete_log_lambda(a, base, bounds, operation='*', identity=None, inverse=None, op=None, hash_function=<built-in function hash>)[source]¶
Pollard Lambda algorithm for computing discrete logarithms. It uses only a logarithmic amount of memory. It’s useful if you have bounds on the logarithm. If you are computing logarithms in a whole finite group, you should use Pollard Rho algorithm.
INPUT:
a
– a group elementbase
– a group elementbounds
– a couple (lb,ub) representing the range where we look for a logarithmoperation
– string: ‘+’, ‘*’ or ‘other’identity
– the identity element of the groupinverse
– function of 1 argumentx
returning inverse ofx
op
– function of 2 argumentsx
,y
returningx*y
in the grouphash_function
– having an efficient hash function is critical for this algorithm
OUTPUT: integer \(n\) such that \(a=base^n\) (or \(a=n*base\))
- ALGORITHM: Pollard Lambda, if bounds are (lb,ub) it has time complexity
O(sqrt(ub-lb)) and space complexity O(log(ub-lb))
EXAMPLES:
sage: F.<a> = GF(2^63) # needs sage.rings.finite_rings sage: discrete_log_lambda(a^1234567, a, (1200000,1250000)) # needs sage.rings.finite_rings 1234567 sage: # needs sage.rings.finite_rings sage.schemes sage: F.<a> = GF(37^5) sage: E = EllipticCurve(F, [1,1]) sage: P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
>>> from sage.all import * >>> F = GF(Integer(2)**Integer(63), names=('a',)); (a,) = F._first_ngens(1)# needs sage.rings.finite_rings >>> discrete_log_lambda(a**Integer(1234567), a, (Integer(1200000),Integer(1250000))) # needs sage.rings.finite_rings 1234567 >>> # needs sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(5), names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> P = E.lift_x(a); P (a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
This will return a multiple of the order of P:
sage: discrete_log_lambda(P.parent().zero(), P, Hasse_bounds(F.order()), # needs sage.rings.finite_rings sage.schemes ....: operation='+') 69327408 sage: K.<a> = GF(89**5) # needs sage.rings.finite_rings sage: hs = lambda x: hash(x) + 15 sage: discrete_log_lambda(a**(89**3 - 3), # long time (10s on sage.math, 2011), needs sage.rings.finite_rings ....: a, (89**2, 89**4), operation='*', hash_function=hs) 704966
>>> from sage.all import * >>> discrete_log_lambda(P.parent().zero(), P, Hasse_bounds(F.order()), # needs sage.rings.finite_rings sage.schemes ... operation='+') 69327408 >>> K = GF(Integer(89)**Integer(5), names=('a',)); (a,) = K._first_ngens(1)# needs sage.rings.finite_rings >>> hs = lambda x: hash(x) + Integer(15) >>> discrete_log_lambda(a**(Integer(89)**Integer(3) - Integer(3)), # long time (10s on sage.math, 2011), needs sage.rings.finite_rings ... a, (Integer(89)**Integer(2), Integer(89)**Integer(4)), operation='*', hash_function=hs) 704966
AUTHOR:
– Yann Laigle-Chapuy (2009-01-25)
- sage.groups.generic.discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=None, op=None, hash_function=<built-in function hash>)[source]¶
Pollard Rho algorithm for computing discrete logarithm in cyclic group of prime order. If the group order is very small it falls back to the baby step giant step algorithm.
INPUT:
a
– a group elementbase
– a group elementord
– the order ofbase
orNone
, in this case we try to compute itoperation
– string (default:'*'
); denoting whether we are in an additive group or a multiplicative oneidentity
– the group’s identityinverse
– function of 1 argumentx
, returning inverse ofx
op
– function of 2 argumentsx
,y
, returningx*y
in the grouphash_function
– having an efficient hash function is critical for this algorithm (see examples)
OUTPUT: integer \(n\) such that \(a = base^n\) (or \(a = n*base\))
ALGORITHM: Pollard rho for discrete logarithm, adapted from the article of Edlyn Teske, ‘A space efficient algorithm for group structure computation’.
EXAMPLES:
sage: F.<a> = GF(2^13) # needs sage.rings.finite_rings sage: g = F.gen() # needs sage.rings.finite_rings sage: discrete_log_rho(g^1234, g) # needs sage.rings.finite_rings 1234 sage: # needs sage.rings.finite_rings sage.schemes sage: F.<a> = GF(37^5) sage: E = EllipticCurve(F, [1,1]) sage: G = (3*31*2^4)*E.lift_x(a) sage: discrete_log_rho(12345*G, G, ord=46591, operation='+') 12345
>>> from sage.all import * >>> F = GF(Integer(2)**Integer(13), names=('a',)); (a,) = F._first_ngens(1)# needs sage.rings.finite_rings >>> g = F.gen() # needs sage.rings.finite_rings >>> discrete_log_rho(g**Integer(1234), g) # needs sage.rings.finite_rings 1234 >>> # needs sage.rings.finite_rings sage.schemes >>> F = GF(Integer(37)**Integer(5), names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve(F, [Integer(1),Integer(1)]) >>> G = (Integer(3)*Integer(31)*Integer(2)**Integer(4))*E.lift_x(a) >>> discrete_log_rho(Integer(12345)*G, G, ord=Integer(46591), operation='+') 12345
It also works with matrices:
sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.modules sage.rings.finite_rings ....: [14601, 41019, 30188], ....: [3081, 736, 27092]]) sage: discrete_log_rho(A^1234567, A) # needs sage.modules sage.rings.finite_rings 1234567
>>> from sage.all import * >>> A = matrix(GF(Integer(50021)), [[Integer(10577), Integer(23999), Integer(28893)], # needs sage.modules sage.rings.finite_rings ... [Integer(14601), Integer(41019), Integer(30188)], ... [Integer(3081), Integer(736), Integer(27092)]]) >>> discrete_log_rho(A**Integer(1234567), A) # needs sage.modules sage.rings.finite_rings 1234567
Beware, the order must be prime:
sage: I = IntegerModRing(171980) sage: discrete_log_rho(I(2), I(3)) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: for Pollard rho algorithm the order of the group must be prime
>>> from sage.all import * >>> I = IntegerModRing(Integer(171980)) >>> discrete_log_rho(I(Integer(2)), I(Integer(3))) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: for Pollard rho algorithm the order of the group must be prime
If it fails to find a suitable logarithm, it raises a
ValueError
:sage: I = IntegerModRing(171980) sage: discrete_log_rho(I(31002), I(15501)) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: Pollard rho algorithm failed to find a logarithm
>>> from sage.all import * >>> I = IntegerModRing(Integer(171980)) >>> discrete_log_rho(I(Integer(31002)), I(Integer(15501))) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: Pollard rho algorithm failed to find a logarithm
The main limitation on the hash function is that we don’t want to have
hash(x*y) == hash(x) + hash(y)
:sage: # needs sage.libs.pari sage: I = IntegerModRing(next_prime(2^23)) sage: def test(): ....: try: ....: discrete_log_rho(I(123456), I(1), operation='+') ....: except Exception: ....: print("FAILURE") sage: test() # random failure FAILURE
>>> from sage.all import * >>> # needs sage.libs.pari >>> I = IntegerModRing(next_prime(Integer(2)**Integer(23))) >>> def test(): ... try: ... discrete_log_rho(I(Integer(123456)), I(Integer(1)), operation='+') ... except Exception: ... print("FAILURE") >>> test() # random failure FAILURE
If this happens, we can provide a better hash function:
sage: discrete_log_rho(I(123456), I(1), operation='+', # needs sage.libs.pari ....: hash_function=lambda x: hash(x*x)) 123456
>>> from sage.all import * >>> discrete_log_rho(I(Integer(123456)), I(Integer(1)), operation='+', # needs sage.libs.pari ... hash_function=lambda x: hash(x*x)) 123456
AUTHOR:
Yann Laigle-Chapuy (2009-09-05)
- sage.groups.generic.has_order(P, n, operation='+')[source]¶
Generic function to test if a group element \(P\) has order exactly equal to a given positive integer \(n\).
INPUT:
P
– group element with respect to the specifiedoperation
n
– positive integer, or itsFactorization
operation
– string, either'+'
(default) or'*'
EXAMPLES:
sage: from sage.groups.generic import has_order sage: E.<P> = EllipticCurve(GF(71), [5,5]) sage: P.order() 57 sage: has_order(P, 57) True sage: has_order(P, factor(57)) True sage: has_order(P, 19) False sage: has_order(3*P, 19) True sage: has_order(3*P, 57) False
>>> from sage.all import * >>> from sage.groups.generic import has_order >>> E = EllipticCurve(GF(Integer(71)), [Integer(5),Integer(5)], names=('P',)); (P,) = E._first_ngens(1) >>> P.order() 57 >>> has_order(P, Integer(57)) True >>> has_order(P, factor(Integer(57))) True >>> has_order(P, Integer(19)) False >>> has_order(Integer(3)*P, Integer(19)) True >>> has_order(Integer(3)*P, Integer(57)) False
sage: R = Zmod(14981) sage: g = R(321) sage: g.multiplicative_order() 42 sage: has_order(g, 42, operation='*') True sage: has_order(g, factor(42), operation='*') True sage: has_order(g, 70, operation='*') False
>>> from sage.all import * >>> R = Zmod(Integer(14981)) >>> g = R(Integer(321)) >>> g.multiplicative_order() 42 >>> has_order(g, Integer(42), operation='*') True >>> has_order(g, factor(Integer(42)), operation='*') True >>> has_order(g, Integer(70), operation='*') False
Note
In some cases, order testing can be much faster than computing the order using
order_from_multiple()
.
- sage.groups.generic.linear_relation(P, Q, operation='+', identity=None, inverse=None, op=None)[source]¶
Function which solves the equation
a*P=m*Q
orP^a=Q^m
.Additive version: returns \((a,m)\) with minimal \(m>0\) such that \(aP=mQ\). Special case: if \(\left<P\right>\) and \(\left<Q\right>\) intersect only in \(\{0\}\) then \((a,m)=(0,n)\) where \(n\) is
Q.additive_order()
.Multiplicative version: returns \((a,m)\) with minimal \(m>0\) such that \(P^a=Q^m\). Special case: if \(\left<P\right>\) and \(\left<Q\right>\) intersect only in \(\{1\}\) then \((a,m)=(0,n)\) where \(n\) is
Q.multiplicative_order()
.ALGORITHM:
Uses the generic
bsgs()
function, and so works in general finite abelian groups.EXAMPLES:
An additive example (in an elliptic curve group):
sage: # needs sage.rings.finite_rings sage.schemes sage: F.<a> = GF(3^6,'a') sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1]) sage: P = E(a^5 + a^4 + a^3 + a^2 + a + 2, 0) sage: Q = E(2*a^3 + 2*a^2 + 2*a, a^3 + 2*a^2 + 1) sage: linear_relation(P, Q, '+') (1, 2) sage: P == 2*Q True
>>> from sage.all import * >>> # needs sage.rings.finite_rings sage.schemes >>> F = GF(Integer(3)**Integer(6),'a', names=('a',)); (a,) = F._first_ngens(1) >>> E = EllipticCurve([a**Integer(5) + Integer(2)*a**Integer(3) + Integer(2)*a**Integer(2) + Integer(2)*a, a**Integer(4) + a**Integer(3) + Integer(2)*a + Integer(1)]) >>> P = E(a**Integer(5) + a**Integer(4) + a**Integer(3) + a**Integer(2) + a + Integer(2), Integer(0)) >>> Q = E(Integer(2)*a**Integer(3) + Integer(2)*a**Integer(2) + Integer(2)*a, a**Integer(3) + Integer(2)*a**Integer(2) + Integer(1)) >>> linear_relation(P, Q, '+') (1, 2) >>> P == Integer(2)*Q True
A multiplicative example (in a finite field’s multiplicative group):
sage: # needs sage.rings.finite_rings sage: F.<a> = GF(3^6,'a') sage: a.multiplicative_order().factor() 2^3 * 7 * 13 sage: b = a^7 sage: c = a^13 sage: linear_relation(b, c, '*') (13, 7) sage: b^13 == c^7 True
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> F = GF(Integer(3)**Integer(6),'a', names=('a',)); (a,) = F._first_ngens(1) >>> a.multiplicative_order().factor() 2^3 * 7 * 13 >>> b = a**Integer(7) >>> c = a**Integer(13) >>> linear_relation(b, c, '*') (13, 7) >>> b**Integer(13) == c**Integer(7) True
- sage.groups.generic.merge_points(P1, P2, operation='+', identity=None, inverse=None, op=None, check=True)[source]¶
Return a group element whose order is the lcm of the given elements.
INPUT:
P1
– a pair \((g_1,n_1)\) where \(g_1\) is a group element of order \(n_1\)P2
– a pair \((g_2,n_2)\) where \(g_2\) is a group element of order \(n_2\)operation
– string;'+'
(default) or'*'
or other. If other, the following must be supplied:identity
– the identity element for the groupinverse
– a function of one argument giving the inverse of a group elementop
– a function of 2 arguments defining the group binary operation
OUTPUT: a pair \((g_3,n_3)\) where \(g_3\) has order \(n_3=\hbox{lcm}(n_1,n_2)\)
EXAMPLES:
sage: # needs sage.rings.finite_rings sage: from sage.groups.generic import merge_points sage: F.<a> = GF(3^6,'a') sage: b = a^7 sage: c = a^13 sage: ob = (3^6-1)//7 sage: oc = (3^6-1)//13 sage: merge_points((b,ob), (c,oc), operation='*') (a^4 + 2*a^3 + 2*a^2, 728) sage: d, od = merge_points((b,ob), (c,oc), operation='*') sage: od == d.multiplicative_order() True sage: od == lcm(ob, oc) True sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1]) sage: P = E(2*a^5 + 2*a^4 + a^3 + 2, a^4 + a^3 + a^2 + 2*a + 2) sage: P.order() 7 sage: Q = E(2*a^5 + 2*a^4 + 1, a^5 + 2*a^3 + 2*a + 2) sage: Q.order() 4 sage: R, m = merge_points((P,7), (Q,4), operation='+') sage: R.order() == m True sage: m == lcm(7,4) True
>>> from sage.all import * >>> # needs sage.rings.finite_rings >>> from sage.groups.generic import merge_points >>> F = GF(Integer(3)**Integer(6),'a', names=('a',)); (a,) = F._first_ngens(1) >>> b = a**Integer(7) >>> c = a**Integer(13) >>> ob = (Integer(3)**Integer(6)-Integer(1))//Integer(7) >>> oc = (Integer(3)**Integer(6)-Integer(1))//Integer(13) >>> merge_points((b,ob), (c,oc), operation='*') (a^4 + 2*a^3 + 2*a^2, 728) >>> d, od = merge_points((b,ob), (c,oc), operation='*') >>> od == d.multiplicative_order() True >>> od == lcm(ob, oc) True >>> # needs sage.libs.gap sage.rings.finite_rings sage.schemes >>> E = EllipticCurve([a**Integer(5) + Integer(2)*a**Integer(3) + Integer(2)*a**Integer(2) + Integer(2)*a, a**Integer(4) + a**Integer(3) + Integer(2)*a + Integer(1)]) >>> P = E(Integer(2)*a**Integer(5) + Integer(2)*a**Integer(4) + a**Integer(3) + Integer(2), a**Integer(4) + a**Integer(3) + a**Integer(2) + Integer(2)*a + Integer(2)) >>> P.order() 7 >>> Q = E(Integer(2)*a**Integer(5) + Integer(2)*a**Integer(4) + Integer(1), a**Integer(5) + Integer(2)*a**Integer(3) + Integer(2)*a + Integer(2)) >>> Q.order() 4 >>> R, m = merge_points((P,Integer(7)), (Q,Integer(4)), operation='+') >>> R.order() == m True >>> m == lcm(Integer(7),Integer(4)) True
- sage.groups.generic.multiple(a, n, operation='*', identity=None, inverse=None, op=None)[source]¶
Return either \(na\) or \(a^n\), where \(n\) is any integer and \(a\) is a Python object on which a group operation such as addition or multiplication is defined. Uses the standard binary algorithm.
INPUT: See the documentation for
discrete_logarithm()
.EXAMPLES:
sage: multiple(2, 5) 32 sage: multiple(RealField()('2.5'), 4) # needs sage.rings.real_mpfr 39.0625000000000 sage: multiple(2, -3) 1/8 sage: multiple(2, 100, '+') == 100*2 True sage: multiple(2, 100) == 2**100 True sage: multiple(2, -100,) == 2**-100 True sage: R.<x> = ZZ[] sage: multiple(x, 100) x^100 sage: multiple(x, 100, '+') 100*x sage: multiple(x, -10) 1/x^10
>>> from sage.all import * >>> multiple(Integer(2), Integer(5)) 32 >>> multiple(RealField()('2.5'), Integer(4)) # needs sage.rings.real_mpfr 39.0625000000000 >>> multiple(Integer(2), -Integer(3)) 1/8 >>> multiple(Integer(2), Integer(100), '+') == Integer(100)*Integer(2) True >>> multiple(Integer(2), Integer(100)) == Integer(2)**Integer(100) True >>> multiple(Integer(2), -Integer(100),) == Integer(2)**-Integer(100) True >>> R = ZZ['x']; (x,) = R._first_ngens(1) >>> multiple(x, Integer(100)) x^100 >>> multiple(x, Integer(100), '+') 100*x >>> multiple(x, -Integer(10)) 1/x^10
Idempotence is detected, making the following fast:
sage: multiple(1, 10^1000) 1 sage: # needs sage.schemes sage: E = EllipticCurve('389a1') sage: P = E(-1,1) sage: multiple(P, 10, '+') (645656132358737542773209599489/22817025904944891235367494656 : 525532176124281192881231818644174845702936831/3446581505217248068297884384990762467229696 : 1) sage: multiple(P, -10, '+') (645656132358737542773209599489/22817025904944891235367494656 : -528978757629498440949529703029165608170166527/3446581505217248068297884384990762467229696 : 1)
>>> from sage.all import * >>> multiple(Integer(1), Integer(10)**Integer(1000)) 1 >>> # needs sage.schemes >>> E = EllipticCurve('389a1') >>> P = E(-Integer(1),Integer(1)) >>> multiple(P, Integer(10), '+') (645656132358737542773209599489/22817025904944891235367494656 : 525532176124281192881231818644174845702936831/3446581505217248068297884384990762467229696 : 1) >>> multiple(P, -Integer(10), '+') (645656132358737542773209599489/22817025904944891235367494656 : -528978757629498440949529703029165608170166527/3446581505217248068297884384990762467229696 : 1)
- class sage.groups.generic.multiples(P, n, P0=None, indexed=False, operation='+', op=None)[source]¶
Bases:
object
Return an iterator which runs through
P0+i*P
fori
inrange(n)
.P
andP0
must be Sage objects in some group; if the operation is multiplication then the returned values are insteadP0*P**i
.EXAMPLES:
sage: list(multiples(1, 10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] sage: list(multiples(1, 10, 100)) [100, 101, 102, 103, 104, 105, 106, 107, 108, 109] sage: E = EllipticCurve('389a1') # needs sage.schemes sage: P = E(-1,1) # needs sage.schemes sage: for Q in multiples(P, 5): print((Q, Q.height()/P.height())) # needs sage.schemes ((0 : 1 : 0), 0.000000000000000) ((-1 : 1 : 1), 1.00000000000000) ((10/9 : -35/27 : 1), 4.00000000000000) ((26/361 : -5720/6859 : 1), 9.00000000000000) ((47503/16641 : 9862190/2146689 : 1), 16.0000000000000) sage: R.<x> = ZZ[] sage: list(multiples(x, 5)) [0, x, 2*x, 3*x, 4*x] sage: list(multiples(x, 5, operation='*')) [1, x, x^2, x^3, x^4] sage: list(multiples(x, 5, indexed=True)) [(0, 0), (1, x), (2, 2*x), (3, 3*x), (4, 4*x)] sage: list(multiples(x, 5, indexed=True, operation='*')) [(0, 1), (1, x), (2, x^2), (3, x^3), (4, x^4)] sage: for i,y in multiples(x, 5, indexed=True): print("%s times %s = %s"%(i,x,y)) 0 times x = 0 1 times x = x 2 times x = 2*x 3 times x = 3*x 4 times x = 4*x sage: for i,n in multiples(3, 5, indexed=True, operation='*'): ....: print("3 to the power %s = %s" % (i,n)) 3 to the power 0 = 1 3 to the power 1 = 3 3 to the power 2 = 9 3 to the power 3 = 27 3 to the power 4 = 81
>>> from sage.all import * >>> list(multiples(Integer(1), Integer(10))) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(multiples(Integer(1), Integer(10), Integer(100))) [100, 101, 102, 103, 104, 105, 106, 107, 108, 109] >>> E = EllipticCurve('389a1') # needs sage.schemes >>> P = E(-Integer(1),Integer(1)) # needs sage.schemes >>> for Q in multiples(P, Integer(5)): print((Q, Q.height()/P.height())) # needs sage.schemes ((0 : 1 : 0), 0.000000000000000) ((-1 : 1 : 1), 1.00000000000000) ((10/9 : -35/27 : 1), 4.00000000000000) ((26/361 : -5720/6859 : 1), 9.00000000000000) ((47503/16641 : 9862190/2146689 : 1), 16.0000000000000) >>> R = ZZ['x']; (x,) = R._first_ngens(1) >>> list(multiples(x, Integer(5))) [0, x, 2*x, 3*x, 4*x] >>> list(multiples(x, Integer(5), operation='*')) [1, x, x^2, x^3, x^4] >>> list(multiples(x, Integer(5), indexed=True)) [(0, 0), (1, x), (2, 2*x), (3, 3*x), (4, 4*x)] >>> list(multiples(x, Integer(5), indexed=True, operation='*')) [(0, 1), (1, x), (2, x^2), (3, x^3), (4, x^4)] >>> for i,y in multiples(x, Integer(5), indexed=True): print("%s times %s = %s"%(i,x,y)) 0 times x = 0 1 times x = x 2 times x = 2*x 3 times x = 3*x 4 times x = 4*x >>> for i,n in multiples(Integer(3), Integer(5), indexed=True, operation='*'): ... print("3 to the power %s = %s" % (i,n)) 3 to the power 0 = 1 3 to the power 1 = 3 3 to the power 2 = 9 3 to the power 3 = 27 3 to the power 4 = 81
- sage.groups.generic.order_from_bounds(P, bounds, d=None, operation='+', identity=None, inverse=None, op=None)[source]¶
Generic function to find order of a group element, given only upper and lower bounds for a multiple of the order (e.g. bounds on the order of the group of which it is an element)
INPUT:
P
– a Sage object which is a group elementbounds
– a 2-tuple(lb,ub)
such thatm*P=0
(orP**m=1
) for somem
withlb<=m<=ub
d
– (optional) a positive integer; onlym
which are multiples of this will be consideredoperation
– string;'+'
(default) or'*'
or other. If other, the following must be supplied:identity
– the identity element for the groupinverse
– a function of one argument giving the inverse of a group elementop
– a function of 2 arguments defining the group binary operation
Note
Typically
lb
andub
will be bounds on the group order, and from previous calculation we know that the group order is divisible byd
.EXAMPLES:
sage: from sage.groups.generic import order_from_bounds sage: # needs sage.rings.finite_rings sage: k.<a> = GF(5^5) sage: b = a^4 sage: order_from_bounds(b, (5^4, 5^5), operation='*') 781 sage: # needs sage.rings.finite_rings sage.schemes sage: E = EllipticCurve(k, [2,4]) sage: P = E(3*a^4 + 3*a, 2*a + 1) sage: bounds = Hasse_bounds(5^5) sage: Q = E(0,2) sage: order_from_bounds(Q, bounds, operation='+') 7 sage: order_from_bounds(P, bounds, 7, operation='+') 3227 sage: # needs sage.rings.number_field sage: K.<z> = CyclotomicField(230) sage: w = z^50 sage: order_from_bounds(w, (200, 250), operation='*') 23
>>> from sage.all import * >>> from sage.groups.generic import order_from_bounds >>> # needs sage.rings.finite_rings >>> k = GF(Integer(5)**Integer(5), names=('a',)); (a,) = k._first_ngens(1) >>> b = a**Integer(4) >>> order_from_bounds(b, (Integer(5)**Integer(4), Integer(5)**Integer(5)), operation='*') 781 >>> # needs sage.rings.finite_rings sage.schemes >>> E = EllipticCurve(k, [Integer(2),Integer(4)]) >>> P = E(Integer(3)*a**Integer(4) + Integer(3)*a, Integer(2)*a + Integer(1)) >>> bounds = Hasse_bounds(Integer(5)**Integer(5)) >>> Q = E(Integer(0),Integer(2)) >>> order_from_bounds(Q, bounds, operation='+') 7 >>> order_from_bounds(P, bounds, Integer(7), operation='+') 3227 >>> # needs sage.rings.number_field >>> K = CyclotomicField(Integer(230), names=('z',)); (z,) = K._first_ngens(1) >>> w = z**Integer(50) >>> order_from_bounds(w, (Integer(200), Integer(250)), operation='*') 23
- sage.groups.generic.order_from_multiple(P, m, plist=None, factorization=None, check=True, operation='+', identity=None, inverse=None, op=None)[source]¶
Generic function to find order of a group element given a multiple of its order.
See
bsgs()
for full explanation of the inputs.INPUT:
P
– a Sage object which is a group elementm
– Sage integer which is a multiple of the order ofP
, i.e. we require thatm*P=0
(orP**m=1
)check
– a Boolean (default:True
), indicating whether we check ifm
really is a multiple of the orderfactorization
– the factorization ofm
, orNone
in which case this function will need to factorm
plist
– list of the prime factors ofm
, orNone
. Kept for compatibility only, prefer the use offactorization
operation
– string:'+'
(default),'*'
orNone
identity
– the identity element of the groupinverse
– function of 1 argumentx
, returning inverse ofx
op
– function of 2 argumentsx
,y
returningx*y
in the group
Note
It is more efficient for the caller to factor
m
and cache the factors for subsequent calls.EXAMPLES:
sage: from sage.groups.generic import order_from_multiple sage: # needs sage.rings.finite_rings sage: k.<a> = GF(5^5) sage: b = a^4 sage: order_from_multiple(b, 5^5 - 1, operation='*') 781 sage: # needs sage.rings.finite_rings sage.schemes sage: E = EllipticCurve(k, [2,4]) sage: P = E(3*a^4 + 3*a, 2*a + 1) sage: M = E.cardinality(); M 3227 sage: F = M.factor() sage: order_from_multiple(P, M, factorization=F, operation='+') 3227 sage: Q = E(0,2) sage: order_from_multiple(Q, M, factorization=F, operation='+') 7 sage: # needs sage.rings.number_field sage: K.<z> = CyclotomicField(230) sage: w = z^50 sage: order_from_multiple(w, 230, operation='*') 23 sage: # needs sage.modules sage.rings.finite_rings sage: F = GF(2^1279,'a') sage: n = F.cardinality() - 1 # Mersenne prime sage: order_from_multiple(F.random_element(), n, ....: factorization=[(n,1)], operation='*') == n True sage: # needs sage.rings.finite_rings sage: K.<a> = GF(3^60) sage: order_from_multiple(a, 3^60 - 1, operation='*', check=False) 42391158275216203514294433200
>>> from sage.all import * >>> from sage.groups.generic import order_from_multiple >>> # needs sage.rings.finite_rings >>> k = GF(Integer(5)**Integer(5), names=('a',)); (a,) = k._first_ngens(1) >>> b = a**Integer(4) >>> order_from_multiple(b, Integer(5)**Integer(5) - Integer(1), operation='*') 781 >>> # needs sage.rings.finite_rings sage.schemes >>> E = EllipticCurve(k, [Integer(2),Integer(4)]) >>> P = E(Integer(3)*a**Integer(4) + Integer(3)*a, Integer(2)*a + Integer(1)) >>> M = E.cardinality(); M 3227 >>> F = M.factor() >>> order_from_multiple(P, M, factorization=F, operation='+') 3227 >>> Q = E(Integer(0),Integer(2)) >>> order_from_multiple(Q, M, factorization=F, operation='+') 7 >>> # needs sage.rings.number_field >>> K = CyclotomicField(Integer(230), names=('z',)); (z,) = K._first_ngens(1) >>> w = z**Integer(50) >>> order_from_multiple(w, Integer(230), operation='*') 23 >>> # needs sage.modules sage.rings.finite_rings >>> F = GF(Integer(2)**Integer(1279),'a') >>> n = F.cardinality() - Integer(1) # Mersenne prime >>> order_from_multiple(F.random_element(), n, ... factorization=[(n,Integer(1))], operation='*') == n True >>> # needs sage.rings.finite_rings >>> K = GF(Integer(3)**Integer(60), names=('a',)); (a,) = K._first_ngens(1) >>> order_from_multiple(a, Integer(3)**Integer(60) - Integer(1), operation='*', check=False) 42391158275216203514294433200
- sage.groups.generic.structure_description(G, latex=False)[source]¶
Return a string that tries to describe the structure of
G
.This methods wraps GAP’s
StructureDescription
method.For full details, including the form of the returned string and the algorithm to build it, see GAP’s documentation.
INPUT:
latex
– boolean (default:False
); ifTrue
, return a LaTeX formatted string
OUTPUT: string
Warning
From GAP’s documentation: The string returned by
StructureDescription
is not an isomorphism invariant: non-isomorphic groups can have the same string value, and two isomorphic groups in different representations can produce different strings.EXAMPLES:
sage: # needs sage.groups sage: G = CyclicPermutationGroup(6) sage: G.structure_description() 'C6' sage: G.structure_description(latex=True) 'C_{6}' sage: G2 = G.direct_product(G, maps=False) sage: LatexExpr(G2.structure_description(latex=True)) C_{6} \times C_{6}
>>> from sage.all import * >>> # needs sage.groups >>> G = CyclicPermutationGroup(Integer(6)) >>> G.structure_description() 'C6' >>> G.structure_description(latex=True) 'C_{6}' >>> G2 = G.direct_product(G, maps=False) >>> LatexExpr(G2.structure_description(latex=True)) C_{6} \times C_{6}
This method is mainly intended for small groups or groups with few normal subgroups. Even then there are some surprises:
sage: D3 = DihedralGroup(3) # needs sage.groups sage: D3.structure_description() # needs sage.groups 'S3'
>>> from sage.all import * >>> D3 = DihedralGroup(Integer(3)) # needs sage.groups >>> D3.structure_description() # needs sage.groups 'S3'
We use the Sage notation for the degree of dihedral groups:
sage: D4 = DihedralGroup(4) # needs sage.groups sage: D4.structure_description() # needs sage.groups 'D4'
>>> from sage.all import * >>> D4 = DihedralGroup(Integer(4)) # needs sage.groups >>> D4.structure_description() # needs sage.groups 'D4'
Works for finitely presented groups (Issue #17573):
sage: F.<x, y> = FreeGroup() # needs sage.groups sage: G = F / [x^2*y^-1, x^3*y^2, x*y*x^-1*y^-1] # needs sage.groups sage: G.structure_description() # needs sage.groups 'C7'
>>> from sage.all import * >>> F = FreeGroup(names=('x', 'y',)); (x, y,) = F._first_ngens(2)# needs sage.groups >>> G = F / [x**Integer(2)*y**-Integer(1), x**Integer(3)*y**Integer(2), x*y*x**-Integer(1)*y**-Integer(1)] # needs sage.groups >>> G.structure_description() # needs sage.groups 'C7'
And matrix groups (Issue #17573):
sage: groups.matrix.GL(4,2).structure_description() # needs sage.libs.gap sage.modules 'A8'
>>> from sage.all import * >>> groups.matrix.GL(Integer(4),Integer(2)).structure_description() # needs sage.libs.gap sage.modules 'A8'