"""
Fast Cython code needed to compute Hilbert modular forms over F = Q(sqrt(5)).
All the code in this file is meant to be highly optimized.
"""
include 'stdsage.pxi'
include 'cdefs.pxi'
include 'python.pxi'
from sage.rings.ideal import is_Ideal
from sage.rings.all import Integers, ZZ, QQ
from sage.matrix.all import MatrixSpace, zero_matrix
from sage.rings.integer cimport Integer
from sqrt5 import F
cdef Integer temp1 = Integer(0)
cdef long SQRT_MAX_LONG = 2**(4*sizeof(long)-1)
def is_ideal_in_F(I):
return is_Ideal(I) and I.number_field() is F
cpdef long ideal_characteristic(I):
return I.number_field()._pari_().idealtwoelt(I._pari_())[0]
GLOBAL_VERBOSE=False
def global_verbose(s):
if GLOBAL_VERBOSE: print s
def ResidueRing(P, unsigned int e):
"""
Create the residue class ring O_F/P^e, where P is a prime ideal of the ring
O_F of integers of Q(sqrt(5)).
INPUT:
- P -- prime ideal
- e -- positive integer
OUTPUT:
- a fast residue class ring
"""
if e <= 0:
raise ValueError, "exponent e must be positive"
cdef long p = ideal_characteristic(P)
if p**e >= SQRT_MAX_LONG:
raise ValueError, "residue field must have size less than %s"%SQRT_MAX_LONG
if p == 5:
if e % 2 == 0:
R = ResidueRing_nonsplit(P, p, e/2)
else:
R = ResidueRing_ramified_odd(P, p, e)
elif p%5 in [2,3]:
R = ResidueRing_nonsplit(P, p, e)
else:
R = ResidueRing_split(P, p, e)
return R
class ResidueRingIterator:
def __init__(self, R):
self.R = R
self.i = 0
def __iter__(self):
return self
def next(self):
if self.i < self.R.cardinality():
self.i += 1
return self.R[self.i-1]
else:
raise StopIteration
cdef class ResidueRing_abstract(CommutativeRing):
def __init__(self, P, p, e):
"""
INPUT:
- P -- prime ideal of O_F
- e -- positive integer
"""
self.P = P
self.e = e
self.p = p
self.F = P.number_field()
def __richcmp__(ResidueRing_abstract left, right, int op):
if left is right:
return _rich_to_bool(op, 0)
if not isinstance(right, ResidueRing_abstract):
return _rich_to_bool(op, cmp(ResidueRing_abstract, type(right)))
cdef ResidueRing_abstract r = right
return _rich_to_bool(op,
cmp((left.p,left.e,left.P), (r.p,r.e,r.P)))
def __hash__(self):
return hash((self.p,self.e,self.P))
def residue_field(self):
if self._residue_field is None:
self._residue_field = self.P.residue_field()
return self._residue_field
cdef object element_to_residue_field(self, residue_element x):
raise NotImplementedError
def _moduli(self):
return self.n0, self.n1
def __call__(self, x):
cdef NumberFieldElement_quadratic y
cdef ResidueRingElement z
try:
y = x
z = self.new_element()
self.coerce_from_nf(z.x, y)
return z
except TypeError:
pass
if hasattr(x, 'parent'):
P = x.parent()
else:
P = None
if P is self:
return x
elif P is not self.F:
x = self.F(x)
return self(x)
cdef ResidueRingElement new_element(self):
raise NotImplementedError
cdef int coefficients(self, long* v0, long* v1, NumberFieldElement_quadratic x) except -1:
cdef long a, b, e, t, f, n
n = self.n0 if (self.n0 > self.n1) else self.n1
cdef int is_even = n%2==0
e = mpz_mod_ui(temp1.value, x.denom, n)
if e == 0 or (is_even and e%2==0):
if is_even:
a = mpz_mod_ui(temp1.value, x.a, 2*n)
b = mpz_mod_ui(temp1.value, x.b, 2*n)
f = mpz_mod_ui(temp1.value, x.denom, 2*n) / 2
if f == 0:
raise ZeroDivisionError, "x = %s"%x
else:
t = a - b
if t%2 != 0:
raise ZeroDivisionError, "x = %s"%x
else:
if t < 0:
t += 2*n
if f != 1:
f = invmod_long(f, n)
v0[0] = ((t/2) * f)%n
v1[0] = (b * f)%n
return 0
else:
raise ZeroDivisionError, "x = %s"%x
a = mpz_mod_ui(temp1.value, x.a, n)
b = mpz_mod_ui(temp1.value, x.b, n)
if e != 1:
e = invmod_long(e, n)
v0[0] = (a*e)%n - (b*e)%n
if v0[0] < 0:
v0[0] += n
v1[0] = (2*b*e)%n
return 0
cdef int coerce_from_nf(self, residue_element r, NumberFieldElement_quadratic x) except -1:
raise NotImplementedError
def __repr__(self):
return "Residue class ring of %s^%s of characteristic %s"%(
self.P._repr_short(), self.e, self.p)
def lift(self, x):
if x.parent() is self:
return x.lift()
raise TypeError
cdef void add(self, residue_element rop, residue_element op0, residue_element op1):
raise NotImplementedError
cdef void sub(self, residue_element rop, residue_element op0, residue_element op1):
raise NotImplementedError
cdef void mul(self, residue_element rop, residue_element op0, residue_element op1):
raise NotImplementedError
cdef int inv(self, residue_element rop, residue_element op) except -1:
raise NotImplementedError
cdef bint is_unit(self, residue_element op):
raise NotImplementedError
cdef void neg(self, residue_element rop, residue_element op):
raise NotImplementedError
cdef bint element_is_1(self, residue_element op):
return op[0] == 1 and op[1] == 0
cdef bint element_is_0(self, residue_element op):
return op[0] == 0 and op[1] == 0
cdef void set_element_to_1(self, residue_element op):
op[0] = 1
op[1] = 0
cdef void set_element_to_0(self, residue_element op):
op[0] = 0
op[1] = 0
cdef void set_element(self, residue_element rop, residue_element op):
rop[0] = op[0]
rop[1] = op[1]
cdef int set_element_from_tuple(self, residue_element rop, x) except -1:
rop[0] = x[0]%self.n0; rop[1] = x[1]%self.n1
return 0
cdef int cmp_element(self, residue_element left, residue_element right):
if left[0] < right[0]:
return -1
elif left[0] > right[0]:
return 1
elif left[1] < right[1]:
return -1
elif left[1] > right[1]:
return 1
else:
return 0
cdef int pow(self, residue_element rop, residue_element op, long e) except -1:
cdef residue_element op2
if e < 0:
self.inv(op2, op)
return self.pow(rop, op2, -e)
self.set_element_to_1(rop)
cdef residue_element z
self.set_element(z, op)
while e:
if e & 1:
self.mul(rop, rop, z)
e /= 2
if e:
self.mul(z, z, z)
cdef bint is_square(self, residue_element op) except -2:
raise NotImplementedError
cdef int sqrt(self, residue_element rop, residue_element op) except -1:
raise NotImplementedError
def __getitem__(self, i):
cdef ResidueRingElement z = PY_NEW(self.element_class)
z._parent = self
self.ith_element(z.x, i)
return z
def __iter__(self):
return ResidueRingIterator(self)
def is_finite(self):
return True
cdef int ith_element(self, residue_element rop, long i) except -1:
if i < 0 or i >= self.cardinality(): raise IndexError
self.unsafe_ith_element(rop, i)
cpdef long cardinality(self):
return self._cardinality
cdef void unsafe_ith_element(self, residue_element rop, long i):
pass
cdef int next_element(self, residue_element rop, residue_element op) except -1:
raise NotImplementedError
cdef bint is_last_element(self, residue_element op):
raise NotImplementedError
cdef long index_of_element(self, residue_element op) except -1:
raise NotImplementedError
cdef long index_of_element_in_P(self, residue_element op) except -1:
raise NotImplementedError
cdef int next_element_in_P(self, residue_element rop, residue_element op) except -1:
raise NotImplementedError
cdef bint is_last_element_in_P(self, residue_element op):
raise NotImplementedError
cdef element_to_str(self, residue_element op):
cdef ResidueRingElement z = PY_NEW(self.element_class)
z._parent = self
z.x[0] = op[0]
z.x[1] = op[1]
return str(z)
cdef class ResidueRing_split(ResidueRing_abstract):
cdef ResidueRingElement new_element(self):
cdef ResidueRingElement_split z = PY_NEW(ResidueRingElement_split)
z._parent = self
return z
def __init__(self, P, p, e):
ResidueRing_abstract.__init__(self, P, p, e)
self.element_class = ResidueRingElement_split
self.n0 = p**e
self.n1 = 1
self._cardinality = self.n0
alpha = P.number_field().gen()
cdef long z, z0, z1
z = sqrtmod_long(5, p, e)
z0 = divmod_long(1+z, 2, p**e)
if alpha - z0 in P:
self.im_gen0 = z0
else:
z1 = divmod_long(1-z, 2, p**e)
if alpha - z1 in P:
self.im_gen0 = z1
else:
raise RuntimeError, "bug -- maybe F is misdefined to have wrong gen"
cdef int coerce_from_nf(self, residue_element r, NumberFieldElement_quadratic x) except -1:
r[1] = 0
cdef long v0, v1
self.coefficients(&v0, &v1, x)
r[0] = v0 + (self.im_gen0*v1)%self.n0
if r[0] >= self.n0:
r[0] -= self.n0
return 0
def __reduce__(self):
return ResidueRing_split, (self.P, self.p, self.e)
cdef object element_to_residue_field(self, residue_element x):
return self.residue_field()(x[0])
cdef void add(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] + op1[0])%self.n0
rop[1] = 0
cdef void sub(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] - op1[0])%self.n0
rop[1] = 0
cdef void mul(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] * op1[0])%self.n0
rop[1] = 0
cdef int inv(self, residue_element rop, residue_element op) except -1:
rop[0] = invmod_long(op[0], self.n0)
rop[1] = 0
return 0
cdef bint is_unit(self, residue_element op):
return gcd_long(op[0], self.n0) == 1
cdef void neg(self, residue_element rop, residue_element op):
rop[0] = self.n0 - op[0] if op[0] else 0
rop[1] = 0
cdef bint is_square(self, residue_element op) except -2:
cdef residue_element rop
if op[0] % self.p == 0:
try:
self.sqrt(rop, op)
except ValueError:
return False
return True
return kronecker_symbol_long(op[0], self.p) != -1
cdef int sqrt(self, residue_element rop, residue_element op) except -1:
rop[0] = sqrtmod_long(op[0], self.p, self.e)
rop[1] = 0
if (rop[0] * rop[0])%self.n0 != op[0]:
raise ValueError, "element must be a square"
return 0
cdef void unsafe_ith_element(self, residue_element rop, long i):
rop[0] = i
rop[1] = 0
cdef int next_element(self, residue_element rop, residue_element op) except -1:
rop[0] = op[0] + 1
rop[1] = 0
if rop[0] >= self.n0:
raise ValueError, "no next element"
cdef bint is_last_element(self, residue_element op):
return op[0] == self.n0 - 1
cdef long index_of_element(self, residue_element op) except -1:
return op[0]
cdef int next_element_in_P(self, residue_element rop, residue_element op) except -1:
rop[0] = op[0] + self.p
rop[1] = 0
if rop[0] >= self.n0:
raise ValueError, "no next element in P"
return 0
cdef bint is_last_element_in_P(self, residue_element op):
return op[0] == self.n0 - self.p
cdef long index_of_element_in_P(self, residue_element op) except -1:
return op[0]/self.p
cdef class ResidueRing_nonsplit(ResidueRing_abstract):
cdef ResidueRingElement new_element(self):
cdef ResidueRingElement_nonsplit z = PY_NEW(ResidueRingElement_nonsplit)
z._parent = self
return z
def __init__(self, P, p, e):
ResidueRing_abstract.__init__(self, P, p, e)
self.element_class = ResidueRingElement_nonsplit
self.n0 = p**e
self.n1 = p**e
self._cardinality = self.n0 * self.n1
cdef int coerce_from_nf(self, residue_element r, NumberFieldElement_quadratic x) except -1:
self.coefficients(&r[0], &r[1], x)
def __reduce__(self):
return ResidueRing_nonsplit, (self.P, self.p, self.e)
cdef object element_to_residue_field(self, residue_element x):
k = self.residue_field()
if self.p != 5:
return k([x[0], x[1]])
return k(x[0] + 3*x[1])
cdef void add(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] + op1[0])%self.n0
rop[1] = (op0[1] + op1[1])%self.n1
cdef void sub(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] - op1[0])%self.n0
rop[1] = (op0[1] - op1[1])%self.n1
cdef void mul(self, residue_element rop, residue_element op0, residue_element op1):
cdef long a = op0[0], b = op0[1], c = op1[0], d = op1[1]
rop[0] = (a*c + b*d)%self.n0
rop[1] = (a*d + b*d + b*c)%self.n1
cdef int inv(self, residue_element rop, residue_element op) except -1:
cdef long a = op[0], b = op[1], w
w = invmod_long(a*a + a*b - b*b, self.n0)
rop[0] = (w*(a+b)) % self.n0
rop[1] = (-b*w) % self.n0
return 0
cdef bint is_unit(self, residue_element op):
cdef long a = op[0], b = op[1], w
return gcd_long(a*a + a*b - b*b, self.n0) == 1
cdef void neg(self, residue_element rop, residue_element op):
rop[0] = self.n0 - op[0] if op[0] else 0
rop[1] = self.n1 - op[1] if op[1] else 0
cdef bint is_square(self, residue_element op) except -2:
"""
Return True only if op is a perfect square.
ALGORITHM:
We view the residue ring as R[x]/(p^e, x^2-x-1).
We first compute the power of p that divides our
element a+bx, which is just v=min(ord_p(a),ord_p(b)).
If v=infinity, i.e., a+bx=0, then it's a square.
If this power is odd, then a+bx can't be a square.
Otherwise, it is even, and we next look at the unit
part of a+bx = p^v*unit. Then a+bx is a square if and
only if the unit part is a square modulo p^(e-v) >= p,
since a+bx!=0.
When p>2, the unit part is a square if and only if it is a
square modulo p, because of Hensel's lemma, and we can check
whether or not something is a square modulo p quickly by
raising to the power (p^2-1)/2, since
"modulo p", means in the ring O_F/p = GF(p^2), since p
is an inert prime (this is why we can't use the Legendre
symbol).
When p=2, it gets complicated to decide if the unit part is a
square using code, so just call the sqrt function and catch
the exception.
"""
cdef residue_element unit, z
cdef int v = 0
cdef long p = self.p
if op[0] == 0 and op[1] == 0:
return True
if p == 5:
try:
self.sqrt(z, op)
except ValueError:
return False
return True
unit[0] = op[0]; unit[1] = op[1]
while unit[0]%p ==0 and unit[1]%p==0:
unit[0] = unit[0]/p; unit[1] = unit[1]/p
v += 1
if v%2 != 0:
return False
if p == 2:
if self.e == 1:
return True
try:
self.sqrt(z, op)
except ValueError:
return False
return True
self.pow(z, unit, (self.p*self.p-1)/2)
return z[0]%p == 1
cdef int sqrt(self, residue_element rop, residue_element op) except -1:
"""
Set rop to a square root of op, if one exists.
ALGORITHM:
We view the residue ring as R[x]/(p^e, x^2-x-1), and want to compute
a square root a+bx of c+dx. We have (a+bx)^2=c+dx, so
a^2+b^2=c and 2ab+b^2=d.
Setting B=b^2 and doing algebra, we get
5*B^2 - (4c+2d)B + d^2 = 0,
so B = ((4c+2d) +/- sqrt((4c+2d)^2-20d^2))/10.
In characteristic 2 or 5, we compute the numerator mod p^(e+1), then
divide out by p first, then 10/p.
"""
if op[0] == 0 and op[1] == 0:
rop[0] = 0; rop[1] = 0
return 0
cdef long a, b
cdef residue_element t
for a in range(self.n0):
rop[0] = a
for b in range(self.n1):
rop[1] = b
self.mul(t, rop, rop)
if t[0] == op[0] and t[1] == op[1]:
return 0
raise ValueError, "not a square"
cdef void unsafe_ith_element(self, residue_element rop, long i):
rop[0] = i%self.n0
rop[1] = (i - rop[0])/self.n0
cdef int next_element(self, residue_element rop, residue_element op) except -1:
rop[0] = op[0] + 1
rop[1] = op[1]
if rop[0] == self.n0:
rop[0] = 0
rop[1] += 1
if rop[1] >= self.n1:
raise ValueError, "no next element"
cdef bint is_last_element(self, residue_element op):
return op[0] == self.n0 - 1 and op[1] == self.n1 - 1
cdef long index_of_element(self, residue_element op) except -1:
return op[0] + op[1]*self.n0
cdef int next_element_in_P(self, residue_element rop, residue_element op) except -1:
"""
For p = 5, we use the following enumeration, for R=O_F/P^(2e). First, note that we
represent R as
R = (Z/5^e)[x]/(x^2-x-1)
The maximal ideal is the kernel of the natural map to Z/5Z sending x to 3, i.e., it
has kernel the principal ideal (x+2).
By considering (a+bx)(x+2) = 2a+b + (a+3b)x, we see that the maximal ideal is
{ a + (3a+5b)x : a in Z/5^eZ, b in Z/5^(e-1)Z }.
We enumerate this by the standard dictionary ordered enumeration
on (Z/5^eZ) x (Z/5^(e-1)Z).
"""
if self.p == 5:
rop[0] = (op[0] + 1)%self.n0
rop[1] = (op[1] + 3)%self.n1
if rop[0] == 0:
if (rop[1]+5) % self.n1 == 0:
raise ValueError, "no next element in P"
rop[0] = 0
rop[1] = (rop[1] + 5)%self.n1
return 0
rop[0] = op[0] + self.p
rop[1] = op[1]
if rop[0] >= self.n0:
rop[0] = 0
rop[1] += self.p
if rop[1] >= self.n1:
raise ValueError, "no next element in P"
return 0
cdef bint is_last_element_in_P(self, residue_element op):
if self.p == 5:
if self.e == 1:
return op[0] == self.n0 - 1
else:
return op[0] == self.n0 - 1 and (op[1]+8)%self.n1 == 0
return op[0] == self.n0 - self.p and op[1] == self.n1 - self.p
cdef long index_of_element_in_P(self, residue_element op) except -1:
if self.p == 5:
return op[0] + ((op[1] - 3*op[0])/5 % (self.n1/self.p)) * self.n0
return op[0]/self.p + op[1]/self.p * (self.n0/self.p)
cdef class ResidueRing_ramified_odd(ResidueRing_abstract):
cdef ResidueRingElement new_element(self):
cdef ResidueRingElement_ramified_odd z = PY_NEW(ResidueRingElement_ramified_odd)
z._parent = self
return z
cdef long two_inv
def __init__(self, P, p, e):
ResidueRing_abstract.__init__(self, P, p, e)
assert self.F.defining_polynomial().list() == [-1,-1,1]
self.n0 = p**(e//2 + 1)
self.n1 = p**(e//2)
self._cardinality = self.n0 * self.n1
self.two_inv = (Integers(self.n0)(2)**(-1)).lift()
self.element_class = ResidueRingElement_ramified_odd
cdef int coerce_from_nf(self, residue_element r, NumberFieldElement_quadratic x) except -1:
cdef long v0, v1
self.coefficients(&v0, &v1, x)
if v0 == 0 and v1 == 0:
r[0] = 0; r[1] = 0
elif v1 == 0:
r[0] = v0; r[1] = 0
else:
r[0] = (v0 + v1*self.two_inv) % self.n0
r[1] = (v1*self.two_inv) % self.n1
return 0
def __reduce__(self):
return ResidueRing_ramified_odd, (self.P, self.p, self.e)
cdef object element_to_residue_field(self, residue_element x):
k = self.residue_field()
return k(x[0])
cdef void add(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] + op1[0])%self.n0
rop[1] = (op0[1] + op1[1])%self.n1
cdef void sub(self, residue_element rop, residue_element op0, residue_element op1):
rop[0] = (op0[0] - op1[0])%self.n0
rop[1] = (op0[1] - op1[1])%self.n1
cdef void mul(self, residue_element rop, residue_element op0, residue_element op1):
cdef long a = op0[0], b = op0[1], c = op1[0], d = op1[1]
rop[0] = (a*c + 5*b*d)%self.n0
rop[1] = (a*d + b*c)%self.n1
cdef void neg(self, residue_element rop, residue_element op):
rop[0] = self.n0 - op[0] if op[0] else 0
rop[1] = self.n1 - op[1] if op[1] else 0
cdef int inv(self, residue_element rop, residue_element op) except -1:
"""
We derive by algebra the inverse of an element of the quotient ring.
We represent the ring as:
Z[x]/(x^2-5, x^e) = {a+b*x with 0<=a<p^((e/2)+1), 0<=b<p^(e/2)}
Assume a+b*x is a unit, so a!=0(mod 5). Let c+d*x be inverse of a+b*x.
We have (a+b*x)*(c+d*x)=1, so ac+5db + (ad+bc)*x = 1. Thus
ac+5bd=1 (mod p^((e/2)+1)) and ad+bc=0 (mod p^(e/2)).
Doing algebra (multiply both sides by a^(-1) of first, and subtract
second equation), we arrive at
d = (a^(-1)*b)*(5*a^(-1)*b^2 - a)^(-1) (mod p^(e/2))
c = a^(-1)*(1-5*b*d) (mod p^(e/2+1)).
Notice that the first equation determines d only modulo p^(e/2), and
the second only requires d modulo p^(e/2)!
"""
cdef long a = op[0], b = op[1], ainv = invmod_long(a, self.n0), n0=self.n0, n1=self.n1
rop[1] = divmod_long(mulmod_long(ainv,b,n1), submod_long(mulmod_long(mulmod_long(mulmod_long(self.p,ainv,n1),b,n1),b,n1), a, n1), n1)
rop[0] = mulmod_long(ainv, submod_long(1,mulmod_long(5,mulmod_long(b,rop[1],n0),n0),n0),n0)
return 0
cdef bint is_unit(self, residue_element op):
"""
Return True if the element op=(a,b) is a unit.
We represent the ring as
Z[x]/(x^2-5, x^e) = {a+b*x with 0<=a<p^((e/2)+1), 0<=b<p^(e/2)}
An element is in the maximal ideal (x) if and only if it is of the form:
(a+b*x)*x = a*x+b*x*x = a*x + 5*b.
Since a is arbitrary, this means the maximal ideal is the set of
elements op=(a,b) with a%5==0, so the units are the elements
with a%5!=0.
"""
return op[0]%self.p != 0
cdef bint is_square(self, residue_element op) except -2:
cdef residue_element rop
try:
self.sqrt(rop, op)
except:
return False
return True
cdef int sqrt(self, residue_element rop, residue_element op) except -1:
"""
Algorithm: Given c+dx in Z[x]/(x^2-5,x^e), we seek a+bx with
(a+bx)^2=a^2+5b^2 + 2abx = c+dx,
so a^2+5b^2 = c (mod p^n0)
2ab = d (mod p^n1)
Multiply first equation through by A=a^2, to get quadratic in A:
A^2 - c*A + 5d^2/4 = 0 (mod n0).
Solve for A = (c +/- sqrt(c^2-5d^2))/2.
Thus the algorithm is:
1. Extract sqrt s of c^2-5d^2 modulo n0. If no sqrt, then not square
2. Extract sqrt a of (c+s)/2 or (c-s)/2. Both are squares.
3. Let b=d/(2*a) for each choice of a. Square each and see which works.
"""
cdef long a, b
cdef residue_element t
for a in range(self.n0):
rop[0] = a
for b in range(self.n1):
rop[1] = b
self.mul(t, rop, rop)
if t[0] == op[0] and t[1] == op[1]:
return 0
raise ValueError, "not a square"
cdef void unsafe_ith_element(self, residue_element rop, long i):
rop[0] = i%self.n0
rop[1] = (i - rop[0])/self.n0
cdef int next_element(self, residue_element rop, residue_element op) except -1:
rop[0] = op[0] + 1
rop[1] = op[1]
if rop[0] == self.n0:
rop[0] = 0
rop[1] += 1
if rop[1] >= self.n1:
raise ValueError, "no next element"
cdef bint is_last_element(self, residue_element op):
return op[0] == self.n0 - 1 and op[1] == self.n1 - 1
cdef long index_of_element(self, residue_element op) except -1:
return op[0] + op[1]*self.n0
cdef int next_element_in_P(self, residue_element rop, residue_element op) except -1:
rop[0] = op[0] + self.p
if rop[0] == self.n0:
rop[0] = 0
rop[1] += 1
if rop[1] >= self.n1:
raise ValueError, "no next element"
cdef bint is_last_element_in_P(self, residue_element op):
return op[0] == self.n0 - self.p and op[1] == self.n1 - 1
cdef long index_of_element_in_P(self, residue_element op) except -1:
return op[0]/self.p + op[1] * (self.n0/self.p)
cdef inline bint _rich_to_bool(int op, int r):
if op == Py_LT:
return (r < 0)
elif op == Py_EQ:
return (r == 0)
elif op == Py_GT:
return (r > 0)
elif op == Py_LE:
return (r <= 0)
elif op == Py_NE:
return (r != 0)
elif op == Py_GE:
return (r >= 0)
cdef class ResidueRingElement:
cpdef parent(self):
return self._parent
cdef new_element(self):
raise NotImplementedError
cpdef long index(self):
"""
Return the index of this element in the enumeration of
elements of the parent.
"""
return self._parent.index_of_element(self.x)
def __add__(ResidueRingElement left, ResidueRingElement right):
cdef ResidueRingElement z = left.new_element()
left._parent.add(z.x, left.x, right.x)
return z
def __sub__(ResidueRingElement left, ResidueRingElement right):
cdef ResidueRingElement z = left.new_element()
left._parent.sub(z.x, left.x, right.x)
return z
def __mul__(ResidueRingElement left, ResidueRingElement right):
cdef ResidueRingElement z = left.new_element()
left._parent.mul(z.x, left.x, right.x)
return z
def __div__(ResidueRingElement left, ResidueRingElement right):
cdef ResidueRingElement z = left.new_element()
left._parent.inv(z.x, right.x)
left._parent.mul(z.x, left.x, z.x)
return z
def __neg__(ResidueRingElement self):
cdef ResidueRingElement z = self.new_element()
self._parent.neg(z.x, self.x)
return z
def __pow__(ResidueRingElement self, e, m):
cdef ResidueRingElement z = self.new_element()
self._parent.pow(z.x, self.x, e)
return z
def __invert__(ResidueRingElement self):
cdef ResidueRingElement z = self.new_element()
self._parent.inv(z.x, self.x)
return z
def __richcmp__(ResidueRingElement left, ResidueRingElement right, int op):
cdef int c
if left.x[0] < right.x[0]:
c = -1
elif left.x[0] > right.x[0]:
c = 1
elif left.x[1] < right.x[1]:
c = -1
elif left.x[1] > right.x[1]:
c = 1
else:
c = 0
return _rich_to_bool(op, c)
def __hash__(self):
return self.x[0] + self._parent.n0*self.x[1]
cpdef bint is_unit(self):
return self._parent.is_unit(self.x)
cpdef bint is_square(self):
return self._parent.is_square(self.x)
cpdef sqrt(self):
cdef ResidueRingElement z = self.new_element()
self._parent.sqrt(z.x, self.x)
return z
cdef class ResidueRingElement_split(ResidueRingElement):
def __init__(self, ResidueRing_split parent, x):
self._parent = parent
assert x.parent() is parent.F
v = x._coefficients()
self.x[1] = 0
if len(v) == 0:
self.x[0] = 0
return
elif len(v) == 1:
self.x[0] = v[0] % self._parent.n0
return
self.x[0] = v[0] + parent.im_gen0*v[1]
self.x[0] = self.x[0] % self._parent.n0
self.x[1] = 0
cdef new_element(self):
cdef ResidueRingElement_split z = PY_NEW(ResidueRingElement_split)
z._parent = self._parent
return z
def __repr__(self):
return str(self.x[0])
def lift(self):
"""
Return lift of element to number field F.
"""
return self._parent.F(self.x[0])
cdef class ResidueRingElement_nonsplit(ResidueRingElement):
"""
EXAMPLES::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import ResidueRing
Inert example::
sage: F.<a> = NumberField(x^2 - x - 1)
sage: P_inert = F.primes_above(3)[0]
sage: R_inert = ResidueRing(P_inert, 2)
sage: s = R_inert(F.0); s
g
sage: s + s + s
3*g
sage: s*s*s*s
2 + 3*g
sage: R_inert(F.0^4)
2 + 3*g
Ramified example (with even exponent)::
sage: P = F.primes_above(5)[0]
sage: R = ResidueRing(P, 2)
sage: s = R(F.0); s
g
sage: s*s*s*s*s
3
sage: R(F.0^5)
3
sage: a = (s+s - R(F(1))); a*a
0
sage: R = ResidueRing(P, 3)
sage: t = R(2*F.0-1); t # reduction of sqrt(5)
s
sage: t*t*t
0
"""
def __init__(self, ResidueRing_nonsplit parent, x):
self._parent = parent
assert x.parent() is parent.F
v = x._coefficients()
if len(v) == 0:
self.x[0] = 0; self.x[1] = 0
return
elif len(v) == 1:
self.x[0] = v[0]
self.x[1] = 0
else:
self.x[0] = v[0]
self.x[1] = v[1]
self.x[0] = self.x[0] % self._parent.n0
self.x[1] = self.x[1] % self._parent.n1
cdef new_element(self):
cdef ResidueRingElement_nonsplit z = PY_NEW(ResidueRingElement_nonsplit)
z._parent = self._parent
return z
def __repr__(self):
if self.x[0]:
if self.x[1]:
if self.x[1] == 1:
return '%s + g'%self.x[0]
else:
return '%s + %s*g'%(self.x[0], self.x[1])
return str(self.x[0])
else:
if self.x[1]:
if self.x[1] == 1:
return 'g'
else:
return '%s*g'%self.x[1]
return '0'
def lift(self):
"""
Return lift of element to number field F.
"""
return self._parent.F([self.x[0], self.x[1]])
cdef class ResidueRingElement_ramified_odd(ResidueRingElement):
"""
Element of residue class ring R = O_F / P^(2f-1), where e=2f-1 is
odd, and P=sqrt(5)O_F is the ramified prime.
Computing with this ring is trickier than all the rest,
since it's not a quotient of Z[x] of the form Z[x]/(m,g),
where m is an integer and g is a polynomial.
The ring R is the quotient of
O_F/P^(2f) = O_F/5^f = (Z/5^fZ)[x]/(x^2-x-1),
by the ideal x^e. We have
O_F/P^(2f-2) subset R subset O_F/P^(2f)
and each successive quotient has order 5 = #(O_F/P).
Thus R has cardinality 5^(2f-1) and characteristic 5^f.
The ring R can't be a quotient of Z[x] of the
form Z[x]/(m,g), since such a quotient has
cardinality m^deg(g) and characteristic m, and
5^(2f-1) is not a power of 5^f.
We thus view R as
R = (Z/5^fZ)[x] / (x^2 - 5, 5^(f-1)*x).
The elements of R are pairs (a,b) in (Z/5^fZ) x (Z/5^(f-1)Z),
which correspond to the class of a + b*x. The arithmetic laws
are thus:
(a,b) + (c,d) = (a+c mod 5^f, b+d mod 5^(f-1))
and
(a,b) * (c,d) = (a*c+b*d*5 mod 5^f, a*d+b*c mod 5^(f-1))
The element omega = F.gen(), which is (1+sqrt(5))/2 maps to
(1+x)/2 = (1/2, 1/2), which is the generator of this ring.
"""
def __init__(self, ResidueRing_ramified_odd parent, x):
self._parent = parent
assert x.parent() is parent.F
cdef long a, b
v = x._coefficients()
if len(v) == 0:
self.x[0] = 0; self.x[1] = 0
return
if len(v) == 1:
self.x[0] = v[0]
self.x[1] = 0
else:
a = v[0]; b = v[1]
self.x[0] = a + b*parent.two_inv
self.x[1] = b*parent.two_inv
self.x[0] = self.x[0] % self._parent.n0
self.x[1] = self.x[1] % self._parent.n1
cdef new_element(self):
cdef ResidueRingElement_ramified_odd z = PY_NEW(ResidueRingElement_ramified_odd)
z._parent = self._parent
return z
def __repr__(self):
if self.x[0]:
if self.x[1]:
if self.x[1] == 1:
return '%s + s'%self.x[0]
else:
return '%s + %s*s'%(self.x[0], self.x[1])
return str(self.x[0])
else:
if self.x[1]:
if self.x[1] == 1:
return 's'
else:
return '%s*s'%self.x[1]
return '0'
cdef mpz_t mpz_temp
mpz_init(mpz_temp)
cdef long kronecker_symbol_long(long a, long b):
mpz_set_si(mpz_temp, b)
return mpz_si_kronecker(a, mpz_temp)
cdef inline long mulmod_long(long x, long y, long m):
return (x*y)%m
cdef inline long submod_long(long x, long y, long m):
return (x-y)%m
cdef inline long addmod_long(long x, long y, long m):
return (x+y)%m
cdef long powmod_long(long x, long e, long m):
cdef long y = 1, z = x
while e:
if e & 1:
y = (y * z) % m
e /= 2
if e: z = (z * z) % m
return y
from sage.rings.fast_arith cimport arith_llong
cdef arith_llong Arith_llong = arith_llong()
cdef long invmod_long(long x, long m) except -1:
cdef long long g, s, t
g = Arith_llong.c_xgcd_longlong(x, m, &s, &t)
if g != 1:
raise ZeroDivisionError
return s%m
cdef long gcd_long(long a, long b) except -1:
return Arith_llong.c_gcd_longlong(a, b)
cdef long divmod_long(long x, long y, long m) except -1:
return (x * invmod_long(y, m)) % m
cpdef long sqrtmod_long(long x, long p, int n) except -1:
cdef long a, r, y, z
if n <= 0:
raise ValueError, "n must be positive"
if x == 0 or x == 1:
return x
if p == 2:
if n == 1:
return x
elif n == 2:
raise ValueError, "not a square"
elif n == 3:
if x == 4: return 2
raise ValueError, "not a square"
elif n == 4:
if x == 4: return 2
if x == 9: return 3
raise ValueError, "not a square"
else:
try:
from sage.libs.pari.all import pari, PariError
cmd = "lift(sqrt(%s+O(2^%s)))%%(2^%s)"%(x, 2*n, n)
return int(pari(cmd))
except PariError:
raise ValueError, "not a square"
if x%p == 0:
a = x/p
y = 1
r = 1
while r < n and a%p == 0:
a /= p
r += 1
if r%2 == 0:
y *= p
if r % 2 != 0:
raise ValueError, "not a square"
return y * sqrtmod_long(a, p, n - r//2)
if p % 4 == 3:
y = powmod_long(x, (p+1)/4, p)
elif p == 5:
z = x % p
if z == 0 or z == 1:
y = x
elif z == 2 or z == 3:
raise ValueError
elif z == 4:
y = 2
else:
assert False, 'bug'
else:
from sage.all import pari
y = pari(x).Mod(p).sqrt().lift()
if n == 1:
return y
cdef int m
cdef long t, pm, ppm
pm = p
for m in range(1, n):
ppm = p*pm
t = divmod_long( ((x - y*y)%ppm) / pm, (2*y)%ppm, ppm)
y += (pm * t) % ppm
pm = ppm
return y
cdef enum:
MAX_PRIME_DIVISORS = 16
ctypedef residue_element modn_element[MAX_PRIME_DIVISORS]
ctypedef modn_element modn_matrix[4]
cdef class ResidueRingModN:
cdef public list residue_rings
cdef object N
cdef int r
def __init__(self, N):
self.N = N
v = [(P.smallest_integer(), P.gens_reduced()[0], e, ResidueRing(P, e)) for P, e in N.factor()]
v.sort()
self.residue_rings = [x[-1] for x in v]
self.r = len(self.residue_rings)
def __repr__(self):
return "Residue class ring modulo the ideal %s of norm %s"%(
self.N._repr_short(), self.N.norm())
cdef int set(self, modn_element rop, x) except -1:
self.coerce_from_nf(rop, self.N.number_field()(x), 0)
cdef int coerce_from_nf(self, modn_element rop, NumberFieldElement_quadratic op, int odd_only) except -1:
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
if odd_only and R.p == 2:
continue
R.coerce_from_nf(rop[i], op)
return 0
cdef void set_element(self, modn_element rop, modn_element op):
cdef int i
for i in range(self.r):
rop[i][0] = op[i][0]
rop[i][1] = op[i][1]
cdef void set_element_omitting_ith_factor(self, modn_element rop, modn_element op, int i):
cdef int j
for j in range(i):
rop[j][0] = op[j][0]
rop[j][1] = op[j][1]
for j in range(i+1, self.r):
rop[j-1][0] = op[j][0]
rop[j-1][1] = op[j][1]
cdef int set_element_reducing_exponent_of_ith_factor(self, modn_element rop, modn_element op, int i) except -1:
cdef ResidueRing_abstract R
cdef int j, e
cdef long p, temp
R = self.residue_rings[i]
e = R.e
p = R.p
for j in range(i):
rop[j][0] = op[j][0]
rop[j][1] = op[j][1]
if (e == 1 and p!=5) or (p == 5 and e == 1 and isinstance(R, ResidueRing_ramified_odd)):
for j in range(i+1, self.r):
rop[j-1][0] = op[j][0]
rop[j-1][1] = op[j][1]
elif p != 5:
rop[i][0] = op[i][0] % (R.n0//p)
if R.n1 > 1:
rop[i][1] = op[i][1] % (R.n1//p)
else:
rop[i][1] = 0
for j in range(i+1, self.r):
rop[j][0] = op[j][0]
rop[j][1] = op[j][1]
else:
assert p == 5
if isinstance(R, ResidueRing_ramified_odd):
rop[i][0] = op[i][0]-op[i][1]
rop[i][1] = 2*op[i][1]
else:
temp = divmod_long(op[i][1], 2, R.n1)
rop[i][0] = (op[i][0] + temp)%R.n1
rop[i][1] = temp
for j in range(i+1, self.r):
rop[j][0] = op[j][0]
rop[j][1] = op[j][1]
return 0
cdef element_to_str(self, modn_element op):
s = ','.join([(<ResidueRing_abstract>self.residue_rings[i]).element_to_str(op[i]) for
i in range(self.r)])
if self.r > 1:
s = '(' + s + ')'
return s
cdef int cmp_element(self, modn_element op0, modn_element op1):
cdef int i, c
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
c = R.cmp_element(op0[i], op1[i])
if c: return c
return 0
cdef void mul(self, modn_element rop, modn_element op0, modn_element op1):
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.mul(rop[i], op0[i], op1[i])
cdef int inv(self, modn_element rop, modn_element op) except -1:
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.inv(rop[i], op[i])
return 0
cdef int neg(self, modn_element rop, modn_element op) except -1:
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.neg(rop[i], op[i])
return 0
cdef void add(self, modn_element rop, modn_element op0, modn_element op1):
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.add(rop[i], op0[i], op1[i])
cdef void sub(self, modn_element rop, modn_element op0, modn_element op1):
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.sub(rop[i], op0[i], op1[i])
cdef bint is_last_element(self, modn_element x):
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
if not R.is_last_element(x[i]):
return False
return True
cdef int next_element(self, modn_element rop, modn_element op) except -1:
cdef int i, done = 0
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
if done:
R.set_element(rop[i], op[i])
else:
if R.is_last_element(op[i]):
R.set_element_to_0(rop[i])
else:
R.next_element(rop[i], op[i])
done = True
cdef bint is_square(self, modn_element op):
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
if not R.is_square(op[i]):
return False
return True
cdef int sqrt(self, modn_element rop, modn_element op) except -1:
cdef int i
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.residue_rings[i]
R.sqrt(rop[i], op[i])
return 0
cdef matrix_to_str(self, modn_matrix A):
return '[%s, %s; %s, %s]'%tuple([self.element_to_str(A[i]) for i in range(4)])
cdef bint matrix_is_invertible(self, modn_matrix x):
cdef modn_element t1, t2
self.mul(t1, x[0], x[3])
self.mul(t2, x[1], x[2])
return self.cmp_element(t1, t2) != 0
cdef int matrix_inv(self, modn_matrix rop, modn_matrix x) except -1:
cdef modn_element d, t1, t2
self.mul(t1, x[0], x[3])
self.mul(t2, x[1], x[2])
self.sub(d, t1, t2)
self.inv(d, d)
self.set_element(rop[0], x[3])
self.set_element(rop[3], x[0])
self.neg(t1, x[1])
self.neg(t2, x[2])
self.set_element(rop[1], t1)
self.set_element(rop[2], t2)
cdef int i
for i in range(4):
self.mul(rop[i], rop[i], d)
return 0
cdef matrix_mul(self, modn_matrix rop, modn_matrix x, modn_matrix y):
cdef modn_element t, t2
self.mul(t, x[0], y[0])
self.mul(t2, x[1], y[2])
self.add(rop[0], t, t2)
self.mul(t, x[0], y[1])
self.mul(t2, x[1], y[3])
self.add(rop[1], t, t2)
self.mul(t, x[2], y[0])
self.mul(t2, x[3], y[2])
self.add(rop[2], t, t2)
self.mul(t, x[2], y[1])
self.mul(t2, x[3], y[3])
self.add(rop[3], t, t2)
cdef matrix_mul_ith_factor(self, modn_matrix rop, modn_matrix x, modn_matrix y, int i):
cdef ResidueRing_abstract R = self.residue_rings[i]
cdef residue_element t, t2
R.mul(t, x[0][i], y[0][i])
R.mul(t2, x[1][i], y[2][i])
R.add(rop[0][i], t, t2)
R.mul(t, x[0][i], y[1][i])
R.mul(t2, x[1][i], y[3][i])
R.add(rop[1][i], t, t2)
R.mul(t, x[2][i], y[0][i])
R.mul(t2, x[3][i], y[2][i])
R.add(rop[2][i], t, t2)
R.mul(t, x[2][i], y[1][i])
R.mul(t2, x[3][i], y[3][i])
R.add(rop[3][i], t, t2)
ctypedef modn_element p1_element[2]
cdef class ProjectiveLineModN:
cdef ResidueRingModN S
cdef int r
cdef long _cardinality
cdef long cards[MAX_PRIME_DIVISORS]
def __init__(self, N):
self.S = ResidueRingModN(N)
self.r = self.S.r
self._cardinality = 1
cdef int i
for i in range(self.r):
R = self.S.residue_rings[i]
self.cards[i] = (R.cardinality() + R.cardinality()/R.residue_field().cardinality())
self._cardinality *= self.cards[i]
cpdef long cardinality(self):
return self._cardinality
def __repr__(self):
return "Projective line modulo the ideal %s of norm %s"%(
self.S.N._repr_short(), self.S.N.norm())
cdef element_to_str(self, p1_element op):
cdef ResidueRing_abstract R
v = [ ]
for i in range(self.r):
R = self.S.residue_rings[i]
v.append('(%s : %s)'%(R.element_to_str(op[0][i]), R.element_to_str(op[1][i])))
s = ', '.join(v)
if self.r > 1:
s = '(' + s + ')'
return s
cdef int matrix_action(self, p1_element rop, modn_matrix op0, p1_element op1) except -1:
cdef modn_element t0, t1
self.S.mul(t0, op0[0], op1[0])
self.S.mul(t1, op0[1], op1[1])
self.S.add(rop[0], t0, t1)
self.S.mul(t0, op0[2], op1[0])
self.S.mul(t1, op0[3], op1[1])
self.S.add(rop[1], t0, t1)
cdef void set_element(self, p1_element rop, p1_element op):
self.S.set_element(rop[0], op[0])
self.S.set_element(rop[1], op[1])
cdef int reduce_element(self, p1_element rop, p1_element op) except -1:
cdef int i
for i in range(self.r):
self.ith_reduce_element(rop, op, i)
cdef int ith_reduce_element(self, p1_element rop, p1_element op, int i) except -1:
cdef residue_element inv, r0, r1
cdef ResidueRing_abstract R = self.S.residue_rings[i]
if R.is_unit(op[1][i]):
R.inv(inv, op[1][i])
R.mul(r0, op[0][i], inv)
R.set_element_to_1(r1)
else:
R.set_element_to_1(r0)
R.inv(inv, op[0][i])
R.mul(r1, inv, op[1][i])
R.set_element(rop[0][i], r0)
R.set_element(rop[1][i], r1)
return 0
def test_reduce(self):
"""
The test passes if this returns the empty list.
Uses different random numbers each time, so seed the
generator if you want control.
"""
cdef p1_element x, y, z
cdef long a
self.first_element(x)
v = []
from random import randrange
cdef ResidueRing_abstract R
while True:
for i in range(self.r):
R = self.S.residue_rings[i]
a = randrange(1, R.p)
y[0][i][0] = (a*x[0][i][0])%R.n0
y[0][i][1] = (a*x[0][i][1])%R.n1
y[1][i][0] = (a*x[1][i][0])%R.n0
y[1][i][1] = (a*x[1][i][1])%R.n1
self.reduce_element(z, y)
v.append((self.element_to_str(x), self.element_to_str(y), self.element_to_str(z)))
try:
self.next_element(x, x)
except ValueError:
return [w for w in v if w[0] != w[2]]
def test_reduce2(self, int m, int n):
cdef int i
cdef p1_element x, y, z
self.first_element(x)
for i in range(m):
self.next_element(x, x)
print self.element_to_str(x)
cdef ResidueRing_abstract R
for i in range(self.r):
R = self.S.residue_rings[i]
y[0][i][0] = (3*x[0][i][0])%R.n0
y[0][i][1] = (3*x[0][i][1])%R.n1
y[1][i][0] = (3*x[1][i][0])%R.n0
y[1][i][1] = (3*x[1][i][1])%R.n1
print self.element_to_str(y)
from sage.all import cputime
t = cputime()
for i in range(n):
self.reduce_element(z, y)
return cputime(t)
cdef long standard_index(self, p1_element z) except -1:
cdef int i
cdef long C=1, ind = self.ith_standard_index(z, 0)
for i in range(1, self.r):
C *= self.cards[i-1]
ind += C * self.ith_standard_index(z, i)
return ind
cdef long ith_standard_index(self, p1_element z, int i) except -1:
cdef ResidueRing_abstract R = self.S.residue_rings[i]
if R.element_is_1(z[1][i]):
return R.index_of_element(z[0][i])
return R._cardinality + R.index_of_element_in_P(z[1][i])
def test_enum(self):
cdef p1_element x
self.first_element(x)
v = []
while True:
c = (self.element_to_str(x), self.standard_index(x))
print c
v.append(c)
try:
self.next_element(x, x)
except ValueError:
return v
def test_enum2(self):
cdef p1_element x
self.first_element(x)
while True:
try:
self.next_element(x, x)
except ValueError:
return
cdef int first_element(self, p1_element rop) except -1:
cdef int i
for i in range(self.r):
rop[0][i][0] = 0
rop[0][i][1] = 0
rop[1][i][0] = 1
rop[1][i][1] = 0
return 0
cdef int next_element(self, p1_element rop, p1_element z) except -1:
if rop != z:
self.set_element(rop, z)
cdef int i=0
while i < self.r and self.next_element_factor(rop, i):
rop[0][i][0] = 0
rop[0][i][1] = 0
rop[1][i][0] = 1
rop[1][i][1] = 0
i += 1
if i == self.r:
raise ValueError
return 0
cdef bint next_element_factor(self, p1_element op, int i) except -2:
cdef ResidueRing_abstract k = self.S.residue_rings[i]
if k.element_is_1(op[1][i]):
if k.is_last_element(op[0][i]):
k.set_element_to_0(op[1][i])
k.set_element_to_1(op[0][i])
else:
k.next_element(op[0][i], op[0][i])
return False
else:
if k.is_last_element_in_P(op[1][i]):
k.set_element_to_0(op[1][i])
return True
else:
k.next_element_in_P(op[1][i], op[1][i])
return False
def quaternion_in_terms_of_icosian_basis2(alpha):
"""
Write the quaternion alpha in terms of the fixed basis for integer icosians.
"""
b0 = alpha[0]; b1=alpha[1]; b2=alpha[2]; b3=alpha[3]
a = F.gen()
return [F_two*b0,
(a-F_one)*b0 - b1 + a*b3,
(a-F_one)*b0 + a*b1 - b2,
(-a-F_one)*b0 + a*b2 - b3]
cdef NumberFieldElement_quadratic F_one = F(1), F_two = F(2), F_gen = F.gen(), F_gen_m1=F_gen-F_one, F_gen_m2 = -F_gen-F_one
cpdef object quaternion_in_terms_of_icosian_basis(object alpha):
"""
Write the quaternion alpha in terms of the fixed basis for integer icosians.
"""
cdef NumberFieldElement_quadratic b0 = alpha[0], b1=alpha[1], b2=alpha[2], b3=alpha[3],
return [F_two._mul_(b0), F_gen_m1._mul_(b0)._sub_(b1)._add_(F_gen._mul_(b3)),
F_gen_m1._mul_(b0)._add_(F_gen._mul_(b1))._sub_(b2),
F_gen_m2._mul_(b0)._add_(F_gen._mul_(b2))._sub_(b3)]
cdef class ModN_Reduction:
cdef ResidueRingModN S
cdef modn_matrix G[4]
cdef bint is_odd
def __init__(self, N, bint init=True):
cdef int i
if not is_ideal_in_F(N):
raise TypeError, "N must be an ideal of F"
cdef ResidueRingModN S = ResidueRingModN(N)
self.S = S
self.is_odd = (N.norm() % 2 != 0)
if init:
S.set(self.G[0][0], 1); S.set(self.G[0][1], 0); S.set(self.G[0][2], 0); S.set(self.G[0][3], 1)
for i in range(S.r):
self.compute_ith_local_splitting(i)
def reduce_exponent_of_ith_factor(self, i):
"""
Let P be the ith prime factor of the modulus N of self. This function
returns the splitting got by reducing self modulo N/P. See
the documentation for the degeneracy_matrix of
IcosiansModP1ModN for why we wrote this function.
"""
cdef ResidueRing_abstract R = self.S.residue_rings[i]
P = R.P
N2 = self.S.N / P
cdef ModN_Reduction f = ModN_Reduction(N2, init=False)
cdef int j, k
for j in range(4):
for k in range(4):
self.S.set_element_reducing_exponent_of_ith_factor(f.G[j][k], self.G[j][k], i)
return f
cdef compute_ith_local_splitting(self, int i):
cdef ResidueRingElement z
cdef long m, n
cdef ResidueRing_abstract R = self.S.residue_rings[i]
F = self.S.N.number_field()
if R.p != 2:
R.set_element_to_0(self.G[1][0][i])
R.set_element_to_1(self.G[1][1][i]); R.neg(self.G[1][1][i], self.G[1][1][i])
R.set_element_to_1(self.G[1][2][i])
R.set_element_to_0(self.G[1][3][i])
else:
from sqrt5_fast_python import find_mod2pow_splitting
w = find_mod2pow_splitting(R.e)
for n in range(4):
v = w[n].list()
for m in range(4):
z = v[m]
self.G[n][m][i][0] = z.x[0]
self.G[n][m][i][1] = z.x[1]
return
cdef residue_element a, b, c, d, t, t2, minus_one
found_it = False
R.set_element_to_1(b)
R.coerce_from_nf(minus_one, F(-1))
while not R.is_last_element(b):
R.mul(t, b, b)
R.mul(t, t, minus_one)
R.add(c, minus_one, t)
if R.is_square(c):
R.sqrt(a, c)
R.set_element(self.G[2][0][i], a)
R.set_element(self.G[2][1][i], b)
R.mul(t, a, a)
R.mul(t, t, minus_one)
R.add(t, t, minus_one)
R.inv(t2, b)
R.mul(self.G[2][2][i], t, t2)
R.mul(self.G[2][3][i], a, minus_one)
good, K = self._matrices_are_independent_mod_ith_prime(i)
if good:
self.S.matrix_mul_ith_factor(self.G[3], self.G[1], self.G[2], i)
return
R.next_element(b, b)
raise NotImplementedError
def _matrices_are_independent_mod_ith_prime(self, int i):
cdef ResidueRing_abstract R = self.S.residue_rings[i]
k = R.residue_field()
M = MatrixSpace(k, 2)
J = M([R.element_to_residue_field(self.G[2][n][i]) for n in range(4)])
I = M([R.element_to_residue_field(self.G[1][n][i]) for n in range(4)])
K = I * J
B = [M.identity_matrix(), I, J, I*J]
V = k**4
W = V.span([x.list() for x in B])
return W.dimension() == 4, K.list()
def __repr__(self):
return 'Reduction modulo %s of norm %s defined by sending I to %s and J to %s'%(
self.S.N._repr_short(), self.S.N.norm(),
self.S.matrix_to_str(self.G[1]), self.S.matrix_to_str(self.G[2]))
cdef int quatalg_to_modn_matrix(self, modn_matrix M, alpha) except -1:
cdef modn_element t
cdef modn_element X[4]
cdef int i, j
cdef ResidueRingModN S = self.S
cdef ResidueRing_abstract R
cdef residue_element z[4]
cdef residue_element t2
for i in range(4):
S.coerce_from_nf(X[i], alpha[i], 1)
for i in range(4):
S.mul(M[i], self.G[0][i], X[0])
for j in range(1, 4):
S.mul(t, self.G[j][i], X[j])
S.add(M[i], M[i], t)
if not self.is_odd:
v = quaternion_in_terms_of_icosian_basis(alpha)
R = self.S.residue_rings[0]
assert R.p == 2, '%s\nBUG: order of factors wrong? '%(self.S.residue_rings,)
for i in range(4):
R.coerce_from_nf(z[i], v[i])
for i in range(4):
R.mul(M[i][0], self.G[0][i][0], z[0])
for j in range(1,4):
R.mul(t2, self.G[j][i][0], z[j])
R.add(M[i][0], M[i][0], t2)
def __call__(self, alpha):
"""
A sort of joke for now. Reduce alpha using this map, then return
string representation...
"""
cdef modn_matrix M
self.quatalg_to_modn_matrix(M, alpha)
return self.S.matrix_to_str(M)
ctypedef modn_matrix icosian_matrices[120]
cdef class IcosiansModP1ModN:
"""
Create object that represents that set of orbits
(Icosian Group) \ P^1(O_F / N).
EXAMPLES::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import F, IcosiansModP1ModN
sage: I = IcosiansModP1ModN(F.prime_above(31)); I
The 2 orbits for the action of the Icosian group on Projective line modulo the ideal (5*a - 2) of norm 31
sage: I = IcosiansModP1ModN(F.prime_above(389)); I
The 7 orbits for the action of the Icosian group on Projective line modulo the ideal (18*a - 5) of norm 389
sage: I = IcosiansModP1ModN(F.prime_above(2011)); I
The 35 orbits for the action of the Icosian group on Projective line modulo the ideal (41*a - 11) of norm 2011
sage: from psage.modform.hilbert.sqrt5.tables import ideals_of_bounded_norm
sage: [len(IcosiansModP1ModN(b)) for b in ideals_of_bounded_norm(300)]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 3, 3, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 6, 5, 5, 4, 4, 6, 4, 4, 6, 6, 4, 4, 4, 4, 5, 5, 6, 6, 6, 5, 5, 5, 5, 4, 4, 6, 6, 7, 7, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6]
"""
cdef icosian_matrices G
cdef ModN_Reduction f
cdef ProjectiveLineModN P1
cdef long *std_to_rep_table
cdef long *orbit_reps
cdef long _cardinality
cdef p1_element* orbit_reps_p1elt
def __init__(self, N, f=None, init=True):
if f is not None:
self.f = f
else:
self.f = ModN_Reduction(N)
self.P1 = ProjectiveLineModN(N)
self.orbit_reps = <long*>0
self.std_to_rep_table = <long*> sage_malloc(sizeof(long) * self.P1.cardinality())
self.orbit_reps_p1elt = <p1_element*>sage_malloc(sizeof(p1_element) * self.P1.cardinality())
from sqrt5 import all_icosians
X = all_icosians()
cdef int i
for i in range(len(X)):
self.f.quatalg_to_modn_matrix(self.G[i], X[i])
if init:
self.compute_std_to_rep_table()
def __reduce__(self):
return unpickle_IcosiansModP1ModN_v1, (self.P1.S.N.gens_reduced()[0], )
def __len__(self):
return self._cardinality
def __dealloc__(self):
sage_free(self.std_to_rep_table)
sage_free(self.orbit_reps_p1elt)
if self.orbit_reps:
sage_free(self.orbit_reps)
def __repr__(self):
return "The %s orbits for the action of the Icosian group on %s"%(self._cardinality, self.P1)
def level(self):
"""
Return the level N, which is the ideal we quotiented out by in
constructing this set.
EXAMPLES::
"""
return self.P1.S.N
cpdef compute_std_to_rep_table(self):
cdef long orbit_cnt
cdef p1_element x, Gx
self.P1.first_element(x)
cdef long ind=0, j, i=0, k
for j in range(self.P1._cardinality):
self.std_to_rep_table[j] = -1
self.std_to_rep_table[0] = 0
orbit_cnt = 1
reps = []
while ind < self.P1._cardinality:
reps.append(ind)
self.P1.set_element(self.orbit_reps_p1elt[i], x)
for j in range(120):
self.P1.matrix_action(Gx, self.G[j], x)
self.P1.reduce_element(Gx, Gx)
k = self.P1.standard_index(Gx)
if self.std_to_rep_table[k] == -1:
self.std_to_rep_table[k] = i
orbit_cnt += 1
else:
assert self.std_to_rep_table[k] == i, "Bug: orbits not disjoint"
assert 120 % orbit_cnt == 0, "orbit size = %s must divide 120"%orbit_cnt
orbit_cnt = 0
while ind < self.P1._cardinality and self.std_to_rep_table[ind] != -1:
ind += 1
if ind < self.P1._cardinality:
self.P1.next_element(x, x)
i += 1
self._cardinality = len(reps)
self.orbit_reps = <long*> sage_malloc(sizeof(long)*self._cardinality)
for j in range(self._cardinality):
self.orbit_reps[j] = reps[j]
def compute_std_to_rep_table_debug(self):
cdef long orbit_cnt
cdef p1_element x, Gx
self.P1.first_element(x)
cdef long ind=0, j, i=0, k
for j in range(self.P1._cardinality):
self.std_to_rep_table[j] = -1
self.std_to_rep_table[0] = 0
orbit_cnt = 1
reps = []
while ind < self.P1._cardinality:
reps.append(ind)
print "Found representative number %s (which has standard index %s): %s"%(
i, ind, self.P1.element_to_str(x))
self.P1.set_element(self.orbit_reps_p1elt[i], x)
for j in range(120):
self.P1.matrix_action(Gx, self.G[j], x)
self.P1.reduce_element(Gx, Gx)
k = self.P1.standard_index(Gx)
if self.std_to_rep_table[k] == -1:
self.std_to_rep_table[k] = i
orbit_cnt += 1
else:
assert self.std_to_rep_table[k] == i, "Bug: orbits not disjoint"
assert 120 % orbit_cnt == 0, "orbit size = %s must divide 120"%orbit_cnt
print "It has an orbit of size %s"%orbit_cnt
orbit_cnt = 0
while ind < self.P1._cardinality and self.std_to_rep_table[ind] != -1:
ind += 1
if ind < self.P1._cardinality:
self.P1.next_element(x, x)
i += 1
self._cardinality = len(reps)
self.orbit_reps = <long*> sage_malloc(sizeof(long)*self._cardinality)
for j in range(self._cardinality):
self.orbit_reps[j] = reps[j]
cpdef long cardinality(self):
return self._cardinality
def hecke_matrix(self, P, sparse=True):
"""
Return matrix of Hecke action, acting from the right, so the
i-th row is the image of the i-th standard basis vector.
INPUT:
- P -- prime ideal
- ``sparse`` -- bool (default: True) if True, then a sparse
matrix is returned, otherwise a dense one
OUTPUT:
- a dense or sparse matrix over the rational numbers
NOTE: This function does not cache its results.
EXAMPLES::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import F, IcosiansModP1ModN
sage: I = IcosiansModP1ModN(F.primes_above(31)[0]); I
The 2 orbits for the action of the Icosian group on Projective line modulo the ideal (5*a - 2) of norm 31
sage: I.hecke_matrix(F.prime_above(2))
[0 5]
[3 2]
sage: I.hecke_matrix(F.prime_above(3))
[5 5]
[3 7]
sage: I.hecke_matrix(F.prime_above(5))
[1 5]
[3 3]
sage: I.hecke_matrix(F.prime_above(3)).fcp()
(x - 10) * (x - 2)
sage: I.hecke_matrix(F.prime_above(2)).fcp()
(x - 5) * (x + 3)
sage: I.hecke_matrix(F.prime_above(5)).fcp()
(x - 6) * (x + 2)
A bigger example::
sage: I = IcosiansModP1ModN(F.prime_above(389)); I
The 7 orbits for the action of the Icosian group on Projective line modulo the ideal (18*a - 5) of norm 389
sage: t2 = I.hecke_matrix(F.prime_above(2)); t2
[0 3 0 1 1 0 0]
[3 0 0 0 1 0 1]
[0 0 2 1 0 1 1]
[1 0 1 0 1 0 2]
[1 1 0 1 0 1 1]
[0 0 2 0 2 1 0]
[0 1 1 2 1 0 0]
sage: t2.is_sparse()
True
sage: I.hecke_matrix(F.prime_above(2), sparse=False).is_sparse()
False
sage: t3 = I.hecke_matrix(F.prime_above(3))
sage: t5 = I.hecke_matrix(F.prime_above(5))
sage: t11a = I.hecke_matrix(F.primes_above(11)[0])
sage: t11b = I.hecke_matrix(F.primes_above(11)[1])
sage: t2.fcp()
(x - 5) * (x^2 + 5*x + 5) * (x^4 - 3*x^3 - 3*x^2 + 10*x - 4)
sage: t3.fcp()
(x - 10) * (x^2 + 3*x - 9) * (x^4 - 5*x^3 + 3*x^2 + 6*x - 4)
sage: t5.fcp()
(x - 6) * (x^2 + 4*x - 1) * (x^2 - x - 4)^2
sage: t11a.fcp()
(x - 12) * (x + 3)^2 * (x^4 - 17*x^2 + 68)
sage: t11b.fcp()
(x - 12) * (x^2 + 5*x + 5) * (x^4 - x^3 - 23*x^2 + 18*x + 52)
sage: t2*t3 == t3*t2, t2*t5 == t5*t2, t11a*t11b == t11b*t11a
(True, True, True)
Examples involving a prime dividing the level::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import F, IcosiansModP1ModN
sage: v = F.primes_above(31); I = IcosiansModP1ModN(v[0])
sage: t31 = I.hecke_matrix(v[1]); t31
[17 15]
[ 9 23]
sage: t31.fcp()
(x - 32) * (x - 8)
sage: t5 = I.hecke_matrix(F.prime_above(5))
sage: t5*t31 == t31*t5
True
sage: I.hecke_matrix(v[0])
Traceback (most recent call last):
...
NotImplementedError: computation of T_P for P dividing the level is not implemented
Another test involving a prime that divides the norm of the level::
sage: v = F.primes_above(809); I = IcosiansModP1ModN(v[0])
sage: t = I.hecke_matrix(v[1])
sage: I
The 14 orbits for the action of the Icosian group on Projective line modulo the ideal (-26*a + 7) of norm 809
sage: t.fcp()
(x - 810) * (x + 46) * (x^4 - 48*x^3 - 1645*x^2 + 65758*x + 617743) * (x^8 + 52*x^7 - 2667*x^6 - 118268*x^5 + 2625509*x^4 + 55326056*x^3 - 1208759312*x^2 + 4911732368*x - 4279699152)
sage: s = I.hecke_matrix(F.prime_above(11))
sage: t*s == s*t
True
"""
if ZZ(self.level().norm()).gcd(ZZ(P.norm())) != 1:
return self._hecke_matrix_badnorm(P, sparse=sparse)
cdef modn_matrix M
cdef p1_element Mx
from sqrt5 import hecke_elements
T = zero_matrix(QQ, self._cardinality, sparse=sparse)
cdef long i, j
for alpha in hecke_elements(P):
self.f.quatalg_to_modn_matrix(M, alpha)
for i in range(self._cardinality):
self.P1.matrix_action(Mx, M, self.orbit_reps_p1elt[i])
self.P1.reduce_element(Mx, Mx)
j = self.std_to_rep_table[self.P1.standard_index(Mx)]
T[i,j] += 1
return T
def _hecke_matrix_badnorm(self, P, sparse=True):
"""
Compute the Hecke matrix for a prime P whose norm divides the
norm of the level.
This function doesn't work if P itself divides the level.
EXAMPLES::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import F, IcosiansModP1ModN; v=F.primes_above(71); I=IcosiansModP1ModN(v[0])
sage: I._hecke_matrix_badnorm(v[1])
[61 11]
[55 17]
sage: I._hecke_matrix_badnorm(v[1]).fcp()
(x - 72) * (x - 6)
"""
cdef modn_matrix M, M0
cdef p1_element Mx
from sqrt5 import hecke_elements
T = zero_matrix(QQ, self._cardinality, sparse=sparse)
cdef long i, j
if P.divides(self.level()):
raise NotImplementedError, "computation of T_P for P dividing the level is not implemented"
for beta in hecke_elements(P):
alpha = beta**(-1)
self.f.quatalg_to_modn_matrix(M0, alpha)
if self.P1.S.matrix_is_invertible(M0):
self.P1.S.matrix_inv(M, M0)
for i in range(self._cardinality):
self.P1.matrix_action(Mx, M, self.orbit_reps_p1elt[i])
self.P1.reduce_element(Mx, Mx)
j = self.std_to_rep_table[self.P1.standard_index(Mx)]
T[i,j] += 1
return T
def hecke_operator_on_basis_element(self, P, long i):
"""
Return image of i-th basis vector of self under the Hecke
operator T_P, for P coprime to the level, computed efficiently
in the sense of not computing images of all basis vectors.
INPUT:
- P -- nonzero prime ideal of ring of integers of Q(sqrt(5))
that does not divide the level
- i -- nonnegative integer less than the dimension
OUTPUT:
- a dense vector over the integers
EXAMPLES::
sage: from psage.modform.hilbert.sqrt5.sqrt5_fast import F, IcosiansModP1ModN
sage: v = F.primes_above(31); I = IcosiansModP1ModN(v[0])
sage: I.hecke_matrix(F.prime_above(2))
[0 5]
[3 2]
sage: I.hecke_matrix(v[1])
[17 15]
[ 9 23]
sage: I.hecke_operator_on_basis_element(F.prime_above(2), 0)
(0, 5)
sage: I.hecke_operator_on_basis_element(F.prime_above(2), 1)
(3, 2)
sage: I.hecke_operator_on_basis_element(v[1], 0)
(17, 15)
sage: I.hecke_operator_on_basis_element(v[1], 1)
(9, 23)
"""
cdef modn_matrix M, M0
cdef p1_element Mx
from sqrt5 import hecke_elements
cdef list Q = hecke_elements(P)
v = (ZZ**self._cardinality)(0)
if ZZ(self.level().norm()).gcd(ZZ(P.norm())) == 1:
for alpha in Q:
self.f.quatalg_to_modn_matrix(M, alpha)
self.P1.matrix_action(Mx, M, self.orbit_reps_p1elt[i])
self.P1.reduce_element(Mx, Mx)
v[self.std_to_rep_table[self.P1.standard_index(Mx)]] += 1
else:
if P.divides(self.level()):
raise NotImplementedError
for beta in Q:
alpha = beta**(-1)
self.f.quatalg_to_modn_matrix(M0, alpha)
if self.P1.S.matrix_is_invertible(M0):
self.P1.S.matrix_inv(M, M0)
self.P1.matrix_action(Mx, M, self.orbit_reps_p1elt[i])
self.P1.reduce_element(Mx, Mx)
v[self.std_to_rep_table[self.P1.standard_index(Mx)]] += 1
return v
def degeneracy_matrix(self, P, sparse=True):
"""
Return degeneracy matrix from level N of self to level N/P.
Here P must be a prime ideal divisor of the ideal N.
"""
N = self.P1.S.N
cdef ResidueRing_abstract R
cdef int k, ind = -1, m = len(self.P1.S.residue_rings)
for k, R in enumerate(self.P1.S.residue_rings):
if R.P == P:
ind = k
break
if ind == -1:
raise ValueError, "P must be a prime divisor of the level"
N2 = N/P
g = self.f.reduce_exponent_of_ith_factor(ind)
cdef IcosiansModP1ModN I2 = IcosiansModP1ModN(N2, g)
D = zero_matrix(QQ, self._cardinality, I2._cardinality, sparse=sparse)
cdef Py_ssize_t i, j
cdef p1_element x, y
for i in range(self._cardinality):
self.P1.S.set_element_reducing_exponent_of_ith_factor(
y[0], self.orbit_reps_p1elt[i][0], ind)
self.P1.S.set_element_reducing_exponent_of_ith_factor(
y[1], self.orbit_reps_p1elt[i][1], ind)
I2.P1.reduce_element(x, y)
j = I2.std_to_rep_table[I2.P1.standard_index(x)]
D[i,j] += 1
return D
def unpickle_IcosiansModP1ModN_v1(x):
import sqrt5
return IcosiansModP1ModN(sqrt5.F.ideal(x))