/**************************************************************************** ** *FPrintCyc(<cyc>)..................printacyclotomic ** **'PrintCyc'printsthecyclotomic<cyc>inthestandardform. ** **Inprinciplethisisveryeasy,butitiscomplicatedbecausewedonot **wanttoprintstufflike'+1*','-1*','E(<n>)^0','E(<n>)^1,etc.
*/ staticvoid PrintCyc(Obj cyc)
{
UInt n; // order of the field
UInt len; // number of terms
UInt i; // loop variable
n = INT_INTOBJ( NOF_CYC(cyc) );
len = SIZE_CYC(cyc);
Pr("%>", 0, 0); for ( i = 1; i < len; i++ ) { // Store value in local variable, as they can change during Pr const Obj * cfs = CONST_COEFS_CYC(cyc); const UInt4 * exs = CONST_EXPOS_CYC(cyc, len);
Obj cfsi = cfs[i];
UInt4 exsi = exs[i];
/**************************************************************************** ** *FEqCyc(<opL>,<opR>).........testiftwocyclotomicsareequal ** **'EqCyc'returns'true'ifthetwocyclotomics<opL>and<opR>areequal **and'false'otherwise. ** **'EqCyc'isprettysimplebecauseeverycyclotomichasaunique **representation,sowejusthavetocomparetheterms.
*/ staticInt EqCyc(Obj opL, Obj opR)
{
UInt len; // number of terms const Obj * cfl; // ptr to coeffs of left operand const UInt4 * exl; // ptr to expnts of left operand const Obj * cfr; // ptr to coeffs of right operand const UInt4 * exr; // ptr to expnts of right operand
UInt i; // loop variable
// compare the order of both fields if ( NOF_CYC(opL) != NOF_CYC(opR) ) return0;
// compare the number of terms if ( SIZE_CYC(opL) != SIZE_CYC(opR) ) return0;
// compare the cyclotomics termwise
len = SIZE_CYC(opL);
cfl = CONST_COEFS_CYC(opL);
cfr = CONST_COEFS_CYC(opR);
exl = CONST_EXPOS_CYC(opL,len);
exr = CONST_EXPOS_CYC(opR,len); for ( i = 1; i < len; i++ ) { if ( exl[i] != exr[i] ) return0; elseif ( ! EQ(cfl[i],cfr[i]) ) return0;
}
// all terms are equal return1;
}
/**************************************************************************** ** *FLtCyc(<opL>,<opR>)....testifonecyclotomicislessthananother ** **'LtCyc'returns'true'ifthecyclotomic<opL>islessthanthe **cyclotomic<opR>and'false'otherwise. ** **Cyclotomicsarefirstsortedaccordingtotheorderoftheprimitiveroot **theyarewrittenin.Thatmeansthattherationalsaresmallest,then **comecyclotomicsfrom$Q(e_3)$followedbycyclotomicsfrom$Q(e_4)$etc. **Cyclotomicsfromthesamefieldaresortedlexicographicalywithrespect **totheirrepresentationinthebaseofthisfield.Thatmeansthatthe **cyclotomicwithsmallercoefficientforthefirstbaserootissmaller, **forcyclotomicswiththesamefirstcoefficienttheseconddecideswhich **issmaller,etc. ** **'LtCyc'isprettysimplebecauseeverycyclotomichasaunique **representation,sowejusthavetocomparetheterms.
*/ staticInt LtCyc(Obj opL, Obj opR)
{
UInt lel; // nr of terms of left operand const Obj * cfl; // ptr to coeffs of left operand const UInt4 * exl; // ptr to expnts of left operand
UInt ler; // nr of terms of right operand const Obj * cfr; // ptr to coeffs of right operand const UInt4 * exr; // ptr to expnts of right operand
UInt i; // loop variable
// compare the order of both fields if ( NOF_CYC(opL) != NOF_CYC(opR) ) { if ( INT_INTOBJ( NOF_CYC(opL) ) < INT_INTOBJ( NOF_CYC(opR) ) ) return1; else return0;
}
// if one cyclotomic has more terms than the other compare it against 0 if ( lel < ler ) return LT( INTOBJ_INT(0), cfr[i] ); elseif ( ler < lel ) return LT( cfl[i], INTOBJ_INT(0) ); else return0;
}
staticInt LtCycYes(Obj opL, Obj opR)
{ return1;
}
staticInt LtCycNot(Obj opL, Obj opR)
{ return0;
}
/**************************************************************************** ** *FConvertToBase(<n>)......convertacyclotomicintothebase,local ** **'ConvertToBase'convertsthecyclotomic'ResultCyc'fromthecyclotomic **fieldof<n>throotsofunity,intothebaseform.Thismeansthatit **replaceseveryroot$e_n^i$thatdoesnotbelongtothebasebyasumof **otherrootsthatdo. ** **Supposethat$c*e_n^i$appearsin'ResultCyc'but$e_n^i$doesnotliein **thebase.Thishappensbecause,forsomeprime$p$dividing$n$,with **maximalpower$q$,$i\in(n/q)*[-(q/p-1)/2..(q/p-1)/2]$mod$q$. ** **Wetaketheidentity$1+e_p+e_p^2+..+e_p^{p-1}=0$,writeitusing$n$th **rootsofunity,$0=1+e_n^{n/p}+e_n^{2n/p}+..+e_n^{(p-1)n/p}$andmultiply **itby$e_n^i$,$0=e_n^i+e_n^{n/p+i}+e_n^{2n/p+i}+..+e_n^{(p-1)n/p+i}$. **Nowwesubtract$c$timesthelefthandsidefrom'ResultCyc'. ** **If$p^2$doesnotdivide$n$thentherootsthatarenotinthebase **becauseof$p$arethosewhoseexponentisdivisibleby$p$.But$n/p$ **isnotdivisibleby$p$,soneitheroftheexponent$k*n/p+i,k=1..p-1$ **isdivisibleby$p$,sothosenewrootsareacceptablew.r.t.$p$. ** **Asimilarargumentshowsthatthenewrootsarealsoacceptablew.r.t. **$p$evenif$p^2$divides$n$... ** **Notethatthenewrootsmightstillnotlieinthecasebecauseofsome **otherprime$p2$.However,because$i=k*n/p+i$mod$p2$,thiscanonly **happenif$e_n^i$didalsonotlieinthebasebecauseof$p2$.Soifwe **removeallrootsthatlieinthebasebecauseof$p$,thelatersteps, **whichremovetherootsthatarenotinthebasebecauseoflargerprimes, **willnotaddnewrootsthatdonotlieinthebasebecauseof$p$again. ** **Foranexample,suppose'ResultCyc'is$e_{45}+e_{45}^5=:e+e^5$.$e^5$ **doesnotlieinthebasebecause$5\in5*[-1,0,1]$mod$9$andalso **becauseitisdivisibleby5.Aftersubtracting$e^5*(1+e_3+e_3^2)= **e^5+e^{20}+e^{35}$from'ResultCyc'weget$e-e^{20}-e^{35}$.Thosetwo **rootsarestillnotinthebasebecauseof5.Butaftersubtracting **$-e^{20}*(1+e_5+e_5^2+e_5^3+e_5^4)=-e^{20}-e^{29}-e^{38}-e^2-e^{11}$and **$-e^{35}*(1+e_5+e_5^2+e_5^3+e_5^4)=-e^{35}-e^{44}-e^8-e^{17}-e^{26}$we **get$e+e^{20}+e^{29}+e^{38}+e^2+e^{11}+e^{35}+e^{44}+e^8+e^{17}+e^{26}$, **whichcontainsonlyrootsthatlieinthebase. ** **'ConvertToBase'and'Cyclotomic'arethefunctionsthatknowaboutthe **structureofthebase.'EqCyc'and'LtCyc'onlyneedthepropertythat **therepresentationofallcyclotomicintegersisunique.Allother **functionsdontevenrequirethatcyclotomicsarewrittenasalinear **combinationoflinearindependentroots,theywouldworkalsoif **cyclotomicintegerswerewrittenaspolynomialsin$e_n$. ** **Theinnerloopsinthisfunctionhavebeenduplicatedtoavoidusingthe **modulo('%')operatortoreducetheexponentsintotherange$0..n-1$. **Thosedivisionsarequiteexpensiveonsomeprocessors,e.g.,MIPSand **SPARC,andtheymaysinglehandedaccountfor20percentoftheruntime.
*/ staticvoid ConvertToBase(UInt n)
{
Obj * res; // pointer to the result
UInt nn; // copy of n to factorize
UInt p, q; // prime and prime power
UInt i, k, l; // loop variables
UInt t; // temporary holds n+i+(n/p-n/q)/2
Obj sum; // sum of two coefficients
// get a pointer to the cyclotomic and a copy of n to factor
res = BASE_PTR_PLIST(ResultCyc);
nn = n;
// first handle 2 if ( nn % 2 == 0 ) {
q = 2; while ( nn % (2*q) == 0 ) q = 2*q;
nn = nn / q;
// get rid of all terms e^{a*q+b*(n/q)} a=0..(n/q)-1 b=q/2..q-1 for ( i = 0; i < n; i += q ) {
t = i + (n/q)*(q-1) + n/q; // end (n <= t < 2n)
k = i + (n/q)*(q/2); // start (0 <= k <= t) for ( ; k < n; k += n/q ) { if ( res[k] != INTOBJ_INT(0) ) {
l = (k + n/2) % n; if ( ! ARE_INTOBJS( res[l], res[k] )
|| ! DIFF_INTOBJS( sum, res[l], res[k] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[l], res[k] );
res = BASE_PTR_PLIST(ResultCyc);
}
res[l] = sum;
res[k] = INTOBJ_INT(0);
}
}
t = t - n; // end (0 <= t < n)
k = k - n; // cont. (0 <= k ) for ( ; k < t; k += n/q ) { if ( res[k] != INTOBJ_INT(0) ) {
l = (k + n/2) % n; if ( ! ARE_INTOBJS( res[l], res[k] )
|| ! DIFF_INTOBJS( sum, res[l], res[k] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[l], res[k] );
res = BASE_PTR_PLIST(ResultCyc);
}
res[l] = sum;
res[k] = INTOBJ_INT(0);
}
}
}
}
// now handle the odd primes for ( p = 3; p <= nn; p += 2 ) { if ( nn % p != 0 ) continue;
q = p; while ( nn % (p*q) == 0 ) q = p*q;
nn = nn / q;
// get rid of e^{a*q+b*(n/q)} a=0..(n/q)-1 b=-(q/p-1)/2..(q/p-1)/2 for ( i = 0; i < n; i += q ) { if ( n <= i+(n/p-n/q)/2 ) {
t = i + (n/p-n/q)/2; // end (n <= t < 2n)
k = i - (n/p-n/q)/2; // start (t-n <= k <= t)
} else {
t = i + (n/p-n/q)/2+n; // end (n <= t < 2n)
k = i - (n/p-n/q)/2+n; // start (t-n <= k <= t)
} for ( ; k < n; k += n/q ) { if ( res[k] != INTOBJ_INT(0) ) { for ( l = k+n/p; l < k+n; l += n/p ) { if ( ! ARE_INTOBJS( res[l%n], res[k] )
|| ! DIFF_INTOBJS( sum, res[l%n], res[k] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[l%n], res[k] );
res = BASE_PTR_PLIST(ResultCyc);
}
res[l%n] = sum;
}
res[k] = INTOBJ_INT(0);
}
}
t = t - n; // end (0 <= t < n)
k = k - n; // start (0 <= k ) for ( ; k <= t; k += n/q ) { if ( res[k] != INTOBJ_INT(0) ) { for ( l = k+n/p; l < k+n; l += n/p ) { if ( ! ARE_INTOBJS( res[l%n], res[k] )
|| ! DIFF_INTOBJS( sum, res[l%n], res[k] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[l%n], res[k] );
res = BASE_PTR_PLIST(ResultCyc);
}
res[l%n] = sum;
}
res[k] = INTOBJ_INT(0);
}
}
}
}
// notify Gasman
CHANGED_BAG( ResultCyc );
}
/**************************************************************************** ** *FCyclotomic(<n>,<m>)..........createapackedcyclotomic,local ** **'Cyclotomic'reducesthecyclotomic'ResultCyc'intothesmallest **possiblecyclotomicsubfieldandreturnsitinpackedform. ** **'ResultCyc'mustalsobealreadyconvertedintothebaseby **'ConvertToBase'.<n>mustbetheorderoftheprimitiverootinwhich **written. ** **<m>mustbeadivisorof$n$andgivesahintaboutpossiblesubfields. **Ifaprime$p$divides<m>thennoreductionintoasubfieldwhoseorder **is$n/p$ispossible.Inthearithmeticfunctionsyoucantake **$lcm(n_l,n_r)/gcd(n_l,n_r)=n/gcd(n_l,n_r)$.Ifyoucannotprovide **suchahintjustpass1. ** **Aspecialcaseofthereductionisthecasethatthecyclotomicisa **rational.Ifthisisthecase'Cyclotomic'reducesitintotherationals **andreturnsitasarational. ** **After'Cyclotomic'hasdoneitsworkitclearsthe'ResultCyc'bag,so **thatitonlycontains'INTOBJ_INT(0)'.Thusthearithmeticfunctionscan **usethisbufferwithoutclearingitfirst. ** **'ConvertToBase'and'Cyclotomic'arethefunctionsthatknowaboutthe **structureofthebase.'EqCyc'and'LtCyc'onlyneedthepropertythat **therepresentationofallcyclotomicintegersisunique.Allother **functionsdontevenrequirethatcyclotomicsarewrittenasalinear **combinationoflinearindependentroots,theywouldworkalsoif **cyclotomicintegerswerewrittenaspolynomialsin$e_n$.
*/ static Obj Cyclotomic(UInt n, UInt m)
{
Obj cyc; // cyclotomic, result
UInt len; // number of terms
Obj * cfs; // pointer to the coefficients
UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
UInt gcd, s, t; // gcd of the exponents, temporary
UInt eql; // are all coefficients equal?
Obj cof; // if so this is the coefficient
UInt i, k; // loop variables
UInt nn; // copy of n to factorize
UInt p; // prime factor static UInt lastN; // remember last n, dont recompute static UInt phi; // Euler phi(n) staticBOOL isSqfree; // is n squarefree? static UInt nrp; // number of its prime factors
// get a pointer to the cyclotomic and a copy of n to factor
res = BASE_PTR_PLIST(ResultCyc);
// count the terms and compute the gcd of the exponents with n
len = 0;
gcd = n;
eql = 1;
cof = 0; for ( i = 0; i < n; i++ ) { if ( res[i] != INTOBJ_INT(0) ) {
len++; if ( gcd != 1 ) {
s = i; while ( s != 0 ) { t = s; s = gcd % s; gcd = t; }
} if ( eql && cof == 0 )
cof = res[i]; elseif ( eql && ! EQ(cof,res[i]) )
eql = 0;
}
}
// if all exps are divisible 1 < k replace $e_n^i$ by $e_{n/k}^{i/k}$ // this is the only way a prime whose square divides $n$ could reduce if ( 1 < gcd ) { for ( i = 1; i < n/gcd; i++ ) {
res[i] = res[i*gcd];
res[i*gcd] = INTOBJ_INT(0);
}
n = n / gcd;
}
// compute $phi(n)$, test if n is squarefree, compute number of primes if ( n != lastN ) {
lastN = n;
phi = n; k = n;
isSqfree = TRUE;
nrp = 0; for ( p = 2; p <= k; p++ ) { if ( k % p == 0 ) {
phi = phi * (p-1) / p; if (k % (p * p) == 0)
isSqfree = FALSE;
nrp++; while ( k % p == 0 ) k = k / p;
}
}
}
// if possible reduce into the rationals, clear buffer bag if ( len == phi && eql && isSqfree ) { for ( i = 0; i < n; i++ )
res[i] = INTOBJ_INT(0); // return as rational $(-1)^{number primes}*{common coefficient}$ if ( nrp % 2 == 0 )
res[0] = cof; else {
CHANGED_BAG( ResultCyc );
Obj negcof = DIFF( INTOBJ_INT(0), cof );
res = BASE_PTR_PLIST(ResultCyc);
res[0] = negcof;
}
n = 1;
}
CHANGED_BAG( ResultCyc );
// for all primes $p$ try to reduce from $Q(e_n)$ into $Q(e_{n/p})$
gcd = phi; s = len; while ( s != 0 ) { t = s; s = gcd % s; gcd = t; }
nn = n; for ( p = 3; p <= nn && p-1 <= gcd; p += 2 ) { if ( nn % p != 0 ) continue;
nn = nn / p; while ( nn % p == 0 ) nn = nn / p;
// if $p$ is not quadratic and the number of terms is divisible // $p-1$ and $p$ divides $m$ not then a reduction is possible if ( n % (p*p) != 0 && len % (p-1) == 0 && m % p != 0 ) {
// test that coeffs for expnts congruent mod $n/p$ are equal
eql = 1; for ( i = 0; i < n && eql; i += p ) {
cof = res[(i+n/p)%n]; for ( k = i+2*n/p; k < i+n && eql; k += n/p ) if ( ! EQ(res[k%n],cof) )
eql = 0;
}
// if all coeffs for expnts in all classes are equal reduce if ( eql ) {
// replace every sum of $p-1$ terms with expnts congruent // to $i*p$ mod $n/p$ by the term with exponent $i*p$ // is just the inverse transformation of 'ConvertToBase' for ( i = 0; i < n; i += p ) {
cof = res[(i+n/p)%n]; if ( ! IS_INTOBJ(cof)
|| (cof == INTOBJ_MIN) ) {
CHANGED_BAG( ResultCyc );
cof = DIFF( INTOBJ_INT(0), cof );
res = BASE_PTR_PLIST(ResultCyc);
res[i] = cof;
} else {
res[i] = INTOBJ_INT( - INT_INTOBJ(cof) );
} for ( k = i+n/p; k < i+n && eql; k += n/p )
res[k%n] = INTOBJ_INT(0);
}
len = len / (p-1);
CHANGED_BAG( ResultCyc );
// now replace $e_n^{i*p}$ by $e_{n/p}^{i}$ for ( i = 1; i < n/p; i++ ) {
res[i] = res[i*p];
res[i*p] = INTOBJ_INT(0);
}
n = n / p;
}
}
}
// if the cyclotomic is a rational return it as a rational if ( n == 1 ) {
cyc = res[0];
res[0] = INTOBJ_INT(0);
}
// otherwise copy terms into a new 'T_CYC' bag and clear 'ResultCyc' else {
cyc = NewBag( T_CYC, (len+1)*(sizeof(Obj)+sizeof(UInt4)) );
cfs = COEFS_CYC(cyc);
exs = EXPOS_CYC(cyc,len+1);
cfs[0] = INTOBJ_INT(n);
exs[0] = 0;
k = 1;
res = BASE_PTR_PLIST(ResultCyc); for ( i = 0; i < n; i++ ) { if ( res[i] != INTOBJ_INT(0) ) {
cfs[k] = res[i];
exs[k] = i;
k++;
res[i] = INTOBJ_INT(0);
}
} // 'CHANGED_BAG' not needed for last bag
}
// get the smallest field that contains both cyclotomics // First Euclid's Algorithm for gcd if (nl > nr) {
a = nl;
b = nr;
} else {
a = nr;
b = nl;
} while (b > 0) {
c = a % b;
a = b;
b = c;
}
*ml = nr/a; // Compute the result (lcm) in 64 bit
n8 = (UInt8)nl * ((UInt8)*ml); // Check if it is too large for a small int if (n8 > INT_INTOBJ_MAX)
ErrorMayQuit("This computation would require a cyclotomic field too large to be handled", 0, 0);
// Switch to UInt now we know we can
n = (UInt)n8;
// Handle the soft limit while (n > CyclotomicsLimit) {
ErrorReturnVoid( "This computation requires a cyclotomic field of degree %d, larger " "than the current limit of %d",
n, (Int)CyclotomicsLimit, "You may return after raising the limit with SetCyclotomicsLimit");
}
// Finish up
*mr = n/nr;
// make sure that the result bag is large enough
GrowResultCyc(n); return n;
}
if (ulimit < CyclotomicsLimit) {
ErrorMayQuit("SetCyclotomicsLimit: <newlimit> must not be less than " "old limit of %d",
CyclotomicsLimit, 0);
} #ifdef SYS_IS_64_BIT if (ulimit >= ((UInt)1 << 32)) {
ErrorMayQuit("Cyclotomic field size limit must be less than 2^32", 0, 0);
} #endif
/**************************************************************************** ** *FSumCyc(<opL>,<opR>).............sumoftwocyclotomics ** **'SumCyc'returnsthesumofthetwocyclotomics<opL>and<opR>. **Eitheroperandmayalsobeanintegerorarational. ** **Thisfunctionislengthybecausewetrytouseimmediateinteger **arithmeticifpossibletoavoidthefunctioncalloverhead.
*/ static Obj SumCyc(Obj opL, Obj opR)
{
UInt nl, nr; // order of left and right field
UInt n; // order of smallest superfield
UInt ml, mr; // cofactors into the superfield
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
Obj sum; // sum of two coefficients
UInt i; // loop variable
// take the cyclotomic with less terms as the right operand if ( TNUM_OBJ(opL) != T_CYC
|| (TNUM_OBJ(opR) == T_CYC && SIZE_CYC(opL) < SIZE_CYC(opR)) ) {
sum = opL; opL = opR; opR = sum;
}
// Copy the left operand into the result if ( TNUM_OBJ(opL) != T_CYC ) {
res = BASE_PTR_PLIST(ResultCyc);
res[0] = opL;
CHANGED_BAG( ResultCyc );
} else {
len = SIZE_CYC(opL);
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc); if ( ml == 1 ) { for ( i = 1; i < len; i++ )
res[exs[i]] = cfs[i];
} else { for ( i = 1; i < len; i++ )
res[exs[i]*ml] = cfs[i];
}
CHANGED_BAG( ResultCyc );
}
// add the right operand to the result if ( TNUM_OBJ(opR) != T_CYC ) {
res = BASE_PTR_PLIST(ResultCyc);
sum = SUM( res[0], opR );
res = BASE_PTR_PLIST(ResultCyc);
res[0] = sum;
CHANGED_BAG( ResultCyc );
} else {
len = SIZE_CYC(opR);
cfs = CONST_COEFS_CYC(opR);
exs = CONST_EXPOS_CYC(opR,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( res[exs[i]*mr], cfs[i] )
|| ! SUM_INTOBJS( sum, res[exs[i]*mr], cfs[i] ) ) {
CHANGED_BAG( ResultCyc );
sum = SUM( res[exs[i]*mr], cfs[i] );
cfs = CONST_COEFS_CYC(opR);
exs = CONST_EXPOS_CYC(opR,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[exs[i]*mr] = sum;
}
CHANGED_BAG( ResultCyc );
}
// return the base reduced packed cyclotomic if ( nl % ml != 0 || nr % mr != 0 ) ConvertToBase( n ); return Cyclotomic( n, ml * mr );
}
/**************************************************************************** ** *FAInvCyc(<op>)............additiveinverseofacyclotomic ** **'AInvCyc'returnstheadditiveinverseelementofthecyclotomic<op>.
*/ static Obj AInvCyc(Obj op)
{
Obj res; // inverse, result
UInt len; // number of terms const Obj * cfs; // ptr to coeffs of left operand const UInt4 * exs; // ptr to expnts of left operand
Obj * cfp; // ptr to coeffs of product
UInt4 * exp; // ptr to expnts of product
UInt i; // loop variable
Obj prd; // product of two coefficients
/**************************************************************************** ** *FDiffCyc(<opL>,<opR>)..........differenceoftwocyclotomics ** **'DiffCyc'returnsthedifferenceofthetwocyclotomic<opL>and<opR>. **Eitheroperandmayalsobeanintegerorarational. ** **Thisfunctionislengthybecausewetrytouseimmediateinteger **arithmeticifpossibletoavoidthefunctioncalloverhead.
*/ static Obj DiffCyc(Obj opL, Obj opR)
{
UInt nl, nr; // order of left and right field
UInt n; // order of smallest superfield
UInt ml, mr; // cofactors into the superfield
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
Obj sum; // difference of two coefficients
UInt i; // loop variable
// get the smallest field that contains both cyclotomics
nl = (TNUM_OBJ(opL) != T_CYC ? 1 : INT_INTOBJ( NOF_CYC(opL) ));
nr = (TNUM_OBJ(opR) != T_CYC ? 1 : INT_INTOBJ( NOF_CYC(opR) ));
n = FindCommonField(nl, nr, &ml, &mr);
// copy the left operand into the result if ( TNUM_OBJ(opL) != T_CYC ) {
res = BASE_PTR_PLIST(ResultCyc);
res[0] = opL;
CHANGED_BAG( ResultCyc );
} else {
len = SIZE_CYC(opL);
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc); if ( ml == 1 ) { for ( i = 1; i < len; i++ )
res[exs[i]] = cfs[i];
} else { for ( i = 1; i < len; i++ )
res[exs[i]*ml] = cfs[i];
}
CHANGED_BAG( ResultCyc );
}
// subtract the right operand from the result if ( TNUM_OBJ(opR) != T_CYC ) {
res = BASE_PTR_PLIST(ResultCyc);
sum = DIFF( res[0], opR );
res = BASE_PTR_PLIST(ResultCyc);
res[0] = sum;
CHANGED_BAG( ResultCyc );
} else {
len = SIZE_CYC(opR);
cfs = CONST_COEFS_CYC(opR);
exs = CONST_EXPOS_CYC(opR,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( res[exs[i]*mr], cfs[i] )
|| ! DIFF_INTOBJS( sum, res[exs[i]*mr], cfs[i] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[exs[i]*mr], cfs[i] );
cfs = CONST_COEFS_CYC(opR);
exs = CONST_EXPOS_CYC(opR,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[exs[i]*mr] = sum;
}
CHANGED_BAG( ResultCyc );
}
// return the base reduced packed cyclotomic if ( nl % ml != 0 || nr % mr != 0 ) ConvertToBase( n ); return Cyclotomic( n, ml * mr );
}
/**************************************************************************** ** *FProdCycInt(<opL>,<opR>)...productofacyclotomicandaninteger ** **'ProdCycInt'returnstheproductofacyclotomicandaintegeror **rational.Whichoperandisthecyclotomicandwhichtheintegerdoesnot **matter. ** **Thisisaspecialcase,becauseiftheintegerisnot0,theproductwill **automaticallybebasereduced.Sowedontneedtocall'ConvertToBase' **or'Reduce'anddirectlywriteintoaresultbag. ** **Thisfunctionislengthybecausewetrytouseimmediateinteger **arithmeticifpossibletoavoidthefunctioncalloverhead.
*/ static Obj ProdCycInt(Obj opL, Obj opR)
{
Obj hdP; // product, result
UInt len; // number of terms const Obj * cfs; // ptr to coeffs of left operand const UInt4 * exs; // ptr to expnts of left operand
Obj * cfp; // ptr to coeffs of product
UInt4 * exp; // ptr to expnts of product
UInt i; // loop variable
Obj prd; // product of two coefficients
// otherwise multiply every coefficient else {
len = SIZE_OBJ(opL);
hdP = NewBag(T_CYC, len);
memcpy( ADDR_OBJ(hdP), CONST_ADDR_OBJ(opL), len);
len = SIZE_CYC(opL);
cfp = COEFS_CYC(hdP); for ( i = 1; i < len; i++ ) {
prd = PROD( cfp[i], opR );
cfp = COEFS_CYC(hdP);
cfp[i] = prd;
CHANGED_BAG( hdP );
}
}
return hdP;
}
/**************************************************************************** ** *FProdCyc(<opL>,<opR>)...........productoftwocyclotomics ** **'ProdCyc'returnstheproductofthetwocyclotomics<opL>and<opR>. **Eitheroperandmayalsobeanintegerorarational. ** **Thisfunctionislengthybecausewetrytouseimmediateinteger **arithmeticifpossibletoavoidthefunctioncalloverhead.
*/ static Obj ProdCyc(Obj opL, Obj opR)
{
UInt nl, nr; // order of left and right field
UInt n; // order of smallest superfield
UInt ml, mr; // cofactors into the superfield
Obj c; // one coefficient of the left op
UInt e; // one exponent of the left op
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
Obj sum; // sum of two coefficients
Obj prd; // product of two coefficients
UInt i, k; // loop variable
// for $rat * cyc$ and $cyc * rat$ delegate if ( TNUM_OBJ(opL) != T_CYC || TNUM_OBJ(opR) != T_CYC ) { return ProdCycInt( opL, opR );
}
// take the cyclotomic with less terms as the right operand if ( SIZE_CYC(opL) < SIZE_CYC(opR) ) {
prd = opL; opL = opR; opR = prd;
}
// get the smallest field that contains both cyclotomics
nl = (TNUM_OBJ(opL) != T_CYC ? 1 : INT_INTOBJ( NOF_CYC(opL) ));
nr = (TNUM_OBJ(opR) != T_CYC ? 1 : INT_INTOBJ( NOF_CYC(opR) ));
n = FindCommonField(nl, nr, &ml, &mr);
// loop over the terms of the right operand for ( k = 1; k < SIZE_CYC(opR); k++ ) {
c = COEFS_CYC(opR)[k];
e = (mr * CONST_EXPOS_CYC( opR, SIZE_CYC(opR) )[k]) % n;
// if the coefficient is 1 just add if ( c == INTOBJ_INT(1) ) {
len = SIZE_CYC(opL);
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( res[(e+exs[i]*ml)%n], cfs[i] )
|| ! SUM_INTOBJS( sum, res[(e+exs[i]*ml)%n], cfs[i] ) ) {
CHANGED_BAG( ResultCyc );
sum = SUM( res[(e+exs[i]*ml)%n], cfs[i] );
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[(e+exs[i]*ml)%n] = sum;
}
CHANGED_BAG( ResultCyc );
}
// if the coefficient is -1 just subtract elseif ( c == INTOBJ_INT(-1) ) {
len = SIZE_CYC(opL);
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( res[(e+exs[i]*ml)%n], cfs[i] )
|| ! DIFF_INTOBJS( sum, res[(e+exs[i]*ml)%n], cfs[i] ) ) {
CHANGED_BAG( ResultCyc );
sum = DIFF( res[(e+exs[i]*ml)%n], cfs[i] );
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[(e+exs[i]*ml)%n] = sum;
}
CHANGED_BAG( ResultCyc );
}
// if the coefficient is a small integer use immediate operations elseif ( IS_INTOBJ(c) ) {
len = SIZE_CYC(opL);
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( cfs[i], res[(e+exs[i]*ml)%n] )
|| ! PROD_INTOBJS( prd, cfs[i], c )
|| ! SUM_INTOBJS( sum, res[(e+exs[i]*ml)%n], prd ) ) {
CHANGED_BAG( ResultCyc );
prd = PROD( cfs[i], c );
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
sum = SUM( res[(e+exs[i]*ml)%n], prd );
cfs = CONST_COEFS_CYC(opL);
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[(e+exs[i]*ml)%n] = sum;
}
CHANGED_BAG( ResultCyc );
}
// otherwise do it the normal way else {
len = SIZE_CYC(opL); for ( i = 1; i < len; i++ ) {
CHANGED_BAG( ResultCyc );
cfs = CONST_COEFS_CYC(opL);
prd = PROD( cfs[i], c );
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
sum = SUM( res[(e+exs[i]*ml)%n], prd );
exs = CONST_EXPOS_CYC(opL,len);
res = BASE_PTR_PLIST(ResultCyc);
res[(e+exs[i]*ml)%n] = sum;
}
CHANGED_BAG( ResultCyc );
}
}
// return the base reduced packed cyclotomic
ConvertToBase( n ); return Cyclotomic( n, ml * mr );
}
/**************************************************************************** ** *FInvCyc(<op>).................inverseofacyclotomic ** **'InvCyc'returnsthemultiplicativeinverseelementofthecyclotomic **<op>. ** **'InvCyc'computestheinverseof<op>bycomputingtheproduct$prd$of **nontrivialGaloisconjugatesof<op>.Then$op*(prd/(op*prd))=1$ **so$prd/(op*prd)$istheinverseof$op$.Becausethedenominator **$op*prd$isthenormof$op$overtherationalsitisrationalsowe **cancomputethequotient$prd/(op*prd)$with'ProdCycInt'. *Tbettermultiplyonlythe*different*conjugates?
*/ static Obj InvCyc(Obj op)
{
Obj prd; // product of conjugates
UInt n; // order of the field
UInt sqr; // if n < sqr*sqr n is squarefree
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
UInt i, k; // loop variable
UInt gcd, s, t; // gcd of i and n, temporaries
// get the order of the field, test if it is squarefree
n = INT_INTOBJ( NOF_CYC(op) ); for ( sqr = 2; sqr*sqr <= n && n % (sqr*sqr) != 0; sqr++ )
;
// compute the product of all nontrivial galois conjugates of <opL>
len = SIZE_CYC(op);
prd = INTOBJ_INT(1); for ( i = 2; i < n; i++ ) {
// if i gives a galois automorphism apply it
gcd = n; s = i; while ( s != 0 ) { t = s; s = gcd % s; gcd = t; } if ( gcd == 1 ) {
// permute the terms
cfs = CONST_COEFS_CYC(op);
exs = CONST_EXPOS_CYC(op,len);
res = BASE_PTR_PLIST(ResultCyc); for ( k = 1; k < len; k++ )
res[(i*exs[k])%n] = cfs[k];
CHANGED_BAG( ResultCyc );
// if n is squarefree conversion and reduction are unnecessary if ( n < sqr*sqr ) {
prd = ProdCyc( prd, Cyclotomic( n, n ) );
} else {
ConvertToBase( n );
prd = ProdCyc( prd, Cyclotomic( n, 1 ) );
}
}
}
// the inverse is the product divided by the norm return ProdCycInt( prd, INV( ProdCyc( op, prd ) ) );
}
/**************************************************************************** ** *FPowCyc(<opL>,<opR>)..............powerofacyclotomic ** **'PowCyc'returnsthe<opR>th,whichmustbeaninteger,powerofthe **cyclotomic<opL>.Theleftoperandmayalsobeanintegerorarational.
*/ static Obj PowCyc(Obj opL, Obj opR)
{
Obj pow; // power (result) Int exp; // exponent (right operand) Int n; // order of the field
UInt i; // exponent of left operand
static Obj FuncE(Obj self, Obj n)
{
Obj * res; // pointer into result bag
// do full operation if ( FIRST_EXTERNAL_TNUM <= TNUM_OBJ(n) ) { return DoOperation1Args( self, n );
}
GetPositiveSmallInt("E", n);
// for $e_1$ return 1 and for $e_2$ return -1 if ( n == INTOBJ_INT(1) ) return INTOBJ_INT(1); elseif ( n == INTOBJ_INT(2) ) return INTOBJ_INT(-1);
// if the root is not known already construct it if ( LastNCyc != INT_INTOBJ(n) ) {
LastNCyc = INT_INTOBJ(n);
GrowResultCyc(LastNCyc);
res = BASE_PTR_PLIST(ResultCyc);
res[1] = INTOBJ_INT(1);
CHANGED_BAG( ResultCyc );
ConvertToBase( LastNCyc );
LastECyc = Cyclotomic( LastNCyc, 1 );
}
static Obj AttrCONDUCTOR(Obj self, Obj cyc)
{
UInt n; // N of the cyclotomic, result
UInt m; // N of element of the list
UInt gcd, s, t; // gcd of n and m, temporaries
Obj list; // list of cyclotomics
UInt i; // loop variable
// do full operation if ( FIRST_EXTERNAL_TNUM <= TNUM_OBJ(cyc) ) { return DoAttribute( ConductorAttr, cyc );
}
if (!IS_CYC(cyc) && !IS_SMALL_LIST(cyc)) {
RequireArgument(SELF_NAME, cyc, "must be a cyclotomic or a small list");
}
// handle cyclotomics if ( IS_INT(cyc) || TNUM_OBJ(cyc) == T_RAT ) {
n = 1;
} elseif ( TNUM_OBJ(cyc) == T_CYC ) {
n = INT_INTOBJ( NOF_CYC(cyc) );
}
// handle a list by computing the lcm of the entries else {
list = cyc;
n = 1; for ( i = 1; i <= LEN_LIST( list ); i++ ) {
cyc = ELMV_LIST( list, i ); if (!IS_INT(cyc) && TNUM_OBJ(cyc) != T_RAT &&
TNUM_OBJ(cyc) != T_CYC) {
ErrorMayQuit( "Conductor: <list>[%d] must be a cyclotomic (not a %s)",
(Int)i, (Int)TNAM_OBJ(cyc));
} if ( IS_INT(cyc) || TNUM_OBJ(cyc) == T_RAT ) {
m = 1;
} else/* if ( TNUM_OBJ(cyc) == T_CYC ) */ {
m = INT_INTOBJ( NOF_CYC(cyc) );
}
gcd = n; s = m; while ( s != 0 ) { t = s; s = gcd % s; gcd = t; }
n = n / gcd * m;
}
}
// return the N of the cyclotomic return INTOBJ_INT( n );
}
static Obj FuncCOEFFS_CYC(Obj self, Obj cyc)
{
Obj list; // list of coefficients, result
UInt n; // order of field
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
UInt i; // loop variable
// do full operation if ( FIRST_EXTERNAL_TNUM <= TNUM_OBJ(cyc) ) { return DoOperation1Args( self, cyc );
}
if (!IS_CYC(cyc)) {
RequireArgument(SELF_NAME, cyc, "must be a cyclotomic");
}
// if <cyc> is rational just put it in a list of length 1 if ( IS_INT(cyc) || TNUM_OBJ(cyc) == T_RAT ) {
list = NewPlistFromArgs(cyc); // 'CHANGED_BAG' not needed for last bag
}
// otherwise make a list and fill it with zeroes and copy the coeffs else {
n = INT_INTOBJ( NOF_CYC(cyc) );
list = NEW_PLIST( T_PLIST, n );
SET_LEN_PLIST( list, n );
len = SIZE_CYC(cyc);
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len); for ( i = 1; i <= n; i++ )
SET_ELM_PLIST( list, i, INTOBJ_INT(0) ); for ( i = 1; i < len; i++ )
SET_ELM_PLIST( list, exs[i]+1, cfs[i] ); // 'CHANGED_BAG' not needed for last bag
}
static Obj FuncGALOIS_CYC(Obj self, Obj cyc, Obj ord)
{
Obj gal; // galois conjugate, result
Obj sum; // sum of two coefficients Int n; // order of the field
UInt sqr; // if n < sqr*sqr n is squarefree Int o; // galois automorphism
UInt gcd, s, t; // gcd of n and ord, temporaries
UInt len; // number of terms const Obj * cfs; // pointer to the coefficients const UInt4 * exs; // pointer to the exponents
Obj * res; // pointer to the result
UInt i; // loop variable
// do full operation for any but standard arguments if (!IS_INT(ord) || !IS_CYC(cyc)) { return DoOperation2Args( self, cyc, ord );
}
// get and check <ord> if ( ! IS_INTOBJ(ord) ) {
ord = MOD( ord, AttrCONDUCTOR( 0, cyc ) );
}
o = INT_INTOBJ(ord);
// every galois automorphism fixes the rationals if (TNUM_OBJ(cyc) != T_CYC) { return cyc;
}
// get the order of the field, test if it squarefree
n = INT_INTOBJ( NOF_CYC(cyc) ); for ( sqr = 2; sqr*sqr <= n && n % (sqr*sqr) != 0; sqr++ )
;
// force <ord> into the range 0..n-1, compute the gcd of <ord> and <n>
o = (o % n + n) % n;
gcd = n; s = o; while ( s != 0 ) { t = s; s = gcd % s; gcd = t; }
// if <ord> = 1 just return <cyc> if ( o == 1 ) {
gal = cyc;
}
// if <ord> == 0 compute the sum of the entries elseif ( o == 0 ) {
len = SIZE_CYC(cyc);
cfs = COEFS_CYC(cyc);
gal = INTOBJ_INT(0); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( gal, cfs[i] )
|| ! SUM_INTOBJS( sum, gal, cfs[i] ) ) {
sum = SUM( gal, cfs[i] );
cfs = COEFS_CYC( cyc );
}
gal = sum;
}
}
// if <ord> == n/2 compute alternating sum since $(e_n^i)^ord = -1^i$ elseif ( n % 2 == 0 && o == n/2 ) {
gal = INTOBJ_INT(0);
len = SIZE_CYC(cyc);
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len); for ( i = 1; i < len; i++ ) { if ( exs[i] % 2 == 1 ) { if ( ! ARE_INTOBJS( gal, cfs[i] )
|| ! DIFF_INTOBJS( sum, gal, cfs[i] ) ) {
sum = DIFF( gal, cfs[i] );
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len);
}
gal = sum;
} else { if ( ! ARE_INTOBJS( gal, cfs[i] )
|| ! SUM_INTOBJS( sum, gal, cfs[i] ) ) {
sum = SUM( gal, cfs[i] );
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len);
}
gal = sum;
}
}
}
// if <ord> is prime to <n> (automorphism) permute the coefficients elseif ( gcd == 1 ) {
// permute the coefficients
len = SIZE_CYC(cyc);
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) {
res[(UInt8)exs[i]*(UInt8)o%(UInt8)n] = cfs[i];
}
CHANGED_BAG( ResultCyc );
// if n is squarefree conversion and reduction are unnecessary if ( n < sqr*sqr || (o == n-1 && n % 2 != 0) ) {
gal = Cyclotomic( n, n );
} else {
ConvertToBase( n );
gal = Cyclotomic( n, 1 );
}
}
// if <ord> is not prime to <n> (endomorphism) compute it the hard way else {
// multiple roots may be mapped to the same root, add the coeffs
len = SIZE_CYC(cyc);
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len);
res = BASE_PTR_PLIST(ResultCyc); for ( i = 1; i < len; i++ ) { if ( ! ARE_INTOBJS( res[(UInt8)exs[i]*(UInt8)o%(UInt8)n], cfs[i] )
|| ! SUM_INTOBJS( sum, res[(UInt8)exs[i]*(UInt8)o%(UInt8)n], cfs[i] ) ) {
CHANGED_BAG( ResultCyc );
sum = SUM( res[(UInt8)exs[i]*(UInt8)o%(UInt8)n], cfs[i] );
cfs = CONST_COEFS_CYC(cyc);
exs = CONST_EXPOS_CYC(cyc,len);
res = BASE_PTR_PLIST(ResultCyc);
}
res[exs[i]*o%n] = sum;
}
CHANGED_BAG( ResultCyc );
// if n is squarefree conversion and reduction are unnecessary if ( n < sqr*sqr ) {
gal = Cyclotomic( n, 1 ); /*N?*/
} else {
ConvertToBase( n );
gal = Cyclotomic( n, 1 );
}
static Obj FuncCycList(Obj self, Obj list)
{
UInt i; // loop variable
Obj * res; // pointer into result bag
Obj val; // one list entry
UInt n; // length of the given list
// do full operation if ( FIRST_EXTERNAL_TNUM <= TNUM_OBJ( list ) ) { return DoOperation1Args( self, list );
}
if ( ! IS_PLIST( list ) || ! IS_DENSE_LIST( list ) ) {
RequireArgument(SELF_NAME, list, "must be a dense plain list");
}
// enlarge the buffer if necessary
n = LEN_PLIST( list );
GrowResultCyc(n);
// transfer the coefficients into the buffer
res = BASE_PTR_PLIST(ResultCyc); for ( i = 0; i < n; i++ ) {
val = ELM_PLIST( list, i+1 ); if ( !IS_INT(val) && TNUM_OBJ(val) != T_RAT ) { // reset ResultCyc, otherwise the next operation using it will see // our left-over garbage data
SET_LEN_PLIST( ResultCyc, 0 );
RequireArgumentEx(SELF_NAME, val, 0, "each entry must be a rational");
}
res[i] = val;
}
// return the base reduced packed cyclotomic
CHANGED_BAG( ResultCyc );
ConvertToBase( n ); return Cyclotomic( n, 1 );
}
/**************************************************************************** ** *FInitInfoCyc()..................tableofinitfunctions
*/ static StructInitInfo module = { // init struct using C99 designated initializers; for a full list of // fields, please refer to the definition of StructInitInfo
.type = MODULE_BUILTIN,
.name = "cyclotom",
.initKernel = InitKernel,
.initLibrary = InitLibrary,
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.