aboutsummaryrefslogtreecommitdiff
path: root/kummer_degree.sage
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano.tronto@gmail.com>2021-02-15 11:30:51 +0100
committerSebastiano Tronto <sebastiano.tronto@gmail.com>2021-02-15 11:30:51 +0100
commit1437da00eeb4a7581df1b54ffc7680ffb491d690 (patch)
tree54d380e0c045c63a95f80691111fdd8c01d60973 /kummer_degree.sage
parentfe5b1e69a3b219d548a8b8b044660e9c936645cc (diff)
downloadkummer-degrees-1437da00eeb4a7581df1b54ffc7680ffb491d690.tar.gz
kummer-degrees-1437da00eeb4a7581df1b54ffc7680ffb491d690.zip
Fixed for python3
Diffstat (limited to '')
-rw-r--r--kummer_degree.sage572
1 files changed, 320 insertions, 252 deletions
diff --git a/kummer_degree.sage b/kummer_degree.sage
index 161e8c9..3085efd 100644
--- a/kummer_degree.sage
+++ b/kummer_degree.sage
@@ -1,16 +1,3 @@
1#############################################################################
2# This program allows one to compute the degree of certain field extensions #
3# of the rational numbers. In particular, it can compute the degree over Q #
4# of extensions of the form Q( sqrt[N](G), \zeta_M ), where: #
5# - N and M are integers with N dividing M; #
6# - G is a finitely generated subgroup of the multiplicative group of Q #
7# - \zeta_M is a primitive M-th root of unity #
8# The group G does not have to be given in a particular format. A finite #
9# set of generators is sufficient. #
10#############################################################################
11
12from sage.misc.cachefunc import CachedFunction
13
14# Computes the "adelic Kummer failure", i.e. the degrees of the intersection 1# Computes the "adelic Kummer failure", i.e. the degrees of the intersection
15# of the the Kummer extension Q(\sqrt{2^n}{G}) with the M-th cyclotomic field 2# of the the Kummer extension Q(\sqrt{2^n}{G}) with the M-th cyclotomic field
16# over Q_{2^n}. 3# over Q_{2^n}.
@@ -18,12 +5,12 @@ from sage.misc.cachefunc import CachedFunction
18# Input: a good basis B for the torsion-free group G, organized as a list of 5# Input: a good basis B for the torsion-free group G, organized as a list of
19# lists, and a non negative integer d. They have to satisfy the following: 6# lists, and a non negative integer d. They have to satisfy the following:
20# 1. Each list B[i] contains all basis elements of 2-divisibility i. 7# 1. Each list B[i] contains all basis elements of 2-divisibility i.
21# 2. The basis given by B is 2-maximal, that is to say it satisfies Theorem 8# 2. The basis given by B is 2-maximal, that is to say it satisfies Theorem 14
22# 14 of Debry-Perucca; i.e. each element of B[i] is, up to plus or minus 9# of Debry-Perucca; i.e. each element of B[i] is, up to plus or minus 1, the
23# 1, the 2^i-th power of a strongly 2-indivisible rational. 10# 2^i-th power of a strongly 2-indivisible rational.
24# 3. For i != d every element of B[i] is positive, and B[d][0] is the only 11# 3. For i != d every element of B[i] is positive, and B[d][0] is the only
25# negative element of B[d] (if d=-1, then there is no negative element). 12# negative element of B[d] (if d=-1, then there is no negative element).
26# 3'. Notice that the existence on negative elements in B[0] does not change 13# 3'. Notice that the existence on negative elements in B[0] does not influence
27# the correctness of the algorithm; in fact, the function adjust_sign 14# the correctness of the algorithm; in fact, the function adjust_sign
28# produces a basis that may have negative elements of divisibility 0, and 15# produces a basis that may have negative elements of divisibility 0, and
29# this basis is given as input for adelic_failure_gb. 16# this basis is given as input for adelic_failure_gb.
@@ -32,9 +19,9 @@ from sage.misc.cachefunc import CachedFunction
32# integer such that the intersection of Q(\sqrt{2^n}{G}) with Q_\infty is 19# integer such that the intersection of Q(\sqrt{2^n}{G}) with Q_\infty is
33# contained in Q_M0. 20# contained in Q_M0.
34# The table ad_fail has N rows, where N is defined below. 21# The table ad_fail has N rows, where N is defined below.
35# Each row R=ad_fail[i] contains a variable number of pairs (d,r), where d 22# Each row R=ad_fail[i] contains a variable number of pairs (d,r), where d is a
36# is a divisor of M0 and r is the degree of Q(\sqrt{2^{i+1}}{G}) \cap Q_d 23# divisor of M0 and r is the degree of Q(\sqrt{2^{i+1}}{G}) \cap Q_d over
37# over Q_{2^n}. 24# Q_{2^n}.
38# Each divisor of M appears at most once on each row, and the last element of 25# Each divisor of M appears at most once on each row, and the last element of
39# the last row is of the form (M0,r0). 26# the last row is of the form (M0,r0).
40def adelic_failure_gb( B, d ): 27def adelic_failure_gb( B, d ):
@@ -42,48 +29,48 @@ def adelic_failure_gb( B, d ):
42 # The table to be returned (or printed at the end), as described above. 29 # The table to be returned (or printed at the end), as described above.
43 ad_fail = [] 30 ad_fail = []
44 31
45 # N is such that for every n > N the adelic failure of Q(\sqrt{2^n}{G}) 32 # N is such that for every n > N the adelic failure of Q(\sqrt{2^n}{G}) is
46 # is the same as that of Q(\sqrt{2^N}{G}). 33 # the same as that of Q(\sqrt{2^N}{G}).
47 # We always have to include n=3, because of problem with sqrt(2) in Q_8 34 # We always have to include n=3, because of problem with sqrt(2) in Q_8 (in
48 # (in theory, this is not necessary in some cases, e.g. if 2 does not 35 # theory, this is not necessary in some cases, e.g. if 2 does not divide
49 # divide any element of G). 36 # any element of G).
50 # If the negative generator is on the last level, we need to increase N 37 # If the negative generator is on the last level, we need to increase N by
51 # by 1, because it would contribute to the shortlist in the next level 38 # 1, because it would contribute to the shortlist in the next level (by
52 # (by taking the root of an even power). 39 # taking the root of an even power).
53 if d == len(B)-1: 40 if d == len(B)-1:
54 N = max(3,len(B)+1) 41 N = max(3,len(B)+1)
55 else: 42 else:
56 N = max(3,len(B)) 43 N = max(3,len(B))
57 44
58 # The intersection is given by adding the square roots of the elements of 45 # The intersection is given by adding the square roots of the elements of
59 # this shortlist (and a "special element", not always of the form sqrt d, 46 # this shortlist (and a "special element", not always of the form sqrt(d),
60 # coming from taking a suitable root of a negative generator; this 47 # coming from taking a suitable root of a negative generator; this special
61 # special element is dealt with later). The shortlist grows at each step, 48 # element is dealt with later). The shortlist grows at each step, so we
62 # so we declare it before starting to loop over n and we build it 49 # declare it before starting to loop over n and we build it incrementally.
63 # incrementally. The "special element" is of the form \zeta_{2^n}\sqrt b, 50 # The "special element" is of the form \zeta_{2^n}\sqrt{b}, which we encode
64 # which we encode as (n,b). We use the value (1,1) (special element -1) 51 # as (n,b). We use the value (1,1) (special element -1) to say that there
65 # to say that there is no special element at this level. 52 # isno special element at this level.
66 shortlist = [] 53 shortlist = []
67 special_element = (1,1) 54 special_element = (1,1)
68 55
69 # The integers M, giving the smallest cyclotomic field in which lies the 56 # The integers M, giving the smallest cyclotomic field in which lies the
70 # whole intersection with Q_\infty, grows with n. As with the shortlist, 57 # whole intersection with Q_\infty, also grows with n. As with the
71 # we declare it here and increase it appropriately at each step. 58 # shortlist, we declare it here and increase it appropriately at each step.
72 M = 1 59 M = 1
73 60
74 for n in range( 1, N+1 ): 61 for n in range( 1, N+1 ): # 1 \leq n \leq N
75 62
76 # We add the new elements to the shortlist, modifying M if needed. 63 # We add the new elements to the shortlist, modifying M if needed.
77 # This is not done in case we are in the extra "fake" level (this 64 # This is not done in case we are in the extra "fake" level (this case
78 # case dealt with immediately below). 65 # dealt with immediately below).
79 if n-1 < len(B): 66 if n-1 < len(B):
80 for g in B[n-1]: 67 for g in B[n-1]:
81 # Case of negative g 68 # Case of negative g
82 if g < 0 and n > 1: 69 if g < 0 and n > 1:
83 # Special element of the form \zeta_{2^{n+1}}\sqrt(b). 70 # Special element of the form \zeta_{2^{n+1}}\sqrt(b).
84 # It is contained in Q_{lcm(2^{n+1},cyc_emb(b))}, except 71 # It is contained in Q_{lcm(2^{n+1},cyc_emb(b))}, except in
85 # in case n=2 and b=2, in which case it is contained in 72 # case n=2 and b=2, in which case it is contained in Q_4.
86 # Q_4. We store it as as pair ( n+1, b ). 73 # We store it as as pair ( n+1, b ).
87 special_element = ( n+1, abs(g)^(1/(2^(n-1))) ) 74 special_element = ( n+1, abs(g)^(1/(2^(n-1))) )
88 M = lcm( M, special_embed( special_element ) ) 75 M = lcm( M, special_embed( special_element ) )
89 else: 76 else:
@@ -91,8 +78,8 @@ def adelic_failure_gb( B, d ):
91 shortlist.append( b ) 78 shortlist.append( b )
92 M = lcm( M, cyc_embed(b) ) 79 M = lcm( M, cyc_embed(b) )
93 80
94 # We add a root of an even power of the negative generator, as soon 81 # We add a root of an even power of the negative generator, as soon as
95 # as we are beyond its level. 82 # we are beyond its level.
96 if d != -1 and n == d+2: 83 if d != -1 and n == d+2:
97 b = abs(B[d][0])^(1/2^d) 84 b = abs(B[d][0])^(1/2^d)
98 shortlist.append( b ) 85 shortlist.append( b )
@@ -114,20 +101,19 @@ def adelic_failure_gb( B, d ):
114 101
115 # For each divisor dM of M, compute the degree of the intersection of 102 # For each divisor dM of M, compute the degree of the intersection of
116 # Q(\sqrt{2^n}{G}) with Q_dM over Q_{2^n}. We need to compute the 103 # Q(\sqrt{2^n}{G}) with Q_dM over Q_{2^n}. We need to compute the
117 # number r of elements in the subgroup of G generated by the 104 # number r of elements in the subgroup of G generated by the shortlist
118 # shortlist that lie in Q_dM. This is going to be a power of 2. The 105 # that lie in Q_dM. This is going to be a power of 2. The sought degree
119 # sought degree will be r, up to considering some special cases 106 # will be r, up to considering some special cases (described below).
120 # (described below).
121 # 107 #
122 # This algorithm could be inefficient for groups of big rank. It can 108 # This algorithm could be inefficient for groups of big rank. It can be
123 # be improved by precomputing subgroups of G/G^2 and the cyclotomic 109 # improved by precomputing subgroups of G/G^2 and the cyclotomic fields
124 # fields containing them. 110 # containing them.
125 111
126 aux = [] # Next line of ad_fail table 112 aux = [] # Next line of ad_fail table
127 113
128 for dM in divisors( M ): 114 for dM in divisors( M ):
129 # We only care of the intersection with Q_dM if it contains the 115 # We only care of the intersection with Q_dM if it contains the 2^n
130 # 2^n roots of unity. 116 # roots of unity.
131 if dM % (2^n) != 0: 117 if dM % (2^n) != 0:
132 continue 118 continue
133 119
@@ -144,7 +130,7 @@ def adelic_failure_gb( B, d ):
144 if n <= d and dM % (2^(n+1)) == 0 and n > 1: 130 if n <= d and dM % (2^(n+1)) == 0 and n > 1:
145 r *= 2 131 r *= 2
146 132
147 # We loose a factor of 2 if we have sqrt(2) in Q_8 or \zeta_8 in 133 # We loose a factor of 2 if we have sqrt(2) in Q_8 or \zeta_8 2 in
148 # Q_4. 134 # Q_4.
149 if 8 in H and dM % 8 == 0 and (n >= 3 or (n == 2 and n <= d)): 135 if 8 in H and dM % 8 == 0 and (n >= 3 or (n == 2 and n <= d)):
150 r = r/2 136 r = r/2
@@ -153,11 +139,11 @@ def adelic_failure_gb( B, d ):
153 # We have to consider all possible special elements arising from 139 # We have to consider all possible special elements arising from
154 # multiplying the given element with the other elements of the 140 # multiplying the given element with the other elements of the
155 # shortlist. 141 # shortlist.
156 # If any of them is of the form \zeta_8 2q for q a square, there 142 # If any of them is of the form \zeta_8 2q for q a square, there is
157 # is nothing to do: in fact \zeta_8 2 embeds in Q_4, which 143 # nothing to do: in fact \zeta_8 2 embeds in Q_4, which coincides
158 # coincides with Q_{2^n} (n must be 2); so we would double the 144 # with Q_{2^n} (n must be 2); so we would double the degree because
159 # degree because of the existence of the special element, but 145 # of the existence of the special element, butthen we would loose
160 # then we would loose another factor of 2 because of this. 146 # another factor of 2 because of this.
161 if special_element != (1,1) and special_element[0] == n+1: 147 if special_element != (1,1) and special_element[0] == n+1:
162 nothing_to_do = False 148 nothing_to_do = False
163 intersecting_QdM = False 149 intersecting_QdM = False
@@ -184,8 +170,11 @@ def cyc_embed( b ):
184 m *= 4 170 m *= 4
185 return abs(m) 171 return abs(m)
186 172
187# Computes the minimal cyclotomic field containing \zeta_{2^n}\sqrt(b). 173# Computes the minimal cyclotomic field containing \zeta_{2^n}\sqrt(b),
188def special_embed( (n,b) ): 174# where (n,b)=p
175def special_embed( p ):
176 n = p.first
177 n = p.second
189 m = squarefree_part(b) 178 m = squarefree_part(b)
190 if n == 3 and m % 2 == 0: 179 if n == 3 and m % 2 == 0:
191 return 4 * cyc_embed(m/2) 180 return 4 * cyc_embed(m/2)
@@ -201,7 +190,7 @@ def special_embed( (n,b) ):
201# degree of Q_{l^m,l^n} over Q_{l^m}, where l = L[i] 190# degree of Q_{l^m,l^n} over Q_{l^m}, where l = L[i]
202def total_l_adic_failure( B ): 191def total_l_adic_failure( B ):
203 192
204 bp = bad_primes(B) 193 bp = bad_primes( B )
205 ret = ( bp, [] ) 194 ret = ( bp, [] )
206 195
207 for l in bp: 196 for l in bp:
@@ -210,32 +199,35 @@ def total_l_adic_failure( B ):
210 return ret 199 return ret
211 200
212# Computes the "bad primes", i.e. the ones for which the l-adic part is not 201# Computes the "bad primes", i.e. the ones for which the l-adic part is not
213# maximal. Used by total_l_adic_failure. 202# maximal. Used by l_adic_degree and total_l_adic_failure.
214def bad_primes( B ): 203def bad_primes( B ):
215 M = exponent_matrix( B ) 204 M = exponent_matrix( B )
216 (a,b) = M.dimensions() 205 (a,b) = M.dimensions()
217
218 if a > b or M.rank() < a: 206 if a > b or M.rank() < a:
219 print "This is not a basis" 207 print( "This is not a basis" )
220 return 208 return
221 209
222 # Compute which primes l divide all minors of the exponent matrix 210 # Compute which primes l divide all minors of the exponent matrix
223 bad_p = list( prime_factors( gcd( M.minors(a) ) ) ) 211 ms = M.minors( a )
224 if 2 not in bad_p: 212 d = ms[0]
225 bad_p += [2] # 2 is always bad 213 for m in ms:
214 d = gcd( d, m )
215 bad_primes = list( prime_factors( d ) )
216 if 2 not in bad_primes:
217 bad_primes += [2] # 2 is always bad
218 bad_primes.sort() # Ensures 2 is always first
226 219
227 return sorted(bad_p) # Ensures 2 is always first 220 return bad_primes
228 221
229# Computes the l-adic failure for a specific l. Returns a "table" as 222# Computes the l-adic failure for a specific l. Returns a "table" as described
230# described above "total_l_adic_failure". 223# above "total_l_adic_failure".
231# B is any basis for G. 224# B is any basis for G.
232def l_adic_failure( B, l ): 225def l_adic_failure( B, l ):
233 226
234 r = len(B) 227 r = len(B)
235 GB = make_good_basis( B, l ) 228 GB = make_good_basis( B, l )
236 229
237 # Computes the parameters for G over Q4. For l odd, they are the same as 230 # Computes the parameters over Q4. For l odd, they are the same as over Q.
238 # over Q.
239 p = parameters_Q4( GB, l ) 231 p = parameters_Q4( GB, l )
240 maxM = max( [ sum(x) for x in p ] ) 232 maxM = max( [ sum(x) for x in p ] )
241 maxN = max( maxM, len(GB)-1 ) 233 maxN = max( maxM, len(GB)-1 )
@@ -277,6 +269,7 @@ def l_adic_failure_from_data( B, l, tablel, M, N ):
277 return 1 269 return 1
278 r = len(B) 270 r = len(B)
279 271
272 # Basically, the "dual" of what we do in l_adic_degree.
280 if m > len(tablel): 273 if m > len(tablel):
281 if n > len(tablel): 274 if n > len(tablel):
282 return l^tablel[-1][1] 275 return l^tablel[-1][1]
@@ -285,70 +278,127 @@ def l_adic_failure_from_data( B, l, tablel, M, N ):
285 278
286 return l^(r*n-tablel[m-1][0][n-1]) 279 return l^(r*n-tablel[m-1][0][n-1])
287 280
288# Returns the l-dvisibility parameters of G over Q, given a good basis b of 281# Computes the l-divisibility parameters of G over Q4, given a good basis b
289# G, as a list of pairs (di,hi).
290def parameters_Q( b, l ):
291 if l != 2:
292 return [x for a in [[(i,0)]*len(b[i]) for i in range(len(b))] \
293 for x in a]
294 else:
295 return [x for a in [[(i,1-max(0,sgn(g))) for g in b[i]] \
296 for i in range(len(b))] for x in a]
297
298# Computes the l-divisibility parameters of G over Q4, given a good basis gb
299# over Q for G. Returns a list of pairs (di,hi). 282# over Q for G. Returns a list of pairs (di,hi).
300# If l is odd it just uses the good basis given to compute the parameters. 283# If l is odd it just uses the good basis given to compute the parameters.
301# If l=2, it uses the results of PST-2.
302def parameters_Q4( gb, l ): 284def parameters_Q4( gb, l ):
285 # Converts from "good basis format" to simple list
286 b = []
287 for x in gb:
288 b += x
289 ret = []
290
303 if l != 2: 291 if l != 2:
304 return parameters_Q( gb, l ) 292 for i in range( len( gb ) ):
293 for j in gb[i]:
294 ret.append( (i,0) )
295 return ret
305 else: 296 else:
306 # Converts from "good basis format" to simple list 297 R.<y> = PolynomialRing( QQ )
307 b = [ x for a in gb for x in a ] 298 pol = R(y^2+1)
308 299 Q4.<eye> = NumberField( pol ) # I already use i for other things
309 M = exponent_matrix( b )
310 300
311 # Thanks to my bad notation, the elements of b are what are called 301 # Factorize basis elements over Q4 and so on.
312 # g_i in the article, while the elements of bb will be the b_i's. 302 d = []
313 bb = [ abs(b[i])^(2^(-divisibility(M[i],2))) for i in range(len(b)) ] 303 B = []
304 h = []
305 ideals_list = set()
306 M = [] # Exponent matrix of the Bi's
314 307
315 # Computing a combination of bb elements of the form 2 * square. 308 # Pre-process to find all ideals appearing in the factorization and fix
316 MM = exponent_matrix( bb + [2] ).change_ring( GF( 2 ) ) 309 # a chosen generator for each of them. This is important in order to
317 310 # compute the "sign" (h-parameter) of an element with respect to it Bi.
318 for a in MM.kernel().basis(): 311 for g in b:
319 if a[-1] != 0: 312 factorization_list = list( Q4.ideal(g).factor() )
320 # the vector a[0:-1] gives the coefficients for a combination 313 ideals_list |= set( [ x[0] for x in factorization_list ] )
321 # of the b_i's of the form 2 * square. 314 ideals_list = list( ideals_list )
322 315 # Chooses a generator of each principal ideal in the list
323 # Index of asis elements that do appear in the combination 316 irreducibles_list = [ J.gens_reduced()[0] for J in ideals_list ]
324 c = [ i for i in range(len(a[0:-1])) if a[i] != 0 ] 317
325 318 # Compute the Q4-parameters of the given basis b. Also computes the
326 # Change of basis to include the element of the form 319 # exponent matrix of the Bi's
327 # 2 * square to some power. 320 for g in b:
328 div_max = -1 321 factorization_list = list( Q4.ideal(g).factor() )
329 i_max = -1 322 exps = [ x[1] for x in factorization_list ]
330 for j in c: 323 d.append( divisibility( exps, l ) )
331 if divisibility(M[j],2) > div_max: 324 Bg = 1
332 div_max = divisibility(M[j],2) 325 for j in range(len(factorization_list)):
333 i_max = j 326 a = 0
334 b[i_max] = prod([b[j]^(2^(div_max-divisibility(M[j],2))) \ 327 for i in range( len( ideals_list ) ):
335 for j in c]) 328 if ideals_list[i] == factorization_list[j][0]:
336 M = exponent_matrix(b) 329 a = irreducibles_list[i]
330 break
331 Bg *= a ^ (exps[j]/(l^d[-1]))
332 B.append(Bg)
333 u = g / (Bg^(l^d[-1]))
334 if not u.is_unit():
335 print( "Error: g is not the right power of the computed Bg." )
336 print( "g:", g, ", Bg:", Bg, ", exponent:", l^d[-1] )
337 if u == 1:
338 h.append( 0 )
339 elif u == -1:
340 h.append( 1 )
341 else:
342 h.append( 2 )
343
344 # Make the exponent matrix M (for now as a list of rows)
345 for g in B:
346 row = [0] * len(ideals_list)
347 for i in range(len(ideals_list)):
348 I = ideals_list[i]
349 ee = 1
350 while (I^ee).divides(g):
351 ee += 1
352 row[i] = ee-1
353 M.append(row)
337 354
338 # Return the parameters, changing only those of the element 355 # If the Bi's are not strongly independent, apply the algorithm (only
339 # of highest divisibility that appears in the combination. 356 # once) to produce a new basis. The new basis has maximal parameters.
340 ret = [(divisibility(M[i],2),1-max(0,sgn(b[i]))) \ 357 coeffs = find_combination( matrix(M), l )
341 for i in range(len(b)) ] 358 if coeffs != []:
342 d1, h1 = ret[i_max]
343 if d1 == 1:
344 h1 = 1 - h1
345 elif d1 == 0:
346 h1 = 2
347 ret[i_max] = ( d1+1, h1 )
348 359
349 return ret 360 maxi = -1
361 maxd = -1
362 for i in range(len(d)):
363 if d[i] > maxd and coeffs[i] != 0:
364 maxd = d[i]
365 maxi = i
350 366
351 return parameters_Q( gb, 2 ) 367 x = [(a/coeffs[maxi]).lift() for a in coeffs] # Now a vector of int
368
369 new_element = 1
370 for i in range(len(d)):
371 new_element *= b[i]^( x[i] * l^(d[maxi]-d[i]) )
372
373 b[maxi] = new_element
374
375 # Compute new B, d and so on.
376 factorization_list = list( Q4.ideal(b[maxi]).factor() )
377 exps = [ x[1] for x in factorization_list ]
378 d[maxi] = divisibility( exps, l )
379 Bg = 1
380
381 for j in range(len(factorization_list)):
382 a = 0
383 for i in range( len( ideals_list ) ):
384 if ideals_list[i] == factorization_list[j][0]:
385 a = irreducibles_list[i]
386 break
387 Bg *= a ^ (exps[j]/(l^d[maxi]))
388 B[maxi] = Bg
389 M[maxi] = [ x[1] for x in list( Q4.ideal(Bg).factor() ) ]
390 u = b[maxi] / (Bg^(l^d[maxi]))
391 if not u.is_unit():
392 print( "Error: new element is not the right power of B." )
393 print( "New el.:",b[maxi],", B:",Bg,", exponent:",l^d[maxi] )
394 if u == 1:
395 h[maxi] = 0
396 elif u == -1:
397 h[maxi] = 1
398 else:
399 h[maxi] = 2
400
401 return [(d[i],h[i]) for i in range(len(d))]
352 402
353# Uses Theorem 18 to compute the degree of Kummer extensions. 403# Uses Theorem 18 to compute the degree of Kummer extensions.
354def compute_vl( p, n, m, r ): 404def compute_vl( p, n, m, r ):
@@ -358,26 +408,16 @@ def compute_vl( p, n, m, r ):
358 408
359 return M - m + r*n - sum( ni ) 409 return M - m + r*n - sum( ni )
360 410
361# Computes a basis for G from a frozen_set of generators.
362# Returns a pair (B,torsion) where B is a list of rational numbers and
363# torsion is true if -1 is in G and false otherwise.
364@CachedFunction
365def generators_to_basis( G ):
366 (BM,BM_primes) = exponent_matrix_with_sign_and_primes( list(G) )
367 BM = BM.echelon_form()
368 BM_primes.append(-1)
369
370 B = [prod([BM_primes[i]^r[i] for i in range(len(r))]) for r in BM.rows()]
371
372 return ( [ x for x in B if abs(x) != 1 ], -1 in B )
373
374
375# Given any basis b of a group G computes an l-good basis for G. This is done 411# Given any basis b of a group G computes an l-good basis for G. This is done
376# using the algorithm outlined in the proof of Theorem 14 of (Debry-Perucca). 412# using the algorithm outlined in the proof of Theorem 14 of (Debry-Perucca).
377def make_good_basis( b, l ): 413def make_good_basis( b, l ):
378 M = exponent_matrix( b ) 414 M = exponent_matrix( b )
379 d = [ divisibility(x,l) for x in M ] 415 d = []
380 B = [ abs(b[i])^(1/(l^d[i])) for i in range(len(b)) ] 416 B = []
417 for i in range(len(b)):
418 di = divisibility( M[i], l )
419 d.append( di )
420 B.append( abs(b[i])^(1/(l^di)) )
381 421
382 # Computes the coeffiecients of a linear combination of the rows of M 422 # Computes the coeffiecients of a linear combination of the rows of M
383 # that is zero modulo l. These coefficients are elements of F_l. 423 # that is zero modulo l. These coefficients are elements of F_l.
@@ -385,8 +425,8 @@ def make_good_basis( b, l ):
385 425
386 while coeffs != []: 426 while coeffs != []:
387 427
388 # Computes which basis element (with non-zero coefficient in the 428 # Computes which basis element (with non-zero coefficient in the linear
389 # linear combination above) has maximal divisibility. 429 # combination above) has maximal divisibility.
390 maxi = -1 430 maxi = -1
391 maxd = -1 431 maxd = -1
392 for i in range(len(d)): 432 for i in range(len(d)):
@@ -394,24 +434,31 @@ def make_good_basis( b, l ):
394 maxd = d[i] 434 maxd = d[i]
395 maxi = i 435 maxi = i
396 436
397 x = [ (a/coeffs[maxi]).lift() for a in coeffs ] # Now a vector of int 437 x = [ (a/coeffs[maxi]).lift() for a in coeffs ] # Now a vector of ints
398 438
399 new_elt = prod([b[i]^(x[i]*l^(d[maxi]-d[i])) for i in range(len(d))]) 439 new_element = 1
440 for i in range(len(d)):
441 new_element *= b[i]^( x[i] * l^(d[maxi]-d[i]) )
400 442
401 b[maxi] = new_elt 443 b[maxi] = new_element
402 M = exponent_matrix( b ) 444 M = exponent_matrix( b )
403 d[maxi] = divisibility( M[maxi], l ) 445 d[maxi] = divisibility( M[maxi], l )
404 B[maxi] = abs(b[maxi])^(1/(l^d[maxi])) 446 B[maxi] = abs(b[maxi])^(1/(l^d[maxi]))
405 447
406 coeffs = find_combination( exponent_matrix( B ), l ) 448 coeffs = find_combination( exponent_matrix( B ), l )
407 449
408 return [[b[i] for i in range(len(b)) if d[i]==j] for j in range(max(d)+1)] 450 GB = [[]]
451 for i in range(len(d)):
452 while( len(GB) <= d[i] ):
453 GB.append([])
454 GB[d[i]].append(b[i])
455 return GB
409 456
410# Takes a good basis B and adjusts the sign of the elements so that there is 457# Takes a good basis B and adjusts the sign of the elements so that there is at
411# at most one negative generator (of positive divisibility). The input is a 458# most one negative generator (of positive divisibility). The input is a good
412# good basis in the format returned by make_good_basis. 459# basis in the format returned by make_good_basis.
413# Returns a pair (B,d), where B is the updated basis and d is the 460# Returns a pair (B,d), where B is the updated basis and d is the divisibility
414# divisibility parameter of the only negative element remained. 461# parameter of the only negative element remained.
415# The sign of the d=0 elements is just ignored in the other steps of the 462# The sign of the d=0 elements is just ignored in the other steps of the
416# algorithm, so we keep them negative. 463# algorithm, so we keep them negative.
417def adjust_sign( B ): 464def adjust_sign( B ):
@@ -430,7 +477,6 @@ def adjust_sign( B ):
430# Given the exponent matrix M of a list of rational numbers B, returns the 477# Given the exponent matrix M of a list of rational numbers B, returns the
431# coefficients of a linear combination that is weakly l-divisible, or [] if 478# coefficients of a linear combination that is weakly l-divisible, or [] if
432# the B[i] are strongly l-independent. 479# the B[i] are strongly l-independent.
433@CachedFunction
434def find_combination( M, l ): 480def find_combination( M, l ):
435 M = M.change_ring( GF( l ) ) 481 M = M.change_ring( GF( l ) )
436 if M.rank() != min( M.dimensions() ): 482 if M.rank() != min( M.dimensions() ):
@@ -443,8 +489,8 @@ def find_combination( M, l ):
443# Returns the minimal l-valuation of the exponents. 489# Returns the minimal l-valuation of the exponents.
444def divisibility( A, l ): 490def divisibility( A, l ):
445 if len(A) == 0: 491 if len(A) == 0:
446 print "Warning: computing the divisibility of a torsion element.", 492 print( "Warning: computing the divisibility of a torsion element.", end="" )
447 print "Returning +Infinity." 493 print( "Returning +Infinity." )
448 return +Infinity 494 return +Infinity
449 return min( [ valuation( x, l ) for x in A ] ) 495 return min( [ valuation( x, l ) for x in A ] )
450 496
@@ -469,10 +515,11 @@ def exponent_matrix( B ):
469# Returns a pair (M,L) where M is the modified exponent matrix and L is the 515# Returns a pair (M,L) where M is the modified exponent matrix and L is the
470# list of primes appearing in the factorization. 516# list of primes appearing in the factorization.
471def exponent_matrix_with_sign_and_primes( B ): 517def exponent_matrix_with_sign_and_primes( B ):
472 518 prime_list = set()
473 prime_list = list({x for a in [prime_factors(g) for g in B] for x in a}) 519 for g in B:
520 prime_list |= set( prime_factors( g ) )
521 prime_list = list( prime_list )
474 np = len( prime_list ) 522 np = len( prime_list )
475
476 rows = [] 523 rows = []
477 for g in B: 524 for g in B:
478 rowg = [0] * np 525 rowg = [0] * np
@@ -480,19 +527,21 @@ def exponent_matrix_with_sign_and_primes( B ):
480 for i in range( np ): 527 for i in range( np ):
481 if f[0] == prime_list[i]: 528 if f[0] == prime_list[i]:
482 rowg[i] = f[1] 529 rowg[i] = f[1]
483 rowg.append( 1 - max(0,sgn(g)) ) 530 s = 0
531 if sgn(g) == -1:
532 s = 1
533 rowg.append( s )
484 rows.append( rowg ) 534 rows.append( rowg )
485 return ( matrix( rows ), prime_list ) 535 return ( matrix( rows ), prime_list )
486 536
487# The function TotalKummerFailure (with its many wrapper functions) computes 537# This is a wrapper function for total_kummer_failure( G, True ), see below.
488# the total table of Kummer Failures for the given group G. Moreover, the 538def TotalKummerFailure( G ):
489# result is cached for speeding up following computations on the same group, 539 total_kummer_failure( G, True )
490# even if the same group is given with a different set of generators. 540
491#
492# Input: any set of generators for a subgroup G of Q*. 541# Input: any set of generators for a subgroup G of Q*.
493# If output=False, returns a 4-uple (t,MM,NN,D): 542# If output=False, returns a 4-uple (t,MM,NN,D):
494# - t is a pair, where t[0] is the rank of G and t[1] is either True (if G 543# - t is a pair, where t[0] is the rank of G and t[1] is either True (if G has
495# has torsion) or False (is it does not). 544# torsion) or False (is it does not).
496# - MM is the pair (M0,divisors(M0)) 545# - MM is the pair (M0,divisors(M0))
497# - NN is the pair (N0,divisors(N0)) 546# - NN is the pair (N0,divisors(N0))
498# - D is a table F, where F[j][i] is the ration between phi(m)n^r and the 547# - D is a table F, where F[j][i] is the ration between phi(m)n^r and the
@@ -501,34 +550,42 @@ def exponent_matrix_with_sign_and_primes( B ):
501# computed for a torsion-free part of G. 550# computed for a torsion-free part of G.
502# If output is True, outputs this data in a human-readable way and does not 551# If output is True, outputs this data in a human-readable way and does not
503# return any value. 552# return any value.
553def total_kummer_failure( G, output ):
504 554
505def TotalKummerFailure( G ): 555 # Computing a basis.
506 total_kummer_failure( G, True ) 556 (BM,BM_primes) = exponent_matrix_with_sign_and_primes( G )
507 557 BM = BM.echelon_form()
508@CachedFunction 558 BM_primes.append(-1)
509def total_kummer_failure_cachable( G ): 559 B = []
510 B, torsion = generators_to_basis( G ) 560 torsion = False
511 return total_kummer_failure_cachable_basis( tuple(B), torsion )
512 561
513@CachedFunction 562 for r in BM.rows():
514def total_kummer_failure_cachable_basis( B, torsion ): 563 gr = product( [ BM_primes[i]^r[i] for i in range(len(r)) ] )
515 B = list(B) 564 if gr == -1:
516 r = len(B) # Rank of G 565 torsion = True
566 break
567 elif gr == 1:
568 break
569 else:
570 B.append(gr)
517 571
518 if r == 0: 572 if len(B) == 0:
519 print "G is torsion. The extension is cyclotomic. Stopping." 573 print( "G is torsion. The extension is cyclotomic. Stopping." )
520 return False 574 return False
521 575
576 r = len(B) # Rank of G
577
522 # Compute l-adic data (straightforward) 578 # Compute l-adic data (straightforward)
523 ( bad_p, l_adic_failure_table ) = total_l_adic_failure( B ) 579 ( bad_primes, l_adic_failure_table ) = total_l_adic_failure( B )
524 580
525 # Compute adelic data. 581 # Compute adelic data.
526 (GB,d) = adjust_sign( make_good_basis( B, 2 ) ) 582 (GB,d) = adjust_sign( make_good_basis( B, 2 ) )
527 adelic_failure_table = adelic_failure_gb( GB, d ) 583 adelic_failure_table = adelic_failure_gb( GB, d )
528 584
529 # Computing the bounds M0 and N0 585 # Computing the bounds M0 and N0
530 N0 = prod( [ bad_p[i] ^ len( l_adic_failure_table[i][-1][0] ) \ 586 N0 = 1
531 for i in range(len(bad_p)) ] ) 587 for i in range(len( bad_primes )):
588 N0 *= bad_primes[i] ^ len( l_adic_failure_table[i][-1][0] )
532 # Extra factors of 2 may come from the adelic failure 589 # Extra factors of 2 may come from the adelic failure
533 N0 = lcm( N0, 2^len( adelic_failure_table ) ) 590 N0 = lcm( N0, 2^len( adelic_failure_table ) )
534 divs_N0 = divisors(N0) 591 divs_N0 = divisors(N0)
@@ -555,20 +612,16 @@ def total_kummer_failure_cachable_basis( B, torsion ):
555 FT[j][l] = lcm( FT[j][l], pp[1] ) 612 FT[j][l] = lcm( FT[j][l], pp[1] )
556 613
557 # Adding l-adic failure to the table 614 # Adding l-adic failure to the table
558 for i in range( len( bad_p ) ): 615 for i in range( len( bad_primes ) ):
559 l = bad_p[i] 616 l = bad_primes[i]
560 for j in range(len(divs_N0)): 617 for j in range(len(divs_N0)):
561 dN = divs_N0[j] 618 dN = divs_N0[j]
562 fl = l_adic_failure_from_data(B,l,l_adic_failure_table[i],dN,dN) 619 fl = l_adic_failure_from_data(B,l,l_adic_failure_table[i],dN,dN)
563 for h in range(len(divs_M0)): 620 for h in range(len(divs_M0)):
564 FT[j][h] *= fl 621 FT[j][h] *= fl
565 622
566 return ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) 623 ret = ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT )
567 624
568def total_kummer_failure( G, output ):
569
570 ret = total_kummer_failure_cachable( frozenset(G) )
571
572 if output: 625 if output:
573 print_total_table( ret ) 626 print_total_table( ret )
574 # Uncomment following line for case list description. 627 # Uncomment following line for case list description.
@@ -581,9 +634,9 @@ def total_kummer_failure( G, output ):
581# as the ration between 2^eN^r and the degree of Q_{M,N} over Q_M, where e=1 634# as the ration between 2^eN^r and the degree of Q_{M,N} over Q_M, where e=1
582# if N is even and e=0 otherwise. 635# if N is even and e=0 otherwise.
583 636
584# Makes the failure table for the torsion case when M/N is even. In this 637# Makes the failure table for the torsion case when M/N is even. In this case,
585# case an entry of the table is doubled if the corresponding value of N 638# an entry of the table is doubled if the corresponding value of N (actually,
586# (actually, of gcd(N,N0) ) is even, and is kept the same otherwise. 639# of gcd(N,N0) ) is even, and is kept the same otherwise.
587# The expected degree (over Q) 2^e * phi(M) * N^r, where e=1 if N is even and 640# The expected degree (over Q) 2^e * phi(M) * N^r, where e=1 if N is even and
588# e=0 otherwise. 641# e=0 otherwise.
589def torsion_table_even( data ): 642def torsion_table_even( data ):
@@ -600,8 +653,8 @@ def torsion_table_even( data ):
600 653
601# Makes the failure table for the torsion case when M/N is odd. In this case 654# Makes the failure table for the torsion case when M/N is odd. In this case
602# the entry at (M,N) is taken from the torsion-free entry at (2M,N). 655# the entry at (M,N) is taken from the torsion-free entry at (2M,N).
603# In other words, the expected degree (over Q) 2^e * phi(M) * N^r, where e=1 656# In other words, the expected degree (over Q) 2^e * phi(M) * N^r, where e=1 if
604# if N is even and e=0 otherwise. 657# N is even and e=0 otherwise.
605def torsion_table_odd( data ): 658def torsion_table_odd( data ):
606 659
607 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data 660 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
@@ -625,101 +678,114 @@ def print_total_table( data ):
625 678
626 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data 679 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
627 680
628 print "M_0 =", M0 681 print( "M_0 =", M0 )
629 print "N_0 =", N0 682 print( "N_0 =", N0 )
630 print "" 683 print( "" )
631 print "The following table shows the total failure of Kummer", 684 print( "The following table shows the total failure of Kummer degrees", end="" )
632 if torsion:
633 print "degrees in case the quotient M/N is EVEN."
634 else:
635 print "degrees."
636 print "The degree of the Kummer extension (M,N) is e / f, where",
637 if torsion: 685 if torsion:
638 print "e = phi(M)*N^rank(G) if N is odd and e = 2*phi(M)*N^rank(G)", 686 print( "in\n case the quotient M/N is EVEN." )
639 print "if N is even",
640 else: 687 else:
641 print "e = phi(M)*N^rank(G)", 688 print( "." )
642 print "and f is the entry of the table below at the row labelled with", 689 print( "Columns correspond to values of M, rows to values of N" )
643 print "gcd(N,N0) and the column labelled with gcd(M,M0)." 690 print( "" )
644 print "" 691 print( "The degree of the Kummer extension (M,N) can be extracted by taking" )
645 692 print( "the value f (failure) of the entry at (gcd(N,N0),gcd(M,M0)) and" )
693 print( "simply computing ed(M,N) / f, where ed(M,N) is the expected degree" )
694 print( "of the Kummer extension." )
646 if torsion: 695 if torsion:
696 print( "In this case (-1 is in G), we have ed(M,N) = 2^e*phi(M)*N^r," )
697 print( "where e=1 if N is even and e=0 if N is odd." )
647 FT1 = torsion_table_even( data ) 698 FT1 = torsion_table_even( data )
648 else: 699 else:
700 print( "In this case (G is torsion-free) we have ed(M,N) = phi(M)*N^r," )
649 FT1 = FT 701 FT1 = FT
702 print( "where r is the rank of G." )
703 print( "" )
650 704
651 tt = [ ["","|"] + divs_M0 ] 705 tt = [ ["","|"] + divs_M0 ]
652 tt.append( "-" * (len(divs_M0)+2) ) 706 tt.append( "-" * (len(divs_M0)+2) )
653 for i in range(len(divs_N0)): 707 for i in range(len(divs_N0)):
654 tt.append( [ divs_N0[i] ] + ["|"] + FT1[i] ) 708 tt.append( [ divs_N0[i] ] + ["|"] + FT1[i] )
655 print table(tt) 709 print( table(tt) )
656 print "" 710 print( "" )
657 711
658 if torsion: 712 if torsion:
659 print "The following table shows the total failure of Kummer degrees", 713 print( "The following table shows the total failure of Kummer degrees in" )
660 print "if the quotient M/N is ODD and is read as the previous one." 714 print( "case the quotient M/N is ODD." )
661 print "" 715 print( "This table can be read exactly as the first one." )
716 print( "" )
662 717
663 # A good strategy is the following: 718 # A good strategy is the following:
664 # A little translation exercise: the failure at (M,N) in the torsion 719 # A little translation exercise: the failure at (M,N) in the torsion
665 # case is either the same as that for (2M,N) in the torsion-free 720 # case is either the same as that for (2M,N) in the torsion-free case
666 # case (if M is even) or its double (if M is odd). 721 # (if M is even) or its double (if M is odd).
667 # However, due to problems in reading the table for bigger M, it is 722 # However, due to problems in reading the table for bigger M, it is
668 # easier to just compute the degree every time, and then deduce the 723 # easier to just compute the degree every time, and then deduce the
669 # failure. This is not too inefficient, since we can use the data 724 # failure. This is not too inefficient, since we can use the data that
670 # that we have already computed via kummer_degree_from_total_table. 725 # we have already computed via kummer_degree_from_total_table.
671 new_FT = torsion_table_odd( data ) 726 new_FT = torsion_table_odd( data )
672 # Printing the new table 727 # Printing the new table
673 tt = [ ["","|"] + divs_M0 ] 728 tt = [ ["","|"] + divs_M0 ]
674 tt.append( "-" * (len(divs_M0)+2) ) 729 tt.append( "-" * (len(divs_M0)+2) )
675 for i in range(len(divs_N0)): 730 for i in range(len(divs_N0)):
676 tt.append( [ divs_N0[i] ] + ["|"] + new_FT[i] ) 731 tt.append( [ divs_N0[i] ] + ["|"] + new_FT[i] )
677 print table(tt) 732 print( table(tt) )
678 print "" 733 print( "" )
679 734
680def print_case_list( data ): 735def print_case_list( data ):
681 736
682 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data 737 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
683 FT_odd = torsion_table_odd( data )
684 738
739 FT_odd = torsion_table_odd( data )
740 # FT1 is either FT or FT_even in the torsion case
741 FT1 = FT
685 if torsion: 742 if torsion:
686 FT1 = torsion_table_even( data ) 743 FT1 = torsion_table_even( data )
687 pf = sorted(list({ x for row in FT_odd for x in row })) 744 pf = []
688 else: 745 for row in FT1:
689 FT1 = FT 746 pf += row
690 pf = sorted(list({ x for row in FT1 for x in row })) 747 if torsion:
691 748 for row in FT_odd:
749 pf += row
750 pf = list(set(pf))
751 pf.sort()
692 for f in pf: 752 for f in pf:
693 print "Failure is", f, "if", 753 print( "Failure is", f, "if", end="" )
694 if torsion: 754 if torsion:
695 print "M/N is EVEN and", 755 print( "M/N is EVEN and", end="" )
696 print "(gcd(M,M0),gcd(N,N0)) is one of the following:" 756 print( "(gcd(M,M0),gcd(N,N0)) is one of the following:" )
697 print [ (divs_M0[j], divs_N0[i]) \ 757 lijst = []
698 for i in range(len(divs_N0)) for j in range(len(divs_M0)) \ 758 for i in range(len(divs_N0)):
699 if FT1[i][j] == f ] 759 for j in range(len(divs_M0)):
700 760 if FT1[i][j] == f:
761 lijst.append( ( divs_M0[j], divs_N0[i] ) )
762 print( lijst )
701 if torsion: 763 if torsion:
702 print "or if M/N is ODD and (gcd(M,M0)),gcd(N,N0)) is one of the", 764 print( "or if M/N is ODD and (gcd(M,M0)),gcd(N,N0)) is one of the", end="" )
703 print "following:" 765 print( "following:" )
704 print [ (divs_M0[j], divs_N0[i]) \
705 for i in range(len(divs_N0)) for j in range(len(divs_M0))\
706 if FT_odd[i][j] == f ]
707 766
708 print "" 767 lijst_odd = []
768 for i in range(len(divs_N0)):
769 for j in range(len(divs_M0)):
770 if FT_odd[i][j] == f:
771 lijst_odd.append( ( divs_M0[j], divs_N0[i] ) )
772 print( lijst_odd )
773
774 print( "" )
709 775
710# Extracts a specific value of failure from the total table. 776# Extracts a specific value of failure from the total table.
711def kummer_failure_from_total_table( M, N, data ): 777def kummer_failure_from_total_table( M, N, data ):
712 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data 778 ( ( r, torsion ), ( M0, divs_M0 ), ( N0, divs_N0 ), FT ) = data
713 779 FT1 = FT
714 if torsion: 780 if torsion:
715 if (M/N) % 2 == 0: 781 if (M/N) % 2 == 0:
716 FT1 = torsion_table_even( data ) 782 FT1 = torsion_table_even( data )
717 else: 783 else:
718 FT1 = torsion_table_odd( data ) 784 FT1 = torsion_table_odd( data )
719 else:
720 FT1 = FT
721 785
722 return FT1[divs_N0.index( gcd( N, N0 ) )][divs_M0.index( gcd( M, M0 ) )] 786 i = divs_N0.index( gcd( N, N0 ) )
787 j = divs_M0.index( gcd( M, M0 ) )
788 return FT1[i][j]
723 789
724# Computes the degree of the Kummer extension (M,N), by taking as input the 790# Computes the degree of the Kummer extension (M,N), by taking as input the
725# table computed by TotalKummerFailure. 791# table computed by TotalKummerFailure.
@@ -737,17 +803,20 @@ def kummer_degree_from_total_table( M, N, data ):
737# M must be a multiple of N. 803# M must be a multiple of N.
738def KummerDegree( G, M, N ): 804def KummerDegree( G, M, N ):
739 if M % N != 0: 805 if M % N != 0:
740 print "M is not a multiple of N" 806 print( "M is not a multiple of N" )
741 return -1 807 return -1
742 808
743 data = total_kummer_failure(G,False) 809 data = total_kummer_failure(G,False)
744 ((r,torsion),(M0,divs_M0),(N0,divs_N0),FT) = data 810 ((r,torsion),(M0,divs_M0),(N0,divs_N0),FT) = data
745 811
746 e_deg = euler_phi(M) * N^r 812 exp_deg = euler_phi(M) * N^r
747 813
748 if torsion and N % 2 == 0: 814 if torsion and N % 2 == 0:
749 e_deg *= 2 815 exp_deg *= 2
750 816
817 j = divs_M0.index(gcd(M,M0))
818 i = divs_N0.index(gcd(N,N0))
819
751 if torsion: 820 if torsion:
752 if (M/N)%2 == 0: 821 if (M/N)%2 == 0:
753 failure = torsion_table_even( data ) 822 failure = torsion_table_even( data )
@@ -756,5 +825,4 @@ def KummerDegree( G, M, N ):
756 else: 825 else:
757 failure = FT 826 failure = FT
758 827
759 return e_deg/failure[divs_N0.index(gcd(N,N0))][divs_M0.index(gcd(M,M0))] 828 return exp_deg / failure[i][j]
760

Generated with cgit - Back to sebastiano.tronto.net