My goal is to implement the algebraic closure of Q along the lines of
\cite{Steel:AlgebraicallyClosedFields:2010} (bibtex item below).
Such an implementation seemingly needs the algebraic closure of a finite
field. This is what I present in this mail.
The main idea is that not a true algebraic closure is implemented, but a
domain that is close to it in the following sense.
At each point in time the domain represents a finite extension of the
ground field (that's currently GF(p) in my attached files).
So the factorization works exactly as in a finite field extension.
However, if one uses rootOf(somepoly), then the polynomial is internally
factored and if there is no linear factor, then one of the irreducible
factors is used to extend the current field.
Technically, this means that a new variable is created and the
irreducible polynomial (considered in this new variable) is added to an
internal triangular set. The new variable is then returned as a new root.
So in fact, this domain DynamicAlgebraicClosurePrimeField behaves
like FiniteFieldExtensionByPolynomial, just that instead of one
polynomial, I use a triangular set.
As can be seen from the code, I actually only build on the functions of
PrimeField and some packages, in particular, the factorization in
ddfact.spad, but not on any FiniteFieldExtension* domains.
Luckily, most things I needed were already implemented as category defaults.
However, there is one important change that would have to be done if
DynamicAlgebraicClosurePrimeField should make it into FriCAS. It
concerns ModMonic. Seemingly for efficiency reasons, ModMonic checks in
"setpoly" for certain conditions and then avoids some recomputations.
It took me some time to realize that ModMonic behaves badly for my
dynamic domain. ModMonic(F, SUP F) silently assumes that for finite
fields size()$F is a constant. Well, a reasonable assumption, but bad
for me, since DynamicAlgebraicClosurePrimeField grows in size with each
new root that is not yet in the field. So I had to patch modmon.spad in
a few places. Since these changes are not too costly, I think they can
also be used for the other cases where size()$F is a true constant.
Attached is the code and a small session to demonstrate the things that
I want to work. Maybe there are still some things that do not work
properly, so just take the code as a "work-in-progress".
Comments and bug reports are welcome.
Ralf
@article{Steel:AlgebraicallyClosedFields:2010,
title = {Computing with algebraically closed fields},
journal = JSC,
volume = 45,
number = 3,
pages = {342 - 372},
year = 2010,
issn = {0747-7171},
doi = {10.1016/j.jsc.2009.09.005},
url =
{http://www.sciencedirect.com/science/article/pii/S0747717109001497},
author = {Allan K. Steel},
keywords = {Algebraic closure, Algebraic number field, Algebraic
function field, Field extension, Inseparability,
Non-perfect field, Polynomial factorization, Root
finding},
}
--
You received this message because you are subscribed to the Google Groups
"FriCAS - computer algebra system" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/fricas-devel/173d86e0-acb5-3b26-d073-476c9831724c%40hemmecke.org.
-------------------------------------------------------------------
---
--- DynamicAlgebraicClosurePrimeField
--- Copyright (C) 2019, Ralf Hemmecke <[email protected]>
---
-------------------------------------------------------------------
-- 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 3 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, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------
)if LiterateDoc
\documentclass{article}
\usepackage{url}
\DeclareUrlCommand\code{\urlstyle{tt}}
\begin{document}
\title{Dynamic Algebraic Closure Prime Field}
\author{Ralf Hemmecke}
\date{22-Oct-2019}
\maketitle
\begin{abstract}
\begin{verbatim}
@article{Steel:AlgebraicallyClosedFields:2010,
title = {Computing with algebraically closed fields},
journal = JSC,
volume = 45,
number = 3,
pages = {342 - 372},
year = 2010,
issn = {0747-7171},
doi = {10.1016/j.jsc.2009.09.005},
url =
{http://www.sciencedirect.com/science/article/pii/S0747717109001497},
author = {Allan K. Steel},
keywords = {Algebraic closure, Algebraic number field, Algebraic
function field, Field extension, Inseparability,
Non-perfect field, Polynomial factorization, Root
finding},
}
\end{verbatim}
\end{abstract}
\tableofcontents
\section{Overview}
For the implementation of an ACF in the sense of
\cite{Steel:AlgebraicallyClosedFields:2010} we need finite fields and
algebraic extensions of them. Since in FriCAS we the type of the
finite field must be known at compile time, we create a finite field
that behaves like the algebraic closure of $GF(p)$.
Maybe, eventually, we must create a domain that behaves like the union
of such fields for all primes $p$.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Implementation}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
)endif
OF==>OutputForm
dbgPrint(x,y) ==> print(([":> "::Symbol::OF, x::Symbol::OF,
y::OF]$List(OF)::OF))
rep x ==> (x@%) pretend Rep
per x ==> (x@Rep) pretend %
P ==> PositiveInteger
N ==> NonNegativeInteger
Z ==> Integer
Q ==> Fraction Z
SI ==> SingleInteger
SY ==> Symbol
asN x ==> x pretend N
asP x ==> x pretend P
asZ x ==> x pretend Z
)if LiterateDoc
%$
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{IndexedVariable}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
)endif
)abbrev domain IVAR IndexedVariable
++ Author: Ralf Hemmecke
++ Description:
++ IndexedVariable(x) represents the variables x1, x2, x3. etc.
++ and orders them like the natural order of their indices.
IndexedVariable(v: String): Exports == Implementation where
Exports ==> Join(OrderedSet, ConvertibleTo Symbol) with
new: () -> %
++ new() returns a new (not yet used) variable.
rank: () -> Z
++ rank() returns the highest index of the currently known
++ variables. It returns 0 if no variables are known.
lookup: % -> P
++ lookup(x) returns the index of the variable.
index: P -> %
++ index(p) returns the variable v such that lookup(v) = p. It
++ is an error to call index(p) for p > rank().
Implementation ==> P add
next: Reference Z := ref(1$Z)
syms: FlexibleArray Symbol := empty()
Rep ==> Z -- we only use positive integers (x>0)
rank(): Z ==
-- free next
deref(next) - 1
lookup(x: %): P == asP rep x
index(p: P): % ==
z: Z := asZ p
z < deref next => per z
error "No variable with that index is known."
new(): % ==
-- free next, syms
m: Z := deref next
s: String := concat(v, string m)
sym: Symbol := s::Symbol
concat!(syms, sym) -- destructively changes syms
setelt!(next, m+1)$Reference(Z)
per m
convert(x: %): Symbol == syms(rep x)
coerce(x: %): OutputForm ==
import from Symbol
(convert(x)@Symbol)::OutputForm
)if LiterateDoc
%$
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Dynamic Algebraic Closure Prime Field}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The constructor \code{DynamicAlgebraicClosurePrimeField} takes as
input a prime $p$ and returns the algebraic closure of
$GF(p)$ in the sense of \cite{Steel:AlgebraicallyClosedFields:2010}.
)endif
)abbrev domain DACPF DynamicAlgebraicClosurePrimeField
++ Author: Ralf Hemmecke
++ Description:
++ Implement an ACF according to \cite{Steel:AlgebraicallyClosedFields:2010}
++ but here for a prime field of characteristic p.
DynamicAlgebraicClosurePrimeField(p: P): Exports == Implementation where
V ==> IndexedVariable("f")
K ==> PrimeField p
EX ==> IndexedExponents V
R ==> NewSparseMultivariatePolynomial(K, V)
TS ==> RegularTriangularSet(K, EX, V, R)
NUP ==> NewSparseUnivariatePolynomial
SUP ==> SparseUnivariatePolynomial
Exports ==> Join(AlgebraicallyClosedField, FiniteFieldCategory) with
rank: () -> Z
show: () -> Void()
extendBy!: SUP % -> Void()
getGenerator: P -> %
Implementation ==> R add
-- We assume that all elements are always reduced wrt. the
-- triangular set ts.
Rep ==> R
import from Rep
xx ==> rep x
yy ==> rep y
RZ ==> SUP R
G1 ==> Record(gcd: NUP %, coef1: NUP %) -- for "inv" computation
FUNION ==> Union("nil", "sqfr", "irred", "prime")
FREC ==> Record(flag: FUNION, factor: SUP %, exponent: N)
FL ==> List FREC
univar(r, v) ==> (univariate(r, v)$R) pretend NUP % -- (%, V) -> NUP %
multivar(s, v) ==> per(multivariate(s pretend RZ, v)$R) -- (NUP %, V) -> %
-- internally used vars, sorted decreasingly f4,f3,f2,f1
mvars: Reference List V := ref(empty()$List(V))
-- assign(sy: Symbol, x: %): Void() == (assignSymbol(sy, x, %)$Lisp; x)
newVariable(): V ==
import from List V
v: V := new()
setelt!(mvars, cons(v, deref mvars))$Reference(List V)
v
-- In contrast to \cite{Steel:AlgebraicallyClosedFields:2010} we
-- only allow irreducible polynomial to extend. In other words,
-- if ts belongs to the current representation of the extension field
-- and f is the polynomial with the highest variable, then f is
-- irreducible over the field that belongs to ts without f.
-- We assume that we can construct all roots of f in the current
-- field belonging to ts. Not that the highest variable is such a root.
rank(): Z ==
import from List V
empty? deref mvars => 0
asZ(lookup(first deref mvars)$V)
-- Domain variable ts represents the ideal I (\subseteq J)
-- Note that the first entry in ts it the one with the highest variable.
ts: Reference TS := ref(empty()$TS)
getRelation(n: P): R ==
--assert(not empty? deref gamma)
import from TS
triset: List R := deref(ts)::List(R)
m: Z := n pretend Z
triset(rank() - m + 1)
getGenerator(n: P): % ==
import from N
v: V := index(n)$V
multivar(monomial(1, 1)$RZ, v)
extendBy!(f: SUP %): Void() ==
v: V := newVariable()
minpoly: R := rep multivar(f, v)
triset: TS := deref ts
triset := extend(triset, minpoly)
setelt!(ts, triset)
--assert(# deref ts = # deref gamma)
--assert(# deref ts = # deref mvars)
--assert((vs := deref mvars; if not empty? vs then #vs = lookup first vs))
show(): Void() ==
import from V
triset: TS := deref ts
print(triset::OutputForm)$OF
vars: List V := deref mvars
l: List(OF) := [v::OutputForm for v in vars]
print(bracket(l)$OF)$OF
reduce(r: Rep): % ==
z: Record(rnum: K, polnum: R, den: K) := remainder(r, deref ts)$TS
per((z.rnum/z.den) * z.polnum)
_-(x: %): % == reduce(- xx)
((x: %) + (y: %)): % == reduce(xx + yy)
((x: %) - (y: %)): % == reduce(xx - yy)
((x: %) * (y: %)): % == reduce(xx * yy)
((x: %) ^ (n: P)): % == expt(x, n)$RepeatedSquaring(%)
((x: %) ^ (n: N)): % ==
zero? n => 1
x^asP(n)
((x: %) ^ (n: Z)): % ==
n < 0 => inv(x) ^ asP(-n)
x^asN(n)
zero?(x: %): Boolean == ground? xx and zero?(ground xx)$K
((x: %) = (y: %)): Boolean == zero?(x-y)
((x: %) / (y: %)): % == x * inv y
inv(x: %): % ==
zero? x => error "cannot invert zero element"
--assert(not zero? x)
ground? xx => per((inv(ground xx)$K)::R)
v: V := mvar xx
f: NUP % := univar(xx, v)
n: P := lookup v
gn: R := getRelation n
g: NUP % := univar(gn, v)
cu: G1 := halfExtendedSubResultantGcd1(f, g)
c: NUP % := cu.gcd
import from N
not zero? degree c => error "ffalgclos: inv: gcd is not a constant"
lcc: % := leadingCoefficient c
inv(lcc) * multivar(cu.coef1, v)
rootOf(poly: SUP %, sy: Symbol): % ==
-- We currently ignore the second parameter.
fl: FL := factorList(factor poly)$Factored(SUP %)
-- Note that fl cannot be empty.
for frec in fl repeat
f: SUP % := frec.factor
if one? degree f then return (- coefficient(f, 0))
-- Arriving here means that there was no linear factor, i.e.,
-- we must extend our field by an irreducible polynomial.
-- For simplicity, we simply take the first element in fl.
extendBy!(first(fl).factor)
-- After extension, there must be a root, namely the value
-- corresponding to the varlable v.
v: V := first(deref mvars)$List(V)
multivar(monomial(1, 1)$RZ, v)
((x: %) ^ (q: Q)): % ==
n: Z := numer q
d: N := asN denom q
f: SUP % := monomial(1, d)$SUP(%) - (x^n)::SUP(%)
rootOf f
sqrt(x: %): % ==
import from N
f: SUP % := monomial(1, 2)$SUP(%) - x::SUP(%)
rootOf f
representationType(): Union("prime","polynomial","normal","cyclic") ==
-- representationType() returns the type of the representation,
-- one of: prime, polynomial, normal, or cyclic.
error "NOT YET: representationType"
primitiveElement(): % == error "NOT YET: primitiveElement"
tableForDiscreteLogarithm(n: Z): Table(P, N) ==
error "NOT YET: tableForDiscreteLogarithm"
factorsOfCyclicGroupSize(): List Record(factor: Z, exponent: N) ==
factors(factor(asZ(size())-1)$Z)$Factored(Z)
levelSizes(triset: List R): List Z == -- local function
empty? triset => [asZ(size()$K)]$List(Z)
d: N := mdeg first triset
triset := rest triset
ls: List Z := levelSizes triset
sz: Z := first ls
cons(sz^d, ls)
allLevelSizes(): List Z == -- local function
import from TS
import from R
triset: List R := deref(ts)::List(R)
levelSizes triset
size(): N == asN(first(allLevelSizes())$List(Z))
indexRec(n: Z, ls: List Z): % ==
--assert(not empty? ls)
--assert(n < first ls)
--assert(n >= 0)
zero? n => 0
empty? ls => per((index(asP n)$K)::R) -- Base case; element of K
b: Z := first ls
n < b => indexRec(n, rest ls)
k: P := asP(#ls)
v: V := index(k)$V
-- Now find the "digits" of n in base b
s: NUP % := 0
j: N := 0
while n > 0 repeat
rec: Record(quotient: Z, remainder: Z) := divide(n, b)
r: Z := rec.remainder
if not zero? r then
c: % := indexRec(r, ls)
s := s + monomial(c, j)$NUP(%)
n := rec.quotient
j := j + 1
multivar(s, v)
index(n: P): % ==
-- Important: Even after extending the field, i.e. growing ts,
-- index(n) should give the same element as before.
-- The basic ideas is that we consider the constant of the
-- polynomial first.
-- The following algorithm works recursively.
ls: List Z := allLevelSizes()
nn: Z := first ls
-- Note that index expects n in the range 1..size(), but
-- indexRec in the range 0..size()-1.
indexRec(positiveRemainder(asZ n - 1, nn), rest ls)
lookupRec(r: R, triset: List R, ls: List Z): Z == -- local function
zero? r => 0
ground? r => asZ(lookup(ground r)$K)
-- assert(#triset + 1 = #ls
-- lookupRec returns values in the range 0..size()-1.
v: V := mvar r
while v < mvar first triset repeat
triset := rest triset
ls := rest ls
triset := rest triset
ls := rest ls
sz: Z := first ls
result: Z := lookupRec(leadingCoefficient(r, v), triset, ls)
d: Z := asZ mdeg r
while not zero?(r := reductum r) repeat
e: N := asN(d - (d := asZ mdeg r))
c: Z := lookupRec(leadingCoefficient(r, v), triset, ls)
result := result * sz^e + c
result * sz^asN(d)
lookup(x: %): P ==
--assert(x = reduce x)
-- lookup returns values in the range 1..size().
import from TS
import from R
triset: List R := deref(ts)::List(R)
ls: List Z := levelSizes triset
asP(1 + lookupRec(xx, triset, ls))
--: *: (%, Fraction Integer) -> %
--: *: (Fraction Integer, %) -> %
--: *: (Integer, %) -> %
--: *: (NonNegativeInteger, %) -> %
--: *: (PositiveInteger, %) -> %
--: ^: (%, Integer) -> %
--: ^: (%, NonNegativeInteger) -> %
--: ^: (%, PositiveInteger) -> %
--: ~=: (%, %) -> Boolean
--: annihilate?: (%, %) -> Boolean
--: antiCommutator: (%, %) -> %
--: associates?: (%, %) -> Boolean
--: associator: (%, %, %) -> %
--: characteristic: () -> NonNegativeInteger
--: charthRoot: % -> %
--: -- charthRoot(a) takes the characteristic’th root of a. Note: such
--: -- a root is alway defined in finite fields.
--: charthRoot: % -> Union(%, failed)
--: coerce: % -> %
--: coerce: Fraction Integer -> %
--: coerce: Integer -> %
--: commutator: (%, %) -> %
--: conditionP: Matrix % -> Union(Vector %, failed)
--: convert: % -> InputForm
--: createPrimitiveElement: () -> %
--: -- createPrimitiveElement() computes a generator of the (cyclic)
--: -- multiplicative group of the field.
--: D: % -> %
--: D: (%, NonNegativeInteger) -> %
--: differentiate: % -> %
--: differentiate: (%, NonNegativeInteger) -> %
--: discreteLog: % -> NonNegativeInteger
--: -- discreteLog(a) computes the discrete logarithm of a with
--: -- respect to primitiveElement() of the field.
--: discreteLog: (%, %) -> Union(NonNegativeInteger, failed)
--: divide: (%, %) -> Record(quotient: %, remainder: %)
--: enumerate: () -> List %
--: euclideanSize: % -> NonNegativeInteger
--: expressIdealMember: (List %, %) -> Union(List %, failed)
--: exquo: (%, %) -> Union(%, failed)
--: extendedEuclidean: (%, %) -> Record(coef1: %, coef2: %, generator: %)
--: extendedEuclidean: (%, %, %) -> Union(Record(coef1: %, coef2: %),
failed)
--: factor: % -> Factored %
--: factorPolynomial: SparseUnivariatePolynomial % -> Factored
SparseUnivariatePolynomial %
--: factorsOfCyclicGroupSize: () -> List Record(factor: Integer, exponent:
NonNegativeInteger)
--: -- factorsOfCyclicGroupSize() returns the factorization of
--: -- size()-1
--: factorSquareFreePolynomial: SparseUnivariatePolynomial % -> Factored
SparseUnivariatePolynomial %
--: gcd: (%, %) -> %
--: gcd: List % -> %
--: gcdPolynomial: (SparseUnivariatePolynomial %,
SparseUnivariatePolynomial %) -> SparseUnivariatePolynomial %
--: hash: % -> SingleInteger
--: hashUpdate!: (HashState, %) -> HashState
--: index: PositiveInteger -> %
--: init: %
--: from StepThrough
--: inv: % -> %
--: latex: % -> String
--: lcm: (%, %) -> %
--: lcm: List % -> %
--: lcmCoef: (%, %) -> Record(llcm_res: %, coeff1: %, coeff2: %)
--: leftPower: (%, NonNegativeInteger) -> %
--: leftPower: (%, PositiveInteger) -> %
--: leftRecip: % -> Union(%, failed)
--: lookup: % -> PositiveInteger
--: multiEuclidean: (List %, %) -> Union(List %, failed)
--: nextItem: % -> Union(%, failed)
--: one?: % -> Boolean
--: opposite?: (%, %) -> Boolean
--: order: % -> OnePointCompletion PositiveInteger
--: order: % -> PositiveInteger
--: -- order(b) computes the order of an element b in the
--: -- multiplicative group of the field. Error: if b equals 0.
--: prime?: % -> Boolean
--: primeFrobenius: % -> %
--: primeFrobenius: (%, NonNegativeInteger) -> %
--: primitive?: % -> Boolean
--: -- primitive?(b) tests whether the element b is a generator of the
--: -- (cyclic) multiplicative group of the field, i.e. is a primitive
--: -- element. Implementation Note: see ch.IX.1.3, th.2 in D. Lipson.
--: primitiveElement: () -> %
--: -- primitiveElement() returns a primitive element stored in a
--: -- global variable in the domain. At first call, the primitive
--: -- element is computed by calling createPrimitiveElement.
--: principalIdeal: List % -> Record(coef: List %, generator: %)
--: quo: (%, %) -> %
--: random: () -> %
--: recip: % -> Union(%, failed)
--: rem: (%, %) -> %
--: representationType: () -> Union(prime, polynomial, normal, cyclic)
--: -- representationType() returns the type of the representation,
--: -- one of: prime, polynomial, normal, or cyclic.
--: rightPower: (%, NonNegativeInteger) -> %
--: rightPower: (%, PositiveInteger) -> %
--: rightRecip: % -> Union(%, failed)
--: sample: %
--: size: () -> NonNegativeInteger
--: sizeLess?: (%, %) -> Boolean
--: smaller?: (%, %) -> Boolean
--: solveLinearPolynomialEquation: (List SparseUnivariatePolynomial %,
SparseUnivariatePolynomial %) -> Union(List SparseUnivariatePolynomial %,
failed)
--: squareFree: % -> Factored %
--: squareFreePart: % -> %
--: squareFreePolynomial: SparseUnivariatePolynomial % -> Factored
SparseUnivariatePolynomial %
--: subtractIfCan: (%, %) -> Union(%, failed)
--: tableForDiscreteLogarithm: Integer -> Table(PositiveInteger,
NonNegativeInteger)
--: -- tableForDiscreteLogarithm(a, n) returns a table of the discrete
--: -- logarithms of a^0 up to a^(n-1) which, called with key
--: -- lookup(a^i) returns i for i in 0..n-1. Error: if not called for
--: -- prime divisors of order of multiplicative group.
--: unit?: % -> Boolean
--: unitCanonical: % -> %
--: unitNormal: % -> Record(unit: %, canonical: %, associate: %)
)if LiterateDoc
\bibliography{qeta}
\end{document}
)endif
)abbrev domain MODMON ModMonic
++ Description:
++ This package \undocumented
-- following line prevents caching ModMonic
)bo PUSH('ModMonic, $mutableDomains)
ModMonic(R, Rep) : C == T
where
R : Ring
Rep : UnivariatePolynomialCategory(R)
C == UnivariatePolynomialCategory(R) with
--operations
setPoly : Rep -> Rep
++ setPoly(x) \undocumented
modulus : -> Rep
++ modulus() \undocumented
reduce : Rep -> %
++ reduce(x) \undocumented
lift : % -> Rep --reduce lift = identity
++ lift(x) \undocumented
coerce : Rep -> %
++ coerce(x) \undocumented
Vectorise : % -> Vector(R)
++ Vectorise(x) \undocumented
UnVectorise : Vector(R) -> %
++ UnVectorise(v) \undocumented
An : % -> Vector(R)
++ An(x) \undocumented
pow : -> PrimitiveArray(%)
++ pow() \undocumented
computePowers : -> PrimitiveArray(%)
++ computePowers() \undocumented
if R has FiniteFieldCategory then
frobenius : % -> %
++ frobenius(x) computes \spad{x^q} where \spad{q} is
++ the size of \spad{R}
--LinearTransf: (%, Vector(R)) -> SquareMatrix<deg> R
--assertions
if R has Finite then Finite
T == add
--constants
m : Rep := monomial(1, 1)$Rep --| degree(m) > 0 and LeadingCoef(m) = R$1
d := degree(m)$Rep
d1 := (d-1)::NonNegativeInteger
twod := 2*d1+1
frobenius? : Boolean := R has FiniteFieldCategory
sz: NonNegativeInteger := size()$R -- to figure out when size() changed
--VectorRep := DirectProduct(d: NonNegativeInteger, R)
--declarations
x, y : %
p : Rep
d, n : Integer
e, k1, k2 : NonNegativeInteger
c : R
--vect: Vector(R)
power : PrimitiveArray(%)
frobeniusPower : PrimitiveArray(%)
computeFrobeniusPowers : () -> PrimitiveArray(%)
--representations
--mutable m --take this out??
--define
power := new(0, 0)
frobeniusPower := new(0, 0)
setPoly (mon : Rep) ==
oldsz := sz
sz := size()$R
sz=oldsz and mon =$Rep m => mon
oldm := m
leadingCoefficient mon ~= 1 => error "polynomial must be monic"
-- following copy code needed since FFPOLY can modify mon
copymon : Rep := 0
while not zero? mon repeat
copymon := monomial(leadingCoefficient mon, degree mon)$Rep + copymon
mon := reductum mon
m := copymon
d := degree(m)$Rep
d1 := (d-1)::NonNegativeInteger
twod := 2*d1+1
power := computePowers()
if frobenius? then
if sz = oldsz then
degree(oldm)>1 and not((oldm exquo$Rep m) case "failed") =>
for i in 1..d1 repeat
frobeniusPower(i) := reduce lift frobeniusPower(i)
frobeniusPower := computeFrobeniusPowers()
m
modulus == m
if R has Finite then
size == d * size$R
random == UnVectorise([random()$R for i in 0..d1])
0 == 0$Rep
1 == 1$Rep
c * x == c *$Rep x
n * x == (n::R) *$Rep x
coerce(c : R) : % == monomial(c, 0)$Rep
coerce(x : %) : OutputForm == coerce(x)$Rep
coefficient(x, e) : R == coefficient(x, e)$Rep
reductum(x) == reductum(x)$Rep
leadingCoefficient x == (leadingCoefficient x)$Rep
degree x == (degree x)$Rep
lift(x) == x pretend Rep
reduce(p) == (monicDivide(p, m)$Rep).remainder
coerce(p) == reduce(p)
x = y == x =$Rep y
x + y == x +$Rep y
- x == -$Rep x
x * y ==
p := x *$Rep y
ans := 0$Rep
while (n := degree p)>d1 repeat
ans := ans + leadingCoefficient(p)*power.(n-d)
p := reductum p
ans+p
Vectorise(x) == [coefficient(lift(x), i) for i in 0..d1]
UnVectorise(vect) ==
reduce(+/[monomial(vect.(i+1), i) for i in 0..d1])
computePowers ==
mat : PrimitiveArray(%) := new(d, 0)
mat.0 := reductum(-m)$Rep
w : % := monomial$Rep (1, 1)
for i in 1..d1 repeat
mat.i := w *$Rep mat.(i-1)
if degree mat.i = d then
mat.i := reductum mat.i + leadingCoefficient mat.i * mat.0
mat
if frobenius? then
computeFrobeniusPowers() ==
mat : PrimitiveArray(%) := new(d, 1)
mat.1 := mult := monomial(1, size$R)$%
for i in 2..d1 repeat
mat.i := mult * mat.(i-1)
mat
frobenius(a : %) : % ==
aq : % := 0
while a ~= 0 repeat
aq := aq + leadingCoefficient(a)*frobeniusPower(degree a)
a := reductum a
aq
pow == power
monomial(c, e)==
if e<d then monomial(c, e)$Rep
else
if e<=twod then
c * power.(e-d)
else
k1 := e quo twod
k2 := (e-k1*twod)::NonNegativeInteger
reduce((power.d1 ^k1)*monomial(c, k2))
if R has Field then
(x:% exquo y:%):Union(%, "failed") ==
uv := extendedEuclidean(y, modulus(), x)$Rep
uv case "failed" => "failed"
return reduce(uv.coef1)
recip(y:%):Union(%, "failed") == 1 exquo y
divide(x : %, y : %) ==
(q := (x exquo y)) case "failed" => error "not divisible"
[q, 0]
-- An(MM) == Vectorise(-(reduce(reductum(m))::MM))
-- LinearTransf(vect, MM) ==
-- ans := 0::SquareMatrix<d>(R)
-- for i in 1..d do setelt!(ans, i, 1, vect.i)
-- for j in 2..d do
-- setelt!(ans, 1, j, elt(ans, d, j-1) * An(MM).1)
-- for i in 2..d do
-- setelt!(ans, i, j, elt(ans, i-1, j-1) + elt(ans, d, j-1) *
An(MM).i)
-- ans
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
-- - Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
--
-- - Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in
-- the documentation and/or other materials provided with the
-- distribution.
--
-- - Neither the name of The Numerical ALgorithms Group Ltd. nor the
-- names of its contributors may be used to endorse or promote products
-- derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
)clear complete
)co src/algebra/modmon.spad
)co src/algebra/ffalgclos.spad
P ==> PositiveInteger
Z ==> Integer
V ==> IndexedVariable("f")
E ==> IndexedExponents V
R ==> NewSparseMultivariatePolynomial(K, V)
TS ==> RegularTriangularSet(Q, E, V, R)
SI ==> SingleInteger
maxi2 ==> shift(max()$SI, -1)::Z
p := qcoerce(prevPrime(maxi2)$IntegerPrimesPackage(Z))@P
--p := 53
K ==> PrimeField p
A ==> DynamicAlgebraicClosurePrimeField(p)
F ==> FiniteFieldExtensionByPolynomial(K, kx)
KZ ==> SUP K
AZ ==> SUP(A)
FZ ==> SUP(F)
az d ==> monomial(1, d)$AZ
kz d ==> monomial(1, d)$KZ
fz d ==> monomial(1, d)$FZ
kx := kz 2 + 18 * kz 1 + 6
ax := az 2 + 18 * az 1 + 6
fx := fz 2 + 18 * fz 1 + 6
rootOf(ax, 'v)
show()$A
-- factor fx -- use only when kx is irreducible over K
factor ax
ax2 := monomial(1,5)$AZ-3
factor ax2
irreducible? ax2
rts := rootsOf(ax2)
[x^5 for x in rts]
[ax2 x for x in rts]
irreducible? ax2
factor(ax2)
ax3 := monomial(1,3)$AZ-7
factor ax3
fl := factorList factor(ax3)
reduce(_*, [x.factor for x in fl])
a7: A := -7
show()$A
f1 := getGenerator(1)$A
ax3 := 9*f1 + 35
inv ax3
r := f1^(1/7)
r^7
r := f1^(1/9)
r^9
show()$A
(1) -> P ==> PositiveInteger
Type: Void
(2) -> Z ==> Integer
Type: Void
(3) -> V ==> IndexedVariable("f")
Type: Void
(4) -> E ==> IndexedExponents V
Type: Void
(5) -> R ==> NewSparseMultivariatePolynomial(K, V)
Type: Void
(6) -> TS ==> RegularTriangularSet(Q, E, V, R)
Type: Void
(7) -> SI ==> SingleInteger
Type: Void
(8) -> maxi2 ==> shift(max()$SI, -1)::Z
Type: Void
(9) -> p := qcoerce(prevPrime(maxi2)$IntegerPrimesPackage(Z))@P
(9) 2305843009213693921
Type: PositiveInteger
(10) -> --p := 53
(10) -> K ==> PrimeField p
Type: Void
(11) -> A ==> DynamicAlgebraicClosurePrimeField(p)
Type: Void
(12) -> F ==> FiniteFieldExtensionByPolynomial(K, kx)
Type: Void
(13) -> KZ ==> SUP K
Type: Void
(14) -> AZ ==> SUP(A)
Type: Void
(15) -> FZ ==> SUP(F)
Type: Void
(16) -> az d ==> monomial(1, d)$AZ
Type: Void
(17) -> kz d ==> monomial(1, d)$KZ
Type: Void
(18) -> fz d ==> monomial(1, d)$FZ
Type: Void
(19) -> kx := kz 2 + 18 * kz 1 + 6
2
(19) ? + 18 ? + 6
Type: SparseUnivariatePolynomial(PrimeField(2305843009213693921))
(20) -> ax := az 2 + 18 * az 1 + 6
2
(20) ? + 18 ? + 6
Type:
SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(21) -> fx := fz 2 + 18 * fz 1 + 6
2
(21) ? + 18 ? + 6
Type:
SparseUnivariatePolynomial(FiniteFieldExtensionByPolynomial(PrimeField(2305843009213693921),?^2+18*?+6))
(22) -> rootOf(ax, 'v)
(22) 2175765076733634914
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(23) -> show()$A
{}
[]
Type: Void
(24) -> -- factor fx -- use only when kx is irreducible over K
(24) -> factor ax
(24) (? + 130077932480059007)(? + 2175765076733634932)
Type:
Factored(SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921)))
(25) -> ax2 := monomial(1,5)$AZ-3
5
(25) ? + 2305843009213693918
Type:
SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(26) -> factor ax2
5
(26) ? + 2305843009213693918
Type:
Factored(SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921)))
(27) -> irreducible? ax2
(27) true
Type: Boolean
(28) -> rts := rootsOf(ax2)
(28)
[f1, 1916729304821096802 f1, 1074224509982630437 f1, 378638512013051338 f1,
1242093691610609264 f1]
Type: List(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(29) -> [x^5 for x in rts]
(29) [3, 3, 3, 3, 3]
Type: List(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(30) -> [ax2 x for x in rts]
(30) [0, 0, 0, 0, 0]
Type: List(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(31) -> irreducible? ax2
(31) false
Type: Boolean
(32) -> factor(ax2)
(32)
(? + 389113704392597119 f1)(? + 1063749317603084657 f1)
*
(? + 1231618499231063484 f1)(? + 1927204497200642583 f1)
*
(? + 2305843009213693920 f1)
Type:
Factored(SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921)))
(33) -> ax3 := monomial(1,3)$AZ-7
3
(33) ? + 2305843009213693914
Type:
SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(34) -> factor ax3
(34)
(? + 8387903196959538)(? + 431301322456251594)(? + 1866153783560482789)
Type:
Factored(SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921)))
(35) -> fl := factorList factor(ax3)
(35)
[[flag = "prime", factor = ? + 8387903196959538, exponent = 1],
[flag = "prime", factor = ? + 431301322456251594, exponent = 1],
[flag = "prime", factor = ? + 1866153783560482789, exponent = 1]]
Type: List(Record(flag: Union("nil","sqfr","irred","prime"),factor:
SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921)),exponent:
NonNegativeInteger))
(36) -> reduce(_*, [x.factor for x in fl])
3
(36) ? + 2305843009213693914
Type:
SparseUnivariatePolynomial(DynamicAlgebraicClosurePrimeField(2305843009213693921))
(37) -> a7: A := -7
(37) 2305843009213693914
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(38) -> show()$A
5
{f1 + 2305843009213693918}
[f1]
Type: Void
(39) -> f1 := getGenerator(1)$A
(39) f1
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(40) -> ax3 := 9*f1 + 35
(40) 9 f1 + 35
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(41) -> inv ax3
(41)
4 3 2
1906771754488978363 f1 + 14715095564764556 f1 + 967593743565335136 f1
+
848821460117751202 f1 + 1823123231128065150
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(42) -> r := f1^(1/7)
3
(42) 1208701501887878640 f1
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(43) -> r^7
(43) f1
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(44) -> r := f1^(1/9)
(44) f2
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(45) -> r^9
(45) f1
Type: DynamicAlgebraicClosurePrimeField(2305843009213693921)
(46) -> show()$A
5 9
{f1 + 2305843009213693918, f2 + 2305843009213693920 f1}
[f2, f1]
Type: Void
(47) ->