-
Notifications
You must be signed in to change notification settings - Fork 59
/
embedding.jl
448 lines (348 loc) · 11.4 KB
/
embedding.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
################################################################################
#
# FinFieldsLattices.jl : Finite Fields Lattices
#
################################################################################
export embed, preimage, preimage_map
################################################################################
#
# Over/Sub fields
#
################################################################################
function overfields(k::FinField)
if !isdefined(k, :overfields)
k.overfields = Dict{Int, Vector{FinFieldMorphism}}()
end
return k.overfields
end
function subfields(k::FinField)
if !isdefined(k, :subfields)
k.subfields = Dict{Int, Vector{FinFieldMorphism}}()
end
return k.subfields
end
@doc raw"""
AddOverfield!(F::T, f::FinFieldMorphism{T, T}) where T <: FinField
Add an overfield to $F$, represented by a morphism $f: F\to G$ where
$G$ is the codomain of $f$.
"""
function AddOverfield!(F::T, f::FinFieldMorphism{T, T}) where T <: FinField
d = degree(codomain(f))
over = overfields(F)
if haskey(over, d)
push!(over[d], f)
else
a = FinFieldMorphism{T, T}[f]
over[d] = a
end
end
@doc raw"""
AddSubfield!(F::T, f::FinFieldMorphism{T, T}) where T <: FinField
Add a subfield to $F$, represented by a morphism $f: G\to F$ where
$G$ is the domain of $f$.
"""
function AddSubfield!(F::T, f::FinFieldMorphism{T, T}) where T <: FinField
d = degree(domain(f))
sub = subfields(F)
if haskey(sub, d)
push!(sub[d], f)
else
a = FinFieldMorphism{T, T}[f]
sub[d] = a
end
end
################################################################################
#
# Root Finding (of a splitting polynomial, internal use only)
#
################################################################################
any_root(x::PolyRingElem) = -coeff(linear_factor(x), 0)
################################################################################
#
# Minimal polynomial of the generator
#
################################################################################
@doc raw"""
berlekamp_massey(a::Vector{Y}, n::Int) where Y <: FieldElem
Compute the minimal polynomial of a linear recurring sequence $a$.
"""
function berlekamp_massey(a::Vector{Y}, n::Int) where Y <: FieldElem
S, T = polynomial_ring(parent(a[1]), "T")
m = 2*n - 1
R0 = T^(2*n)
R1 = S(reverse(a))
V0 = S(0)
V1 = S(1)
while n <= degree(R1)
Q, R = divrem(R0,R1)
V = V0-Q*V1
V0 = V1
V1 = V
R0 = R1
R1 = R
end
return V1*leading_coefficient(V1)^(-1)
end
@doc raw"""
generator_minimum_polynomial(f::FinFieldMorphism)
Compute the minimal polynomial of the generator of the codomain
of $f$ over the domain of $f$.
"""
function generator_minimum_polynomial(f::FinFieldMorphism)
E = domain(f)
F = codomain(f)
d::Int = div(degree(F), degree(E))
b = 2*d
# We define `sec`, the preimage of the morphism `f`
sec = inverse_fn(f)
x = gen(F)
y = one(F)
# We compute the recurring sequence sec(x^j) for j from 0 to 2d - 1
A = Array{typeof(x)}(undef, b)
for j in 1:(b - 1)
A[j] = sec(y)
y *= x
end
A[b] = sec(y)
# We apply Berlekamp Massey algorithm to the sequence
return berlekamp_massey(A, d)
end
################################################################################
#
# Embedding
#
################################################################################
@doc raw"""
is_embedded(k::T, K::T) where T <: FinField
If $k$ is embedded in $K$, return the corresponding embedding.
"""
function is_embedded(k::T, K::T) where T <: FinField
d = degree(K)
ov = overfields(k)
# We look for an embedding that has k as domain and K as codomain
if haskey(ov, d)
for f in ov[d]
if domain(f) == k && codomain(f) == K
return f
end
end
end
end
@doc raw"""
embed_any(k::T, K::T) where T <: FinField
Embed $k$ in $K$ without worrying about compatibility conditions.
"""
function embed_any(k::T, K::T) where T <: FinField
# We call the Flint algorithms directly, currently this is based on
# factorization
M, N = embed_matrices(k, K)
f(x) = embed_pre_mat(x, K, M)
inv(y) = embed_pre_mat(y, k, N)
return FinFieldMorphism(k, K, f, inv)
end
@doc raw"""
find_morphism(k::T, K::T) where T <: FinField
Returns a compatible embedding from $k$ to $K$.
"""
function find_morphism(k::T, K::T) where T <: FinField
S = polynomial_ring(K, "T")[1]
Q = S()
needy = false
m, n = degree(k), degree(K)
# For each common subfield S of k and K, we compute the minimal polynomial
# of the canonical generator of k over S, with coefficient seen in K and we
# compute the gcd of all these polynomials
for l in keys(subfields(k))
if haskey(subfields(K), l)
f = subfields(k)[l][1]
g = subfields(K)[l][1]
P = embed_polynomial(generator_minimum_polynomial(f), g)
if needy
Q = gcd(Q, P)
else
Q = P
end
needy = true
end
end
# If there is at least one common subfield, we define the embedding from k
# to K by sending the canonical generator of k to a root of the gcd
# computed above
if needy
defPol = modulus(k)
t = any_root(Q)
M, N = embed_matrices_pre(gen(k), t, defPol)
f(x) = embed_pre_mat(x, K, M)
g(y) = embed_pre_mat(y, k, N)
morph = FinFieldMorphism(k, K, f, g)
# If there is no common subfield, there is no compatibility condition to
# fulfill
else
morph = embed_any(k, K)
end
return morph
end
@doc raw"""
transitive_closure(f::FinFieldMorphism)
Compute the transitive closure.
"""
function transitive_closure(f::FinFieldMorphism)
k = domain(f)
K = codomain(f)
# Subfields
subk = subfields(k)
subK = subfields(K)
# We go through all subfields of k and check if they are also subfields of
# K, we add them if they are not
for d in keys(subk)
if !haskey(subK, d)
for g in subk[d]
t(y) = f(g(y))
tinv(x) = inverse_fn(g)(inverse_fn(f)(x))
phi = FinFieldMorphism(domain(g), K, t, tinv)
AddSubfield!(K, phi)
AddOverfield!(domain(g), phi)
end
else
val = [domain(v) for v in subK[d]]
for g in subk[d]
if !(domain(g) in val)
t(y) = f(g(y))
tinv(x) = inverse_fn(g)(inverse_fn(f)(x))
phi = FinFieldMorphism(domain(g), K, t, tinv)
AddSubfield!(K, phi)
AddOverfield!(domain(g), phi)
end
end
end
end
# Overfields
ov = overfields(K)
# We call the same procedure on the overfields
for d in keys(ov)
for g in ov[d]
transitive_closure(g)
end
end
end
@doc raw"""
intersections(k::T, K::T) where T <: FinField
For each subfield $S$ of $K$, embed $I$ in $S$ and $k$, where $I$ is the intersection
between $S$ and $k$.
"""
function intersections(k::T, K::T) where T <: FinField
d = degree(k)
subk = subfields(k)
subK = subfields(K)
needmore = true
# We loop through the subfields of K and we have different cases
for l in keys(subK)
c = gcd(d, l)
# The intersection may be trivial, I = F_p
if c == 1
# I = k, so k is a subfield of S and we embed k in S
# In that case, we finally have the embeddings k in S and S in K, so by
# transitive closure we have k in K and we do not need more work
elseif c == d
for g in subK[l]
embed(k, domain(g))
end
needmore = false
# I = S, so S is a subfield of k, and we embed S in k
elseif c == l
for g in subK[l]
embed(domain(g), k)
end
# If I is a subfield of k, we embed it in S
elseif haskey(subk, c)
L = domain(subk[c][1])
for h in subK[l]
embed(L, domain(h))
end
# If I is a subfield of K, we embed it in k and S
elseif haskey(subK, c)
L = domain(subK[c][1])
embed(L, k)
for h in subK[l]
embed(L, domain(h))
end
# Otherwise it means that there is no field I around so we create one
# and we embed it in k and S
else
# kc of same type as k but degree c
kc = FiniteField(k, c, string("x", c))
embed(kc, k)
for g in subK[l]
embed(kc, domain(g))
end
end
end
# We return a boolean to tell if some more work needs to be done
return needmore
end
@doc raw"""
embed(k::T, K::T) where T <: FinField
Embed $k$ in $K$, with some additional computations in order to satisfy
compatibility conditions with previous and future embeddings.
"""
function embed(k::T, K::T) where T <: FinField
degree(K) % degree(k) != 0 && error("Embedding impossible")
# Special cases of k == K or degree(k) == 1
if k == K
identity(x) = x
morph = FinFieldMorphism(k, k, identity, identity)
return morph
elseif degree(k) == 1
f(x) = K(k isa FqField ? _coeff(x, 0) : coeff(x, 0))
finv(y) = k(k isa FqField ? _coeff(y, 0) : coeff(y, 0))
morph = FinFieldMorphism(k, K, f, finv)
return morph
end
# If k is already embedded in K, we return the corresponding embedding
tr = is_embedded(k, K)
if tr !== nothing
return tr
end
# If the finite fields are different but have the same degree, we check that
# the embedding in the other direction does not exist. This is done in order
# to prevent the creation of loops in the lattice
if degree(k) == degree(K)
tr = is_embedded(K, k)
if tr !== nothing
return preimage_map(tr)
end
end
# Prior to embed k in K, we compute the needed embeddings
# The embedding might be computed during the process !
needmore = intersections(k, K) # recursive calls to embed
# And, if the wanted embeddings has not been computed during the process, we
# finally compute a compatible embedding
if needmore
# We compute a compatible embedding
morph = find_morphism(k, K)
# We had it to the over and sub fields of k and K
AddOverfield!(k, morph)
AddSubfield!(K, morph)
# We compute the transitive closure induced by the new embedding
transitive_closure(morph)
# And return the embedding
return morph
else
# If the embedding has already been computing, we return it
return is_embedded(k, K)
end
end
################################################################################
#
# Preimage map
#
################################################################################
@doc raw"""
preimage_map(k::T, k::T) where T <: FinField
Computes the preimage map corresponding to the embedding of $k$ into $K$.
"""
function preimage_map(k::T, K::T) where T <: FinField
f = embed(k, K)
return preimage_map(f)
end
preimage(f::FinFieldMorphism{S, T}, x::T) where {S <: FinField, T <: FinField} = preimage_map(f)(x)