GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
###############################################################################
##
#F ppowerpoly.gi The SymbCompCC package D�rte Feichtenschlager
##
###############################################################################
##
## p-power-poly-elements (= lists representing p-power-poly-elements)
## sum_(i=0)^n k_i p^(ix)
##
## where: x is an indeterminate over the integers,
## p is a prime,
## k_u in Q,
## n in N_0.
##
## Input: prime p, coeffs [ k_0, k_1, ..., k_n ] and optional: ispdiv and pval
## where ispdiv indicates whether the element is p-divsible or not,
## i.e., whether all k_i are p-divisible,
## and pval is the p-adic valuation [i,j] of the element,
## i.e., p^{i+jx} is the largest p-power, dividing the element.
## -> [p,[k_0,k_1, ..., k_n]]
## or [p,[k_0,k_1, ..., k_n],ispdiv]
## or [p,[k_0,k_1, ..., k_n],ispdiv,[i,j]].
##
## Example: 3^(x-1) + 2*3^(x-3) - 2*3^(2x+2) - 3^(2x-1)
## = 3^x ((3^2+2)/3^3) + 3^2x ((-2*3^3-1)/3)
## = 3^x (11/27) + 3^(2x) (-55/3)
## Input: [3, [0,11/27,-55/3]].
##
## The One: [p, [ 1 ], true, [0,0] ].
## The Zero: [p, [ ], true, [infinity, infinity] ].
##
###############################################################################
##
#F RedPPowerPolyCoeffs( elm )
##
## Input: a p-power-poly element [p,coeffs(,ispdiv,pval)].
##
## Output: a p-polwer-poly element with coeffs as short as possible,
## i.e., that the last entry in coeffs is not 0.
##
InstallGlobalFunction( RedPPowerPolyCoeffs,
function( elm )
local list, i;
list := elm[2];
# check if there are 0's at the end
i := Length(list);
while i > 0 and list[i] = 0 do
i := i - 1;
od;
## eliminate 0's at the end if neccessary
if i = 0 then
return [elm[1],[], true, [infinity, infinity] ];
elif i = Length( list ) then
return elm;
else
elm[2] := list{ [1..i] };
return elm;
fi;
end);
###############################################################################
##
#M IsPDivRat( q, p )
##
## Input: a rational q and an integer p.
##
## Output: a boolean, that indicates whether q is p-divisible or not, i.e.,
## if the denominator of q is a power of p or not.
##
InstallMethod( IsPDivRat, true, [ IsRat, IsInt ], 0,
function( q, p )
local denom, test;
denom := DenominatorRat( q );
## trivial case that denominator is 1
if denom = 1 then return true; fi;
## check if denom is p-power (p given)
test := p;
while denom > test do
test := p * test;
od;
if denom = test then
return true;
else
return false;
fi;
end);
###############################################################################
##
#M IsPDivisiblePPP( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: a boolean that indivates obj = sum_i k_i p^{ix} is p-divisible,
## i.e., whether all coeffcients k_i are p-divisible, i.e., the
## denominator of k_i is a power of p.
##
InstallGlobalFunction( IsPDivisiblePPP,
function( obj )
local p;
## check if already computed
if Length( obj ) > 2 then return obj[3]; fi;
p := obj[1];
return ForAll( obj[2], x -> IsPDivRat(x,p));
end);
###############################################################################
##
#M PPP_Check( elm )
##
## Input: a p-power-poly element elm = [p,coeffs(,ispdiv,pval)].
##
## Output: elm with probably reduced coefficient list (if the last entries
## in coeffs are 0) or an Error occurs if the input does not
## represent a p-power-poly element.
##
InstallGlobalFunction( PPP_Check,
function( elm )
local p, coeffs, ispdiv, pval;
if not IsList( elm ) then
Error( "The input has to be [p, coeffs(, ispdiv, pval) ] where p is prime and coeffs is a list of coefficients to represent sum_(i=0)^n k_i p^(ix) and TODO." );
fi;
p := elm[1];
if not IsPrimeInt( p ) then
Error( "The first entry in the list has to be a prime." );
fi;
coeffs := elm[2];
if not IsList( coeffs ) then
Error( "The second entry in the list has to be a list." );
fi;
if Length( elm ) > 2 then
ispdiv := elm[3];
if not IsBool( ispdiv) or IsPDivisiblePPP( elm ) <> ispdiv then
Error( "The third entry in the list has to be a boolean indicating p-divisibility." );
fi;
if Length( elm ) > 3 then
pval := elm[4];
if PPP_PadicValue( elm ) <> pval then
Error( "The fourth entry in the list has to be the p-adic-value of the element." );
fi;
fi;
fi;
## make sure that last coefficient is not 0
if Length( coeffs ) > 0 and coeffs[Length( coeffs )] = 0 then
elm := RedPPowerPolyCoeffs( elm );
fi;
return elm;
end);
###############################################################################
##
#M PPP_OneNC( elm )
##
## Input: a p-power-poly element elm = [p,coeffs(,ispdiv,pval)].
##
## Output: the corresponding one p-power-poly element.
##
InstallGlobalFunction( PPP_OneNC,
function( elm )
return [ elm[1], [ 1 ], true, [ 0, 0 ] ];
end );
###############################################################################
##
#M PPP_ZeroNC( elm )
##
## Input: a p-power-poly element elm = [p,coeffs(,ispdiv,pval)].
##
## Output: the corresponding zero p-power-poly element.
##
InstallGlobalFunction( PPP_ZeroNC,
function( elm )
return [ elm[1], [], true, [ infinity, infinity ] ];
end );
###############################################################################
##
#M Int2PPowerPoly( p, i )
##
## Input: a prime p and an integer i.
##
## Output: the p-power-poly element representing i.
##
InstallGlobalFunction( Int2PPowerPoly,
function( p, i )
if not IsPrimeInt(p) then
Error("Wrong input, the first input has to be a prime.");
fi;
if not IsInt(i) then
Error("Wrong input, the second input has to be an integer.");
fi;
## get p-power-poly if possible with p-adic valuation
if i = 0 then
return [ p, [], true, [ infinity, infinity ] ];
elif 0 < i and i < p then
return [ p, [ i ], true, [ 0, 0 ] ];
elif i < 0 and -p < i then
return [ p, [ i ], true, [ 0, 0 ] ];
else
return [ p, [ i ] ];
fi;
end);
###############################################################################
##
#M PPP_Print( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: this function prints in an easy to read way.
##
InstallGlobalFunction( PPP_Print,
function( obj )
local p, c, l_c, strg, i, One1, start;
p := obj[1];
c := obj[2];
One1 := PPP_OneNC( obj );
## check for trivial cases
if c = [] then
Print( "0" );
elif PPP_Equal( obj, One1 ) then
Print( "1" );
## start printing
else l_c := Length( c );
start := false;
## print p^(0x)-part
if c[1] <> 0 then
Print( c[1] );
start := true;
fi;
## print p^x-part
if l_c > 1 then
if c[2] < 0 then
if c[2] = -1 then
Print( "-" );
else Print( c[2], "*" );
fi;
Print( p, "^x" );
start := true;
elif c[2] > 0 then
if start then
Print( "+" );
fi;
if c[2] <> 1 then
Print( c[2], "*" );
fi;
Print( p, "^x" );
start := true;
fi;
fi;
## print rest
for i in [3..l_c] do
if c[i] < 0 then
if c[i] = -1 then
Print( "-" );
else Print( c[i], "*" );
fi;
Print( p, "^(", i-1, "x)" );
start := true;
elif c[i] > 0 then
if start then
Print( "+" );
fi;
if c[i] <> 1 then
Print( c[i], "*" );
fi;
Print( p, "^(", i-1, "x)" );
start := true;
fi;
od;
fi;
end);
###############################################################################
##
#M PPP_PrintMat( obj )
##
## Input: a matrix with p-power-poly entries.
##
## Output: the function prints the matrix in an easy to read way.
##
InstallGlobalFunction( PPP_PrintMat,
function( obj )
local i, j;
Print( "[" );
for i in [1..Length( obj )] do
Print( "[ " );
PPP_Print( obj[i][1] );
for j in [2..Length( obj[i] )] do
Print( ", " );
PPP_Print( obj[i][j] );
od;
Print( "] " );
if i <> Length( obj ) then
Print( ",\n" );
fi;
od;
Print( "]\n" );
end);
###############################################################################
##
#M PPP_PrintRow( obj )
##
## Input: a row of p-power-poly elements.
##
## Output: the functions prints the row in an easy to read way.
##
InstallGlobalFunction( PPP_PrintRow,
function( obj )
local i, j;
## catch trivial case
if obj = [] then Print( "[ ]\n" ); fi;
Print( "[ " );
if IsInt( obj[1] ) then
Print( obj[1] );
else PPP_Print( obj[1] );
fi;
for i in [2..Length( obj )] do
Print( ", " );
if IsInt( obj[i] ) then
Print( obj[i] );
else PPP_Print( obj[i] );
fi;
od;
Print( " ]\n" );
end);
#############################################################################
##
#M PPP_Equal( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2
##
## Output: a boolean indicating whether obj1 and obj2 are equal where
## comparison is defined as follows:
##
## Let obj1 = [p_1, coeffs_1] and obj2 = [p_2, coeffs_2].
## If p_1 < p_2 then obj1 < obj2.
## If p_1 = p_2 = p and obj1 represents f = sum_{i=0}^n f_i p^{ix}
## and obj2 represents g = sum_{i=0}^m g_i p^{ix}
## then f < 0 <=> f_n < 0
## and f < g <=> f-g < 0.
##
InstallGlobalFunction( PPP_Equal,
function( obj1, obj2 )
return obj1[1] = obj2[1] and obj1[2] = obj2[2];
end);
#############################################################################
##
#M PPP_Smaller( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2
##
## Output: a boolean indicating whether obj1 is smaller than obj2 where
## comparison is defined as follows:
##
## Let obj1 = [p_1, coeffs_1] and obj2 = [p_2, coeffs_2].
## If p_1 < p_2 then obj1 < obj2.
## If p_1 = p_2 = p and obj1 represents f = sum_{i=0}^n f_i p^{ix}
## and obj2 represents g = sum_{i=0}^m g_i p^{ix}
## then f < 0 <=> f_n < 0
## and f < g <=> f-g < 0.
##
InstallGlobalFunction( PPP_Smaller,
function( obj1, obj2 )
local Zero0;
if PPP_Equal( obj1, obj2 ) then return false; fi;
if obj1[1] < obj2[1] then return true; fi;
if obj1[1] > obj2[1] then return false; fi;
Zero0 := PPP_ZeroNC( obj1 );
## get trivial case, one obj is zero
if PPP_Equal( obj1, Zero0 ) then
if obj2[2][Length(obj2[2])] > 0 then
return true;
else
return false;
fi;
elif PPP_Equal( obj2, Zero0 ) then
if obj1[2][Length(obj1[2])] < 0 then
return true;
else
return false;
fi;
## reduce to trivial case, that comparison of obj and zero
else return PPP_Smaller( PPP_Subtract( obj1, obj2 ), Zero0 );
fi;
end);
#############################################################################
##
#M PPP_Greater( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2.
##
## Output: a boolean indicating whether obj1 is greater than obj2 where
## comparison is defined as follows:
##
## Let obj1 = [p_1, coeffs_1] and obj2 = [p_2, coeffs_2].
## If p_1 < p_2 then obj1 < obj2.
## If p_1 = p_2 = p and obj1 represents f = sum_{i=0}^n f_i p^{ix}
## and obj2 represents g = sum_{i=0}^m g_i p^{ix}
## then f < 0 <=> f_n < 0
## and f < g <=> f-g < 0.
##
InstallGlobalFunction( PPP_Greater,
function( obj1, obj2 )
if PPP_Equal( obj1, obj2 ) then return false;
elif PPP_Smaller( obj1, obj2 ) then return false;
else return true;
fi;
end);
#############################################################################
##
#M PPP_Add( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2
##
## Output: the sum of obj1 and obj2.
##
InstallGlobalFunction( PPP_Add,
function( obj1, obj2 )
local Zero0, obj;
if IsInt( obj1 ) and IsInt( obj2 ) then
Error( "No underlying prime is known." );
fi;
if IsInt( obj1 ) then obj1 := Int2PPowerPoly( obj2[1], obj1 ); fi;
if IsInt( obj2 ) then obj2 := Int2PPowerPoly( obj1[1], obj2 ); fi;
if obj1[1] <> obj2[1] then
Error( "The underlying primes are different." );
fi;
# catch trivial cases
Zero0 := PPP_ZeroNC( obj1 );
if PPP_Equal( obj1, Zero0 ) then
return obj2;
elif PPP_Equal( obj2, Zero0 ) then
return obj1;
fi;
## add coefficients and asign values
obj := [];
obj[1] := obj1[1];
obj[2] := obj1[2]+obj2[2];
## check for p-divisibility
if Length( obj1 ) > 3 and obj1[3] and Length( obj2 ) > 3 and obj2[3] then
obj[3] := true;
fi;
return PPP_Check( obj );
end);
#############################################################################
##
#M PPP_Subtract( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2.
##
## Output: the differenz obj1-obj2.
##
InstallGlobalFunction( PPP_Subtract,
function( obj1, obj2 )
local obj;
if IsInt( obj2 ) then
obj := -obj2;
else obj := PPP_AdditiveInverse( obj2 );
fi;
return PPP_Add( obj1, obj );
end);
#############################################################################
##
#M PPP_Mult( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2.
##
## Output: the product of obj1 and obj2.
##
InstallGlobalFunction( PPP_Mult,
function( obj1, obj2 )
local Zero0, One1, c1, c2, c, i, j, obj, l, p, coeffs;
if IsRat( obj1 ) and IsRat( obj2 ) then
Error( "No underlying prime is known." );
fi;
if IsInt( obj1 ) then obj1 := Int2PPowerPoly( obj2[1], obj1 ); fi;
if IsInt( obj2 ) then obj2 := Int2PPowerPoly( obj1[1], obj2 ); fi;
## do rational cases
if IsRat( obj1 ) then
p := obj2[1];
coeffs := obj2[2];
## check
if not IsPDivRat( obj1, p ) then return Error( "TODO" ); fi;
if not IsInt( coeffs[1] * obj1 ) then return Error( "TODO" ); fi;
coeffs := List( coeffs, x -> x*obj1 );
return [p, coeffs];
fi;
if IsRat( obj2 ) then
p := obj1[1];
coeffs := obj1[2];
## check
if not IsPDivRat( obj2, p ) then return Error( "TODO" ); fi;
if not IsInt( coeffs[1] * obj2 ) then return Error( "TODO" ); fi;
coeffs := List( coeffs, x -> x*obj2 );
return [p, coeffs];
fi;
## check underlying primes
if obj1[1] <> obj2[1] then
Error( "The underlying primes are different." );
fi;
## catch trivial cases
Zero0 := PPP_ZeroNC( obj1 );
One1 := PPP_OneNC( obj1 );
if PPP_Equal( obj1, One1 ) then
return obj2;
elif PPP_Equal( obj2, One1 ) then
return obj1;
elif PPP_Equal( obj1, Zero0 ) then
return Zero0;
elif PPP_Equal( obj2, Zero0 ) then
return Zero0;
fi;
## initialize
c1 := obj1[2];
c2 := obj2[2];
l := Length( c1 ) - 1 + Length( c2 ) - 1;
c := List( [0..l], x -> 0 );
## multiply
for i in [0..l] do
for j in [0..i] do
if IsBound( c1[j+1] ) and IsBound( c2[i-j+1] ) then
c[i+1] := c[i+1] + ( c1[j+1] * c2[i-j+1] );
fi;
od;
od;
obj := [obj1[1], c ];
## check for p-divisibility
if Length( obj1 ) > 2 and Length( obj2 ) > 2 then
if obj1[3] or obj2[3] then
obj[3] := true;
fi;
if Length( obj1 ) > 3 and Length( obj2 ) > 3 then
obj[4] := obj1[4] + obj2[4];
fi;
fi;
return PPP_Check( obj );
end);
#############################################################################
##
#M PPP_AdditiveInverse( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: the additive inverse of obj.
##
InstallGlobalFunction( PPP_AdditiveInverse,
function( obj )
local objinv;
if PPP_Equal( obj, PPP_ZeroNC( obj ) ) then return obj; fi;
## get inverse
objinv := ShallowCopy( obj );
objinv[2] := -objinv[2];
return objinv;
end);
###############################################################################
##
#M EvaluatePPowerPoly( obj, n )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)] and a not
## negative integer n.
##
## Output: the value of the p-power-poly element obj at n.
##
InstallGlobalFunction( EvaluatePPowerPoly,
function( obj , n )
local res, p, c, i;
if not IsInt( n ) or n < 0 then
Error( "The second input has to be non-negative integer." );
fi;
## initialize
res := 0;
p := obj[1];
c := obj[2];
## evaluate
for i in [1..Length( c )] do
res := res + c[i] * p^((i-1)*n);
od;
return res;
end);
###############################################################################
##
#M PPP_AbsValue( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: the absolute value of obj as p-power-poly element.
##
InstallGlobalFunction( PPP_AbsValue,
function( obj )
local lead_c;
if PPP_Equal( obj, PPP_ZeroNC( obj ) ) then return obj; fi;
## check if element is < or > zero
lead_c := obj[2][Length(obj[2])];
if lead_c < 0 then
return PPP_AdditiveInverse( obj );
else
return obj;
fi;
end);
###############################################################################
##
#M PadicValueRatNC( q, p )
##
## Input: a rational q and a prime p which is not checked.
##
## Output: the p-adic value of q.
##
InstallMethod( PadicValueRatNC, [ IsRat, IsInt ],
function( q, p )
local a, b;
## get p-power-divisor of numerator and denominator and compare
a := AbsoluteValue( NumeratorRat(q) );
a := Length(Filtered(Factors(a), x -> x = p));
b := DenominatorRat(q);
b := Length(Filtered(Factors(b), x -> x = p));
return a-b;
end);
###############################################################################
##
#M PPP_PadicValue( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: the p-adic value [i,j] of obj, i.e., the largest p-power
## p^{i+jx} dividing obj.
##
InstallGlobalFunction( PPP_PadicValue,
function( obj )
local i, s;
if not IsPDivisiblePPP( obj ) then return fail; fi;
##check if already computed
if IsBound( obj[4] ) then return obj[4]; fi;
if obj[2] = [] then return [ infinity, infinity ]; fi;
## get smallest p^((i-1)x) and then p-adic value of that coefficient
i := PositionNonZero( obj[2] );
s := PadicValueRatNC( obj[2][i], obj[1] );
return [ i-1, s ];
end);
###############################################################################
##
#M PPartPoly( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: largest p-power p^{i+jx} dividing obj as p-power-poly element.
##
InstallGlobalFunction( PPartPoly,
function( obj )
local e, l;
if not IsPDivisiblePPP( obj ) then return fail; fi;
## catch trivial cases
if PPP_Equal( obj, PPP_OneNC( obj ) ) then return obj; fi;
if PPP_Equal( obj, PPP_ZeroNC( obj ) ) then return Error( "Wrong input, zero.!" ); fi;
## get p-part and make that into element
e := PPP_PadicValue( obj );
l := 0*[0..e[1]];
l[e[1]+1] := obj[1]^e[2];
return [obj[1], l, true, e ];
end);
###############################################################################
##
#M PPrimePartPoly( obj )
##
## Input: a p-power-poly element obj = [p,coeffs(,ispdiv,pval)].
##
## Output: the p-prime-part of obj as a p-power-poly element, i.e., an
## element b such that a = bc where c is the p-part of obj.
##
InstallGlobalFunction( PPrimePartPoly,
function( obj )
local p, e, c, n, Zero0;
if not IsPDivisiblePPP( obj ) then return fail; fi;
## catch trivial case
if PPP_Equal( obj, PPP_OneNC( obj ) ) then return obj; fi;
Zero0 := PPP_ZeroNC( obj );
if PPP_Equal( obj, Zero0 ) then
return Error( "Wrong input, zero.!" );
fi;
## get p-part-poly
p := obj[1];
e := PPP_PadicValue( obj );
c := obj[2];
## divide by p^(ix) by eliminating the first entries in the list
## and then divide by remaining integer part of p-part
n := c{[e[1]+1..Length(c)]} / p^e[2];
return [p, n, true, [0,0] ];
end);
###############################################################################
##
#M DivPPartPoly( obj1, obj2 )
##
## Input: p-power-poly elements obj1 and obj2.
##
## Output: the division of obj1 by obj2 as p-power-poly element, where obj2
## is equal to its p-part and the p-adic value of obj1 is greater or
## equal to the p-adic value of obj2.
##
InstallGlobalFunction( DivPPartPoly,
function( obj1, obj2 )
local padicval, c, p, obj, One1;
## check
p := obj1[1];
if p <> obj2[1] then
return Error( "The underlying primes are different." );
fi;
padicval := PPP_PadicValue( obj2 );
if PPP_PadicValue( obj1 ) < padicval then
return Error( "The second object does not divide the first one." );
fi;
if not PPP_Equal( PPartPoly( obj2 ), obj2 ) then
return Error( "The second object is not equal to its p-part TODO." );
fi;
## trivial cases
One1 := PPP_OneNC( obj1 );
if obj1[2] = [] then return obj1; fi;
if PPP_Equal( obj1, obj2 ) then return One1; fi;
if PPP_Equal( obj2, One1 ) then return obj1; fi;
## divide by shifting
c := obj1[2]{ [padicval[1]+1..Length(obj1[2])] };
c := c/p^(padicval[2]);
return [ p, c, true, PPP_PadicValue( obj1 ) - padicval ];
end);
#E ppowerpoly.gi . . . . . . . . . . . . . . . . . . . . . . . . . ends here