changeset 0:a296fa27420d

Initial release.
author Marc Masdeu <masdeu@math.columbia.edu>
date Wed, 15 Jun 2011 12:01:49 +0200
parents
children c3d3ff442f2c
files __init__.py all.py ocmodule.py pautomorphicform.py shimuracurve.py utility.py
diffstat 6 files changed, 1980 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/__init__.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,1 @@
+pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/all.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,4 @@
+from shimuracurve import ShimuraCurve, ShimuraCurveFiber
+from pautomorphicform import HarmonicCocycle, HarmonicCocycles, pAutomorphicForm, pAutomorphicForms
+from ocmodule import OCVn,OCVnElement
+from utility import our_conj, act_flt,getcoords,our_sqrt,our_log,our_exp,fix_deg_monomials
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ocmodule.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,211 @@
+from sage.structure.element import ModuleElement
+from sage.modules.module import Module
+from sage.matrix.constructor import Matrix
+from sage.matrix.matrix_space import MatrixSpace_generic
+from sage.matrix.matrix_space import MatrixSpace
+from copy import copy
+from sage.rings.all import IntegerRing
+from sage.rings.finite_rings.integer_mod_ring import Zmod
+from sage.rings.power_series_ring import PowerSeriesRing
+from sage.modular.btquotients.utility import our_log
+from sage.modular.btquotients.utility import our_exp
+
+class OCVnElement(ModuleElement):
+    def __init__(self,_parent,val=0,prec=100):
+        ModuleElement.__init__(self, _parent)
+        self._parent=_parent
+        self._n=self._parent._n
+        self._depth=self._parent._depth
+        self._prec=prec
+        if(isinstance(val,OCVnElement)):
+            d=min([val._parent._depth,_parent._depth])
+            assert(val._parent.weight()==_parent.weight())
+            self._val=Matrix(self._parent._ZZp,self._depth,1,0)
+            for ii in range(d):
+                self._val[ii,0]=val._val[ii,0]
+        else:
+            try:
+                self._val=MatrixSpace(self._parent._ZZp,self._depth,1)(val)
+            except:
+                self._val=Matrix(self._parent._ZZp,self._depth,1,[val]*self._depth)
+
+    def __getitem__(self,r):
+        return self._val[r,0]
+
+    def matrix_rep(self,B=None):
+        #Express the element in terms of the basis B
+        if(B==None):
+            B=self._parent.basis()
+        A=Matrix(self._parent._ZZp,self._parent.dimension(),self._parent.dimension(),[[b._val[ii,0] for b in B] for ii in range(self._depth)])
+        return A.solve_right(self._val)
+
+    def _add_(self,y):
+        prec=min(self._prec,y._prec)
+        assert(self._depth==y._depth and (self._n==y._n or any[self._val==0,y._val==0]))
+        val=self._val+y._val
+        return OCVnElement(self._parent,val,prec)
+
+    def _sub_(self,y):
+        prec=min(self._prec,y._prec)
+        assert(self._depth==y._depth and (self._n==y._n or any[self._val==0,y._val==0]))
+        val=self._val-y._val
+        return OCVnElement(self._parent,val,prec)
+
+    def l_act_by(self,x):
+        assert(x.nrows()==2 and x.ncols()==2) #An element of GL2
+        K=self._parent._ZZp
+        assert(x!=0)
+        y=self._l_act_by(x[0,0],x[0,1],x[1,0],x[1,1])
+        return (K(x.determinant())**(-IntegerRing()(self._n/2)))*y
+
+    def r_act_by(self,x):
+        assert(x.nrows()==2 and x.ncols()==2) #An element of GL2
+        K=self._parent._ZZp
+        assert(x!=0)
+        y=self._l_act_by(x[1,1],-x[0,1],-x[1,0],x[0,0])
+        return (K(x.determinant())**(-IntegerRing()(self._n/2)))*y
+
+    def _l_act_by(self,a,b,c,d):
+        R=self._parent._ZZp
+        t=min([R(a).valuation(),R(b).valuation(),R(c).valuation(),R(d).valuation()])
+        factor=R.prime()**(IntegerRing()(-t))
+        tmp=self._parent._get_powers_and_mult(factor*a,factor*b,factor*c,factor*d,factor**(-self._n),self._val)
+        return(OCVnElement(self._parent,tmp,self._prec))
+
+    def _rmul_(self,a):
+    #assume that a is a scalar
+        if(a.parent().is_exact()):
+            prec=self._prec
+        else:
+            prec=min([self._prec,a.precision_relative()])
+        y=OCVnElement(self._parent,a*self._val,prec)
+        return y
+
+    def precision(self):
+        return self._prec
+
+    def _repr_(self):
+        R=PowerSeriesRing(self._parent._ZZp,default_prec=self._depth,name='z')
+        z=R.gen()
+        s=str(sum([R(self._val[ii,0]*z**ii) for ii in range(self._depth)]))
+        return s
+
+    def __nonzero__(self):
+        return(any([self._val[ii,0].__nonzero__() for ii in range(self._depth)]))
+
+    def evaluate(self,P):
+        p=self._parent._ZZp.prime()
+        try:
+            r=min([P.degree()+1,self._depth])
+            return sum([self._val[ii,0]*P[ii] for ii in range(r)])
+        except:
+            return self._val[0,0]*P
+
+    def valuation(self):
+        return min([self._val[ii,0].valuation() for ii in range(self._depth)])
+
+class OCVn(Module):
+    Element=OCVnElement
+    def __init__(self,n,ZZp,depth=None,basis=[]):
+        Module.__init__(self,base=ZZp)
+        self._basis=copy(basis)
+        self._n=n
+        self._ZZp=ZZp
+        if(depth==None):
+            depth=n+1
+        self._depth=depth
+        self._powers=dict()
+        self._populate_coercion_lists_()
+
+    def _an_element_(self):
+        return OCVnElement(self,1)
+
+    def _coerce_map_from_(self, S):
+       """
+       Nothing coherces here, except OCVnElement
+       """
+       if(isinstance(S,OCVnElement)):
+           return True
+       else:
+           return False
+
+    def _element_constructor_(self,x):
+        #Code how to coherce x into the space
+        #Admissible values of x?
+        return OCVnElement(self,x)
+
+    def base(self):
+        return self._ZZp
+
+    def _get_powers_and_mult(self,a,b,c,d,lambd,vect):
+        p=self._ZZp.prime()
+        #print a,b,c,d
+        ZZmod=Zmod(p**(self._ZZp.precision_cap()))
+        R=PowerSeriesRing(ZZmod,default_prec=self._depth,name='z')
+        z=R.gen()
+        if(not self._powers.has_key((a,b,c,d))):
+            r=R([b,a])
+            s=R([d,c])
+            rpows=[R(1)]
+            spows=[R(1)]
+            for ii in range(self._depth-1):
+                rpows.append(r*rpows[ii])
+            for ii in range(self._n):
+                spows.append(s*spows[ii])
+            if(self._depth>self._n+1):
+                assert(s[1].valuation(p)>s[0].valuation(p))
+                sinv=R(s**(-1))
+                sinvpows=[sinv]
+                for ii in range(self._n+1,self._depth):
+                    sinvpows.append(sinv*sinvpows[ii-self._n-1])
+
+            #x=Matrix(self._ZZp,self._depth,self._depth,0)
+            x=Matrix(ZZmod,self._depth,self._depth,0)
+            for ii in range(self._n+1):
+                y=rpows[ii]*spows[self._n-ii]
+                for jj in range(self._depth):
+                    #x[ii,jj]=self._ZZp(y[jj])
+                    x[ii,jj]=y[jj]
+            for ii in range(self._n+1,self._depth):
+                y=rpows[ii]*sinvpows[ii-self._n-1]
+                for jj in range(self._depth):
+                    #x[ii,jj]=self._ZZp(y[jj])
+                    x[ii,jj]=y[jj]
+            self._powers[(a,b,c,d)]=x
+        x=self._powers[(a,b,c,d)]
+        return lambd*Matrix(self._ZZp,x.nrows(),x.ncols(),[self._ZZp(x[ii,jj]) for ii in range(x.nrows()) for jj in range(x.ncols())])*vect
+
+    def _repr_(self):
+        s='Overconvergent coefficient module of weight n='+str(self._n)+' over the ring '+ str(self._ZZp)+' and depth '+str(self._depth)
+        return s
+
+    def basis(self):
+        if(self._basis==[]):
+            for jj in range(self._depth):
+                x=OCVnElement(self,Matrix(self._ZZp,self._depth,1,{(jj,0):1},sparse=False),self._ZZp.precision_cap())
+                self._basis.append(x)
+        return self._basis
+
+    def base_ring(self):
+        return self._ZZp
+
+    def depth(self):
+        return self._depth
+
+    def dimension(self):
+        return self._depth
+    def weight(self):
+        return self._n
+
+    def l_matrix_representation(self,g,B=None):
+        if(B==None):
+            B=self.basis()
+        if(isinstance(g,list)):
+            tmp=[]
+            for gg in g:
+                A=[(b.l_act_by(gg)).matrix_rep(B) for b in B]
+                tmp.append(Matrix(self._ZZp,self.dimension(),self.dimension(),[A[jj][ii,0] for ii in range(self.dimension()) for jj in range(self.dimension())]))
+        else:
+            A=[(b.l_act_by(g)).matrix_rep(B) for b in B]
+            tmp=Matrix(self._ZZp,self.dimension(),self.dimension(),[A[jj][ii,0] for ii in range(self.dimension()) for jj in range(self.dimension())])
+        return tmp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pautomorphicform.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,749 @@
+from collections import namedtuple
+from sage.structure.element import Element
+from sage.groups.matrix_gps.general_linear import GeneralLinearGroup_generic
+from sage.structure.element import ModuleElement
+from sage.modules.module import Module
+from sage.rings.all import Integer
+from sage.structure.element import Element
+from sage.modular.btquotients.shimuracurve import ShimuraCurve, ShimuraCurveFiber
+from sage.modular.btquotients.ocmodule import OCVn, OCVnElement
+from sage.matrix.constructor import Matrix
+from sage.matrix.matrix_space import MatrixSpace_generic
+from sage.matrix.matrix_space import MatrixSpace
+from sage.rings.all import Qp
+from sage.rings.all import IntegerRing
+from sage.rings.all import RationalField
+from sage.rings.number_field.all import NumberField
+from copy import copy
+from sage.quadratic_forms.quadratic_form import QuadraticForm
+from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
+from sage.rings.laurent_series_ring import LaurentSeriesRing
+from sage.modular.btquotients.utility import our_log
+from sage.modular.btquotients.utility import our_exp
+from sage.modular.btquotients.utility import our_pow
+from sage.modular.btquotients.shimuracurve import EdgeDecomposition
+
+###########################
+### Automorphic code    ###
+###########################
+
+class HarmonicCocycle(ModuleElement):
+    def __init__(self,_parent,vec=None,prec=None):
+        ModuleElement.__init__(self,_parent)
+
+        if(isinstance(vec,HarmonicCocycle)):
+            #The first argument is just a modular form that we copy as is
+            self._parent=vec._parent
+            self._prec=vec._prec
+            self._ZZp=vec._ZZp
+            self._nE=vec._nE
+            self._wt=vec._wt
+            self._F=[OCVnElement(_parent,vec._F[ii],self._prec) for ii in range(self._nE)]
+        elif(isinstance(vec,pAutomorphicForm)):
+            self._parent=_parent
+            #need to put some assertions
+            self._prec=vec._prec
+            self._ZZp=Qp(_parent._X._p,prec=prec)
+            self._nE=IntegerRing()(len(_parent._E))
+            self._wt=_parent._k
+            self._F=[OCVnElement(_parent._U,vec._F[ii],self._prec).l_act_by(self._parent._X._edge_list[ii].rep) for ii in range(self._nE)]
+        elif(isinstance(vec,dict)):
+            self._parent=_parent
+            assert(vec['p'])==self._parent._X._p
+            assert(vec['t'])==self._parent._U._depth
+            assert(vec['level'])==self._parent._X._level
+            assert(vec['k']==self._parent._U.weight()+2)
+            self._prec=mydata['prec']
+            self._prec=prec
+            self._ZZp=Qp(_parent._X._p,prec=self._prec)
+            self._wt=_parent._k
+            self._nE=len(_parent._E)
+            self._F=[self._parent._U(c) for c in mydata['value']]
+        elif(isinstance(vec,list) and len(vec)==len(_parent._E) and vec[0].parent() is _parent._U):
+            self._ZZp=Qp(_parent._X._p,prec=prec)
+            self._prec=prec
+            self._parent=_parent
+            self._wt=_parent._k
+            self._nE=len(_parent._E)
+            self._F=copy(vec)
+        else:
+            try:
+                veczp=_parent._U._ZZp(vec)
+                self._ZZp=Qp(_parent._X._p,prec=prec)
+                self._prec=prec
+                self._parent=_parent
+                self._wt=_parent._k
+                self._nE=len(_parent._E)
+                self._F=[self._parent._U(veczp) for ii in range(self._nE)]
+            except:
+                assert(0)
+
+
+    def _add_(self,g):
+        prec=min([self._prec,g._prec])
+        #Should ensure that self and g are modular forms of the same weight and on the same curve
+        vec=[self._F[e]+g._F[e] for e in range(self._nE)]
+        return HarmonicCocycle(self._parent,vec,prec)
+    def _sub_(self,g):
+        prec=min([self._prec,g._prec])
+        #Should ensure that self and g are modular forms of the same weight and on the same curve
+        vec=[self._F[e]-g._F[e] for e in range(self._nE)]
+        return HarmonicCocycle(self._parent,vec,prec)
+
+    def _rmul_(self,a):
+        if(a.parent().is_exact()):
+            prec=self._prec
+        else:
+            prec=min([self._prec,a.precision_relative()])
+        #Should ensure that 'a' is a scalar
+        return HarmonicCocycle(self._parent,[a*self._F[e] for e in range(self._nE)],prec)
+
+    def _repr_(self):
+        tmp='Harmonic cocycle on '+str(self._parent)+':\n'
+        tmp+='   e   |   f(e)'
+        tmp+='\n'
+        for e in range(self._nE):
+            tmp+='  '+str(e)+' | '+str(self._F[e])+'\n'
+        return tmp
+
+    def _nonzero_(self):
+        return(any([self._F[e]._nonzero_() for e in range(self._nE)]))
+
+    def valuation(self):
+        return min([self._F[e].valuation() for e in range(self._nE)])
+
+    #In HarmonicCocycle
+    def evaluate(self,e1):
+        X=self._parent._X
+        p=X._p
+        u=EdgeDecomposition(X,e1)
+        return (u.sign*self._F[u.label]).l_act_by(self._parent.iota(u.gamma)*(p**(-u.power)))
+
+    #In HarmonicCocycle
+    def riemann_sum(self,f,center=1,level=0,E=None):
+        R1=LaurentSeriesRing(f.base_ring(),'r1')
+        R1.set_default_prec(self._parent._k-1)
+        R2=PolynomialRing(f.base_ring(),'r2')
+
+        if(E==None):
+            E=self._parent._X._BT.get_ball(center,level)
+        else:
+            E=self._parent._X._BT.subdivide(E,level)
+        value=0
+        ii=0
+        for e in E:
+            ii+=1
+            print ii,"/",len(E)
+            exp=R1([e[1,1],e[1,0]])**(self._parent._k-2)*e.determinant()**(-(self._parent._k-2)/2)*f(R1([e[0,1],e[0,0]])/R1([e[1,1],e[1,0]])).truncate(self._parent._k-1)
+            new2=self.evaluate(e).l_act_by(e.inverse()).evaluate(exp)
+            value+=new
+        return value
+
+    # In HarmonicCocycle
+    def mod_form(self,z=None,level=0):
+        def F(z):
+            R=PolynomialRing(z.parent(),'x')
+            x=R.gen()
+            center=self._parent._X._BT.whereis(z)
+            return self.riemann_sum(1/(x-z),center,level)
+        if(z==None):
+            return F
+        else:
+            return F(z)
+
+    # In HarmonicCocycle
+    # So far only works for k=2 !!
+    def coleman(self,t1,t2,center=1,level=1,E=None):
+        if(center==None):
+            center=self._parent._X._BT.whereis(t1)
+        if(E==None):
+            E=self._parent._X._BT.get_ball(center,0)
+        EList=[]
+        for e in E:
+            EList.extend(self._parent._X._BT.subdivide([e],level))
+
+        K=t1.parent()
+        R1=LaurentSeriesRing(K,'r1')
+        R1.set_default_prec(self._parent._U.weight()+1)
+        r1=R1.gen()
+        value=0
+        ii=0
+        for e in EList:
+            ii+=1
+            print ii,"/",len(EList)
+            b=e[0,1]
+            d=e[1,1]
+            y=(b-d*t1)/(b-d*t2)
+            exp=R1(our_log(y))
+            new=(self.evaluate(e)).evaluate(exp)
+            value+=new
+        return value
+
+    # In HarmonicCocycle
+    # So far only works for k=2 !!
+    def coleman_mult(self,t1,t2,center=1,level=1,E=None):
+        if(center==None):
+            center=self._parent._X._BT.whereis(t1)
+        if(E==None):
+            E=self._parent._X._BT.get_ball(center,0)
+        EList=[]
+        for e in E:
+            EList.extend(self._parent._X._BT.subdivide([e],level))
+
+        K=t1.parent()
+        R1=LaurentSeriesRing(K,'r1')
+        R1.set_default_prec(self._parent._U.weight()+1)
+        r1=R1.gen()
+        p=self._parent._X._p
+        value=1
+        ii=0
+        for e in EList:
+            ii+=1
+            print ii,"/",len(EList)
+            b=e[0,1]
+            d=e[1,1]
+            if(d!=0):
+                print b/d
+                y=(b-d*t1)/(b-d*t2)
+                power=IntegerRing()(self.evaluate(e)._val[0,0].rational_reconstruction())
+                new=y**power
+                value*=new
+        return value
+
+class HarmonicCocycles(Module):
+    Element=HarmonicCocycle
+    def __init__(self,X,k,prec=100):
+        self._k=k
+        self._prec=prec
+        self._X=X
+        self._ZZp=Qp(self._X._p,prec=prec)
+        Module.__init__(self,base=self._ZZp)
+        self._V=self._X._vertex_list
+        self._E=self._X._edge_list
+        self._basis_matrix=None
+        self._U=OCVn(self._k-2,self._ZZp,self._k-1)
+        self._populate_coercion_lists_()
+
+    def _repr_(self):
+        s='Space of harmonic cocycles of weight '+str(self._k)+' on '+str(self._X)
+        return s
+
+    def _latex_(self):
+        s='\\text{Space of harmonic cocycles of weight }'+latex(self._k)+'\\text{ on }'+latex(self._X)
+        return s
+
+    def _an_element_(self):
+        return HarmonicCocycle(self,1)
+
+    def _coerce_map_from_(self, S):
+       """
+       Can coerce from other HarmonicCocycles or from pAutomorphicForms
+       """
+       if(isinstance(S,HarmonicCocycles)):
+           if(S._k!=self._k):
+               return False
+           if(S._X!=self._X):
+               return False
+           return True
+       if(isinstance(S,pAutomorphicForms)):
+           if(S._n!=self._k-2):
+               return False
+           if(S._X!=self._X):
+               return False
+           return True
+       return False
+
+    def _element_constructor_(self,x):
+        #Code how to coherce x into the space
+        #Admissible values of x?
+        if(isinstance(x,HarmonicCocycle) or isinstance(x,pAutomorphicForm)):
+            return HarmonicCocycle(self,x,prec=x._prec)
+
+    def iota(self,g):
+        return self._X.iota(g,self._prec)
+
+    def get_basis_matrix(self):
+        if (self._basis_matrix==None):
+            self._compute_basis()
+        return self._basis_matrix
+
+    def get_basis(self):
+        nE=len(self._E)
+        basis_matrix=self.get_basis_matrix()
+        basis=[]
+        for ii in range(basis_matrix.nrows()):
+            r=list(basis_matrix.row(ii))
+            basis.append(HarmonicCocycle(self,[OCVnElement(self._U,Matrix(self._ZZp,self._k-1,1,r[e*(self._k-1):(e+1)*(self._k-1)]),self._prec) for e in range(nE)],self._prec))
+        return basis
+
+    def dimension(self):
+        if (self._basis_matrix==None):
+            self._compute_basis()
+        return self._basis_matrix.nrows()
+
+    def _compute_basis(self):
+        nV=len(self._V)
+        nE=len(self._E)
+        stab_conds=[]
+        tmp=self._X._get_edge_stabs()
+        p=self._X._p
+        for e in self._E:
+            gg=tmp[e.label]
+            if(gg!=1):
+                for g in gg:
+                    if(g[2]):
+                        C=self._U.l_matrix_representation(self.iota(g[0]))
+                        C-=self._U.l_matrix_representation(Matrix(RationalField(),2,2,p**g[1]))
+                        stab_conds.append([e.label,C])
+                        break
+        self._M=Matrix(self._ZZp,(nV+len(stab_conds))*(self._k-1),nE*(self._k-1),0,sparse=True)
+        for v in self._V:
+            for e in v.leaving_edges:
+                if e.rep.determinant().valuation(p)%2!=0:
+                    continue
+                C=sum(self._U.l_matrix_representation([self.iota(x[0]) for x in e.links]))
+                for ii in range(self._k-1):
+                    for jj in range(self._k-1):
+                        self._M[v.label*(self._k-1)+ii,e.label*(self._k-1)+jj]+=C[ii,jj]
+            for e in v.entering_edges:
+                if e.rep.determinant().valuation(p)%2!=0:
+                    continue
+
+                C=sum(self._U.l_matrix_representation([self.iota(x[0]) for x in e.opposite[0].links]))
+                for ii in range(self._k-1):
+                    for jj in range(self._k-1):
+                        self._M[v.label*(self._k-1)+ii,e.opposite[0].label*(self._k-1)+jj]-=C[ii,jj]
+        for kk in range(len(stab_conds)):
+            C=stab_conds[kk]
+            for ii in range(self._k-1):
+                 for jj in range(self._k-1):
+                       self._M[(nV+kk)*(self._k-1)+ii,C[0]*(self._k-1)+jj]=C[1][ii,jj]
+        x1=self._M.right_kernel().matrix()
+        K=[c for c in x1.rows()]
+        for ii in range(len(K)):
+            s=min([t.valuation() for t in K[ii]])
+            for jj in range(len(K[ii])):
+                K[ii][jj]=(p**(-s))*K[ii][jj]
+
+        self._basis_matrix=Matrix(self._ZZp,len(K),nE*(self._k-1),K)
+
+    def hecke_operator(self,l):
+        assert(IntegerRing()(l).is_prime())
+
+        def T(f):
+            R=Qp(f._ZZp.prime(),prec=f._ZZp.precision_cap())
+            HeckeData=self._X.hecke_coset_reps(l)
+            Tf=[]
+            if(self._X.generic_fiber().level()%l==0):
+                factor=RationalField()(l**(IntegerRing()((self._k-2)/2))/(l+1))
+            else:
+                factor=RationalField()(l**(IntegerRing()((self._k-2)/2)))
+            for jj in range(len(self._E)):
+                tmp=OCVnElement(self._U,MatrixSpace(self._ZZp,self._k-1,1)(0),self._prec)
+                for ii in range(l+1):
+                    t=HeckeData[ii][1][jj]#.edge_list[jj]
+                    gg=HeckeData[ii][0]#.acter
+                    iota=self.iota
+                    tmp=tmp+(t.sign*f._F[t.label]).l_act_by((self._X._p**(-t.power))*(iota(gg)*iota(self._X._alpha1[l]))*(iota(t.gamma)))
+                Tf.append(factor*tmp)
+            return HarmonicCocycle(self,Tf,self._prec)
+        return T
+
+    def operator_matrix(self,T):
+        R=Qp(self._ZZp.prime(),prec=self._ZZp.precision_cap())
+        A=self.get_basis_matrix()
+        basis=self.get_basis()
+        tmp=[]
+        for f in basis:
+            g=T(f)
+            tmp.append([g._F[e]._val[ii,0] for e in range(len(self._E)) for ii in range(self._k-1) ])
+        B=Matrix(R,len(basis),len(self._E)*(self._k-1),tmp)
+        return (A.solve_left(B)).transpose()
+
+    def hecke_operator_matrix(self,l):
+        return self.operator_matrix(self.hecke_operator(l))
+
+    def rational_charpoly(self,l):
+        v1=self.hecke_operator_matrix(l).characteristic_polynomial().coeffs()
+        v=[x.rational_reconstruction() for x in v1]
+        return PolynomialRing(RationalField(),'x')(v)
+
+
+
+######################
+###  New code that is really automorphic
+######################
+
+class pAutomorphicForm(ModuleElement):
+    def __init__(self,_parent,vec,prec=None):
+
+        ModuleElement.__init__(self,_parent)
+        self._parent=_parent
+        self._nE=2*len(_parent._E) # We record the values at the opposite edges
+        if(isinstance(vec,pAutomorphicForm)):
+            self._ZZp=Qp(_parent._X._p,prec=vec._prec)
+            self._prec=vec._prec
+
+            self._F=[self._parent._U(vec._F[ii]) for ii in range(self._nE)]
+
+            self._make_invariant()
+
+        elif(isinstance(vec,HarmonicCocycle)):
+            assert(_parent._U.weight()==vec._wt-2)
+            self._prec=vec._prec
+            self._ZZp=Qp(_parent._X._p,prec=self._prec)
+            self._F=[]
+            assert(2*len(vec._F)==self._nE)
+            assert(isinstance(_parent._U,OCVn))
+
+            for ii in range(len(vec._F)):
+                self._F.append(_parent._U(OCVnElement(vec._parent._U,vec._F[ii],self._prec).l_act_by(self._parent._X._edge_list[ii].rep.inverse())))
+            for ii in range(len(vec._F)):
+
+                self._F.append(_parent._U((-1*(OCVnElement(vec._parent._U,self._F[ii],self._prec)).r_act_by(Matrix(RationalField(),2,2,[0,-1/_parent._X._p,-1,0])))))
+            self._make_invariant()
+
+        elif(isinstance(vec,list) and len(vec)==self._nE):
+            self._ZZp=Qp(_parent._X._p,prec=prec)
+            try:
+                self._prec=prec
+                self._F=[self._parent._U(v) for v in vec]
+            except:
+                try:
+                    veczp=_parent._U._ZZp(vec)
+                    self._prec=prec
+                    self._parent=_parent
+                    self._F=[self._parent._U(veczp) for ii in range(self._nE)]
+                except:
+                    print vec
+                    assert(0)
+        else:
+            try:
+                self._ZZp=Qp(_parent._X._p,prec=prec)
+                veczp=_parent._U._ZZp(vec)
+                self._prec=prec
+                self._parent=_parent
+                self._F=[self._parent._U(veczp) for ii in range(self._nE)]
+            except:
+                raise ValueError,"Cannot initialize a p-adic automorphic form with the given input="+str(vec)
+
+    def _make_invariant(self):
+        S=self._parent._X._get_edge_stabs()
+        M=[e.rep for e in self._parent._X._edge_list]+[e.opposite[0].rep for e in self._parent._X._edge_list]
+        lS=len(S)
+        assert(2*len(S)==self._nE)
+        for ii in range(self._nE):
+            Si=S[ii%lS]
+            if(Si!=1):
+                x=OCVnElement(self._F[ii].parent(),self._F[ii],self._prec)
+                self._F[ii]=OCVnElement(self._F[ii].parent(),0,self._prec)
+                s=RationalField()(0)
+                m=M[ii]
+                for v in Si:
+                    s+=1
+                    self._F[ii]+=x.r_act_by(m.adjoint()*self._parent.iota(v[0])*m)
+                self._F[ii]=(1/s)*self._F[ii]
+
+    def save(self):
+        mydata=dict([])
+        mydata['p']=self._parent._X._p
+        mydata['t']=self._parent._U._depth
+        mydata['prec']=self._prec
+        mydata['level']=self._parent._X._level
+        mydata['weight']=self._parent._U.weight()
+        mydata['value']=[c._val for c in self._F]
+        return mydata
+
+    def load(self,s):
+        mydata=load(s)
+        assert(mydata['p'])==self._parent._X._p
+        assert(mydata['t'])==self._parent._U._depth
+        assert(mydata['level'])==self._parent._X._level
+        assert(mydata['weight']==self._parent._U.weight())
+        self._prec=mydata['prec']
+        self._F=[self._parent._U(c) for c in mydata['value']]
+
+    def _add_(self,g):
+        prec=min([self._prec,g._prec])
+        #Should ensure that self and g are of the same weight and on the same curve
+        vec=[self._F[e]+g._F[e] for e in range(self._nE)]
+        return pAutomorphicForm(self._parent,vec,prec)
+    def _sub_(self,g):
+        prec=min([self._prec,g._prec])
+        #Should ensure that self and g are of the same weight and on the same curve
+        vec=[self._F[e]-g._F[e] for e in range(self._nE)]
+        return pAutomorphicForm(self._parent,vec,prec)
+
+
+    def _getitem_(self,e1):
+        return self.evaluate(e1)
+
+    def evaluate(self,e1):
+        X=self._parent._X
+        u=EdgeDecomposition(X,e1)
+        return (self._F[u.label+IntegerRing()((1-u.sign)*self._nE/4)]).r_act_by((u.t)*X._p**(u.power))
+
+    def _rmul_(self,a):
+        if(a.parent().is_exact()):
+            prec=self._prec
+        else:
+            prec=min([self._prec,a.precision_relative()])
+        #Should ensure that 'a' is a scalar
+        return pAutomorphicForm(self._parent,[a*self._F[e] for e in range(self._nE)],prec)
+
+
+    def l_act_by(self,alpha):
+        edge_list=self._parent._X._edge_list
+        gg=1#alpha.inverse()
+        iota=self._parent.iota
+        Tf=[]
+        for e in edge_list:
+            u=EdgeDecomposition(self._parent._X,alpha*e.rep)
+            r=u.t*gg*self._parent._X._p**(-(u.power))
+            if(u.sign==1):
+                tmp=self._F[u.label].r_act_by(r)
+            else:
+                tmp=self._F[u.label+len(self._parent._E)].r_act_by(r)
+            Tf.append(tmp)
+        for e in edge_list:
+            u=EdgeDecomposition(self._parent._X,alpha*e.opposite[0].rep)
+            r=u.t*gg*self._parent._X._p**(-(u.power))
+            if(u.sign==1):
+                tmp=self._F[u.label].r_act_by(r)
+            else:
+                tmp=self._F[u.label+len(self._parent._E)].r_act_by(r)
+            Tf.append(tmp)
+        return pAutomorphicForm(self._parent,Tf,self._prec)
+
+
+    def _repr_(self):
+        tmp='p-adic automorphic form on '+str(self._parent)+':\n'
+        tmp+='   e   |   c(e)'
+        tmp+='\n'
+        for e in range(IntegerRing()(self._nE/2)):
+            tmp+='  '+str(e)+' | '+str(self._F[e])+'\n'
+        return tmp
+
+    def valuation(self):
+        return(min([self._F[e].valuation() for e in range(self._nE)]))
+
+    def _nonzero_(self):
+        return(any([self._F[e]._nonzero_() for e in range(self._nE)]))
+
+    def improve(self):
+        MMM=self._parent
+        h2=MMM.Up(self,True)
+        ii=0
+        current_val=0
+        while(current_val<self._prec and ii<self._prec+2):
+            ii+=1
+            print ii,"th iteration, val=",current_val
+            self._F=[self._parent._U(c) for c in h2._F]
+            h2=MMM.Up(self,True)
+            current_val=(h2-self).valuation()
+        self._F=[self._parent._U(c) for c in h2._F]
+        print "Done!"
+        return self
+
+    def integrate(self,f,center=1,level=0,method='moments'):
+        E=self._parent._X._BT.get_ball(center,level)
+        R1=LaurentSeriesRing(f.base_ring(),'r1')
+        R2=PolynomialRing(f.base_ring(),'r2')
+        value=0
+        ii=0
+        if(method=='riemann_sum'):
+            R1.set_default_prec(self._parent._U.weight()+1)
+            for e in E:
+                ii+=1
+                print ii,"/",len(E)
+                exp=((R1([e[1,1],e[1,0]]))**(self._parent._U.weight())*e.determinant()**(-(self._parent._U.weight())/2))*f(R1([e[0,1],e[0,0]])/R1([e[1,1],e[1,0]]))
+                #exp=R2([tmp[jj] for jj in range(self._parent._k-1)])
+                new=self.evaluate(e).evaluate(exp.truncate(self._parent._U.weight()+1))
+                value+=new
+        elif(method=='moments'):
+            R1.set_default_prec(self._parent._U.dimension())
+            for e in E:
+                ii+=1
+                #print ii,"/",len(E)
+                tmp=((R2([e[1,1],e[1,0]]))**(self._parent._U.weight())*e.determinant()**(-(self._parent._U.weight())/2))*f(R2([e[0,1],e[0,0]])/R2([e[1,1],e[1,0]]))
+                exp=R1(tmp.numerator())/R1(tmp.denominator())
+                new=self.evaluate(e).evaluate(exp)
+                value+=new
+        else:
+            print 'The available methods are either "moments" or "riemann_sum". The latter is only provided for consistency check, and should never be used.'
+            return False
+        return value
+
+    def mod_form(self,z=None,level=0,method='moments'):
+        def F(z,level=0,method='moments'):
+            R=PolynomialRing(z.parent(),'x')
+            x=R.gen()
+            center=self._parent._X._BT.whereis(z)
+            return self.integrate(R(1)/(x-z),center,level,method)
+        if(z==None):
+            return F
+        else:
+            return F(z,level)
+
+
+    def _exp_factor(self,t1,t2,center=1,level=0,E=None):
+        K=t1.parent()
+        if(center==None):
+            center=self._parent._X._BT.whereis(t1)
+        if(E==None):
+            E=self._parent._X._BT.get_ball(center,level)
+        R1=LaurentSeriesRing(t1.parent(),'r1')
+        R1.set_default_prec(self._parent._U.weight()+1)
+
+        value=1
+        ii=0
+        p=self._parent._X._p
+        prec=self._prec
+        for e in E:
+            ii+=1
+            print ii,"/",len(E)
+            b=e[0,1]
+            d=e[1,1]
+            y=(b-d*t1)/(b-d*t2)
+            power=IntegerRing()(self.evaluate(e)._val[0,0].rational_reconstruction())
+            new=K.teichmuller(y)**power
+            value*=new
+        return value
+
+
+    def coleman(self,t1,t2,center=None,level=0,E=None,method='moments',mult=False):
+        p=self._parent._X._p
+        K=t1.parent()
+        R=PolynomialRing(K,'x')
+        x=R.gen()
+        R1=LaurentSeriesRing(K,'r1')
+        r1=R1.gen()
+        if(center==None):
+            center=self._parent._X._BT.whereis(t1)
+        if(E==None):
+            E=self._parent._X._BT.get_ball(center,level)
+        value=0
+        ii=0
+
+        if(method=='riemann_sum'):
+            R1.set_default_prec(self._parent._U.weight()+1)
+            for e in E:
+                ii+=1
+                print ii,"/",len(E)
+                b=e[0,1]
+                d=e[1,1]
+                y=(b-d*t1)/(b-d*t2)
+                exp=R1(our_log(y))
+                new=self.evaluate(e).evaluate(exp)
+                value+=new
+        elif(method=='moments'):
+            R1.set_default_prec(self._parent._U.dimension())
+            for e in E:
+                ii+=1
+                print ii,"/",len(E)
+                f=(x-t1)/(x-t2)
+                y0=f(R1([e[0,1],e[0,0]])/R1([e[1,1],e[1,0]]))
+                y0=p**(-y0(0).valuation())*y0
+                mu=K.teichmuller(y0(0))
+                y=y0/mu-1
+                exp=R1(0)
+                ypow=y
+                for jj in range(1,R1.default_prec()+10):
+                    exp+=(-1)**(jj+1)*ypow/jj
+                    ypow*=y
+                new=self.evaluate(e).evaluate(exp)
+                value+=new
+        else:
+            print 'The available methods are either "moments" or "riemann_sum". The latter is only provided for consistency check, and should never be used.'
+            return False
+
+        if(mult):
+            value=our_exp(value)*self._exp_factor(t1,t2)
+        return value
+
+class pAutomorphicForms(Module):
+    Element=pAutomorphicForm
+    def __init__(self,X,U,prec=None,t=None,R=None):
+        if(R==None):
+            if(not isinstance(U,Integer)):
+                self._ZZp=U.base_ring()
+            else:
+                if(prec==None):
+                    prec=100
+                self._ZZp=Qp(X._p,prec)
+        else:
+            self._ZZp=R
+        #U is a CoefficientModuleSpace
+
+
+        if(isinstance(U,Integer)):
+            if(t==None):
+                t=0
+            self._U=OCVn(U-2,self._ZZp,U-1+t)
+        else:
+            self._U=U
+        self._X=X
+        self._V=self._X._vertex_list
+        self._E=self._X._edge_list
+        self._prec=self._ZZp.precision_cap()
+        self._n=self._U.weight()
+        Module.__init__(self,base=self._ZZp)
+        self._populate_coercion_lists_()
+
+    def _coerce_map_from_(self, S):
+       """
+       Can coerce from other HarmonicCocycles or from pAutomorphicForms
+       """
+       if(isinstance(S,HarmonicCocycles)):
+           if(S._k-2!=self._n):
+               return False
+           if(S._X!=self._X):
+               return False
+           return True
+       if(isinstance(S,pAutomorphicForms)):
+           if(S._n!=self._n):
+               return False
+           if(S._X!=self._X):
+               return False
+           return True
+       return False
+
+    def _element_constructor_(self,x):
+        #Code how to coherce x into the space
+        #Admissible values of x?
+        if(isinstance(x,(HarmonicCocycle,pAutomorphicForm))):
+            return pAutomorphicForm(self,x,prec=x._prec)
+
+    def _an_element_(self):
+        return pAutomorphicForm(self,1)
+
+    def iota(self,g):
+        return self._X.iota(g,self._prec)
+
+    def Up(self,v=None,scale=False):
+        def T(f):
+            iota=self.iota
+            HeckeData=self._X.Up_data()
+            #Should find a way to replace this n/2 with something more intrinsinc to U
+            if(scale==False):
+                factor=(self._X._p)**(self._U.weight()/2)
+            else:
+                factor=1
+            #print 'factor=',factor
+            Tf=[]
+            for jj in range(2*len(self._E)):
+                tmp=self._U(0)
+                for ii in range(self._X._p):
+                    u=HeckeData[ii][1][jj]#.edge_list[jj]
+                    gg=HeckeData[ii][0]#.acter
+                    iota=self.iota
+                    r=u.t*gg*self._X._p**(-(u.power))
+                    tmp=tmp+f._F[u.label+IntegerRing()((1-u.sign)/2)*len(self._E)].r_act_by(r)
+                Tf.append(factor*tmp)
+            return pAutomorphicForm(self,Tf,self._prec)
+        if(v==None):
+            return T
+        else:
+            return T(v)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/shimuracurve.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,872 @@
+r"""
+Arithmetic quotients of the Bruhat-Tits tree
+
+"""
+
+#########################################################################
+#       Copyright (C) 2011 Cameron Franc and Marc Masdeu
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#
+#                  http://www.gnu.org/licenses/
+#########################################################################
+
+
+from sage.structure.element import Element
+from sage.groups.matrix_gps.general_linear import GeneralLinearGroup_generic
+from sage.matrix.constructor import Matrix
+from sage.matrix.matrix_space import MatrixSpace
+from sage.structure.sage_object import SageObject
+from sage.rings.all import RationalField
+from sage.rings.all import IntegerRing
+from sage.plot import plot
+from sage.rings.arith import *
+from sage.rings.integer import Integer
+from sage.graphs.graph import Graph
+from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra
+from sage.rings.padics.factory import Qp
+from sage.rings.padics.factory import Zp
+from sage.rings.finite_rings.integer_mod_ring import Zmod
+from sage.rings.number_field.all import NumberField
+from sage.quadratic_forms.quadratic_form import QuadraticForm
+from sage.combinat.combinat import permutations
+from sage.interfaces.magma import magma
+from sage.rings.number_field.order import AbsoluteOrder
+from sage.rings.all import Qq
+from sage.modular.btquotients.utility import our_sqrt
+from copy import copy
+from sage.structure.unique_representation import UniqueRepresentation
+
+r"""
+  Initialized with an element x0 of GL2(ZZ), finds elements
+  gamma, t and an edge e such that g e t=x.
+  It stores these values as members sign,g,label,t
+  satisfying:
+     if sign==+1:
+        gamma*edge_list[label].rep*t==x0
+     if sign==-1:
+        gamma*edge_list[label].opposite[0].rep*t==x0
+
+  It also returns power, so that:
+  p**(2*power)=gamma.reduced_norm()
+
+  If one wants the usual decomposition, then it would be:
+  g=gamma/(p**power) (in the arithmetic subgroup)
+  e=edge_list[label]
+  t'=t*p**power (in the stabilizer of the standard edge)
+
+INPUT:: Y: ShimuraCurveFiber object in which to work
+        x0: Something coercible into a matrix in GL2(ZZ)
+            Note that we should allow elements in GL2(Qp),
+            but for what we do it is enough to work with
+            integral entries
+       extrapow: gets added to the power attribute, and
+                 it is used for the Hecke action.
+"""
+class EdgeDecomposition(SageObject):
+    def __init__(self,Y,x0,extrapow=0):
+        x=MatrixSpace(IntegerRing(),2,2)(x0)
+        assert(x.determinant()!=0)
+        e1=Y._BT.edge(x)
+        e1opp=Y._BT.opposite(e1)
+        sign=0
+        E=Y._edge_list
+        for ii in range(len(E)):
+            e2=E[ii]
+            g=Y._are_equivalent(e2.rep,e1,Y._Xe)
+            if(g!=0):
+                sign=1
+                t=(Y.iota(g[0])*e2.rep).inverse()*x
+                break
+            e3=e2.opposite[0]
+            g=Y._are_equivalent(e3.rep,e1,Y._Xe)
+            if(g!=0):
+                sign=-1
+                t=(Y.iota(g[0])*e3.rep).inverse()*x
+                break
+        self.sign=sign
+        self.label=e2.label
+        self.gamma=g[0]
+        self.power=g[1]+extrapow
+        self.t=t
+
+r"""
+   Structure that holds vertices in the quotient, as they are computed. At time of creation, it is given an owner (always ShimuraCurveFiber), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) giving a basis for a lattice representing the vertex).
+   The lists leaving_edges and entering_edges store this
+   information as it is known
+"""
+class Vertex():
+    def __init__(self,owner,label,rep,leaving_edges,entering_edges):
+        self.owner=owner
+        self.label=label
+        self.rep=rep
+        self.leaving_edges=leaving_edges
+        self.entering_edges=entering_edges
+
+
+r"""
+   Structure that holds edges in the quotient, as they are computed. At time of creation, it is given an owner (always ShimuraCurveFiber), a label (a non-negative integer) and a rep (an element of GL_2(ZZ) which is chosen so that when acting on ZZp it takes it to the
+   compact open ball corresponding to the ends containing the
+   given edge in the Bruhat-Tits tree.
+
+   The members origin and target store Vertices, known at creation,
+   the member opposite is its opposite edge in the quotient,
+   and links is a list of other edges in the Bruhat-Tits tree
+   that are equivalent to this one in the quotient.
+"""
+class Edge():
+    def __init__(self,owner,label,rep,origin,target,links,opposite):
+        self.owner=owner
+        self.label=label
+        self.rep=rep
+        self.origin=origin
+        self.target=target
+        self.links=links
+        self.opposite=opposite
+
+class BruhatTitsTree(SageObject):
+    def __init__(self,p):
+        self._p=p
+        self._Mzp=None
+
+    def update_prec(self,ZZp):
+        self._MZp=MatrixSpace(ZZp,2,2)
+
+    def target(self,M):
+        return self.vertex(M)
+    def origin(self,M):
+        return self.target(self.opposite(M))
+
+    def edge(self,A):
+        p=self._p
+        B=self._MZp(A)
+        v=min([B[i,j].valuation() for i in range(2) for j in range(2)])
+        B=(p**(-v))*B
+        assert(B.determinant()!=0)
+        if (B[0,0] != 0) and (B[0,0].valuation() <= B[0,1].valuation()):
+            tmp=(B.determinant()).valuation()-B[0,0].valuation()
+            B=Matrix(ZZ,2,2,[p**(B[0,0].valuation()),0,(B[1,0]/B[0,0].unit_part()).residue(1+tmp),p**(tmp)])
+        else:
+            tmp=(B.determinant()).valuation()-B[0,1].valuation()
+            B=Matrix(ZZ,2,2,[0,p**(B[0,1].valuation()),p**(tmp),(B[1,1]/B[0,1].unit_part()).residue(tmp)])
+        v=min([valuation(B[i,j],p) for i in range(2) for j in range(2)])
+        B=p**(-v)*B
+        return(B)
+
+    def vertex(self,A):
+        p=self._p
+        B=self._MZp(A)
+        v=min([B[i,j].valuation() for i in range(2) for j in range(2)])
+        B=p**(-v)*B
+        assert(B.determinant()!=0)
+        if B[0,1].valuation()<B[0,0].valuation():
+            B.swap_columns(0,1)
+        tmp=(B.determinant()).valuation()-B[0,0].valuation()
+        B=Matrix(ZZ,2,2,[p**(B[0,0].valuation()),0,(B[1,0]/B[0,0].unit_part()).residue(tmp),p**(tmp)])
+        v=min([valuation(B[i,j],p) for i in range(2) for j in range(2)])
+        B=p**(-v)*B
+        return(B)
+
+    def leaving_edges(self,M):
+        R=MatrixSpace(ZZ,2,2)
+        p=self._p
+        #Construct the edges leaving v0
+        v=[[R([0,-1,p,0]),self.target(R([0,-1,p,0]))]]
+        v.extend([[R([p,i,0,1]),self.target(R([p,i,0,1]))] for i in range(p)])
+
+        MM=R(M)
+        return [[self.edge(MM*A[0]),self.vertex(MM*A[1])] for A in v]
+
+    def opposite(self,M):
+        return self.edge(Matrix(ZZ,2,2,M)*Matrix(ZZ,2,2,[0,1,self._p,0]))
+
+    def entering_edges(self,M):
+         v=[[self.opposite(e[0]),M] for e in self.leaving_edges(M)]
+         return v
+
+
+    # Given a list of edges (which we think of finite union of open balls)
+    # return a list of lists of edges
+    # corresponding to the level-th subdivision of that open.
+    # That is, if level==0, return the given list, unchanged.
+    def subdivide(self,edgelist,level):
+        all_edges=[]
+        if(level<0):
+            return []
+        if(level==0):
+            return [MatrixSpace(ZZ,2,2)(edge) for edge in edgelist]
+        else:
+            newEgood=[]
+            for edge in edgelist:
+                edge=MatrixSpace(ZZ,2,2)(edge)
+                origin=self.origin(edge)
+                newE=self.leaving_edges(self.target(edge))
+                newEgood.extend([e[0] for e in newE if e[1]!=origin])
+            return self.subdivide(newEgood,level-1)
+
+    def get_ball(self,center=1,level=1):
+        newE=self.leaving_edges(center)
+        return self.subdivide([e[0] for e in newE],level)
+
+    def whereis(self,z):
+        #Assume z belongs to some extension of QQp.
+        p=self._p
+        if(z.valuation()<0):
+            flag=True
+            z=1/(p*z)
+        else:
+            flag=False
+        a=0
+        pn=1
+        val=z.valuation()
+        L=[]
+        for ii in range(val):
+            L.append(0)
+        L.extend(z.list())
+        for n in range(len(L)):
+            if(L[n]!=0):
+                if(len(L[n])>1):
+                    break
+                if(len(L[n])>0):
+                    a+=pn*L[n][0]
+            pn*=p
+        #return self._normalize_vertex(Matrix(ZZ,2,2,[1,0,a,pn]))
+        if(flag==True):
+            return (self.vertex(Matrix(ZZ,2,2,[0,1,p,0])*Matrix(ZZ,2,2,[pn,a,0,1])))
+        else:
+            return (self.vertex(Matrix(ZZ,2,2,[pn,a,0,1])))
+
+
+class ShimuraCurveFiber(SageObject):
+    def __init__(self,X,p,source='sage',magma_proc=None,compute=True):
+        if(X.level()%p!=0):
+            raise ValueError, "p must divide the level of X"
+        self._generic_fiber=X
+        self._pN=p
+        self._p=p
+        self._Nminus=ZZ(X.level()/p)
+        if(not self._Nminus.is_prime() and source=='sage'):
+            raise NotImplementedError,'Sage does not know yet how to compute maximal orders of this kind of quaternion algebras. Use only the product of two primes as the level.'
+        self._Nplus=X._Nplus
+        if(self._Nplus!=1 and source=='sage'):
+            raise NotImplementedError,'Sage does not know yet how to deal with non-maximal orders, so this feature is disabled for now. Try with Nplus=1 or, if you have Magma installed, with source="sage"'
+
+        self._S=Graph(0,multiedges=True,weighted=True)
+        self._BT=BruhatTitsTree(p)
+        self._edge_list=[]
+        self._vertex_list=[]
+        self._preci=-1
+        self._hecke_coset_reps=dict([])
+        self._alpha1=dict([])
+        self._cached=False
+        self._CM_points=dict()
+        self._edge_stabs=[]
+        self._source=source
+        if(source=='magma'):
+            from sage.interfaces.magma import Magma
+            if(magma_proc!=None):
+                self._magma=magma_proc
+            else:
+                self._magma=Magma()
+
+        self._V=RationalField()**4
+        XX=MatrixSpace(ZZ,2,2)
+        self._Xv=[XX([1,0,0,0]),XX([0,1,0,0]),XX([0,0,1,0]),XX([0,0,0,1])]
+        self._Xe=[XX([1,0,0,0]),XX([0,1,0,0]),XX([0,0,self._p,0]),XX([0,0,0,1])]
+        self._num_edges=0
+
+        self._O=None
+        self._A=None
+        self._B=None
+        self._OQuadForm=None
+
+        if(compute):
+            self._compute()
+
+    def __getstate__(self):
+        from sage.all import dumps
+        odict=self.__dict__.copy()
+        odict['_S']=self._S.sparse6_string()
+        del odict['_generic_fiber']
+        if(self._source=='magma'):
+            del odict['_magma']
+        return odict
+
+    def __setstate__(self,ndict):
+        from sage.all import loads
+        self.__dict__.update(ndict)
+        self._generic_fiber=ShimuraCurve(ndict['_p']*ndict['_Nminus'],ndict['_Nplus'])
+        self._S=Graph(ndict['_S'],multiedges=True,weighted=True)
+        if(self._source=='magma'):
+            from sage.interfaces.magma import Magma
+            self._magma=Magma()
+
+    def _repr_(self):
+        return "Fiber of Shimura curve of level ("+str(factor(self._generic_fiber.level()))+") and conductor ("+str(factor(self._Nplus))+") at the prime "+str(self._p)
+
+    def _latex_(self):
+        return "\\text{Fiber of Shimura curve of level (}"+latex(factor(self._generic_fiber.level()))+"\\text{) and conductor (}"+latex(factor(self._Nplus))+"\\text{) at the prime }"+latex(self._p)
+
+    def generic_fiber(self):
+        return self._generic_fiber
+    def _is_cached(self):
+        return (self._cached)
+    def genus(self):
+        if (not self._is_cached()):
+            self._compute()
+        return ZZ(1- len(self._vertex_list)+self._num_edges)
+    def Nplus(self):
+        return(self._Nplus)
+    def Nminus(self):
+        return(self._Nminus)
+    def get_graph(self):
+        return self._S
+    def plot(self):
+        A=self._S.adjacency_matrix()
+        return Graph(A).plot(edge_labels=False)
+
+    def is_admissible(self,disc):
+        disc=ZZ(disc)
+        if(disc.squarefree_part()!=disc):
+            return False
+        R = RationalField()['x']
+        K=NumberField(R([-disc,0,1]), 'sq', check=False)
+        ff=factor(self._p*self._Nminus)
+        for f in ff:
+            if kronecker_symbol(K.discriminant(),f[0])!=-1:
+                return False
+        ff=factor(self._Nplus)
+        for f in ff:
+            if kronecker_symbol(K.discriminant(),f[0])!=1:
+                return False
+        return True
+
+    def _local_splitting_map(self,preci):
+        I,J,K=self._local_splitting(preci)
+        def phi(q):
+            R=parent(I)
+            return R(q[0] + I*q[1] + J*q[2] + K*q[3])
+        return phi
+
+    def _local_splitting(self,prec):
+        assert(self._source=='sage')
+        preci=max([5,prec])
+        i=self._A.gens()[0]
+        j=self._A.gens()[1]
+        if(self._ZZp.precision_cap()<preci):
+            self._ZZp=Zp(self._p,preci)
+        ZZp=self._ZZp
+        a =ZZp(i*i)
+        b = ZZp(j*j)
+        if (self._A.base_ring() != RationalField()):
+            raise ValueError, "must be rational quaternion algebra"
+        if (self._A.discriminant() % self._p == 0):
+            raise ValueError, "p (=%s) must be an unramified prime"%self._p
+        M = MatrixSpace(ZZp, 2)
+
+        # Included here only for testing Darmon's examples
+        if(self._p==7 and a==-1 and b==-1):
+            print "Splitting as in Darmon's book"
+            I=M([0,1,-1,0])
+            r=ZZp(2)
+            rnew=r**self._p
+            while(rnew!=r):
+                r=rnew
+                rnew=r**self._p
+            J=M([r,r+1,r+1,-r])
+            K=M([r+1,-r,-r,-r-1])
+            assert I*I==a*M([1,0,0,1])
+            assert J*J==b*M([1,0,0,1])
+            assert K == -J*I
+            return I,J,K
+        # --- End of special bit
+        if a.is_square():
+            alpha=a.sqrt()
+            I=M([alpha,0,2*alpha,-alpha])
+            J=M([b,-b,b-1,-b])
+        else:
+            I = M([0,a,1,0])
+            z=0
+            J=0
+            while(not J):
+                c=a*z*z+b
+                if c.is_square():
+                    x=c.sqrt()
+                    J=M([x,-a*z,z,-x])
+                else:
+                    z+=1
+        K = I*J
+        # assert I^2==a*M([1,0,0,1])
+        # assert J^2==b*M([1,0,0,1])
+        # assert K == -J*I
+
+        return I, J, K
+
+
+    # Need to understand why pMatrixRing sometimes returns low precision
+    def _get_Iota0(self,preci):
+        if(self._source=='magma'):
+            M,f,rho=self._magma.function_call('pMatrixRing',args=[self._O,self._p],params={'Precision':100},nvals=3)
+            OBasis=self._O.Basis()
+            v=[f.Image(OBasis[i]) for i in [1,2,3,4]]
+            A=Matrix(Zmod(self._pN),4,4,[v[kk][ii,jj].sage() for ii in range(1,3) for jj in range(1,3) for kk in range(4)])
+            return A
+        else:
+            assert(self._source=='sage')
+            phi=self._local_splitting_map(preci)
+            return Matrix(Zmod(self._pN),4,4,[phi(self._B[kk,0])[ii,jj] for ii in range(2) for jj in range(2) for kk in range(4)])
+
+    def get_Iota(self,preci=None):
+        if(preci==None):
+            preci=self._preci
+        self._pN=self._p**preci
+
+        self._ZZp=Qp(self._p,prec=preci)
+        self._BT.update_prec(self._ZZp)
+
+        if(preci>self._preci):
+            Iotamod=self._get_Iota0(preci)
+            self._Iotainv_lift=Iotamod.inverse().lift()
+            self._Iota=MatrixSpace(self._ZZp,4,4)(Iotamod)
+
+        self._preci=preci
+
+        self._Iotainv=Matrix(ZZ,4,4,[self._Iotainv_lift[ii,jj]%self._pN for ii in range(4) for jj in range(4)])
+        return self._Iota
+
+    def iota(self,g,prec=None):
+        if(prec==None):
+            prec=self._preci
+        x=self.get_Iota(prec)
+        gm=Matrix(self._ZZp,4,1,g)
+        return Matrix(self._ZZp,2,2,x*gm)
+
+
+    def _get_edge_stabs(self):
+        if(len(self._edge_stabs)>0):
+            return self._edge_stabs
+        for e in self._edge_list:
+            tmp=self._edge_stabilizer(e.rep)
+            self._edge_stabs.append(tmp)
+        return self._edge_stabs
+
+    def _edge_stabilizer(self,e):
+        p=self._p
+        m=valuation(e.determinant(),p)
+        if((2*m+1)>self._preci):
+            self.get_Iota(2*m+1)
+        vecM=[e*self._Xe[ii]*e.adjoint() for ii in range(4)]
+        M=Matrix(ZZ,4,4,[[vecM[ii][jj,kk] for ii in range(4) ] for jj in range(2) for kk in range(2)])
+        M=self._Iotainv*M
+        vec=[[ZZ(M[ii,kk]%(self._pN)) for ii in range(4)] for kk in range(4) ]
+        vec+=permutations([self._pN,0,0,0])
+        L=self._V.span(vec,ZZ)
+        E=Matrix(ZZ,4,4,L.basis())
+        OGram=self._OQuadForm.matrix()
+        A=E*OGram*E.transpose()
+        n_units=len(self.get_units_of_order())
+        ## Using PARI to get the shortest vector in the lattice (via LLL)
+        v=pari('qfminim('+str(A._pari_())+','+str(0)+','+str(n_units)+',flag=0)')
+        n_vecs=ZZ(v[0].python()/2)
+        if(n_vecs>1):
+            tmp=[]
+            for jj in range(n_vecs):
+                vec=Matrix(ZZ,1,4,[v[2][ii,jj].python() for ii in range(4)])
+                nrd=ZZ((vec*A*vec.transpose())[0,0]/2)
+                w=vec*E
+                x=(w*self._B)[0,0]
+                if(nrd.is_power_of(p**2) and valuation(nrd,p)==2*m):
+                    tmp.append([w.transpose(),m,x!=p**m])
+            return tmp
+        else:
+            return 1
+        return 1
+
+    def _are_equivalent(self,v1,v2,X):
+        p=self._p
+        m=valuation(v1.determinant(),p)+valuation(v2.determinant(),p)
+        if (m%2==1):
+            return 0
+        if((m+1)>self._preci):
+            self.get_Iota(m+1)
+        m=ZZ(m/2)
+        vecM=[v2*X[ii]*v1.adjoint() for ii in range(4)]
+        M=Matrix(RationalField(),4,4,[[vecM[ii][jj,kk] for ii in range(4) ] for jj in range(2) for kk in range(2)])
+        M=self._Iotainv*M
+
+        vec=[[M[ii,kk]%(self._pN) for ii in range(4)] for kk in range(4) ]
+        vec+=permutations([self._pN,0,0,0])
+        L=self._V.span(vec,ZZ)
+        E=Matrix(ZZ,4,4,L.basis())
+        A=E*self._OQuadForm.matrix()*E.transpose()
+
+        ## Using PARI to get the shortest vector in the lattice (via LLL)
+        v=pari('qfminim('+str(A._pari_())+','+str(0)+',1,flag=0)')
+        vec=Matrix(ZZ,1,4,[v[2][ii,0].python() for ii in range(4)])
+        nrd=ZZ((vec*A*vec.transpose())[0,0]/2)
+        if(nrd.is_power_of(p**2) and valuation(nrd,p)==2*m):
+                return [(vec*E).transpose(),m]
+        else:
+               return 0
+
+    def _get_Eichler_order(self):
+        if(self._source=='magma'):
+            #global magma
+            A=self._magma.QuaternionAlgebra(self._Nminus)
+            g=A.gens()
+            #We store the order because we need to split it
+            self._O=A.QuaternionOrder(self._Nplus)
+            OBasis=self._O.Basis()
+            self._A=QuaternionAlgebra((g[0]**2).sage(),(g[1]**2).sage())
+            v=[1]+self._A.gens()
+            self._B=Matrix(self._A,4,1,[sum([OBasis[tt+1][rr+1].sage()*v[rr] for rr in range(4)]) for tt in range(4)])
+
+        else:
+            assert(self._source=='sage')
+            # Note that we can't work with non-maximal orders in sage
+            assert(self._Nplus==1)
+            self._A=QuaternionAlgebra(self._Nminus)
+            v=[1]+self._A.gens()
+            self._O=self._A.maximal_order()
+            OBasis=self._O.basis()
+
+            # Added to test example from Darmon's Book
+            if(self._p==7 and self._Nminus==2):
+                OBasis=[self._A(1),OBasis[1],OBasis[2],OBasis[0]]
+            # End of addition
+
+            self._B=Matrix(self._A,4,1,[OBasis[tt] for tt in range(4)])
+        self._OQuadForm=QuadraticForm(Matrix(ZZ,4,4,[(self._B[ii,0]*self._B[jj,0].conjugate()).reduced_trace() for ii in range(4) for jj in range(4)]))
+
+
+    def _compute(self):
+        self._get_Eichler_order()
+        self.get_Iota(1)
+        num_verts=0
+        V=[Vertex(self,num_verts,Matrix(ZZ,2,2,[1,0,0,1]),[],[])]
+        E=[]
+        v0=V[0]
+        self._vertex_list.append(V[0])
+        num_verts+=1
+        while len(V)>0:
+            #print '#E=',self._S.num_edges(),'#V=',self._S.num_verts()
+            v=V.pop(0)
+            E=self._BT.leaving_edges(v.rep)
+            for e in E:
+                new_edge=True
+                for e1 in v.leaving_edges:
+                    g=self._are_equivalent(e1.rep,e[0],self._Xe)
+                    if(g):
+                        e1.links.append(g)
+                        new_edge=False
+                        break
+                if(new_edge):
+                    #Now we found a new edge. Decide whether its target vertex is new
+                    target=e[1]
+                    new_vertex=True
+                    for v1 in self._vertex_list:
+                        g1=self._are_equivalent(target,v1.rep,self._Xv)
+                        if(g1):
+                            target=v1.rep
+                            #The edge is new, but the vertices are not
+                            new_vertex=False
+                            break
+                    if(new_vertex):
+                        #The vertex is also new
+                        v1=Vertex(self,num_verts,target,[],[])
+                        self._vertex_list.append(v1)
+                        num_verts+=1
+                        self._S.add_vertex(v1.label)
+                        #Add the vertex to the list of pending vertices
+                        V.append(v1)
+
+                    #Find the opposite edge
+                    opp=self._BT.opposite(e[0])
+                    #We add the edge
+                    new_e=Edge(self,self._num_edges,e[0],v,v1,[],[])
+
+                    new_e.links.append(self._are_equivalent(new_e.rep,e[0],self._Xe))
+
+                    #Add the edge to the graph
+                    self._S.add_edge(v.label,v1.label,self._num_edges)
+
+                    new_e_opp=Edge(self,self._num_edges,opp,v1,v,[],[new_e])
+                    assert(len(new_e.opposite)==0)
+                    new_e.opposite.append(new_e_opp)
+
+
+                    if(valuation(new_e.rep.determinant(),self._p)%2==0):
+                        self._edge_list.append(new_e)
+                    else:
+                        self._edge_list.append(new_e_opp)
+
+                    v.leaving_edges.append(new_e)
+                    v.entering_edges.append(new_e_opp)
+                    v1.entering_edges.append(new_e)
+                    v1.leaving_edges.append(new_e_opp)
+
+                    self._num_edges += 1
+        self._cached=True
+
+
+    #Returns (computes if necessary) Up data. This is a vector of length p,
+    # and each entry consists of the corresponding data for the matrix [p,a,0,1]
+    # where a varies from 0 to p-1.
+    # The data is a tuple (acter,edge_images), with edge images being of type
+    # EdgeDecomposition
+    def Up_data(self):
+        try: return self._Up_data
+        except AttributeError: pass
+        self._Up_data=[]
+        vec_a=self._BT.subdivide([1],1)
+        for ii in range(self._p):
+            alpha=vec_a[ii]
+            tmp=[alpha.inverse(),[]]#GraphActionData(alpha.inverse(),[])
+            for e in self._edge_list:
+                tmp[1].append(EdgeDecomposition(self,e.rep*alpha)) #edge_list
+            for e in self._edge_list:
+                tmp[1].append(EdgeDecomposition(self,e.opposite[0].rep*alpha)) #edge_list
+            self._Up_data.append(tmp)
+        return self._Up_data
+
+    def hecke_coset_reps(self,l):
+        try: return self._hecke_coset_reps[l]
+        except KeyError: pass
+
+        if((self._p*self.Nminus()*self.Nplus())%l==0):
+            Sset=[]
+        else:
+            Sset=[self._p]
+        B=self._B
+        BB=Matrix(RationalField(),4,4,[[B[ii,0][jj] for ii in range(4)] for jj in range(4)]).inverse()
+        T=[]
+        T0=[]
+        nn=0
+        alpha1=self._find_elements_in_order(l)[0]
+        print "Got a vector of length l=",l
+        alpha0=sum([alpha1[ii]*B[ii,0] for ii in range(4)])
+        self._alpha1[l]=Matrix(RationalField(),4,1,alpha1)
+
+        if(l==self._p):
+            nninc=1
+        else:
+            nninc=0
+
+        while(len(T)<l+1):
+            print '#T=',len(T),' and bound=',l+1
+            print 'Iteration number: ',1+ZZ(nn)
+            tmp=self._find_elements_in_order(self._p**(2*nn))
+            vec0=[sum([v[ii]*B[ii,0] for ii in range(4)])*alpha0 for v in tmp]
+            vec1=[Matrix(RationalField(),4,1,v) for v in tmp]
+            print 'Analyzing ',len(vec0),' vectors.'
+            while(len(T)<l+1 and len(vec0)>0):
+                print '#T=',len(T),' and bound=',l+1
+                v0=vec0.pop(0)
+                v1=vec1.pop(0)
+                vinv=(v0)**(-1)
+                new=True
+                for tt in range(len(T)):
+                    r=self._A(vinv*T0[tt])
+                    x=BB*Matrix(RationalField(),4,1,[r[ii] for ii in range(4)])
+                    if(all([x[jj,0].is_S_integral(Sset) for jj in range(4)])):
+                        new=False
+                        break
+                if(new):
+                    tmp=[v1,[]]#GraphActionData(v1,[])
+                    x=self.iota(v1)*self.iota(self._alpha1[l])
+                    for e in self._edge_list:
+                        tmp[1].append(EdgeDecomposition(self,x.adjoint()*e.rep,extrapow=nn+nninc))
+                    T.append(tmp)
+                    T0.append(v0)
+            nn+=1
+        self._hecke_coset_reps[l]=T
+        return T
+
+    def _fixed_points(self,x,K):
+        a=x[0,0]
+        b=x[0,1]
+        c=x[1,0]
+        d=x[1,1]
+        t=x.trace()
+        n=x.determinant()
+        A=K(a-d)
+        D=our_sqrt(t**2-4*n,K)
+        return [(A+D)/(2*c),(A-D)/(2*c)]
+
+    def get_units_of_order(self):
+        try: return self._O_units
+        except AttributeError: pass
+        OGram=self._OQuadForm.matrix()
+        v=pari('qfminim('+str(OGram._pari_())+',2,0,flag=0)')
+        n_units=ZZ(v[0].python()/2)
+        v=pari('qfminim('+str(OGram._pari_())+','+str(0)+','+str(n_units)+',flag=0)')
+        self._O_units=[]
+        for jj in range(n_units):
+            vec=Matrix(ZZ,1,4,[v[2][ii,jj].python() for ii in range(4)])
+            self._O_units.append(vec)
+        return self._O_units
+
+    def _conv(self,v):
+        return (Matrix(ZZ,1,4,v)*self._B)[0,0]
+
+    def _find_elements_in_order(self,n,t=None,primitive=False):
+        p=self._ZZp.prime()
+        V=self._OQuadForm.vectors_by_length(n)[n]
+        if(primitive):
+            W=[]
+            for v in V:
+                if not all([v[ii]%p==0 for ii in range(4)]):
+                    W.append(v)
+        else:
+            W=V
+        if(t==None):
+            return W
+        WW=[]
+        for v in W:
+            x=self._conv(v)
+            assert(x.reduced_norm()==n)
+            if(x.reduced_trace()==t):
+                WW.append(v)
+        return WW
+
+    def get_CM_points(self,O,prec=None,ignore_units=False):
+        p=self._ZZp.prime()
+        if(prec==None):
+            prec=self._preci
+        if(not isinstance(O,AbsoluteOrder)):
+            disc=IntegerRing()(O)
+            R = RationalField()['x']
+            f = R([-disc, 0, 1])
+            K=NumberField(f, 'sq', check=False)
+            O=K.maximal_order()
+        else:
+            disc=O.discriminant()
+            K=O.fraction_field()
+        try: all_elts=self._CM_points[disc]
+        except KeyError:
+            if(not self.is_admissible(disc)):
+                return []
+            #var('x')
+
+            w=O.ring_generators()[0]
+            all_elts=[]
+            nn=-1
+            while(len(all_elts)==0):
+                nn+=1
+                print 'nn=',nn
+                all_elts=self._find_elements_in_order(p**(2*nn)*w.norm(),(p**nn)*w.trace())
+
+            all_elts=[[v[ii]/(p**nn) for ii in range(4)] for v in all_elts]
+            # Now we take into account the action of units
+            all_elts0=[self._conv(v) for v in all_elts]
+
+            if(not ignore_units):
+                print 'Looking for units...'
+                units=self._find_elements_in_order(p**(2*nn))
+                units=[[u[ii]/(p**nn) for ii in range(4)] for u in units]
+                print 'Done.'
+                units0=[self._conv(u) for u in units]
+            else:
+                units=[]
+                units0=[]
+
+            all_elts_purged0=[]
+            all_elts_purged=[]
+
+            while(len(all_elts0)>0):
+                v0=all_elts0.pop(0)
+                v1=all_elts.pop(0)
+                new=True
+                for tt in all_elts_purged0:
+                    #compare v1 with tt
+                    for u in units0:
+                        if(tt*u==u*v0):
+                            new=False
+                            break
+                    if(not new):
+                        break
+                if(new):
+                    all_elts_purged0.append(v0)
+                    all_elts_purged.append(v1)
+
+            self._CM_points[disc]=copy(all_elts_purged)
+            if(2*K.class_number()!=len(self._CM_points[disc])):
+                print 'K.class_number()=',K.class_number()
+                print 'Found ',len(self._CM_points[disc]), 'points...'
+        V=self._CM_points[disc]
+
+        all_elts_split=[self.iota(y,prec) for y in V]
+        Kp=Qq(p**2,prec=prec,names='g')
+        W=[]
+        for m in all_elts_split:
+            v=self._fixed_points(m,Kp)
+            W.append(v[0])
+        return W
+
+class ShimuraCurve(SageObject,UniqueRepresentation):
+    def __init__(self,lev,Nplus=1):
+        if (not is_squarefree(lev)):
+            raise NotImplementedError, "level must be squarefree"
+        if(gcd(lev,Nplus)>1):
+            raise ValueError, "level and conductor must be coprime"
+        self._level=lev
+        self._Nplus=Nplus
+
+        if(len(factor(lev))%2!=0):
+            raise ValueError, "level should be divisible by an even number of primes"
+        self._fibers=dict([])
+
+        #Compute the genus using the genus formulas
+        e4=1
+        e3=1
+        mu=Nplus
+        for f in factor(lev):
+            e4=e4*(1-kronecker_symbol(-4,ZZ(f[0])))
+            e3=e3*(1-kronecker_symbol(-3,ZZ(f[0])))
+            mu=mu*(ZZ(f[0])-1)
+        for f in factor(Nplus):
+            if (f[1]==1):
+                e4=e4*(1+kronecker_symbol(-4,ZZ(f[0])))
+                e3=e3*(1+kronecker_symbol(-3,ZZ(f[0])))
+            else:
+                if(kronecker_symbol(-4,ZZ(f[0]))==1):
+                    e4=2*e4
+                else:
+                    e4=0
+                if(kronecker_symbol(-3,ZZ(f[0]))==1):
+                    e3=2*e3
+                else:
+                    e3=0
+            mu=mu*(1+1/ZZ(f[0]))
+        if(lev==1):
+            einf=sum([euler_phi(gcd(d,ZZ(Nplus/d))) for d in ZZ(Nplus).divisors()])
+        else:
+            einf=0
+        self._genus=1+ZZ(mu/12-e3/3-e4/4-einf/2)
+
+    def _repr_(self):
+        return "Shimura Curve of level "+str(self._level)+" and conductor "+str(self._Nplus)
+    def level(self):
+        return self._level
+    def genus(self):
+        return self._genus
+
+    def __getitem__(self,key):
+        return self.special_fiber(key)
+
+    def special_fiber(self,p,source='sage',magma_proc=None):
+        if(not Integer(p).is_prime()):
+            raise ValueError, "must be a prime"
+        if(self._level%Integer(p)!= 0):
+            raise ValueError, "must divide the level"
+
+        if(not self._fibers.has_key(p)):
+            self._fibers[p]=ShimuraCurveFiber(self,p,source=source,magma_proc=magma_proc,compute=False)
+
+        if(not self._fibers[p]._is_cached()):
+            self._fibers[p]._compute()
+            if(self._fibers[p].genus()!=self.genus()):
+                print 'You found a bug!'
+                print 'Computed genus=',self._fibers[p].genus()
+                print 'Theoretical genus=',self._genus
+        return self._fibers[p]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utility.py	Wed Jun 15 12:01:49 2011 +0200
@@ -0,0 +1,143 @@
+from sage.misc.mrange import cartesian_product_iterator
+from sage.rings.all import Qp
+
+def our_conj(x):
+    return x.trace()-x
+
+def act_flt(g,x):
+    return (g[0,0]*x+g[0,1])/(g[1,0]*x+g[1,1])
+
+def getcoords(E,u,prec=20):
+    q = E.parameter(prec=prec)
+    un = u * q**(-(u.valuation()/q.valuation()).floor())
+
+    precn = (prec/q.valuation()).floor() + 4
+
+    # formulas in Silverman II (Advanced Topics in the Arithmetic of Elliptic curves, p. 425)
+
+    xx = un/(1-un)**2 + sum( [q**n*un/(1-q**n*un)**2 + q**n/un/(1-q**n/un)**2-2*q**n/(1-q**n)**2 for n in range(1,precn) ])
+
+    yy = un**2/(1-un)**3 + sum( [q**(2*n)*un**2/(1-q**n*un)**3 - q**n/un/(1-q**n/un)**3+q**n/(1-q**n)**2 for n in range(1,precn) ])
+
+    P=[xx,yy]
+
+    isom = E._inverse_isomorphism(prec=prec)
+    C = isom[0]
+    r = isom[1]
+    s = isom[2]
+    t = isom[3]
+    xx1 = r + C**2 * P[0]
+    yy1 = t + s * C**2 * P[0] + C**3 * P[1]
+    return (xx1,yy1)
+
+
+def our_pow(x,y,K):
+    return K.teichmuller(x)*our_exp(y*our_log(x))
+
+def our_sqrt(x,K):
+    if(x==0):
+        return x
+    x=K(x)
+    p=K.base_ring().prime()
+    z=K.gen()
+    found=False
+    for a,b in cartesian_product_iterator([range(p),range(p)]):
+        y0=a+b*z
+        if((y0**2-x).valuation()>0):
+            found=True
+            break
+    y1=y0
+    y=0
+    while(y!=y1):
+        y=y1
+        y1=(y**2+x)/(2*y)
+    return y
+
+# def our_sqrt0(x,K,x0sqrt,FF):
+#     if(x==0):
+#         return x
+#     x=K(x)
+#     FF=K.residue_field()
+#     z=K.gen()
+#     v=x0sqrt._vector_()
+# 
+#     y0=K(v[0])+K(v[1])*z
+#     assert((y0**2-x).valuation()>0)
+#     y1=y0
+#     y=0
+#     while(y!=y1):
+#         y=y1
+#         y1=(y**2+x)/(2*y)
+#     return y
+
+# def our_sqrt(x,K,allroots=False):
+#     if(x==0):
+#         return x
+#     x=K(x)
+#     FF=K.residue_field()
+#     Q=K.base_ring()
+#     z=K.gen()
+#     z0=FF.gen()
+#     print '.'
+#     x0=FF(x)
+#     print '..'
+#     x0sqrt=x0.sqrt(allroots)
+#     if(allroots):
+#         V=[xsq._vector_() for xsq in x0sqrt]
+#     else:
+#         V=[x0sqrt._vector_()]
+# 
+#     Y0=[K(v[0])+K(v[1])*z for v in V]
+#     assert(all([(y0**2-x).valuation()>0 for y0 in Y0]))
+#     Y1=[y0 for y0 in Y0]
+#     L=range(len(Y1))
+#     Y=[0 for ii in L]
+#     while(not all([Y[ii]==Y1[ii] for ii in L])):
+#         Y=[y1 for y1 in Y1]
+#         Y1=[(y**2+x)/(2*y) for y in Y]
+#     if(len(Y1)==1):
+#         return Y[0]
+#     else:
+#         return Y
+
+def our_log(x,prec=None):
+    K=x.parent()
+    if prec==None:
+        prec=K.precision_cap()+10
+    x0=x.unit_part()
+    y=x0/K.teichmuller(x0)-1
+    tmp=K(0)
+    ypow=y
+    for ii in range(1,prec):
+        tmp+=(-1)**(ii+1)*ypow/ii
+        ypow*=y
+    return tmp
+
+def our_exp(x,prec=None):
+    K=x.parent()
+    if prec==None:
+        prec=K.precision_cap()+10
+    tmp=K(1+x)
+    xpow=x**2
+    iifact=2
+    for ii in range(3,prec):
+        tmp+=xpow/iifact
+        xpow*=x
+        iifact*=ii
+    return tmp
+
+
+def fix_deg_monomials(v,n):
+    if(len(v)==0):
+        return [1]
+    if(len(v)==1):
+        return [v[0]**n]
+    W=[]
+    tmp=1
+    for ii in range(n+1):
+        newW=fix_deg_monomials(v[1:],n-ii)
+        for jj in range(len(newW)):
+            newW[jj]=tmp*newW[jj]
+        tmp=tmp*v[0]
+        W.extend(newW)
+    return W