|
#############################################################################
##
#A StandardFF.gi Frank Lübeck
##
## The files StandardFF.g{i,d} contain code to compute standard models of
## finite fields. Fields are returned as field towers with prime degree
## indices, and as single extensions over the prime field.
##
# args: p, r, k
# Let f = X^r + g(X) our standard irreducible polynomial for the
# degree r extension over the field FF(p,r^(k-1)). This function
# returns the Steinitz number of the polynomial g(X).
InstallGlobalFunction(SteinitzNumberForPrimeDegree, function(p, r, k)
local Fp, stpd, stpdr, q, F, o, i, nr, x, a, l;
Fp := StandardFiniteField(p, 1);
if not IsBound(Fp!.SteinitzPrimeDeg) then
Fp!.SteinitzPrimeDeg := [];
fi;
stpd := Fp!.SteinitzPrimeDeg;
if not IsBound(stpd[r]) then
stpd[r] := [];
fi;
stpdr := stpd[r];
if not IsBound(stpdr[k]) then
if p = r then
# Artin-Schreier case
# Xk^p - Xk - \prod_{1..k-1} x_i^(p-1)
# for q = p^(p^(k-1)) this yields (p-1)*(q + q/p)
q := p^(p^(k-1));
stpdr[k] := (p-1)*(q+q/p);
elif r = 2 and p mod 4 = 3 then
# special case for k = 1, 2
if k = 1 then
# X^2 + 1
stpdr[k] := 1;
elif k = 2 then
# find a non-square
F := StandardFiniteField(p, 2);
o := One(F);
i := 1;
nr := StandardAffineShift(p^2, i);
x := ElementSteinitzNumber(F, nr);
while nr = 0 or x^((p^2-1)/2) = o do
i := i+1;
nr := StandardAffineShift(p^2, i);
x := ElementSteinitzNumber(F, nr);
od;
# X^2 - x
stpdr[k] := SteinitzNumber(-x);
else
# Xk - x{k-1}
stpdr[k] := (p-1)*p^(2^(k-2));
fi;
elif (p-1) mod r = 0 then
if k = 1 then
# find a non r-th power
i := 1;
nr := StandardAffineShift(p, i);
while nr = 0 or PowerMod(nr, (p-1)/r, p) = 1 do
i := i+1;
nr := StandardAffineShift(p, i);
od;
# X^r - nr
stpdr[k] := p - nr;
else
# Xk^r - x{k-1}
stpdr[k] := (p-1)*p^(r^(k-2));
fi;
else
# in general we use pseudo random polynomials
F := StandardFiniteField(p, r^(k-1));
if k = 1 then
# new generator will have norm 1
a := -One(F);
else
# new generator will have previous one as norm
a := -PrimitiveElement(F);
fi;
l := StandardIrreducibleCoeffList(F, r, a);
Remove(l);
while IsZero(l[Length(l)]) do
Remove(l);
od;
stpdr[k] := ValuePol(List(l, SteinitzNumber), Size(F));
fi;
fi;
return stpdr[k];
end);
# a version that tries to call external program using NTL
InstallGlobalFunction(SteinitzNumberForPrimeDegreeNTL, function(p, r, k)
local Fp, stpd, stpdr, d, prg, inp, res;
# for cache same as above
Fp := StandardFiniteField(p, 1);
if not IsBound(Fp!.SteinitzPrimeDeg) then
Fp!.SteinitzPrimeDeg := [];
fi;
stpd := Fp!.SteinitzPrimeDeg;
if not IsBound(stpd[r]) then
stpd[r] := [];
fi;
stpdr := stpd[r];
if not IsBound(stpdr[k]) then
d := DirectoriesPackageLibrary("StandardFF","ntl");
# currently we only have standalone programs for k = 1 and p < 2^50
if k = 1 then
if p = 2 then
if r <> p and (p-1) mod r <> 0 then
prg := Filename(d, "findstdirrGF2");
if prg <> fail then
inp := String(r);
res := IO_PipeThrough(prg,[],inp);
stpdr[k] := Int(Filtered(res, IsDigitChar));
fi;
fi;
elif p < 2^50 then
if r <> p and (p-1) mod r <> 0 then
prg := Filename(d, "findstdirrGFp");
if prg <> fail then
inp := Concatenation(String(p), "\n", String(r));
res := IO_PipeThrough(prg,[],inp);
stpdr[k] := Int(Filtered(res, IsDigitChar));
fi;
fi;
fi;
fi;
if not IsBound(stpdr[k]) then
# use the GAP function
SteinitzNumberForPrimeDegree(p, r, k);
fi;
fi;
return stpdr[k];
end);
# for displaying the corresponding polynomial
InstallGlobalFunction(StandardPrimeDegreePolynomial, function(p, r, k)
local st, qq, K, c, v;
st := SteinitzNumberForPrimeDegree(p, r, k);
qq := p^(r^(k-1));
K := FF(p, r^(k-1));
c := CoefficientsQadic(st, qq);
while Length(c) < r do
Add(c, 0);
od;
Add(c, 1);
c := List(c, i-> AsPolynomial(ElementSteinitzNumber(K, i)));
v := Indeterminate(FF(p,1), Concatenation("x",String(r),"_",String(k)));
return ValuePol(c, v);
end);
# args: K, deg, lcoeffs, b
# Let f = poly(lcoeffs) + X^deg be irreducible in K[X].
# Return K[X] / f with primitve element bX mod f, where b in K
# such that bX mod f is a generator over the prime field.
# We assume that K = F[Y] / g and K knows powers of the primitive element
# Y mod g in its tower basis.
# The returned fields also stores the powers of bX mod f in its tower
# basis.
# (The semiechelon code is similar to 'Matrix_OrderPolynomialInner'.)
InstallGlobalFunction(_ExtensionWithTowerBasis, function(K, deg, lcoeffs, b)
local dK, zK, zKl, co, d, F, zero, one, pK, vec, vecs, pols,
zeroes, pmat, v, w, p, piv, x, c, vnam, var, ivar, Kp, fam, i, j;
dK := DegreeOverPrimeField(K);
d := dK * deg;
F := LeftActingDomain(K);
# shortcut for prime degree over prime field
if dK = 1 then
p := ShallowCopy(lcoeffs);
while Length(p) < deg do
Add(p, 0*p[1]);
od;
Add(p, One(p[1]));
MakeImmutable(p);
pmat := IdentityMat(deg, F);
else
zK := Zero(K);
zero := Zero(F);
one := One(F);
pK := PrimitivePowersInTowerBasis(K);
co := function(x)
local res;
if dK = 1 then
res := [x];
else
res := x![1];
if not IsList(res) then
res := zero * [1..dK];
res[1] := x![1];
fi;
fi;
ConvertToVectorRep(res);
return res;
end;
# We collect (bX)^i mod f, i = 0..d*dK-1, and compute
# the minimal polynomial of bX over F.
# one
vec := NullMat(1, d, F)[1];
vec[1] := one;
vecs := [];
pols := [];
zeroes := [];
pmat := [];
# X
v := NullMat(1,deg,K)[1];
v[1] := One(K);
for i in [1..d+1] do
if i <= d then
Add(pmat,vec);
fi;
w := ShallowCopy(vec);
p := ShallowCopy(zeroes);
Add(p, one);
ConvertToVectorRepNC(p, F);
piv := PositionNonZero(w, 0);
# reduce
while piv <= d and IsBound(vecs[piv]) do
x := -w[piv];
if IsBound(pols[piv]) then
AddCoeffs(p, pols[piv], x);
fi;
AddRowVector(w, vecs[piv], x, piv, d);
piv := PositionNonZero(w,piv);
od;
if i <= d then
x := Inverse(w[piv]);
MultVector(p, x);
MakeImmutable(p);
pols[piv] := p;
MultVector(w, x );
MakeImmutable(w);
vecs[piv] := w;
Add(zeroes,zero);
# multiply by bX and find next vec in tower basis
v := b*v;
c := -v[deg];
for j in [deg, deg-1..2] do
v[j] := v[j-1];
od;
v[1] := zK;
if not IsZero(c) then
for j in [1..Length(lcoeffs)] do
v[j] := v[j] + c*lcoeffs[j];
od;
fi;
vec := [];
for c in v do
Append(vec, co(c) * pK);
od;
ConvertToVectorRepNC(vec, F);
fi;
od;
# Now the last p is the minimal polynomial over F
# and pmat are the primitive powers in the tower basis for the new
# extension.
MakeImmutable(p);
ConvertToMatrixRep(pmat);
fi;
# generate the new extension
vnam := Concatenation("x", String(d));
var := Indeterminate(F, vnam);
ivar := IndeterminateNumberOfUnivariateLaurentPolynomial(var);
Kp := AlgebraicExtensionNC(F, UnivariatePolynomial(F, p, ivar), vnam);
# temporary work around for a bug in GAP-dev
if Characteristic(K) > 2^16 then
fam := ElementsFamily(FamilyObj(Kp));;
fam!.reductionMat := List(fam!.reductionMat,List);
fi;
Setter(IsStandardFiniteField)(Kp, true);
Setter(PrimitivePowersInTowerBasis)(Kp, pmat);
if dK = 1 then
# identity matrix
Setter(TowerBasis)(Kp, pmat);
fi;
# let elements know to be in standard field
fam := FamilyObj(RootOfDefiningPolynomial(Kp));
fam!.extType := Subtype(fam!.extType, IsStandardFiniteFieldElement);
fam!.baseType := Subtype(fam!.baseType, IsStandardFiniteFieldElement);
SetFilterObj(OneImmutable(Kp), IsStandardFiniteFieldElement);
SetFilterObj(ZeroImmutable(Kp), IsStandardFiniteFieldElement);
SetFilterObj(RootOfDefiningPolynomial(Kp), IsStandardFiniteFieldElement);
return Kp;
end);
## <#GAPDoc Label="FF">
## <ManSection>
## <Heading>Constructing standard finite fields</Heading>
## <Func Name="StandardFiniteField" Arg="p, n"/>
## <Func Name="FF" Arg="p, n"/>
## <Returns>a finite field</Returns>
## <Func Name="StandardPrimeDegreePolynomial" Arg="p, r, k" />
## <Returns>a polynomial of degree <A>r</A></Returns>
## <Description>
## The arguments are a prime <A>p</A> and a positive integer
## <A>n</A>. The function <Ref Func="FF"/> (or its synomym <Ref
## Func="StandardFiniteField"/>) is one of the main functions of this
## package. It returns a standardized field <C>F</C> of order
## <M><A>p</A>^{<A>n</A>}</M>. It is implemented as a simple extension
## over the prime field <C>GF(p)</C> using <Ref BookName="Reference"
## Oper="AlgebraicExtension" />
## <P/>
## The polynomials used for the prime degree extensions are
## accessible with <Ref Func="StandardPrimeDegreePolynomial"/>. For
## arguments <A>p, r, k</A> it returns the irreducible polynomial of
## degree <A>r</A> for the <A>k</A>-th iterated extension of degree
## <A>r</A> over the prime field. The polynomial is in the variable
## <C>x</C><Emph>r</Emph><C>_</C><Emph>k</Emph> and the coefficients can
## contain variables <C>x</C><Emph>r</Emph><C>_</C><Emph>l</Emph> with <M>l
## < k</M>.
## <P/>
## <Example>gap> Fp := FF(2, 1);
## GF(2)
## gap> F := FF(2, 100);
## FF(2, 100)
## gap> Size(F);
## 1267650600228229401496703205376
## gap> p := NextPrimeInt(10^50);
## 100000000000000000000000000000000000000000000000151
## gap> K := FF(p, 60);
## FF(100000000000000000000000000000000000000000000000151, 60)
## gap> LogInt(Size(K), 10);
## 3000
## gap> F := FF(13, 9*5);
## FF(13, 45)
## gap> StandardPrimeDegreePolynomial(13, 3, 1);
## x3_1^3+Z(13)^7
## gap> StandardPrimeDegreePolynomial(13, 3, 2);
## x3_2^3-x3_1
## gap> StandardPrimeDegreePolynomial(13, 5, 1);
## x5_1^5+Z(13)^4*x5_1^2+Z(13)^4*x5_1-Z(13)^0
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallGlobalFunction(StandardFiniteField, function(p, n)
local F, id, ext, fac, lf, n1, nK, st, q1, l, K, lK, c, L, b;
if not IsPrimeInt(p) then
Error("StandardFiniteField: first argument must be a prime\n");
fi;
F := GF(p);
# we cache extensions in prime field
if not IsBound(F!.extensions) then
SetIsStandardPrimeField(F, true);
F!.extensions := [F];
id := IdentityMat(1, F);
ConvertToMatrixRep(id, p);
Setter(PrimitivePowersInTowerBasis)(F, id);
fi;
ext := F!.extensions;
if IsBound(ext[n]) then
return ext[n];
fi;
# construct field recursively via prime degree extensions
fac := Collected(Factors(n));
lf := fac[Length(fac)];
n1 := lf[1]^(lf[2]-1);
nK := n/lf[1];
st := SteinitzNumberForPrimeDegree(p, lf[1], lf[2]);
q1 := p^n1;
l := CoefficientsQadic(st, q1);
K := StandardFiniteField(p, nK);
lK := List(l, y-> EmbedSteinitz(p, n1, nK, y));
c := List(lK, nr-> ElementSteinitzNumber(K, nr));
# primitive element of new extension is product of prime power
# degree generators, class of bX, we construct b (product of the
# other prime power degree generators):
b := ElementSteinitzNumber(K, p^(Position(StdMonDegs(nK), nK/n1)-1));
L := _ExtensionWithTowerBasis(K, lf[1], c, b);
ext[n] := L;
return L;
end);
# default for other fields
InstallOtherMethod(IsStandardFiniteField, ["IsField"], ReturnFalse);
# maps between elements of standard finite field and its
# standard finite field tower
InstallMethod(TowerBasis, ["IsStandardFiniteField"], function(F)
return PrimitivePowersInTowerBasis(F)^-1;
end);
# elements as coefficient vectors in tower basis
InstallMethod(AsVector, ["IsStandardFiniteFieldElement"], function(x)
local F;
F := FamilyObj(x)!.wholeField;
return ExtRepOfObj(x) * PrimitivePowersInTowerBasis(F);
end);
InstallOtherMethod(AsVector,
["IsStandardFiniteField", "IsStandardFiniteFieldElement"],
function(F, x)
return ExtRepOfObj(x) * PrimitivePowersInTowerBasis(F);
end);
# elements as polynomials
InstallMethod(GeneratorMonomials, [IsStandardFiniteField],
function(F)
local res, d, f, Fp, s, a, i, v;
res := rec(vars := []);
d := DegreeOverPrimeField(F);
if d = 1 then
return res;
fi;
f := Collected(Factors(d));
Fp := PrimeField(F);
for a in f do
res.(a[1]) := rec();
for i in [1..a[2]] do
s := Concatenation("x",String(a[1]),"_",String(i));
v := Indeterminate(Fp, s);
res.(a[1]).(i) := v;
Add(res.vars, v);
od;
od;
return res;
end);
InstallMethod(TowerBasisMonomials, [IsStandardFiniteField],
function(F)
local d, m, l, o, res, b, i, a;
d := DegreeOverPrimeField(F);
m := GeneratorMonomials(F);
l := StdMon(d)[1];
o := One(PrimeField(F));
res := [];
for a in l do
b := o;
i := 1;
while i < Length(a) do
b := b*m.(a[i]).(a[i+1])^a[i+2];
i := i+3;
od;
Add(res, b);
od;
return res;
end);
InstallOtherMethod(AsPolynomial, ["IsFFE"], IdFunc);
InstallMethod(AsPolynomial, ["IsStandardFiniteFieldElement"],
function(x)
local F;
F := FamilyObj(x)!.wholeField;
return AsVector(x) * TowerBasisMonomials(F);
end);
# vice versa, we also support non-reduced polynomials
InstallMethod(ElementPolynomial, [IsStandardFiniteField, IsPolynomial],
function(F, pol)
local gm, tbm, poss, a, tb, res;
gm := GeneratorMonomials(F);
if not IsBound(F!.gmeltvars) then
tbm := TowerBasisMonomials(F);
poss := List(gm.vars, x-> Position(tbm, x));
a := PrimitiveElement(F);
tb := TowerBasis(F);
F!.gmeltvars := List(poss, i-> ValuePol(tb[i], a));
fi;
res := Value(pol, gm.vars, F!.gmeltvars);
if not res in F then
return fail;
fi;
return res;
end);
# miscellaneous utilities
InstallMethod(PrimeField, ["IsStandardFiniteField"],
F -> FF(Characteristic(F), 1));
## <#GAPDoc Label="IsFF">
## <ManSection>
## <Heading>Filters for standard fields</Heading>
## <Prop Name="IsStandardPrimeField" Arg="F" />
## <Prop Name="IsStandardFiniteField" Arg="F" />
## <Filt Name="IsStandardFiniteFieldElement" Arg="x" Type="Category"/>
## <Returns><K>true</K> or <K>false</K> </Returns>
## <Description>
## These properties identify the finite fields constructed with <Ref
## Func="FF" />. Prime fields constructed as <C>FF(p, 1)</C> have the
## property <Ref Prop="IsStandardPrimeField"/>. They are identical with
## <C>GF(p)</C>, but calling them via <Ref Func="FF" /> we store some
## additional information in these objects.
## <P/>
## The fields constructed by <C>FF(p,k)</C> with <C>k</C> <M> > 1</M>
## have the property <Ref Prop="IsStandardFiniteField"/>. Elements <A>x</A> in
## such a field are in <Ref Filt="IsStandardFiniteFieldElement"/>.
## <P/>
## <Example>gap> F := FF(19,1);
## GF(19)
## gap> IsStandardFiniteField(F);
## false
## gap> IsStandardPrimeField(F);
## true
## gap> F := FF(23,48);
## FF(23, 48)
## gap> IsStandardFiniteField(F);
## true
## gap> IsStandardFiniteFieldElement(Random(F));
## true
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallOtherMethod(IsStandardPrimeField, ["IsField"], function(K)
local p;
if not IsFinite(K) then
return false;
fi;
p := Size(K);
if not IsPrimeInt(p) then
return false;
fi;
if K = FF(p,1) then
return true;
fi;
return false;
end);
## <#GAPDoc Label="FFElmConv">
## <ManSection>
## <Heading>Maps for elements of standard finite fields</Heading>
## <Meth Name="AsVector" Label="for elements in standard finite fields"
## Arg="a"/>
## <Returns>a vector over prime field of <C>F</C> </Returns>
## <Meth Name="ElementVector" Arg="F, v"/>
## <Returns>an element in <C>F</C> </Returns>
## <Meth Name="AsPolynomial" Label="for elements in standard finite fields"
## Arg="a"/>
## <Returns>a polynomial in variables of the tower of <C>F</C> </Returns>
## <Meth Name="ElementPolynomial" Arg="F, pol"/>
## <Returns>an element in <C>F</C> </Returns>
## <Meth Name="SteinitzNumber" Arg="a"/>
## <Returns>an integer</Returns>
## <Meth Name="ElementSteinitzNumber" Arg="F, nr"/>
## <Returns>an element in <C>F</C></Returns>
## <Description>
## Here, <A>F</A> is always a standard finite field (<Ref
## Filt="IsStandardFiniteField"/>) and <A>a</A> is an element of <A>F</A>.
## <P/>
## <Ref Meth="AsVector" Label="for elements in standard finite fields"/>
## returns the coefficient vector of <A>a</A> with respect to the tower
## basis of <A>F</A>. And vice versa <Ref Meth="ElementVector"/> returns
## the element of <A>F</A> with the given coefficient vector.
## <P/>
## Similarly, <Ref Meth="AsPolynomial" Label="for elements in standard
## finite fields"/> returns the (reduced) polynomial in the
## indeterminates defining the tower of <A>F</A>. Here, for each prime
## <M>r</M> dividing the degree of the field the polynomial defining the
## <M>k</M>-th extension of degree <M>r</M> over the prime field is
## written in the variable <C>x</C><M>r</M><C>_</C><M>k</M>. And <Ref
## Meth="ElementPolynomial"/> returns the element of <A>F</A> represented
## by the given polynomial (which does not need to be reduced).
## <P/>
## Finally, <Ref Meth="SteinitzNumber"/> returns the Steinitz number of
## <A>a</A>. And <Ref Meth="ElementSteinitzNumber"/> returns the element
## with given Steinitz number.
## <Example><![CDATA[gap> F := FF(17, 12);
## FF(17, 12)
## gap> a := PrimitiveElement(F);; a := a^11-3*a^5+a;
## ZZ(17,12,[0,1,0,0,0,14,0,0,0,0,0,1])
## gap> v := AsVector(a);
## < immutable compressed vector length 12 over GF(17) >
## gap> a = ElementVector(F, v);
## true
## gap> ExtRepOfObj(a) = v * TowerBasis(F);
## true
## gap> pol := AsPolynomial(a);;
## gap> ElementPolynomial(F, pol^10) = a^10;
## true
## gap> nr := SteinitzNumber(a);
## 506020624175737
## gap> a = ElementSteinitzNumber(F, nr);
## true
## gap> ## primitive element of FF(17, 6)
## gap> y := ElementSteinitzNumber(F, 17^5);
## ZZ(17,12,[0,0,1,0,0,0,12,0,0,0,5,0])
## gap> y = ValuePol([0,0,1,0,0,0,12,0,0,0,5,0], PrimitiveElement(F));
## true
## gap> x6 := Indeterminate(FF(17,1), "x6");;
## gap> MinimalPolynomial(FF(17,1), y, x6) = DefiningPolynomial(FF(17,6));
## true
## ]]></Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallMethod(ElementVector, ["IsStandardFiniteField", "IsRowVector"],
function(F, vec)
local fam, l;
fam := FamilyObj(PrimitiveElement(F));
l := vec * TowerBasis(F);
if ForAll([2..Length(l)], i-> IsZero(l[i])) then
l := l[1];
fi;
return ObjByExtRep(fam, l);
end);
InstallOtherMethod(SteinitzNumber, ["IsFFE"], IntFFE);
InstallOtherMethod(SteinitzNumber,
["IsStandardPrimeField", "IsRingElement"],
function(F, c)
return IntFFE(c);
end);
InstallMethod(SteinitzNumber, ["IsStandardFiniteField", "IsRingElement"],
function(F, c)
local v;
v := List(ExtRepOfObj(c) * PrimitivePowersInTowerBasis(F), IntFFE);
return ValuePol(v, Characteristic(F));
end);
InstallOtherMethod(SteinitzNumber, ["IsStandardFiniteFieldElement"],
function(x)
return SteinitzNumber(FamilyObj(x)!.wholeField, x);
end);
# reverse
InstallOtherMethod(ElementSteinitzNumber,
["IsStandardPrimeField", "IsInt"],
function(F, nr)
return nr*One(F);
end);
InstallMethod(ElementSteinitzNumber, ["IsStandardFiniteField", "IsInt"],
function(F, nr)
local p, fam, tc, l;
if nr < 0 or nr >= Size(F) then
return fail;
fi;
if nr = 0 then
return Zero(F);
fi;
p := Characteristic(F);
fam := FamilyObj(PrimitiveElement(F));
tc := One(GF(p))*CoefficientsQadic(nr, p);
ConvertToVectorRep(tc, p);
l := tc * TowerBasis(F);
if ForAll([2..Length(l)], i-> IsZero(l[i])) then
l := l[1];
fi;
return ObjByExtRep(fam, l);
end);
InstallMethod(ViewString, ["IsStandardFiniteField"], function(F)
return Concatenation("FF(", String(Characteristic(F)),
", ", String(DegreeOverPrimeField(F)), ")");
end);
InstallMethod(ViewObj, ["IsStandardFiniteField"], function(F)
Print(ViewString(F));
end);
InstallMethod(PrintString, ["IsStandardFiniteField"], ViewString);
InstallMethod(PrintObj, ["IsStandardFiniteField"], function(F)
Print(PrintString(F));
end);
# helper: describe monomials in tower basis and their degrees
InstallGlobalFunction(StdMon, function(n)
local rs, res, deg, r, k, new, ndeg, i, j;
rs := Factors(n);
res := [[]];
deg := [1];
for i in [1..Length(rs)] do
r := rs[i];
if i = 1 or rs[i-1] <> r then
k := 1;
else
k := k+1;
fi;
new := ShallowCopy(res);
ndeg := ShallowCopy(deg);
for j in [1..r-1] do
Append(new, List(res, a-> Concatenation(a, [r,k,j])));
Append(ndeg, List(deg, a-> LcmInt(a,r^k)));
od;
res := new;
deg := ndeg;
od;
return [res, deg];
end);
# just the degrees
InstallGlobalFunction(StdMonDegs, function(n)
local f, a, res, new, i;
if n = 1 then return [1]; fi;
f := Collected(Factors(n));
a := f[Length(f)];
res := StdMonDegs(n/a[1]);
new := List(res, l-> LcmInt(l, a[1]^a[2]));
for i in [1..a[1]-1] do
Append(res, new);
od;
return res;
end);
# map of monomials for degree n into monomials of degree m (by positions)
InstallGlobalFunction(StdMonMap, function(n, m)
local d;
d := StdMonDegs(m);
return Filtered([1..Length(d)], i-> n mod d[i] = 0);
end);
# Embedding of element in FF(p,n) into FF(p,m) by Steinitz numbers
InstallGlobalFunction(EmbedSteinitz, function(p, n, m, nr)
local l, map, c;
if n=m or nr = 0 then return nr; fi;
l := CoefficientsQadic(nr, p);
map := StdMonMap(n, m){[1..Length(l)]};
c := 0*[1..map[Length(map)]];
c{map} := l;
return ValuePol(c, p);
end);
InstallMethod(ExtRepOfObj,"baseFieldElm",true,
[IsAlgebraicElement and IsAlgBFRep],0,
function(e)
local f,l;
f:=FamilyObj(e);
l := 0*ShallowCopy(f!.polCoeffs{[1..f!.deg]});
l[1] := e![1];
MakeImmutable(l);
return l;
end);
InstallMethod(Embedding, ["IsStandardPrimeField", "IsStandardPrimeField"],
function(K, L)
local emb;
if K <> L then
Error("Embedding need identical prime fields.");
fi;
emb := MappingByFunction(K, L, IdFunc, false, IdFunc);
SetFilterObj(emb, IsStandardFiniteFieldEmbedding);
return emb;
end);
InstallMethod(Embedding, ["IsStandardPrimeField", "IsStandardFiniteField"],
function(K, L)
local p, famL, fun, pre, emb;
p := Characteristic(K);
famL := FamilyObj(PrimitiveElement(L));
fun := function(x)
return ObjByExtRep(famL, x);
end;
pre := function(y)
if IsAlgBFRep(y) then
return y![1];
else
return fail;
fi;
end;
emb := MappingByFunction(K, L, fun, false, pre);
SetFilterObj(emb, IsStandardFiniteFieldEmbedding);
return emb;
end);
## <#GAPDoc Label="FFEmbedding">
## <ManSection>
## <Meth Name="Embedding" Label="for standard finite fields" Arg="H, F"/>
## <Returns>a field homomorphism </Returns>
## <Description>
## Let <A>F</A> and <A>H</A> be standard finite fields and <A>H</A> be
## isomorphic to a subfield of <A>F</A>. This function returns the canonical
## embedding of <A>H</A> into <A>F</A>.
## <P/>
## <Example>gap> F := FF(7, 360);
## FF(7, 360)
## gap> H := FF(7, 45);
## FF(7, 45)
## gap> emb := Embedding(H, F);
## MappingByFunction( FF(7, 45), FF(7, 360), function( x ) ... end )
## gap> y := PrimitiveElement(H);
## ZZ(7,45,[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
## 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
## gap> x := y^emb;;
## gap> ((y+One(H))^12345)^emb = (x+One(F))^12345;
## true
## gap> PreImageElm(emb, x^5);
## ZZ(7,45,[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
## 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
## gap> PreImageElm(emb, PrimitiveElement(F));
## fail
## gap> SteinitzNumber(y);
## 13841287201
## gap> SteinitzNumber(x) mod 10^50;
## 72890819326613454654477690085519113574118965817601
## gap> SteinitzPair(x);
## [ 45, 13841287201 ]
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallMethod(Embedding, ["IsStandardFiniteField", "IsStandardFiniteField"],
function(K, L)
local p, d, a, monK, monL, map, z, famL, famK, fun, imgen, fun2, pre, emb;
p := Characteristic(K);
if Characteristic(L) <> p then
Error("different characteristic");
fi;
d := DegreeOverPrimeField(L);
a := DegreeOverPrimeField(K);
if not d mod a = 0 then
Error("not a subfield");
fi;
if not IsBound(K!.embcache) then
K!.embcache := rec();
fi;
if IsBound(K!.embcache.(d)) then
return K!.embcache.(d);
fi;
monK := StdMon(a);
monL := StdMon(d);
map := List(monK[1], x-> Position(monL[1], x));
z := AsVector(Zero(L));
famL := FamilyObj(PrimitiveElement(L));
famK := FamilyObj(PrimitiveElement(K));
fun := function(x)
local v, res, l;
v := AsVector(x);
res := ShallowCopy(z);
res{map} := v;
l := res*TowerBasis(L);
if ForAll([2..Length(l)], i-> IsZero(l[i])) then
l := l[1];
fi;
return ObjByExtRep(famL, l);
end;
## # Maybe this version of "fun" can be made faster?
## imgen := fun(PrimitiveElement(K));
## fun2 := function(x)
## local c;
## c := x![1];
## if not IsList(c) then
## return c*One(L);
## fi;
## return ValuePol(c, imgen);
## end;
pre := function(y)
local v, l;
v := AsVector(L, y);
if ForAny([1..d], i-> not (i in map or IsZero(v[i]))) then
return fail;
fi;
l := v{map};
if ForAll([2..a], i-> IsZero(l[i])) then
return ObjByExtRep(famK, l[1]);
else
return ObjByExtRep(famK, l*TowerBasis(K));
fi;
end;
emb := MappingByFunction(K, L, fun, false, pre);
SetFilterObj(emb, IsStandardFiniteFieldEmbedding);
K!.embcache.(d) := emb;
return emb;
end);
InstallMethod(IsSurjective, ["IsStandardFiniteFieldEmbedding"],
function(emb)
return DegreeOverPrimeField(Source(emb)) = DegreeOverPrimeField(Range(emb));
end);
InstallOtherMethod(PreImageElm,
["IsStandardFiniteFieldEmbedding", "IsRingElement"],
function(emb, y)
return emb!.prefun(y);
end);
## <#GAPDoc Label="SteinitzPair">
## <ManSection>
## <Oper Name="SteinitzPair" Arg="a"/>
## <Returns>a pair of integers</Returns>
## <Meth Name="SteinitzPair" Label="for Steinitz number" Arg="K, snr"/>
## <Returns>a pair of integers</Returns>
## <Meth Name="SteinitzNumber" Label="for Steinitz pair" Arg="K, pair"/>
## <Returns>an integer</Returns>
## <Description>
## The argument <A>a</A> must be an element in <Ref
## Filt="IsStandardFiniteFieldElement"/>. Then <Ref Oper="SteinitzPair"/>
## returns a pair <C>[d, nr]</C> where <C>d</C> is the degree of <A>a</A>
## over the prime field <C>FF(p, 1)</C> and <C>nr</C> is the Steinitz
## number of <A>a</A> considered as element of <C>FF(p, d)</C>.
## <P/>
## In the second variant a standard finite field <A>K</A> is given and the
## Steinitz number of an element in <A>K</A> and the result is the Steinitz
## pair of the corresponding element.
## <P/>
## The inverse map is provided by a method for <Ref Meth="SteinitzNumber"
## Label="for Steinitz pair"/> which gets a standard finite field and a
## Steinitz pair.
## <Example>gap> F := FF(7, 360);
## FF(7, 360)
## gap> t := ElementSteinitzNumber(F, 7^10);; # prim. elt of FF(7,12)
## gap> sp := SteinitzPair(t);
## [ 12, 117649 ]
## gap> H := FF(7, 12);
## FF(7, 12)
## gap> b := ElementSteinitzNumber(H, 117649);
## ZZ(7,12,[0,1,0,0,0,0,0,0,0,0,0,0])
## gap> Value(MinimalPolynomial(FF(7,1), t), b);
## ZZ(7,12,[0])
## gap> nr := SteinitzNumber(t);
## 282475249
## gap> nr = SteinitzNumber(F, sp);
## true
## gap> sp = SteinitzPair(F, nr);
## true
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
# SteinitzPair(x): [deg, nr] where deg is the degree of x over prime field
# and nr the Steinitz number of x as element in FF(p, deg).
BindGlobal("StPairIntVec", function(K, v)
local p, d, map, ind, sd, vv;
p := Characteristic(K);
d := DegreeOverPrimeField(K);
map := StdMonDegs(d);
ind := Filtered([1..d], i-> IsBound(v[i]) and not IsZero(v[i]));
if Length(ind) = 0 then
return [1,0];
fi;
sd := Lcm(Set(map{ind}));
ind := Filtered([1..d], i-> sd mod map[i] = 0);
while ind[Length(ind)] > Length(v) do
Remove(ind);
od;
vv := v{ind};
return [sd, ValuePol(vv, p)];
end);
GAPInfo.tmpmeth := function(F, x)
return StPairIntVec(F, List(AsVector(x), IntFFE));
end;
InstallOtherMethod(SteinitzPair,
["IsStandardFiniteField", "IsStandardFiniteFieldElement"], GAPInfo.tmpmeth);
Unbind(GAPInfo.tmpmeth);
InstallOtherMethod(SteinitzPair, ["IsStandardPrimeField", "IsFFE"],
function(F, x)
return [1, SteinitzNumber(F,x)];
end);
# with Steinitz number instead of element
GAPInfo.tmpmeth := function(F, nr)
local p;
p := Characteristic(F);
return StPairIntVec(F, CoefficientsQadic(nr, p));
end;
InstallOtherMethod(SteinitzPair,
["IsStandardFiniteField", "IsInt"], GAPInfo.tmpmeth);
Unbind(GAPInfo.tmpmeth);
InstallOtherMethod(SteinitzPair, ["IsStandardPrimeField", "IsInt"],
function(F, nr)
return [1, nr];
end);
InstallMethod(SteinitzPair, ["IsStandardFiniteFieldElement"],
function(x)
return SteinitzPair(FamilyObj(x)!.wholeField, x);
end);
# works only for prime fields
InstallOtherMethod(SteinitzPair, ["IsStandardPrimeField","IsFFE"],
{F, x}-> [1, IntFFE(x)]);
InstallOtherMethod(SteinitzPair, ["IsFFE"], x-> [1, IntFFE(x)]);
# convert Steinitz pair in Steinitz number
GAPInfo.tmp :=
function(K, st)
local p, d, v, map, ind, vv;
p := Characteristic(K);
d := DegreeOverPrimeField(K);
if st[1] = d then
return st[2];
fi;
v := CoefficientsQadic(st[2], p);
map := StdMonDegs(d);
# indices of subsequence of tower basis of subfield
ind := Filtered([1..d], i-> st[1] mod map[i] = 0);
if Length(ind) > Length(v) then
ind := ind{[1..Length(v)]};
fi;
vv := 0*[1..d];
vv{ind} := v;
return ValuePol(vv, p);
end;
InstallOtherMethod(SteinitzNumber, ["IsStandardFiniteField", "IsList"],
GAPInfo.tmp);
InstallOtherMethod(SteinitzNumber, ["IsStandardPrimeField", "IsList"],
function(K, pair)
if pair[1] <> 1 then
Error("Steinitz pair not in prime field.");
fi;
return pair[2];
end);
## <#GAPDoc Label="SteinitzPairConwayGenerator">
## <ManSection>
## <Func Name="SteinitzPairConwayGenerator" Arg="F"/>
## <Returns>a pair of integers </Returns>
## <Description>
## For a standard finite field <A>F</A> of order <M>q</M> for which a Conway
## polynomial (see <Ref BookName="Reference" Func="ConwayPolynomial"/>) is
## known this function returns the <Ref Oper="SteinitzPair"/> for the element
## of <A>F</A> corresponding to <C>Z(q)</C> (which is by definition the zero
## of the Conway polynomial in <A>F</A> with the smallest Steinitz number
## which is compatible with the choice in all proper subfields).
## <P/>
## This is used to construct the
## <Ref Func="StandardIsomorphismGF"/> for <A>F</A>.
## <Example>gap> F := FF(23,18);
## FF(23, 18)
## gap> st := SteinitzPairConwayGenerator(F);
## [ 18, 1362020736983803830549380 ]
## gap> st9 := SteinitzPairConwayGenerator(FF(23,9));
## [ 9, 206098743447 ]
## gap> st6 := SteinitzPairConwayGenerator(FF(23,6));
## [ 6, 45400540 ]
## gap> z := ElementSteinitzNumber(F, st[2]);;
## gap> z9 := ElementSteinitzNumber(F, SteinitzNumber(F, st9));;
## gap> z6 := ElementSteinitzNumber(F, SteinitzNumber(F, st6));;
## gap> e9 := (Size(F)-1)/(23^9-1);
## 1801152661464
## gap> e6 := (Size(F)-1)/(23^6-1);
## 21914624580056211
## gap> z9 = z^e9;
## true
## gap> z6 = z^e6;
## true
## gap> l := Filtered(ZeroesConway(F), x-> x^e9 = z9 and x^e6 = z6);;
## gap> List(l, SteinitzNumber);
## [ 1362020736983803830549380 ]
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallOtherMethod(SteinitzPairConwayGenerator, ["IsStandardPrimeField"],
function(K)
local pl;
pl := ConwayPol(Characteristic(K), 1) * One(K);
return [1, SteinitzNumber(K, -pl[1])];
end);
InstallMethod(SteinitzPairConwayGenerator, ["IsStandardFiniteField"],
function(K)
local d, p, rs, compat, exp, L, c, emb, pl, z, st, i, r;
d := DegreeOverPrimeField(K);
p := Characteristic(K);
if not IsCheapConwayPolynomial(p, d) then
return fail;
fi;
rs := Set(Factors(d));
compat := [];
exp := [];
for r in rs do
L := FF(p, d/r);
c := SteinitzPairConwayGenerator(L);
emb := Embedding(L, K);
Add(compat, Image(emb, ElementSteinitzNumber(L,c[2])));
Add(exp, (Size(K)-1)/(Size(L)-1));
od;
pl := ConwayPol(p, d) * One(K);
#z := FindConjugateZeroes(K, pl, p);
z := ZeroesConway(K);
st := List(z, c-> SteinitzNumber(K, c));
SortParallel(st, z);
# now find Steinitz smallest zero compatible with compat
i := First([1..d], i-> ForAll([1..Length(rs)], j-> z[i]^exp[j] = compat[j]));
return SteinitzPair(K, z[i]);
end);
## <#GAPDoc Label="StandardIsomorphismGF">
## <ManSection>
## <Func Name="StandardIsomorphismGF" Arg="F" />
## <Returns>a field isomorphism </Returns>
## <Description>
## The argument <A>F</A> must be a standard finite field, say
## <C>FF(p,n)</C> such that &GAP; can generate <C>GF(p,n)</C>. This
## function returns the field isomorphism from <C>GF(p,n)</C> to <A>F</A>,
## which sends <C>Z(p,n)</C> to the element with Steinitz pair computed by
## <Ref Func="SteinitzPairConwayGenerator" />.
## <P/>
## <Example>gap> F := FF(13,21);
## FF(13, 21)
## gap> iso := StandardIsomorphismGF(F);
## MappingByFunction( GF(13^21), FF(13, 21), function( x ) ... end )
## gap> K := GF(13,21);
## GF(13^21)
## gap> x := Random(K);;
## gap> l := [1,2,3,4,5];;
## gap> ValuePol(l, x)^iso = ValuePol(l, x^iso);
## true
## gap> y := ElementSteinitzNumber(F, SteinitzPairConwayGenerator(F)[2]);;
## gap> PreImageElm(iso, y);
## z
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
InstallGlobalFunction(StandardIsomorphismGF,
function(L)
local p, d, K, z, fun, a, mat, imat, zz, pre, emb, i;
p := Characteristic(L);
d := DegreeOverPrimeField(L);
if IsBound(L!.isocache) then
return L!.isocache;
fi;
if d = 1 then
emb := IdentityMapping(L);
SetFilterObj(emb, IsFieldHomomorphism and IsBijective);
return emb;
fi;
K := GF(p,d);
z := ElementSteinitzNumber(L, SteinitzPairConwayGenerator(L)[2]);
fun := function(x)
local c, bas;
if IsPositionalObjectRep(x) then
c := x![1];
else
bas := CanonicalBasis(K);
c := Coefficients(bas, x);
fi;
return ValuePol(c, z);
end;
a := One(L);
mat := [];
for i in [1..d] do
Add(mat, ExtRepOfObj(a));
a := a*z;
od;
imat := mat^-1;
zz := Z(Size(K));
pre := function(y)
return ValuePol(ExtRepOfObj(y)*imat, zz);
end;
emb := MappingByFunction(K, L, fun, false, pre);
SetFilterObj(emb, IsFieldHomomorphism and IsBijective);
L!.isocache := emb;
return emb;
end);
## <#GAPDoc Label="FFArith">
## <ManSection>
## <Oper Name="ZZ" Arg="p, n, coeffs" />
## <Oper Name="ZZ" Arg="p, n, ffe" Label="for IsFFE" />
## <Returns>an element in <C>FF(<A>p</A>, <A>n</A>)</C></Returns>
## <Description>
## For a prime <A>p</A>, positive integer <A>n</A> and an integer list
## <A>coeffs</A> this function returns the element in <C>FF(<A>p</A>,
## <A>n</A>)</C> represented by the polynomial with coefficient list
## <A>coeffs</A> modulo <A>p</A>. Elements in standard finite fields are
## also printed in this way.
## <P/>
## For convenience the third argument <A>ffe</A> can be in `GF(p,n)` (see
## <Ref BookName="Reference" Func="GF" Label="for characteristic and
## degree" /> and <Ref BookName="Reference" Filt="IsFFE"/>). This returns
## the image of <A>ffe</A> under the <Ref Func="StandardIsomorphismGF"/>
## of <C>FF(<A>p</A>,<A>n</A>)</C>.
## <Example>gap> x := ZZ(19,5,[1,2,3,4,5]);
## ZZ(19,5,[1,2,3,4,5])
## gap> a := PrimitiveElement(FF(19,5));
## ZZ(19,5,[0,1,0,0,0])
## gap> x = [1,2,3,4,5]*[a^0,a^1,a^2,a^3,a^4];
## true
## gap> One(FF(19,5)); # elements in prime field abbreviated
## ZZ(19,5,[1])
## gap> One(FF(19,5)) = ZZ(19,5,[1]);
## true
## gap> ZZ(19,5,Z(19^5)); # zero of ConwayPolynomial(19,5)
## ZZ(19,5,[12,5,3,4,5])
## </Example>
## </Description>
## </ManSection>
##
## <ManSection>
## <Func Name="MoveToSmallestStandardField" Arg="x" />
## <Meth Name="\+" Arg="x, y" Label="for standard finite field elements" />
## <Meth Name="\*" Arg="x, y" Label="for standard finite field elements" />
## <Meth Name="\-" Arg="x, y" Label="for standard finite field elements" />
## <Meth Name="\/" Arg="x, y" Label="for standard finite field elements" />
## <Returns>a field element </Returns>
## <Description>
## Here <A>x</A> and <A>y</A> must be elements in standard finite fields
## (of the same characteristic).
## <P/>
## Then <Ref Func="MoveToSmallestStandardField" /> returns the element
## <A>x</A> as element of the smallest possible degree extension over the
## prime field.
## <P/>
## The arithmetic operations are even possible when <A>x</A> and <A>y</A>
## are not represented as elements in the same field. In this case the
## elements are first mapped to the smallest field containing both.
## <Example>gap> F := FF(1009,4);
## FF(1009, 4)
## gap> G := FF(1009,6);
## FF(1009, 6)
## gap> x := (PrimitiveElement(F)+One(F))^13;
## ZZ(1009,4,[556,124,281,122])
## gap> y := (PrimitiveElement(G)+One(G))^5;
## ZZ(1009,6,[1,5,10,10,5,1])
## gap> x+y;
## ZZ(1009,12,[557,0,936,713,332,0,462,0,843,191,797,0])
## gap> x-y;
## ZZ(1009,12,[555,0,73,713,677,0,97,0,166,191,212,0])
## gap> x*y;
## ZZ(1009,12,[253,289,700,311,109,851,345,408,813,657,147,887])
## gap> x/y;
## ZZ(1009,12,[690,599,714,648,184,217,563,130,251,675,73,782])
## gap> z := -y + (x+y);
## ZZ(1009,12,[556,0,0,713,0,0,784,0,0,191,0,0])
## gap> SteinitzPair(z);
## [ 4, 125450261067 ]
## gap> x=z;
## true
## gap> MoveToSmallestStandardField(z);
## ZZ(1009,4,[556,124,281,122])
## </Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
# creating finite field elements
InstallMethod(ZZ, ["IsPosInt", "IsPosInt", "IsList"],
function(p,d,c)
local F, i, fam;
F := FF(p,d);
if ForAll([2..Length(c)], i-> c[i] = 0) then
return c[1] * One(F);
fi;
if Length(c) < d then
c := ShallowCopy(c);
for i in [Length(c)+1..d] do
c[i] := 0;
od;
fi;
fam := FamilyObj(PrimitiveElement(F));
c := [One(FF(p,1)) * c];
ConvertToVectorRep(c[1], p);
Objectify(fam!.extType, c);
return c;
end);
# utility method to embed FFE into standard field
InstallOtherMethod(ZZ, ["IsPosInt", "IsPosInt", "IsFFE"],
function(p,d,x)
local F, emb;
F := FF(p,d);
emb := StandardIsomorphismGF(F);
return x^emb;
end);
# nicer print/view for standard finite field elements
InstallMethod(PrintString, ["IsStandardFiniteFieldElement"],
function(x)
local F, c, res;
F := FamilyObj(x)!.wholeField;
c := x![1];
if IsFFE(c) then c := [c]; fi;
res := Concatenation( "ZZ(", String(Characteristic(F)),",",
String(DegreeOverPrimeField(F)),",",String(List(c, IntFFE)),")");
RemoveCharacters(res, " ");
res := SubstitutionSublist(res, ",", "\<,\>");
return res;
end);
InstallMethod(PrintObj, ["IsStandardFiniteFieldElement"],
function(x) Print(PrintString(x)); end);
InstallMethod(ViewString, ["IsStandardFiniteFieldElement"], PrintString);
InstallMethod(String, ["IsStandardFiniteFieldElement"],
x-> StripLineBreakCharacters(PrintString(x)));
# arithmetic for elements from different standard fields
InstallGlobalFunction(MoveToCommonStandardField, function(a, b)
local Fa, Fb, p, da, db, d, F;
Fa := FamilyObj(a)!.wholeField;
Fb := FamilyObj(b)!.wholeField;
p := Characteristic(Fa);
if p <> Characteristic(Fb) then Error("different characteristic"); fi;
da := DegreeOverPrimeField(Fa);
db := DegreeOverPrimeField(Fb);
d := LcmInt(da, db);
F := FF(p, d);
if da < d then
a := a^Embedding(Fa, F);
fi;
if db < d then
b := b^Embedding(Fb, F);
fi;
return [a, b];
end);
InstallMethod(\+, IsNotIdenticalObj, ["IsStandardFiniteFieldElement",
"IsStandardFiniteFieldElement"], function(a, b)
return CallFuncList(\+, MoveToCommonStandardField(a, b));
end);
InstallMethod(\*, IsNotIdenticalObj, ["IsStandardFiniteFieldElement",
"IsStandardFiniteFieldElement"], function(a, b)
return CallFuncList(\*, MoveToCommonStandardField(a, b));
end);
InstallMethod(\=, IsNotIdenticalObj, ["IsStandardFiniteFieldElement",
"IsStandardFiniteFieldElement"], function(a, b)
return Characteristic(a) = Characteristic(b) and
SteinitzPair(a) = SteinitzPair(b);
end);
# utility to represent element in smallest field
InstallGlobalFunction(MoveToSmallestStandardField, function(a)
local Fa, d, st, F;
if IsFFE(a) then
# also handle elements in GF(p,n)
d := DegreeOverPrimeField(DefaultField(a));
if d = 1 then return a; fi;
a := a^StandardIsomorphismGF(FF(Characteristic(a), d));
fi;
Fa := FamilyObj(a)!.wholeField;
d := DegreeOverPrimeField(Fa);
st := SteinitzPair(a);
if st[1] = d then
return a;
else
F := FF(Characteristic(Fa), st[1]);
return PreImageElm(Embedding(F, Fa), a);
fi;
end);
[ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet)
]
|