|
#############################################################################
##
#W cvec.gi GAP 4 package `cvec' Max Neunhoeffer
##
## Copyright (C) 2007 Max Neunhoeffer, Lehrstuhl D f. Math., RWTH Aachen
## This file is free software, see license information at the end.
##
## This file contains the higher levels for compact vectors over finite
## fields.
##
#############################################################################
## Info Class:
#############################################################################
SetInfoLevel(InfoCVec,1);
#############################################################################
## The technical stuff for typing:
#############################################################################
# A set holding all q's for which we have cvec classes:
BindGlobal( "CVEC_q", [] );
# A list holding field infos:
BindGlobal( "CVEC_F", [] );
# A list holding lengths of vectors existing for each q:
BindGlobal( "CVEC_lens", [] );
# A list holding cvec classes (which are pairs [fieldinfo,len])
BindGlobal( "CVEC_classes", [] );
#############################################################################
## Administration of field info and cvec class data:
#############################################################################
BindGlobal( "CVEC_BestGreaseTab",
[ , # q=1
8, # q=2
5, # q=3
4, # q=4
3, # q=5
# No longer valid:
# Note that we reduce here the grease level to 2 such
# that elsperword (= 8) is divisible by the grease level,
# this is used to make the multiplication function much
# simpler (no bad case at right edge of A!)
, # q=6
3, # q=7
3, # q=8
3, # q=9
, # q=10
2, # q=11
, # q=12
2, # q=13
, # q=14
, # q=15
2, # q=16
] );
InstallGlobalFunction( CVEC_NewCVecClass, function(p,d,len)
# Creates a new class of cvecs or returns a cached one:
local bestgrease,bitsperel,cl,elsperword,filts,greasetabl,j,l,po,pos,pos2,
q,s,scafam,size,tab1,tab2,ty,wordlen,filtscmat;
# First check the arguments:
if d <= 0 then
Error("CVEC_NewCVecClass: Degree must be positive.");
return fail;
fi;
if d >= CVEC_MAXDEGREE then
Error("CVEC_NewCVecClass: Degree must be < ",CVEC_MAXDEGREE,".");
return fail;
fi;
if not(IsPrime(p)) then
Error("CVEC_MakeField: p must be a prime.");
return fail;
fi;
if not(IsSmallIntRep(p)) then
Error("CVEC_NewCVecClass: p must be a prime that is an immediate int .");
return fail;
fi;
if not(IsSmallIntRep(len)) then
Error("CVEC_NewCVecClass: len must be an immediate integer.");
return fail;
fi;
q := p^d;
if q <= MAXSIZE_GF_INTERNAL then size := 0;
elif IsSmallIntRep(q) then size := 1;
else size := 2; fi;
# First try to find q:
pos := Position(CVEC_q,q);
if pos = fail then
l := []; # Here we collect the information
l[CVEC_IDX_p] := p;
l[CVEC_IDX_d] := d;
l[CVEC_IDX_q] := q;
l[CVEC_IDX_size] := size;
# We have to make the new field info structure:
po := -CoefficientsOfLaurentPolynomial(ConwayPolynomial(p,d));
if po[2] <> 0 then Error("Unexpected case #1"); fi;
po := List(po[1],IntFFE);
s := CVEC_COEFF_LIST_TO_C(po,"");
l[CVEC_IDX_conway] := s;
# Bits per element, will be increased in the following loop:
if IsOddInt(p) then
bitsperel := 1;
else
bitsperel := 0;
fi;
j := p-1;
while j > 0 do
bitsperel := bitsperel + 1;
j := QuoInt(j,2);
od;
l[CVEC_IDX_bitsperel] := bitsperel;
# Prime field elements per Word:
# Note that for 64 bit machines we always put only twice as much
# prime field elements into a Word than for 32 bit machines (even if
# one more would fit!) such that conversion between binary formats
# is easier later on.
elsperword := QuoInt(32,bitsperel);
if CVEC_BYTESPERWORD = 8 then
elsperword := elsperword * 2;
fi;
l[CVEC_IDX_elsperword] := elsperword;
# Set up best greasing level:
if q <= 16 and IsBound(CVEC_BestGreaseTab[q]) then
bestgrease := CVEC_BestGreaseTab[q];
greasetabl := q^bestgrease;
elif q <= 256 then
bestgrease := 1;
greasetabl := q;
else
bestgrease := 0; # no grease
greasetabl := 0;
fi;
l[CVEC_IDX_bestgrease] := bestgrease;
l[CVEC_IDX_greasetabl] := greasetabl;
# Now the starting filter list:
filts := IsCVecRep;
filtscmat := IsCMatRep;
if size = 0 then
filts := filts and IsCVecRepOverSmallField;
filtscmat := filtscmat and IsCVecRepOverSmallField;
fi;
if d = 1 then
filts := filts and IsCVecRepOverPrimeField;
filtscmat := filtscmat and IsCVecRepOverPrimeField;
fi;
# Note that IsMutable is added below, when we create the vector type
l[CVEC_IDX_filts] := filts;
l[CVEC_IDX_filtscmat] := filtscmat;
# We use the "official" families:
scafam := FFEFamily(p);
l[CVEC_IDX_scafam] := scafam;
# Now for small finite fields two tables for conversion:
if q <= MAXSIZE_GF_INTERNAL then
tab1 := 0*[1..q];
tab2 := 0*[1..q];
CVEC_INIT_SMALL_GFQ_TABS(p,ConwayPol(p,d),tab1,tab2);
else
# If p is < 65536, we need access to the prime field:
if p < MAXSIZE_GF_INTERNAL then
tab1 := 0*[1..p];
tab2 := 0*[1..p];
CVEC_INIT_SMALL_GFQ_TABS(p,ConwayPol(p,1),tab1,tab2);
else
tab1 := [];
tab2 := [];
fi;
fi;
l[CVEC_IDX_tab1] := tab1;
l[CVEC_IDX_tab2] := tab2;
# Now l is nearly ready!
#l := [p,d,q,s,bitsperel,elsperword,0,bestgrease,greasetabl,filts,
# tab1,tab2,size,scafam];
Objectify(NewType(CVecFieldInfoFamily,IsCVecFieldInfo),l);
# Do the internal part: This does index CVEC_IDX_wordinfo:
CVEC_FINALIZE_FIELDINFO(l);
pos := PositionSorted(CVEC_q,q);
Add(CVEC_q,q,pos);
Add(CVEC_F,l,pos);
Add(CVEC_lens,[],pos);
Add(CVEC_classes,[],pos);
else # pos <> fail
elsperword := CVEC_F[pos]![CVEC_IDX_elsperword]; # for later use
filts := CVEC_F[pos]![CVEC_IDX_filts]; # for later use
filtscmat := CVEC_F[pos]![CVEC_IDX_filtscmat]; # for later use
scafam := CVEC_F[pos]![CVEC_IDX_scafam]; # for later use
fi;
# Now we know that the field info is at position pos:
pos2 := Position(CVEC_lens[pos],len);
if pos2 <> fail then
return CVEC_classes[pos][pos2];
fi;
# Build the class object, note that we need elsperword and filts from above:
cl := [];
cl[CVEC_IDX_fieldinfo] := CVEC_F[pos];
cl[CVEC_IDX_len] := len;
wordlen := d * (QuoInt( len + elsperword - 1, elsperword ));
cl[CVEC_IDX_wordlen] := wordlen;
ty := NewType(CollectionsFamily(scafam),filts and IsMutable,cl);
cl[CVEC_IDX_type] := ty;
cl[CVEC_IDX_GF] := GF(p,d);
cl[CVEC_IDX_lens] := CVEC_lens[pos];
cl[CVEC_IDX_classes] := CVEC_classes[pos];
ty := NewType(CollectionsFamily(CollectionsFamily(scafam)),
filtscmat and IsMutable);
cl[CVEC_IDX_typecmat] := ty;
Objectify(NewType(CVecClassFamily,IsCVecClass),cl);
# Put it into the cache:
pos2 := PositionSorted(CVEC_lens[pos],len);
Add(CVEC_lens[pos],len,pos2);
Add(CVEC_classes[pos],cl,pos2);
# Now add zero, one, and primitive root for the case d=1:
return CVEC_classes[pos][pos2];
end );
InstallGlobalFunction( CVEC_NewCVecClassSameField, function(c,len)
# Creates a new class in the case that another length is already known:
local pos;
pos := Position(c![CVEC_IDX_lens],len);
if pos = fail then
return CVEC_NewCVecClass(c![CVEC_IDX_fieldinfo]![CVEC_IDX_p],
c![CVEC_IDX_fieldinfo]![CVEC_IDX_d],len);
else
return c![CVEC_IDX_classes][pos];
fi;
end );
InstallMethod( CVecClass, "for a cvec", [IsCVecRep],
DataObj);
InstallMethod( CVecClass, "for a cvec and a length", [IsCVecRep, IsInt],
function(v,l)
return CVEC_NewCVecClassSameField(DataObj(v),l);
end );
InstallMethod( CVecClass, "for a cvecclass and a length", [IsCVecClass, IsInt],
function(c,l)
return CVEC_NewCVecClassSameField(c,l);
end );
InstallMethod( CVecClass, "for three integers", [IsPosInt, IsPosInt, IsInt],
function(p,d,l)
return CVEC_NewCVecClass(p,d,l);
end );
InstallMethod( CVecClass, "for a finite field and an integer",
[IsField and IsFinite, IsInt],
function(f,l)
local p,d;
p := Characteristic(f);
d := DegreeOverPrimeField(f);
return CVEC_NewCVecClass(p,d,l);
end );
InstallGlobalFunction( CVEC_New, function(arg)
local c,d,l,p;
if Length(arg) = 1 then
c := arg[1];
if IsCVecRep(c) then
c := DataObj(c);
fi;
if IsCVecClass(c) then
return CVEC_NEW(c,c![CVEC_IDX_type]);
fi;
elif Length(arg) = 2 then
p := Characteristic(arg[1]);
d := DegreeOverPrimeField(arg[1]);
l := arg[2];
elif Length(arg) = 3 then
p := arg[1];
d := arg[2];
l := arg[3];
if IsInt(p) and IsPrime(p) and p > 0 and IsInt(d) and d >= 1 and
IsInt(l) and l >= 1 then
c := CVEC_NewCVecClass(p,d,l);
return CVEC_NEW(c,c![CVEC_IDX_type]);
fi;
fi;
Error("Usage: CVEC_New( [ cvec | cvecclass | field,l | p,d,l ] )");
end );
##############################
# Some nice viewing methods: #
##############################
InstallMethod( ViewObj, "for a cvec field info", [IsCVecFieldInfo],
function(f)
Print("<cvec-fieldinfo p=",f![CVEC_IDX_p],
" d=",f![CVEC_IDX_d]," q=",f![CVEC_IDX_q],
" bpl=",f![CVEC_IDX_bitsperel]," epw=",f![CVEC_IDX_elsperword],
" grease=",f![CVEC_IDX_bestgrease],
" gtablen=",f![CVEC_IDX_greasetabl],">");
end);
InstallMethod( ViewObj, "for a cvec class", [IsCVecClass],
function(c)
local f;
f := c![CVEC_IDX_fieldinfo];
Print("<cvec-class field=GF(",f![CVEC_IDX_p],",",f![CVEC_IDX_d],
") len=",c![CVEC_IDX_len]," wordlen=",c![CVEC_IDX_wordlen],">");
end);
InstallMethod( ViewObj, "for a cvec", [IsCVecRep],
function(v)
local c;
c := DataObj(v);
Print("<");
if not(IsMutable(v)) then Print("immutable "); fi;
Print("cvec over GF(",c![CVEC_IDX_fieldinfo]![CVEC_IDX_p],",",
c![CVEC_IDX_fieldinfo]![CVEC_IDX_d],") of length ",
c![CVEC_IDX_len],">");
end);
InstallMethod( PrintObj, "for a cvec class", [IsCVecClass],
function(c)
Print("CVEC_NewCVecClass(",c![CVEC_IDX_fieldinfo]![CVEC_IDX_p],",",
c![CVEC_IDX_fieldinfo]![CVEC_IDX_d],",",c![CVEC_IDX_len],")");
end);
InstallMethod( PrintObj, "for a cvec", [IsCVecRep],
function(v)
local l,c,i;
c := DataObj(v);
Print("NewVector(IsCVecRep,GF(",c![CVEC_IDX_fieldinfo]![CVEC_IDX_p],",",
c![CVEC_IDX_fieldinfo]![CVEC_IDX_d],"),[");
if c![CVEC_IDX_fieldinfo]![CVEC_IDX_size] = 0 then # GAP FFEs
l := Unpack(v);
for i in l do Print(i,","); od;
else
l := Unpack(v);
for i in l do Print(i,","); od;
fi;
Print("])");
end);
InstallMethod( String, "for a cvec", [IsCVecRep],
function(v)
local l,c,i,res;
c := DataObj(v);
res := "NewVector(IsCVecRep,GF(";
Append(res,String(c![CVEC_IDX_fieldinfo]![CVEC_IDX_p]));
Add(res,',');
Append(res,String(c![CVEC_IDX_fieldinfo]![CVEC_IDX_d]));
Append(res,"),[");
if c![CVEC_IDX_fieldinfo]![CVEC_IDX_size] = 0 then # GAP FFEs
l := Unpack(v);
for i in l do Append(res,String(i)); Append(res,","); od;
else
l := Unpack(v);
for i in l do Append(res,String(i)); Append(res,","); od;
fi;
Append(res,"])");
return res;
end);
BindGlobal( "CVEC_CharactersForDisplay",
".123456789abcdefghijklmnopqrstuvwxyz" );
InstallMethod( Display, "for a cvec", [IsCVecRep],
function(v)
local i,l,c,q,lo;
c := DataObj(v);
Print("[");
q := c![CVEC_IDX_fieldinfo]![CVEC_IDX_q];
if q <= 36 then
l := IntegerRep(v);
Print(CVEC_CharactersForDisplay{l+1},"]\n");
elif c![CVEC_IDX_fieldinfo]![CVEC_IDX_size] = 1 then
l := Unpack(v);
lo := LogInt(q,10)+7; # This is the number of digits needed plus 6
for i in l do Print(String(i,lo)); od;
Print("]\n");
else
l := Unpack(v);
for i in l do Print(i,","); od;
Print("]\n");
fi;
end);
#########################################
# Handling of scalars on the GAP level: #
#########################################
InstallGlobalFunction( CVEC_HandleScalar, function(cl,s)
# cl is a cvecclass and s a scalar
local v,d;
if IsInternalRep(s) then return s;
# Note that this case also covers integers!
elif IsZmodnZObj(s) then return s![1];
fi;
# Now we have to check, whether the field element is over the right field:
d := cl![CVEC_IDX_fieldinfo]![CVEC_IDX_d];
if s![2] < d then
s := FFECONWAY.WriteOverLargerField(s,d);
elif s![2] > d then
s := FFECONWAY.TryToWriteInSmallerField(s,d);
# s now could be internal
if IsInternalRep(s) then return s; fi;
if s = fail then Error("input vector not defined over the expected field"); fi;
fi;
if IsGF2VectorRep(s![1]) then
v := ShallowCopy(s![1]);
PLAIN_GF2VEC(v);
return v;
elif Is8BitVectorRep(s![1]) then
v := ShallowCopy(s![1]);
PLAIN_VEC8BIT(v);
return v;
elif cl![CVEC_IDX_fieldinfo]![CVEC_IDX_p] < MAXSIZE_GF_INTERNAL then
return s![1];
else
return List(s![1],x->x![1]); # this unpacks ZmodnZObjs
fi;
end );
#################################################
# Now to the installation of methods for cvecs: #
#################################################
# Length:
InstallOtherMethod( Length, "for cvecs", [IsCVecRep],
function(v)
return DataObj(v)![CVEC_IDX_len];
end);
# AddRowVector(v,w [,s][,fr,to]) where s is integer or FFE:
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep],
function(v,w) CVEC_ADD2(v,w,0,0); end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsInt, IsInt],
function(v,w,fr,to) CVEC_ADD2(v,w,fr,to); end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsFFE and IsInternalRep],
function(v,w,s)
CVEC_ADDMUL(v,w,s,0,0);
end );
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsFFE],
function(v,w,s)
CVEC_ADDMUL(v,w,CVEC_HandleScalar(DataObj(v),s),0,0);
end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsInt and IsSmallIntRep],
function(v,w,s) CVEC_ADDMUL(v,w,s,0,0); end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsFFE and IsInternalRep, IsInt, IsInt],
function(v,w,s,fr,to)
CVEC_ADDMUL(v,w,s,fr,to);
end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep, IsCVecRep, IsFFE, IsInt, IsInt],
function(v,w,s,fr,to)
CVEC_ADDMUL(v,w,CVEC_HandleScalar(DataObj(v),s),fr,to);
end);
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep,IsCVecRep,IsInt and IsSmallIntRep,IsInt,IsInt],
CVEC_ADDMUL );
InstallOtherMethod( AddRowVector, "for cvecs",
[IsMutable and IsCVecRep,IsCVecRep,IsFFE and IsInternalRep,IsInt,IsInt],
CVEC_ADDMUL );
# MultVector(v,s [,fr,to]) where s is integer or FFE:
# Note that we do not give a method for MultVector with 5 arguments!
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsInt and IsSmallIntRep],
function(v,s) CVEC_MUL1(v,s,0,0); end);
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsInt and IsSmallIntRep, IsInt, IsInt],
CVEC_MUL1 );
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsFFE and IsInternalRep],
function(v,s) CVEC_MUL1(v,s,0,0); end);
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsFFE and IsInternalRep, IsInt, IsInt],
CVEC_MUL1 );
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsFFE],
function(v,s)
CVEC_MUL1(v,CVEC_HandleScalar(DataObj(v),s),0,0);
end);
InstallOtherMethod( MultVector, "for cvecs",
[IsMutable and IsCVecRep, IsFFE, IsInt, IsInt],
function(v,s,fr,to)
CVEC_MUL1(v,CVEC_HandleScalar(DataObj(v),s),fr,to);
end);
# Addition of vectors:
InstallOtherMethod( \+, "for cvecs", [IsCVecRep, IsCVecRep],
function(v,w)
local u,vcl;
vcl := DataObj(v);
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_ADD3(u,v,w);
if not(IsMutable(v)) and not(IsMutable(w)) then MakeImmutable(u); fi;
return u;
end);
# Subtraction of vectors:
InstallOtherMethod( \-, "for cvecs", [IsCVecRep, IsCVecRep],
function(v,w)
local u,vcl,p;
vcl := DataObj(v);
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_COPY(v,u);
CVEC_ADDMUL(u,w,p-1,0,0);
if not(IsMutable(v)) and not(IsMutable(w)) then MakeImmutable(u); fi;
return u;
end);
# Additive inverse of vectors:
InstallOtherMethod( AdditiveInverseMutable, "for cvecs", [IsCVecRep],
function(v)
local u,vcl,p;
vcl := DataObj(v);
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_MUL2(u,v,p-1);
return u;
end);
InstallOtherMethod( AdditiveInverseSameMutability, "for cvecs", [IsCVecRep],
function(v)
local u,vcl,p;
vcl := DataObj(v);
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_MUL2(u,v,p-1);
if not(IsMutable(v)) then MakeImmutable(u); fi;
return u;
end);
# Multiplication of vectors by scalars:
BindGlobal( "CVEC_VECTOR_TIMES_SCALAR", function(v,s)
# The scalar here must already be run through CVEC_HandleScalar
# if necessary! Of course, integers and FFEs in internal representation
# of course is allowed.
local u,vcl;
vcl := DataObj(v);
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_MUL2(u,v,s);
if not(IsMutable(v)) then MakeImmutable(u); fi;
return u;
end );
InstallOtherMethod( \*, "for cvecs", [IsCVecRep, IsInt],
CVEC_VECTOR_TIMES_SCALAR);
InstallOtherMethod( \*, "for cvecs", [IsCVecRep, IsFFE and IsInternalRep],
CVEC_VECTOR_TIMES_SCALAR);
InstallOtherMethod( \*, "for cvecs", [IsCVecRep, IsFFE],
function (v,s)
return CVEC_VECTOR_TIMES_SCALAR(v,
CVEC_HandleScalar(DataObj(v),s));
end);
InstallOtherMethod( \*, "for cvecs", [IsInt,IsCVecRep],
function(s,v) return CVEC_VECTOR_TIMES_SCALAR(v,s); end);
InstallOtherMethod( \*, "for cvecs", [IsFFE and IsInternalRep,IsCVecRep],
function(s,v) return CVEC_VECTOR_TIMES_SCALAR(v,s); end);
InstallOtherMethod( \*, "for cvecs", [IsFFE, IsCVecRep],
function (s,v)
return CVEC_VECTOR_TIMES_SCALAR(v,
CVEC_HandleScalar(DataObj(v),s));
end);
#############################################################################
# Applying Frobenius automorphisms element-wise:
#############################################################################
InstallOtherMethod( \^, "for a cvec and a trivial frobenius automorphism",
[IsCVecRep and IsFFECollection, IsMapping and IsOne],
function( v, f )
return v;
end );
InstallOtherMethod( \^,
"for a mutable cvec and a trivial frobenius automorphism",
[IsCVecRep and IsFFECollection and IsMutable, IsMapping and IsOne],
function( v, f )
return ShallowCopy(v);
end );
InstallOtherMethod( \^, "for a mutable cvec and a frobenius automorphism",
[IsCVecRep and IsFFECollection and IsMutable, IsFrobeniusAutomorphism],
function( v, f )
local w,i;
w := ShallowCopy(v);
for i in [1..Length(w)] do
w[i] := v[i]^f;
od;
return w;
end );
InstallOtherMethod( \^, "for a cvec and a frobenius automorphism",
[IsCVecRep and IsFFECollection, IsFrobeniusAutomorphism],
function( v, f )
local w,i;
w := ShallowCopy(v);
for i in [1..Length(w)] do
w[i] := v[i]^f;
od;
return MakeImmutable(w);
end );
InstallOtherMethod( ScalarProduct, "for two cvecs, kernel method",
[ IsCVecRep and IsCVecRepOverSmallField and IsCVecRepOverPrimeField,
IsCVecRep and IsCVecRepOverSmallField and IsCVecRepOverPrimeField],
1, CVEC_SCALAR_PRODUCT );
InstallOtherMethod( ScalarProduct, "for two cvecs, GAP method",
[ IsCVecRep, IsCVecRep ],
function( v,w )
return PROD_LIST_LIST_DEFAULT( Unpack(v), Unpack(w), 0 );
end );
# Comparison of vectors:
InstallOtherMethod( \=, "for cvecs", [IsCVecRep, IsCVecRep], CVEC_CVEC_EQ );
InstallOtherMethod( \<, "for cvecs", [IsCVecRep, IsCVecRep], CVEC_CVEC_LT );
InstallOtherMethod( IsZero, "for cvecs", [IsCVecRep], CVEC_CVEC_ISZERO);
# Element access for vectors:
InstallOtherMethod( \[\]\:\=, "for a cvec, a pos, and an int",
[IsCVecRep and IsMutable, IsPosInt, IsInt and IsSmallIntRep], CVEC_ASS_CVEC );
InstallOtherMethod( \[\]\:\=, "for a cvec, a pos, and an int",
[IsCVecRep and IsMutable, IsPosInt, IsFFE and IsInternalRep], CVEC_ASS_CVEC );
InstallOtherMethod( \[\]\:\=, "for a cvec, a pos, and a ffe",
[IsCVecRep and IsMutable, IsPosInt, IsFFE],
function(v,pos,s)
CVEC_ASS_CVEC(v,pos,CVEC_HandleScalar(DataObj(v),s));
end);
InstallOtherMethod( \[\]\:\=, "for cvecs", [IsCVecRep, IsPosInt, IsInt],
function(v,p,o) Error("cvec is immutable"); end);
InstallOtherMethod( \[\]\:\=, "for cvecs", [IsCVecRep, IsPosInt, IsFFE],
function(v,p,o) Error("cvec is immutable"); end);
InstallOtherMethod( \[\], "for cvecs",
[IsCVecRep and IsCVecRepOverSmallField, IsPosInt],CVEC_ELM_CVEC );
InstallOtherMethod( \[\], "for cvecs", [IsCVecRep, IsPosInt],
function(v,pos)
local d,fam,i,p,s,size,vcl;
vcl := DataObj(v);
size := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_size];
s := CVEC_ELM_CVEC(v,pos);
if size = 0 then return s; fi;
d := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_d];
if d = 1 then
if IsFFE(s) then
return s;
else
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
return ZmodnZObj(s,p);
fi;
else
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
if p > 65536 then
for i in [1..d] do
s[i] := ZmodnZObj(s[i],p);
od;
fi;
ConvertToVectorRep(s,p);
s := [s,d,fail];
fam := FFEFamily(p);
Objectify(fam!.ConwayFldEltDefaultType,s);
return s;
fi;
end);
# PositionNonZero and friends:
InstallOtherMethod( PositionNonZero, "for cvecs",
[IsCVecRep], CVEC_POSITION_NONZERO_CVEC);
InstallOtherMethod( PositionLastNonZero, "for cvecs",
[IsCVecRep], CVEC_POSITION_LAST_NONZERO_CVEC);
InstallOtherMethod( PositionNot, "for cvecs",
[IsCVecRep, IsFFE],
function(v,z)
local j;
if z <> Zero(z) then
for j in [1..Length(v)] do
if v[j] <> z then
return j;
fi;
od;
return Length(v)+1;
fi;
return PositionNonZero(v);
end);
InstallOtherMethod( PositionNot, "for cvecs",
[IsCVecRep, IsFFE, IsInt],
function(v,z,i)
local j;
if not(IsZero(z)) or i <> 0 then
for j in [i+1..Length(v)] do
if v[j] <> z then
return j;
fi;
od;
return Length(v)+1;
fi;
return PositionNonZero(v);
end);
# Copying:
InstallOtherMethod( ShallowCopy, "for cvecs", [IsCVecRep],
function(v)
local u,vcl;
vcl := DataObj(v);
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
CVEC_COPY(v,u);
return u;
end);
# Zeroing:
InstallOtherMethod( ZeroMutable, "for cvecs", [IsCVecRep],
function(v)
local u,vcl;
vcl := DataObj(v);
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
return u;
end);
InstallOtherMethod( ZeroSameMutability, "for cvecs", [IsCVecRep],
function(v)
local u,vcl;
vcl := DataObj(v);
u := CVEC_NEW(vcl,vcl![CVEC_IDX_type]);
if not(IsMutable(v)) then
MakeImmutable(u);
fi;
return u;
end);
InstallMethod( ZeroVector, "for an integer and a cvec",
[IsInt, IsCVecRep],
function(len,v)
local cl;
cl := DataObj(v);
if cl![CVEC_IDX_len] <> len then
cl := CVEC_NewCVecClassSameField(cl,len);
fi;
return CVEC_NEW(cl,cl![CVEC_IDX_type]);
end);
InstallMethod( ZeroVector, "for an integer and a cmat",
[IsInt, IsCMatRep],
function(len,m)
local cl;
cl := m!.vecclass;
if cl![CVEC_IDX_len] <> len then
cl := CVEC_NewCVecClassSameField(cl,len);
fi;
return CVEC_NEW(cl,cl![CVEC_IDX_type]);
end);
# The making of:
InstallMethod( CVec, "for a cvec class",
[IsCVecClass],
function(c)
return CVEC_NEW(c,c![CVEC_IDX_type]);
end);
InstallMethod( CVec, "for a homogeneous list and two posints",
[IsList, IsPosInt, IsPosInt],
function(l,p,d)
local c,v;
if IsSmallIntRep(p^d) then
c := CVEC_NewCVecClass(p,d,Length(l));
else
c := CVEC_NewCVecClass(p,d,Length(l)/d);
fi;
return CVec(l,c); # Delegate to the following routine
end);
InstallMethod( CVec, "for a homogeneous list and a finite field",
[IsList, IsField and IsFinite],
function(l,f)
local c,p,d,v;
p := Characteristic(f);
d := DegreeOverPrimeField(f);
if IsSmallIntRep(p^d) then
c := CVEC_NewCVecClass(p,d,Length(l));
else
c := CVEC_NewCVecClass(p,d,Length(l)/d);
fi;
return CVec(l,c); # Delegate to the following routine
end);
InstallMethod( CVec, "for a homogeneous list and a cvec class",
[IsList, IsCVecClass],
function(l,c)
local v;
v := CVEC_NEW(c,c![CVEC_IDX_type]);
# We recognise the type of entries by looking at the first one:
if Length(l) > 0 then
if IsZmodnZObj(l[1]) then
CVEC_INTREP_TO_CVEC(List(l,x->x![1]),v);
return v;
elif IsFFE(l[1]) and not(IsInternalRep(l[1])) then # large scalars:
CVEC_INTREP_TO_CVEC(List(l,x->CVEC_HandleScalar(c,x)),v);
return v;
fi;
fi;
# This can only handle integers and internal FFEs:
CVEC_INTREP_TO_CVEC(l,v);
return v;
end);
InstallOtherMethod( CVec, "for a compressed gf2 vector",
[IsList and IsGF2VectorRep],
function(v)
local c,w;
v := ShallowCopy(v);
PLAIN_GF2VEC(v);
c := CVEC_NewCVecClass(2,1,Length(v));
w := CVEC_NEW(c,c![CVEC_IDX_type]);
CVEC_INTREP_TO_CVEC(v,w);
return w;
end);
InstallOtherMethod( CVec, "for a compressed 8bit vector",
[IsList and Is8BitVectorRep],
function(v)
local c,pd,w;
pd := Factors(Size(Field(v)));
v := ShallowCopy(v);
PLAIN_VEC8BIT(v);
c := CVEC_NewCVecClass(pd[1],Length(pd),Length(v));
w := CVEC_NEW(c,c![CVEC_IDX_type]);
CVEC_INTREP_TO_CVEC(v,w);
return w;
end);
InstallMethod( ConstructingFilter, "for a cvec",
[IsCVecRep],
function(v)
return IsCVecRep;
end );
# `NewVector` was a constructor
# in GAP <= 4.12 but is a tag based operation in GAP 4.13.
Perform( [ function( filt, f, l )
local p,d,c;
p := Characteristic(f);
d := DegreeOverPrimeField(f);
if IsSmallIntRep(p^d) then
c := CVEC_NewCVecClass(p,d,Length(l));
else
c := CVEC_NewCVecClass(p,d,Length(l)/d);
fi;
return CVec(l,c); # Delegate to another routine
end ],
function( method )
if IS_CONSTRUCTOR( NewVector ) then
# This holds in GAP <= 4.12.
InstallMethod( NewVector,
["IsCVecRep", "IsField and IsFinite", "IsList"], method );
elif IsBoundGlobal( "InstallTagBasedMethod" ) then
# This should hold in GAP 4.13.
ValueGlobal("InstallTagBasedMethod")( NewVector, IsCVecRep, method );
else
# This should not happen.
Error( "NewVector is neither a constructor not a tag based operation" );
fi;
end );
# `NewZeroVector` was a constructor
# in GAP <= 4.12 but is a tag based operation in GAP 4.13.
Perform( [ function( filt, f, l )
local p,d,cl;
p := Characteristic(f);
d := DegreeOverPrimeField(f);
cl := CVEC_NewCVecClass(p,d,l);
return CVEC_NEW(cl,cl![CVEC_IDX_type]);
end ],
function( method )
if IS_CONSTRUCTOR( NewZeroVector ) then
# This holds in GAP <= 4.12.
InstallMethod( NewZeroVector,
["IsCVecRep", "IsField and IsFinite", "IsInt"], method );
elif IsBoundGlobal( "InstallTagBasedMethod" ) then
# This should hold in GAP 4.13.
ValueGlobal("InstallTagBasedMethod")( NewZeroVector, IsCVecRep, method );
else
# This should not happen.
Error( "NewZeroVector is neither a constructor not a tag based operation" );
fi;
end );
InstallMethod( Vector, "for a list of finite field elements, and a cvec",
[IsList, IsCVecRep],
function( l, v )
local c;
if Length(l) = Length(v) then
return CVec(l,DataObj(v));
else
c := CVEC_NewCVecClassSameField(DataObj(v),Length(l));
return CVec(l,c);
fi;
end );
InstallMethod( ChangedBaseDomain, "for a cvec and a finite field",
[IsCVecRep,IsField and IsFinite],
function( v, f )
local cl;
cl := CVEC_NewCVecClass(Characteristic(f),DegreeOverPrimeField(f),
Length(v));
return CVec(Unpack(v),cl);
end );
# And the way back:
InstallMethod( Unpack, "for cvecs", [IsCVecRep],
function(v)
local d,f,fam,i,l,p,q,vcl;
vcl := DataObj(v);
f := vcl![CVEC_IDX_fieldinfo];
p := f![CVEC_IDX_p];
d := f![CVEC_IDX_d];
q := f![CVEC_IDX_q];
if q <= MAXSIZE_GF_INTERNAL or d = 1 then
l := 0*[1..Length(v)];
else
l := List([1..Length(v)],i->0*[1..d]);
fi;
CVEC_CVEC_TO_INTREP(v,l);
# Now convert things to FFEs:
if q <= MAXSIZE_GF_INTERNAL then
# We return internal FFEs:
CVEC_INTLI_TO_FFELI(f,l);
elif d = 1 then
# We return ZmodnZObjs:
for i in [1..Length(l)] do
l[i] := ZmodnZObj(l[i],p);
od;
elif p < MAXSIZE_GF_INTERNAL then
# We build a large FFE with GF2 or 8bit coeffs
fam := FFEFamily(p);
for i in [1..Length(l)] do
CVEC_INTLI_TO_FFELI(f,l[i]);
ConvertToVectorRep(l[i],p);
l[i] := [l[i],d,fail];
Objectify(fam!.ConwayFldEltDefaultType,l[i]);
od;
else
# We build a large FFE with ZmodnZObj coeffs
fam := FFEFamily(p);
for i in [1..Length(l)] do
l[i] := [List(l[i],x->ZmodnZObj(x,p)),d,fail];
Objectify(fam!.ConwayFldEltDefaultType,l[i]);
od;
fi;
return l;
end);
InstallMethod( IntegerRep, "for cvecs", [IsCVecRep],
function(v)
local d,l,p,q,vcl;
vcl := DataObj(v);
p := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
d := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_d];
q := vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_q];
if q <= MAXSIZE_GF_INTERNAL or d = 1 then
l := 0*[1..Length(v)];
else
l := List([1..Length(v)],i->0*[1..d]);
fi;
CVEC_CVEC_TO_INTREP(v,l);
return l;
end);
InstallOtherMethod( NumberFFVector, "for a cvec, and a field size",
[IsCVecRep and IsRowVector and IsFFECollection, IsPosInt],
function(v,sz)
local bas,c,f,halfword,i,l,res,wordlen;
c := DataObj(v);
f := c![CVEC_IDX_fieldinfo];
if sz <> f![CVEC_IDX_q] then
ErrorNoReturn("CVEC_NumberFFVector: vector over wrong field");
fi;
wordlen := c![CVEC_IDX_wordlen];
bas := f![CVEC_IDX_p] ^ f![CVEC_IDX_elsperword];
if IsSmallIntRep(bas) then
l := 0*[1..wordlen];
CVEC_CVEC_TO_NUMBERFFLIST(v,l,false);
res := l[1];
for i in [2..wordlen] do
res := res * bas + l[i];
od;
else
l := 0*[1..2*wordlen];
CVEC_CVEC_TO_NUMBERFFLIST(v,l,true);
halfword := 2^(CVEC_BYTESPERWORD*4);
res := l[1] + l[2]*halfword;
for i in [3,5..2*wordlen-1] do
res := res * bas + l[i] + halfword * l[i+1];
od;
fi;
return res;
end);
InstallOtherMethod( NumberFFVector, "for a cvec",
[IsCVecRep and IsRowVector and IsFFECollection],
function(v)
local c;
c := DataObj(v);
return NumberFFVector(v,c![CVEC_IDX_fieldinfo]![CVEC_IDX_q]);
end);
InstallMethod( CVecNumber, "for four integers",
[IsInt,IsPosInt,IsPosInt,IsPosInt],
function(nr,p,d,l)
local c;
c := CVEC_NewCVecClass(p,d,l);
return CVecNumber(nr,c);
end );
InstallMethod( CVecNumber, "for an integer, and a cvecclass",
[IsInt, IsCVecClass],
function(nr,c)
local bas,f,halfword,i,l,q,qq,v,wordlen;
v := CVEC_NEW(c,c![CVEC_IDX_type]);
wordlen := c![CVEC_IDX_wordlen];
f := c![CVEC_IDX_fieldinfo];
bas := f![CVEC_IDX_p] ^ f![CVEC_IDX_elsperword];
if IsSmallIntRep(bas) then
l := 0*[1..wordlen];
for i in [wordlen,wordlen-1..1] do
q := QuotientRemainder(nr,bas);
l[i] := q[2];
nr := q[1];
od;
CVEC_NUMBERFFLIST_TO_CVEC(l,v,false);
else
l := 0*[1..2*wordlen];
halfword := 2^(CVEC_BYTESPERWORD*4);
for i in [2*wordlen-1,2*wordlen-3..1] do
q := QuotientRemainder(nr,bas);
qq := QuotientRemainder(q[2],halfword);
l[i] := qq[2];
l[i+1] := qq[1];
nr := q[1];
od;
CVEC_NUMBERFFLIST_TO_CVEC(l,v,true);
fi;
return v;
end );
#############################################################################
# Access to the base field:
#############################################################################
InstallOtherMethod( Characteristic, "for cvecs", [IsCVecRep],
function(v)
local c;
c := DataObj(v);
return c![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
end);
InstallOtherMethod( DegreeFFE, "for cvecs", [IsCVecRep],
function(v)
local c;
c := DataObj(v);
return c![CVEC_IDX_fieldinfo]![CVEC_IDX_d];
end);
InstallMethod( BaseDomain, "for cvecs", [IsCVecRep],
function(v)
local c;
c := DataObj(v);
return c![CVEC_IDX_GF];
end);
# compatibility with GAP <= 4.11
if IsBound(BaseField) and not IsIdenticalObj(BaseDomain, BaseField) then
InstallMethod( BaseField, "for cvecs", [IsCVecRep],
function(v)
local c;
c := DataObj(v);
return c![CVEC_IDX_GF];
end);
fi;
#############################################################################
# Slicing:
#############################################################################
InstallGlobalFunction( CVEC_Slice, function(src,dst,srcpos,len,dstpos)
local cdst,csrc;
csrc := DataObj(src);
cdst := DataObj(dst);
if not(IsIdenticalObj(csrc![CVEC_IDX_fieldinfo],
cdst![CVEC_IDX_fieldinfo])) then
ErrorNoReturn("CVEC_Slice: vectors not over common field");
fi;
if srcpos < 1 or srcpos+len-1 > csrc![CVEC_IDX_len] or len <= 0 then
ErrorNoReturn("CVEC_Slice: source area not valid");
fi;
if dstpos < 1 or dstpos+len-1 > cdst![CVEC_IDX_len] then
ErrorNoReturn("CVEC_Slice: destination area not valid");
fi;
if not(IsMutable(dst)) then
ErrorNoReturn("CVEC_Slice: destination vector immutable");
fi;
CVEC_SLICE(src,dst,srcpos,len,dstpos);
end );
InstallGlobalFunction( CVEC_SliceList, function(src,dst,srcposs,dstposs)
if not(IsMutable(dst)) then
ErrorNoReturn("CVEC_SliceList: destination vector immutable");
fi;
CVEC_SLICE_LIST(src,dst,srcposs,dstposs);
end );
InstallOtherMethod( \{\}, "for a cvec and a range",
[IsCVecRep, IsList and IsRangeRep],
function(v,r)
# note that ranges in IsRangeRep always have length at least 2!
local c,cl,w;
cl := DataObj(v);
c := CVEC_NewCVecClassSameField(cl,Length(r));
w := CVEC_NEW(c,c![CVEC_IDX_type]);
CVEC_SLICE_LIST(v,w,r,[1..Length(r)]);
if not(IsMutable(v)) then
MakeImmutable(w);
fi;
return w;
end);
InstallOtherMethod( \{\}, "for a cvec and a list",
[IsCVecRep, IsList],
function(v,l)
local c,cl,w;
cl := DataObj(v);
c := CVEC_NewCVecClassSameField(cl,Length(l));
w := CVEC_NEW(c,c![CVEC_IDX_type]);
CVEC_SLICE_LIST(v,w,l,[1..Length(l)]);
if not(IsMutable(v)) then
MakeImmutable(w);
fi;
return w;
end);
InstallOtherMethod( CopySubVector, "for two cvecs and stuff",
[IsCVecRep, IsCVecRep and IsMutable, IsList, IsList],
function(src,dst,scols,dcols)
if Length(scols) = 0 then return; fi; # a little speedup
CVEC_SLICE_LIST(src,dst,scols,dcols);
end );
# Note that slicing assignment is intentionally not supported, because
# this will usually be used only by inefficient code. Use CVEC_Slice
# or even CVEC_SLICE instead.
# Concatenation of vectors:
InstallGlobalFunction( CVEC_Concatenation, function(arg)
local c,cc,i,len,pos,v;
if Length(arg) = 0 or not(IsCVecRep(arg[1])) then
ErrorNoReturn("CVEC_Concatenation: Need at least one cvec");
fi;
c := DataObj(arg[1]);
len := Length(arg[1]);
for i in [2..Length(arg)] do
if not(IsCVecRep(arg[i])) or
not(IsIdenticalObj(c,DataObj(arg[i]))) then
ErrorNoReturn("CVEC_Concatenation: Arguments must all be cvecs ",
"over the same field ");
fi;
len := len + Length(arg[i]);
od;
cc := CVEC_NewCVecClassSameField(c,len);
v := CVEC_New(cc);
pos := 1;
for i in [1..Length(arg)] do
CVEC_SLICE(arg[i],v,1,Length(arg[i]),pos);
pos := pos + Length(arg[i]);
od;
return v;
end );
############################################################################
# For polynomial arithmetic:
############################################################################
InstallOtherMethod( ProductCoeffs, "for cvecs",
[IsCVecRep, IsCVecRep],
function(v,w)
local cl,u,vcl,wcl;
vcl := DataObj(v);
if vcl![CVEC_IDX_fieldinfo]![CVEC_IDX_d] > 1 then
ErrorNoReturn("Non-prime fields not yet implemented (doable)!");
fi;
wcl := DataObj(w);
if not(IsIdenticalObj(vcl![CVEC_IDX_fieldinfo],wcl![CVEC_IDX_fieldinfo])) then
ErrorNoReturn("ProductCoeffs: Not over same field!");
fi;
cl := CVEC_NewCVecClassSameField(vcl,vcl![CVEC_IDX_len]+wcl![CVEC_IDX_len]-1);
u := CVEC_NEW(cl,cl![CVEC_IDX_type]);
CVEC_PROD_COEFFS_CVEC_PRIMEFIELD(u,v,w);
return u;
end);
############################################################################
# For (pseudo) random vectors:
############################################################################
InstallMethod( Randomize, "for cvecs", [IsCVecRep and IsMutable],
v -> Randomize(GlobalMersenneTwister, v) );
InstallOtherMethod( Randomize, "for a random source and a cvec",
[IsRandomSource, IsCVecRep and IsMutable],
function( rs, v )
local cl,d,j,len,li,p,q,size;
cl := DataObj(v);
len := Length(v);
size := cl![CVEC_IDX_fieldinfo]![CVEC_IDX_size];
d := cl![CVEC_IDX_fieldinfo]![CVEC_IDX_d];
if size <= 1 then
q := cl![CVEC_IDX_fieldinfo]![CVEC_IDX_q];
li := 0*[1..len];
for j in [1..len] do
li[j] := Random(rs,0,q-1);
od;
CVEC_INTREP_TO_CVEC(li,v);
else # big scalars!
li := 0*[1..len*d];
p := cl![CVEC_IDX_fieldinfo]![CVEC_IDX_p];
for j in [1..len*d] do
li[j] := Random(rs,0,p-1);
od;
CVEC_INTREP_TO_CVEC(li,v);
fi;
return v;
end );
# for compatibility with GAP < 4.11
InstallOtherMethod( Randomize, "for a cvec and a random source",
[IsCVecRep and IsMutable, IsRandomSource],
{ v, rs } -> Randomize( rs, v ) );
#############################################################################
# The making of good hash functions:
#############################################################################
InstallGlobalFunction( CVEC_HashFunctionForCVecs, function(v,data)
return HashKeyBag(v,257,CVEC_BYTESPERWORD,data[2]) mod data[1] + 1;
end );
InstallMethod( ChooseHashFunction, "for cvecs",
[IsCVecRep,IsInt],
function(p,hashlen)
local bytelen,c;
c := CVecClass(p);
bytelen := c![CVEC_IDX_wordlen] * CVEC_BYTESPERWORD;
return rec( func := CVEC_HashFunctionForCVecs,
data := [hashlen,bytelen] );
end );
#############################################################################
# (Un-)Pickling:
#############################################################################
CVEC_CMatMaker := fail; # this is to get rid of a warning in this method
InstallMethod( IO_Pickle, "for cvecs",
[IsFile, IsCVecRep and IsList],
function( f, v )
local m,tag;
if IsMutable(v) then tag := "MCVC";
else tag := "ICVC"; fi;
if IO_Write(f,tag) = fail then return IO_Error; fi;
m := CVEC_CMatMaker( [0,v], DataObj(v) );
if CVEC_WriteMat( f, m ) = fail then return IO_Error; fi;
return IO_OK;
end );
Unbind(CVEC_CMatMaker);
IO_Unpicklers.MCVC :=
function( f )
local m;
m := CVEC_ReadMat( f ); if m = fail then return IO_Error; fi;
return m[1];
end;
IO_Unpicklers.ICVC :=
function( f )
local m;
m := CVEC_ReadMat( f ); if m = fail then return IO_Error; fi;
MakeImmutable(m);
return m[1];
end;
#############################################################################
# Memory usage information:
#############################################################################
InstallOtherMethod( Memory, "for a cvec", [ IsCVecRep ],
function( v )
# the header is 2 words on 64bit and 3 words on 32bit machines:
# we count the master pointer!
return SIZE_OBJ(v) + 8 + 2 * GAPInfo.BytesPerVariable;
end );
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License,
## or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
[ Verzeichnis aufwärts0.56unsichere Verbindung
Übersetzung europäischer Sprachen durch Browser
]
|