#Throughout we try to denote Sqrt(-d) by S

############################################################
############################################################
InstallGlobalFunction(HAP_BianchiFundamentalRectangle, #####
function(OQ)                                           #####
local s1, s2, t1, t2;                       
#The fundamental rectangle is [s1,t1*S]x[s2,t2*S].
#The numbers s1,t1,s2,t2 are returned. We also use the 
#subregion Q=[0,t1*S]x[0,t2*S].

if not -OQ!.bianchiInteger mod 4 = 3  then
s1:=-1/2;t1:=1/2;s2:=-1/2;t2:=1/2;
else
s1:=-1/2;t1:=1/2;s2:=-1/4;t2:=1/4;
fi;
return [s1,t1,s2,t2];                                  #####
end);                                                  #####
############################################################ 
############################################################


############################################################
############################################################
InstallGlobalFunction(UnimodularPairStandardForm,      #####
function(x)                                            #####
local y;                                    
#The unimodular pairs [a,b] and [-a,-b] are equivalent.
#One of these pairs is returned.
y:=1*x;
y[1]:=-y[1];
y[2]:=-y[2];
return Maximum(x,y);;                                  #####
end);                                                  #####
############################################################
############################################################


####################################################
####################################################
InstallGlobalFunction(QuadraticIntegersByNorm, #####
function(OQ,N)      
local BI, NW, L, x,y,z;                      
#Returns all quadratic integers a with 
#-N <= Norm(OQ,a) <= N where OQ is a ring of integers

if N<0 then return []; fi;

BI:=OQ!.bianchiInteger;

if IsBound(OQ!.normomega) then NW:=OQ!.normomega; 
else
    if -BI mod 4 =3 then NW:=(1+AbsInt(BI))/4; OQ!.normomega:=NW;
    else NW:=AbsInt(BI); OQ!.normomega:=NW;
    fi;
fi;

L:=[]; #To be a list of integers of appropriate norm.

if -BI mod 4 =3 then
for x in [-(1+Int(Sqrt(1.0*N)))..1+Int(Sqrt(1.0*N))] do
for y in [-(1+Int(Sqrt(1.0*N/NW)))..1+Int(Sqrt(1.0*N/NW))] do
z:=QuadraticNumber((2*x+y)/2,y/2,BI);  
if HAPNorm(OQ,z)<=N then Add(L,z); fi;
od;
od;
else
for x in [-(1+Int(Sqrt(1.0*N)))..1+Int(Sqrt(1.0*N))] do
for y in [-(1+Int(Sqrt(1.0*N/NW)))..1+Int(Sqrt(1.0*N/NW))] do
z:=QuadraticNumber(x,y,BI);
if HAPNorm(OQ,z)<=N then Add(L,z); fi;
od;
od;
fi;

SortBy(L,x->HAPNorm(OQ,x));

return L;                                    #####
end);                                        #####
##################################################
##################################################


########################################################
########################################################
InstallGlobalFunction(HAP_UnimodularComplements,   #####
function(OQ,a)                                     #####
local Q, nrma,BI, rat, irrat, N, L, U, b, x,s1, t1, s2, t2;
#Returns the list of unimodular pairs (a,b) in OQxOQ that 
#intersect with the fundamental rectangle Q. The pair (a,b) 
#is returned as [a,b,Norm(a)]. Here we think of (a,b) as 
#being represented by a circular disk with centre b/a and radius |a| 
#in the complex plane.


BI:=OQ!.bianchiInteger;

Q:=HAPQuadratic(a);
nrma:=HAPNorm(OQ,a);

L:=QuadraticIntegersByNorm(OQ,(1-BI)*nrma);   

L:=Filtered(L,x->not x=0);
U:=[];
for b in L do
if IsQQUnimodularPair(OQ,[a,b,nrma]) then

if Norm(QuadraticIdeal(OQ,[QuadraticToCyclotomic(a),QuadraticToCyclotomic(b)])) = 1 
then Add(U,UnimodularPairStandardForm([a,b,nrma])); fi;

fi;

#fi;
od;
#Add(U,[1,0,1]);      #Added July 2025
return SSortedList(U);                       #####
end);                                        #####
##################################################
##################################################


#######################################################
#######################################################
InstallGlobalFunction(HAP_SqrtInequality,         #####
function(a,b,c)                                  #####
#Inputs three positive rational numbers and       
#returns Sqrt(a) + Sqrt(b) <= Sqrt(c).
if a+b>c then return false; fi;
return  4*a*b <= (c-a-b)^2;                       #####
end);                                             #####
#######################################################
#######################################################


#######################################################
#######################################################
InstallGlobalFunction(HAP_SqrtStrictInequality,   #####
function(a,b,c)                                  #####
#Inputs three positive rational numbers and      
#returns Sqrt(a) + Sqrt(b) <= Sqrt(c).
if a+b>=c then return false; fi;
return  4*a*b < (c-a-b)^2;                        #####
end);                                             #####
#######################################################
#######################################################


##########################################################
##########################################################
InstallGlobalFunction(HAP_IsRedundantUnimodularPair, #####
function(OQ,L,x)                                     #####
local y,l,m,lm,nrm,a,b,ba,rm,ra,nrmm,nrma;        
#Inputs a ring of integers OQ, a list L of unimodular 
#pairs in OQ, and a unimodular pair x. Returns true if 
#x is 'redundant' and false otherwise. We think of a
#unimodular pair [a,b] as a sphere of radius 1/|a| and centre
#b/a in the complex plane. So we assume not a=0. A sphere is
#redundant in this function if it lies underneath some other 
#sphere.

l:=x[2];
m:=x[1];
nrmm:=x[3];

for y in L do
a:=y[1];
b:=y[2];
nrma:=y[3];

nrm:=HAPNorm(OQ,l*a - b*m)/(nrmm*nrma);
if not nrm > 1/nrma then #so circle y might contain circle x
if HAP_SqrtInequality(nrm,1/nrmm,1/nrma) then return true; fi;
fi;
od;

return false;                                #####
end);                                        #####
##################################################
##################################################


######################################################
######################################################
InstallGlobalFunction(UnimodularPairCoordinates, #####
function(OQ,x)                                   #####
local a,b,Q,ax,ay,bx,by, D,X,Y,BI;          
#inputs a unimodular pair [a,b,1/r^2] and returns [x1,x2,r^2] 
#where r is the radius and [x1,x2*S] the centre of the 
#corresponding hemisphere. Here S:=Sqrt(BianchiInteger)

if Length(x)>3 then return [x[4],x[5],x[6]]; fi;

a:=x[2];
b:=x[1];
BI:=OQ!.bianchiInteger;

Q:=HAPQuadratic(2*a);
ax:=Q.a/(2*Q.d);
ay:=Q.b/(2*Q.d);
Q:=HAPQuadratic(2*b);
bx:=Q.a/(2*Q.d);
by:=Q.b/(2*Q.d);

D:=bx^2 - by^2*BI;
X:=ax*bx-ay*by*BI;
Y:=ay*bx-ax*by;

D:=D^-1;
x[4]:=X*D;
x[5]:=Y*D;
x[6]:=1/x[3];

return  [x[4],x[5],x[6]];
                                             #####
end);                                        #####
##################################################
##################################################


##################################################
##################################################
InstallGlobalFunction(UnimodularPairs,       #####
function(arg)                                #####
local OQ,N,bool,BOOL,L,LL,a,b,fn,U,UU,u,w,   
K,i,j,nrm,NRM;
#Returns a list of pairs [a,b] with Norm(a)<=N and (a,b)
#a unimodular pair intersecting the closed rectangle Q=
#[0,t1*S]x[0,t2*S]. With arg[3]=false no redundancies are 
#eliminated. With arg[4]=LL a list of unimodular pairs up 
#to a given norm -- some work is avoided.

OQ:=arg[1];
N:=arg[2];
if Length(arg)>2 then bool:=arg[3]; else bool:=true; fi;
#bool=true means that some obvious redundancies will be removed.
if Length(arg)>3 then LL:=List(arg[4],x->1*x); else LL:=[]; fi;
#LL is a list of all unimodular pairs of a neighbourhood of the rectangle 
#up to a certain norm.
if Length(arg)>4 then BOOL:=arg[5]; else BOOL:=false; fi;
#BOOL=true means a slightly larger neighbourhood of the fundamental recangle is considered.


L:=QuadraticIntegersByNorm(OQ,N);
L:=Filtered(L,x->not x=0);
if Length(LL)>0 then
NRM:=Maximum(List(LL,x->x[3]));
L:=Filtered(L,x->HAPNorm(OQ,x)>NRM);
else NRM:=0;
fi;

for a in L do
U:=HAP_UnimodularComplements(OQ,a);

if bool then
UU:=[];
for u in U do
  if not HAP_IsRedundantUnimodularPair(OQ,LL,u) then
  Add(UU,u);
  fi;
od;
Append(LL,UU);
else
Append(LL,U);
fi;
od;

#LL:=SSortedList(LL);
LL:=Classify(LL,x->x);;
LL:=List(LL,x->x[1]);
UU:= Filtered(LL,x->x[3]>NRM);
Append(LL,UU);

if bool then
for i in Reversed([2..Length(LL)]) do
nrm:=LL[i][3];
for j in Reversed([1..i-1]) do
if LL[j][3] < nrm then
if HAP_IsRedundantUnimodularPair(OQ,LL{[1..j]},LL[i]) then
LL[i]:=0;
fi;
break;
fi;
od;
od;
fi;

LL:=Filtered(LL,x->not x=0);
LL:=List(LL,x->[x[1],x[2],HAPNorm(OQ,x[1])]);
#LL:=SSortedList(LL);
LL:=Classify(LL,x->x);;
LL:=List(LL,x->x[1]);

SortBy(LL,x->x[3]);
#List(LL,x->UnimodularPairCoordinates(OQ,x));
return LL;                                   #####
end);                                        #####
##################################################
##################################################


#######################################################
#######################################################
InstallGlobalFunction(UnimodularIntersectingLine, #####
function(OQ,a,b)                                  #####
local ca,cb,u,v,w,S,ra2,rb2,u2,lambda;
#Inputs two unimodular pairs a, b representing circles
#in the complex plane. It is assumed that the circles 
#intersect. A pair [v,w] is returned where w is the 
#direction vector of the radical axis and v is the point 
#where the radical acis intersects the line joining 
#the centres of a and b. 

S:=QuadraticNumber(0,1,-OQ!.bianchiInteger);

ca:=UnimodularPairCoordinates(OQ,a);
ca[2]:=ca[2]*S;
cb:=UnimodularPairCoordinates(OQ,b);
cb[2]:=cb[2]*S;
u:=cb-ca;
u:=u{[1,2]};
w:=[u[2],-u[1]];

if u=[0,0] then return [ca{[1,2]},w]; fi;

ra2:=ca[3];
rb2:=cb[3];
#u2:=Norm(OQ,u[1])+Norm(OQ,u[2]);
u2:=u[1]^2+u[2]^2;

ca:=ca{[1,2]};
lambda:=(ra2-rb2+u2)/(2*u2);

v:=ca+lambda*u;
return [v,w];                                     #####
end);                                             #####
#######################################################
#######################################################


#########################################################
#########################################################
InstallGlobalFunction(UnimodularIntersectingPoint,  #####
function(OQ,a,b,c,Lab,Lac)                          #####
local v,w,vv,ww,D,lambda;
#Inputs three unimodular pairs a,b,c thought of as
#circles in the complex plane. It is assumed that the
#circles intersect pairwise. The radical centre v
#is returned.

#Lab:=UnimodularIntersectingLine(OQ,a,b);
#Lac:=UnimodularIntersectingLine(OQ,a,c);

v:=Lab[1];
w:=Lab[2];
vv:=Lac[1];
ww:=Lac[2];
D:=w[1]*(-ww[2])+w[2]*ww[1];
if D=0 then
return fail; fi;
D:=D^-1;
lambda:=D*( -ww[2]*(vv[1]-v[1]) + ww[1]*(vv[2]-v[2]) );

return v+lambda*w;                                #####
end);                                             #####
#######################################################
#######################################################


############################################################
############################################################
InstallGlobalFunction(HAP_HeightOfPointOnSphere,       #####
function(OQ,z,p)                                       #####
local c,S;                                          
#Inputs the real and imaginary coordinates [x,y] of a point
#z=x+i*y in the complex plain and unimodular pair p. 
#The pair p determines a hemisphere centred at some point 
#on the complex plane. The square h^2 of the height h of 
#the lift of z to the hemisphere is returned. If z does 
#not lie under the hemisphere then no lift exists and a 
#negative square height will be returned. 

S:=QuadraticNumber(0,1,-OQ!.bianchiInteger);

c:=UnimodularPairCoordinates(OQ,p);
S:=c[3]-(z[1]-c[1])^2-(z[2]-S*c[2])^2;
return S;                                             #####
end);                                                 #####
###########################################################
###########################################################


################################################################
################################################################
InstallGlobalFunction(HAP_AreIntersectingUnimodularPairs,  #####
function(OQ,a,b)                                           #####
local A, B, BI;                                        
#Inputs a ring of integers OQ of an imaginary quadratic field
#together with two unimodular pairs.
#It returns true if the two circles in the commplex plane
#corresponding to the unimodular pairs intersect.  

BI:=AbsInt(OQ!.bianchiInteger);

A:=UnimodularPairCoordinates(OQ,a);
B:=UnimodularPairCoordinates(OQ,b);
return not HAP_SqrtStrictInequality(A[3],B[3], (A[1]-B[1])^2 + BI*(A[2]-B[2])^2 );
                                                       #####
end);                                                  #####
############################################################
############################################################


################################################################
################################################################
InstallGlobalFunction(HAP_AreStrictlyIntersectingUnimodularPairs,  #####
function(OQ,a,b)                                           #####
local A, B, BI;
#Inputs a ring of integers OQ of an imaginary quadratic field
#together with two unimodular pairs.
#It returns true if the two circles in the commplex plane
#corresponding to the unimodular pairs intersect.

BI:=AbsInt(OQ!.bianchiInteger);

A:=UnimodularPairCoordinates(OQ,a);
B:=UnimodularPairCoordinates(OQ,b);
return not HAP_SqrtInequality(A[3],B[3], (A[1]-B[1])^2 + BI*(A[2]-B[2])^2 );
                                                       #####
end);                                                  #####
############################################################
############################################################



#################################################################
#################################################################
InstallGlobalFunction(HAP_Are3IntersectingUnimodularPairs,  #####
function(arg)                                               #####
local OQ, a,b,c, Lab, Lac, Lbc, A, B, C, v, w;                
#Inputs a ring of integers OQ of an imaginary quadratic field
#together with three unimodular pairs that intersect pairwise.
#It returns true if the three circles in the commplex plane
#corresponding to the unimodular pairs have common region 
#of overlap. The common intersections may be a point. 
#Otherwise false is returned.

OQ:=arg[1];
a:=arg[2];
b:=arg[3];
c:=arg[4];
Lab:=arg[5];
Lac:=arg[6];
Lbc:=arg[7];

###########################
A:=UnimodularPairCoordinates(OQ,a);
B:=UnimodularPairCoordinates(OQ,b);
C:=UnimodularPairCoordinates(OQ,c);

if HAP_HeightOfPointOnSphere(OQ,Lab[1],c)>=0 then return [true]; fi;

if HAP_HeightOfPointOnSphere(OQ,Lac[1],b)>=0 then return [true]; fi;

v:=Lab[2]{[2,1]};
v[1]:=-v[1];
if v*Lac[2]=0 then return [true]; fi;    #circles are colinear

#v:=UnimodularIntersectingLine(OQ,c,b);
if HAP_HeightOfPointOnSphere(OQ,Lbc[1],a)>=0 then return [true]; fi;

v:=UnimodularIntersectingPoint(OQ,a,b,c,Lab,Lac);

if HAP_HeightOfPointOnSphere(OQ,v,a)<0 then return [false]; fi;
if HAP_HeightOfPointOnSphere(OQ,v,b)<0 then return [false]; fi;
if HAP_HeightOfPointOnSphere(OQ,v,c)<0 then return [false]; fi;

return [true];                               #####
end);                                        #####
##################################################
##################################################


##################################################
##################################################
InstallGlobalFunction(HAP_PrintFloat,        #####
function(xx)                                 #####
local x,s,m, p, l, sgn;                      
x:=xx;
sgn:=SignFloat(x);
x:=AbsoluteValue(x);

s:=SplitString(String(x),'e');;
if Length(s)=1 then
s:=SplitString(String(xx),'e');;
return s[1]{[1..Minimum(5,Length(s[1]))]}; fi;

p:=EvalString(s[2]);;
l:=Length(s[1]);
m:=s[1]{[2..l]};
if p<0 then
s:=Concatenation(ListWithIdenticalEntries(-p,"0"));
s:=Concatenation(".",s,m{[1..l-1]});
s:=s{[1..5]};
if sgn<0 then s:=Concatenation("-",s); fi;
return s;
fi;
m:=EvalString(m);
s:=10^p*m;
s:=Concatenation(String(s),"00000");
s:=Concatenation(s{[1..p]},".",s{[p+1..Minimum(l,p+5)]});
if sgn<0 then s:=Concatenation("-",s); fi;
return s;                                    #####
end);                                        #####
##################################################
##################################################


###################################################
###################################################
InstallGlobalFunction(DisplayUnimodularPairs, #####
function(arg)                                #####
local OQ,L, C, file, i, x, w, BI,ABI, S1,S,SS,s1,t1,s2,t2;

OQ:=arg[1];
L:=arg[2];
if Length(arg)=3 then C:=arg[3]; 
else
C:=[];
fi;


BI:=OQ!.bianchiInteger;
ABI:=AbsInt(BI);
S:=Sqrt(1.0*ABI);
S1:=Sqrt(1.0*ABI);
if -BI mod 4 =3 then S:=0.25*S; else S:=0.5*S; fi;
SS:=HAP_PrintFloat(S);
SS:=SS{[1..Minimum(Length(SS),5)]};

C:=List(C,x->[x[1], x[2]*(1/QuadraticNumber(0,1,ABI))]);

C:=List(C,x->[x[1]!.rational,x[2]!.rational]);

C:=List(C,x->[1.0*x[1],S1*x[2]]);

file:="tmpp.asy";
PrintTo(file,"size(0,200);\n");
AppendTo(file,"defaultpen(0.2);\n");
AppendTo(file,"pen colour=blue+opacity(0.50);\n");
AppendTo(file,"real S=");
AppendTo(file,SS);
AppendTo(file,";\n");

s1:=HAP_PrintFloat(-0.5);t1:=HAP_PrintFloat(0.5);s2:=HAP_PrintFloat(-S);t2:=HAP_PrintFloat(S);

AppendTo(file,"path g=(-0.5,-S)--(0.5,-S)--(0.5,S)--(-0.5,S)--cycle;\n");

AppendTo(file,"picture fd;\n");

S:=Sqrt(1.0*ABI);
for x in L do
w:=UnimodularPairCoordinates(OQ,x);
w:=1.0*w;
AppendTo(file,"real r=");
AppendTo(file,"sqrt(");
AppendTo(file,HAP_PrintFloat(w[3]));
AppendTo(file,");\n");
AppendTo(file,"pair z=(");
AppendTo(file,HAP_PrintFloat(w[1]));
AppendTo(file,",");
AppendTo(file,HAP_PrintFloat(w[2]*S));
AppendTo(file,");\n");
AppendTo(file,"path c=circle(z,r);\n");
AppendTo(file,"fill(fd,c,colour);");
AppendTo(file,"draw(fd,c);\n");
od;

#AppendTo(file,"clip(fd,g);\n");
AppendTo(file,"draw(fd,g,white+linewidth(.3));\n");

for x in C do
AppendTo(file,"real r=0.01");
AppendTo(file,";\n");
AppendTo(file,"pair z=(");
AppendTo(file,HAP_PrintFloat(x[1]));
AppendTo(file,",");
AppendTo(file,HAP_PrintFloat(x[2]));
AppendTo(file,");\n");
AppendTo(file,"path c=circle(z,r);\n");
AppendTo(file,"fill(fd,c,red);");
AppendTo(file,"draw(fd,c);\n");
od;

AppendTo(file,"add(fd);\n");


Exec("asy -f pdf tmpp.asy; evince tmpp.pdf");     #####
end);                                              #####
#######################################################
#######################################################



#####################################################
#####################################################
InstallGlobalFunction(Display3DUnimodularPairs, #####
function(arg)                                   #####
local  OQ,LL,Lst,file, cols,x,xx, w, BI,ABI, S,SS, L,R,T,B,sz,V,clr;

OQ:=arg[1];
Lst:=arg[2];
if Length(arg)=2 then
LL:=List(Lst,a->[a,"blue"]);
else
cols:=arg[3];
LL:=List([1..Length(Lst)],i->[Lst[i],cols[i]]);
fi;

V:=HAP_VertexHeights(OQ,Lst)[2];
V:=V!.Heights;

V:=Filtered(V,x->x>0);

V:=List(V,x->x!.rational);

V:=1.0*Minimum(V);
V:=Sqrt(V);
V:=Minimum(0.05,V/2);


BI:=OQ!.bianchiInteger;
ABI:=AbsInt(BI);
S:=Sqrt(1.0*ABI);
if -BI mod 4 = 3 then S:=0.25*S; else S:=0.5*S; fi;
SS:=HAP_PrintFloat(S);
SS:=SS{[1..Minimum(Length(SS),5)]};
sz:=SizeScreen();
SizeScreen([200,24]);
file:="tmpp.asy";
PrintTo(file,"import solids;\n import graph3;\n import three;\n size(1000);\n");
AppendTo(file,"currentprojection=orthographic(5,4,10);\n");
AppendTo(file,"currentlight=Headlamp;\n");
AppendTo(file,"nslice=4*nslice;\n");
AppendTo(file,"revolution b=sphere(O,1);\n");

L:=[];R:=[];T:=[];B:=[];
S:=Sqrt(1.0*ABI);
for xx in LL do
x:=xx[1]; clr:=xx[2];
w:=UnimodularPairCoordinates(OQ,x);
w:=1.0*w;
w[2]:=w[2]*S;
Add(L,w[1]-Sqrt(w[3]));
Add(R,w[1]+Sqrt(w[3]));
Add(T,w[2]+Sqrt(w[3]));
Add(B,w[2]-Sqrt(w[3]));
AppendTo(file,"real r=");
AppendTo(file,"sqrt(");
AppendTo(file,HAP_PrintFloat(w[3]));
AppendTo(file,");\n");
AppendTo(file,"draw(shift(",HAP_PrintFloat(w[1]),",",HAP_PrintFloat(w[2]),",0)*scale3(r)*surface(b),",clr,"+opacity(1));\n");
od;


AppendTo(file,"real S=");
AppendTo(file,SS);
AppendTo(file,";\n");
AppendTo(file,"real V=");
AppendTo(file,V);
AppendTo(file,";\n");

#if -BI mod 4 = 3 then
AppendTo(file,"path3 g=(-0.5,-S,1)--(0.5,-S,1)--(0.5,S,1)--(-0.5,S,1)--cycle;\n");
#else
#AppendTo(file,"path3 g=(0,0,1)--(1,0,1)--(1,S,1)--(0,S,1)--cycle;\n");
#AppendTo(file,"path3 gg=(0,0,V)--(1,0,V)--(1,S,V)--(0,S,V)--cycle;\n");
#fi;

AppendTo(file,"draw(g,white+linewidth(0.3));");

AppendTo(file,"real L=");
AppendTo(file,Minimum(L));
AppendTo(file,";\n");

AppendTo(file,"real R=");
AppendTo(file,Maximum(R));
AppendTo(file,";\n");

AppendTo(file,"real T=");
AppendTo(file,Maximum(T));
AppendTo(file,";\n");

AppendTo(file,"real B=");
AppendTo(file,Minimum(B));
AppendTo(file,";\n");


AppendTo(file,"triple p0 =(L,T,V), p1=(R,T,V), p2 = (R,B,V), p3 = (L,B,V);\n");
AppendTo(file,"triple q0 =(L,T,-1), q1=(R,T,-1), q2 = (R,B,-1), q3 = (L,B,-1);\n");

AppendTo(file,"skeleton s;\n");
AppendTo(file,"b.transverse(s,reltime(b.g,0.5),P=currentprojection);\n");
AppendTo(file,"draw(surface(p0--p1--p2--p3--cycle),white);\n");
AppendTo(file,"draw(surface(q0--q1--q2--q3--cycle),white);\n");
AppendTo(file,"draw(surface(p0--p1--q1--q0--cycle),white);\n");
AppendTo(file,"draw(surface(q1--p1--p2--q2--cycle),white);\n");
AppendTo(file,"draw(surface(q3--q2--p2--p3--cycle),white);\n");
AppendTo(file,"draw(surface(p0--q0--q3--p3--cycle),white);\n");

SizeScreen(sz);

Exec("asy -V tmpp.asy");                           #####
end);                                              #####
########################################################
########################################################


###################################################
###################################################
InstallGlobalFunction(IsQUnimodularPair,     #####
function(arg)                                 #####
local OQ,x,BI,ABI,L,s1,t1,s2,t2,T,S;                
#Inputs a unimodular pair x=[a,b] and returns true
#if the associated hemisphere touches the 
#fundamental rectangle [s1,t1*S]x[s2,t2*S] in the 
#complex plane.


OQ:=arg[1];
BI:=OQ!.bianchiInteger;
ABI:=AbsInt(BI);
x:=arg[2];
L:=HAP_BianchiFundamentalRectangle(OQ);
s1:=L[1];t1:=L[2];; s2:=L[3]; t2:=L[4];

T:=UnimodularPairCoordinates(OQ,x);

if T[1]>=0 and
(not  HAP_SqrtStrictInequality(t1^2,T[3],T[1]^2) ) and
 T[2]>=s2 and T[2]<=t2
then return true; fi;

if T[2]>=0 and 
(not HAP_SqrtStrictInequality(ABI*t2^2,T[3],ABI*T[2]^2) ) and
 T[1]>=s1 and T[1]<=t1
then return true; fi;


if T[1]<=0 and #=
(not  HAP_SqrtStrictInequality(s1^2,T[3],T[1]^2)  ) and
 T[2]>=s2 and T[2]<=t2
then return true; fi;


if T[2]<=0 and   #=
(not  HAP_SqrtStrictInequality(ABI*t2^2,T[3],ABI*T[2]^2)  ) and
 T[1]>=s1 and T[1]<=t1
then return true; fi;


if (T[1]-s1)^2 + ABI*(T[2]-s2)^2<=T[3]
then return true; fi;

if (T[1]-s1)^2 + ABI*(T[2]-t2)^2<=T[3]
then  return true; fi;

if (T[1]-t1)^2 + ABI*(T[2]-s2)^2<=T[3]
then  return true; fi;

if (T[1]-t2)^2 + ABI*(T[2]-t2)^2<=T[3]
then  return true; fi;

return false;
                                             #####
end);                                        #####
##################################################
##################################################


###################################################
###################################################
InstallGlobalFunction(IsQQUnimodularPair,     #####
function(arg)                                 #####
local OQ,x,BI,ABI,L,s1,t1,s2,t2,T,S;
#Inputs a unimodular pair x=[a,b] and returns true
#if the associated hemisphere touches the 
#fundamental Q-rectangle [0,t1]x[0,t2] in the complex 
#plane.


OQ:=arg[1];
BI:=OQ!.bianchiInteger;
ABI:=AbsInt(BI);
x:=arg[2];
L:=HAP_BianchiFundamentalRectangle(OQ);
s1:=0;t1:=L[2];; s2:=0; t2:=L[4];
#s1:=L[1];t1:=L[2];; s2:=L[3]; t2:=L[4];

T:=UnimodularPairCoordinates(OQ,x);

if T[1]>=0 and   #=
(not  HAP_SqrtStrictInequality(t1^2,T[3],T[1]^2) )
and T[2]>=0 and T[2]<=t2
then return true; fi;

if T[2]>=0 then    #=
if (not HAP_SqrtStrictInequality(ABI*t2^2,T[3],ABI*T[2]^2) )
and T[1]>=0 and T[1]<=t1
then return true; fi;
fi;

if T[1]<=0 then #=
if T[1]^2<=T[3]
and T[2]>=0 and T[2]<=t2
then return true; fi;
fi;

if T[2]<=0 then  #=
if ABI*T[2]^2<=T[3]
and T[1]>=0 and T[1]<=t1
then return true; fi;
fi;

if (T[1]-s1)^2 + ABI*(T[2]-t1)^2<=T[3]
then return true; fi;

if (T[1]-s1)^2 + ABI*(T[2]-t2)^2<=T[3]
then return true; fi;

if (T[1]-s2)^2 + ABI*(T[2]-t1)^2<=T[3]
then return true; fi;

if (T[1]-s2)^2 + ABI*(T[2]-t2)^2<=T[3]
then return true; fi;

return false;
                                             #####
end);                                        #####
##################################################
##################################################


################################################################
################################################################
InstallGlobalFunction(SimplicialComplexOfUnimodularPairs,  #####
function(OQ,L)                                             #####
local E,S,K,Lines,Points,bool,p,i,j,k,x;                  
#Inputs a list of unimodular pairs corresponding to disks 
#in the complex plane. The nerve of these disks is
#returned as an abstract simplicial complex.

Lines:=[];
K:=List(L,x->[Position(L,x)]);;
E:=[];
for i in [1..Length(L)] do
Lines[i]:=[];
for j in [i+1..Length(L)] do
if HAP_AreIntersectingUnimodularPairs(OQ,L[i],L[j])
then
Add(E,[i,j]);
fi;
od;od;

E:=SSortedList(E);

Append(K,E);

Points:=[];
for x in E do
i:=x[1]; j:=x[2];
for k in [j+1..Length(L)] do
if [i,k] in E and [j,k] in E then
  if not IsBound(Lines[i][j]) then
     Lines[i][j]:=UnimodularIntersectingLine(OQ,L[i],L[j]);
  fi;
  if not IsBound(Lines[i][k]) then
     Lines[i][k]:=UnimodularIntersectingLine(OQ,L[i],L[k]);
  fi;
  if not IsBound(Lines[j][k]) then
     Lines[j][k]:=UnimodularIntersectingLine(OQ,L[j],L[k]);
  fi;
  p:= HAP_Are3IntersectingUnimodularPairs(OQ,L[i],L[j],L[k],
                                  Lines[i][j],Lines[i][k],Lines[j][k]);
  if p[1] then
     Add(K,[i,j,k]);
     if not IsBound(p[2]) then
         p[2]:=UnimodularIntersectingPoint(OQ,
                  L[i],L[j],L[k],Lines[i][j],Lines[i][k]);
     fi;
     Add(Points,p[2]);
  fi;
fi;
od;od;


K:=MaximalSimplicesToSimplicialComplex(K);
K!.Lines:=Lines;
K!.Points:=Points;
K!.unimodularPairs:=L;
return K;                                      #####
end);                                          #####
####################################################
####################################################


#####################################################
#####################################################
InstallGlobalFunction(QQNeighbourhoodOfUnimodularPairs,
function(arg)                                   #####
local OQ,L,U,F,FF, Q, BI, x, p, u, S,St;      #####
#Inputs a list of unimodular pairs and returns a list 
#of all pairs/hemispheres in L that intersect
#the rectangle Q=[0,t1]x[0,t2]. 
OQ:=arg[1];
L:=arg[2];
F:=[];
Q:= Filtered(L,x->IsQQUnimodularPair(OQ,x));   
Q:=List(Q,x->UnimodularPairStandardForm(x));
BI:=OQ!.bianchiInteger;
if -BI mod 4 = 3 then
St:=[QuadraticNumber(1/2,1/2,BI),QuadraticNumber(-1/2,1/2,BI)];
else
St:=[QuadraticNumber(0,1,BI)];
fi;

for x in Q do
for S in St do
Add(F,x);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2]),x[3]]);
Add(F,[x[1],-x[2],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2]),x[3]]);
##############
Add(F,[x[1],x[2]-x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])+QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])-QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+x[1],x[3]]);
##############
Add(F,[x[1],x[2]-S*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])-S*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],x[2]-(1+S)*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])+(1-S)*QuadraticNumberConjugate(x[1]),x[3]]);
##############
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])+S*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+S*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])+(-1+S)*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+(1+S)*x[1],x[3]]);
od;
od;

F:=Classify(F,x->UnimodularPairStandardForm(x){[1,2]});;
F:=List(F,x->x[1]);
F:=List(F,x->x{[1,2,3]});

U:=Filtered(F,x->IsQQUnimodularPair(OQ,x));     
FF:=[];
for x in F do
for u in U do
if HAP_AreIntersectingUnimodularPairs(OQ,x,u) then
Add(FF,x); break; fi;
od;od;

return FF;                                       #####
end);                                           #####
#####################################################
#####################################################


#####################################################
#####################################################
InstallGlobalFunction(QNeighbourhoodOfUnimodularPairs,
function(arg)                                   #####
local OQ,L,U,F,FF, Q, BI, x, p, u, S,St;      #####
#Inputs a list of unimodular pairs and returns a list 
#of the 'images' of pairs in L that intersect the  
#fundamental rectangle [s1,t1]x[s2,t2]. 
OQ:=arg[1];
L:=arg[2];
F:=[];
Q:= Filtered(L,x->IsQQUnimodularPair(OQ,x));   
Q:=List(Q,x->UnimodularPairStandardForm(x));
BI:=OQ!.bianchiInteger;
if -BI mod 4 = 3 then
St:=[QuadraticNumber(1/2,1/2,BI),QuadraticNumber(-1/2,1/2,BI)];
else
St:=[QuadraticNumber(0,1,BI)];
fi;

for x in Q do
for S in St do
Add(F,x);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2]),x[3]]);
Add(F,[x[1],-x[2],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2]),x[3]]);
##############
Add(F,[x[1],x[2]-x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])+QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])-QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+x[1],x[3]]);
##############
Add(F,[x[1],x[2]-S*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])-S*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],x[2]-(1+S)*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),-QuadraticNumberConjugate(x[2])+(1-S)*QuadraticNumberConjugate(x[1]),x[3]]);
##############
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])+S*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+S*x[1],x[3]]);
Add(F,[QuadraticNumberConjugate(x[1]),QuadraticNumberConjugate(x[2])+(-1+S)*QuadraticNumberConjugate(x[1]),x[3]]);
Add(F,[x[1],-x[2]+(1+S)*x[1],x[3]]);
od;
od;

F:=Classify(F,x->UnimodularPairStandardForm(x){[1,2]});;
F:=List(F,x->x[1]);
F:=List(F,x->x{[1,2,3]});
#return F;    

U:=Filtered(F,x->IsQUnimodularPair(OQ,x));

return U;                                       #####
end);                                           #####
#####################################################
#####################################################



#####################################################
#####################################################
InstallGlobalFunction(HAP_VertexHeights,        #####
function(OQ,LL)                                 #####
local L,Lines, Points, Heights, Cusps, K, 
 s, c, D,i,bool,S,ABI,a,b;

ABI:=AbsInt(OQ!.bianchiInteger);
L:=QQNeighbourhoodOfUnimodularPairs(OQ,LL);    
K:=SimplicialComplexOfUnimodularPairs(OQ,L);  
Lines:=K!.Lines;
Points:=K!.Points;
S:=K!.simplicesLst[3];

Heights:=[];
for i in [1..Length(S)]  do
if Points[i] = fail then Heights[i]:=-infinity; else
Heights[i]:= HAP_HeightOfPointOnSphere(OQ,Points[i],L[S[i][1]]);
fi;
od;
K!.Heights:=Heights;

Cusps:=[];
for i in [1..Length(Points)] do
if Heights[i]=0 then
bool:=true;
for s in L do 
c:=UnimodularPairCoordinates(OQ,s);
b:=Points[i][2]*(1/QuadraticNumber(0,1,ABI));
a:=(c[1]-Points[i][1])^2 + ABI*(c[2]-b)^2;
if a < c[3] then
bool:=false; break; fi;
od;
if bool then Add(Cusps, Points[i]); fi;
fi;
od;

Cusps:=SSortedList(Cusps);
K!.Cusps:=Cusps;

return [Cusps,K];                               #####
end);                                           #####
#####################################################
#####################################################


###################################################
###################################################
InstallGlobalFunction(UnimodularPairsReduced_NN, #####
function(OQ,LL)                                  #####
local L, Lines, Points, K, K3, E,EE, V, VV,
fn, fn2, v, w, h, dist, bool, BOOL, C,D,DD,i,F,S,M;

#L:=QQNeighbourhoodOfUnimodularPairs(OQ,LL);
if LL=[] then return [LL,[]]; fi;
L:=LL;

K:=SimplicialComplexOfUnimodularPairs(OQ,L);
Lines:=K!.Lines;
Points:=K!.Points;

K3:=K!.simplicesLst[3];

DD:=Filtered([1..K!.nrSimplices(2)], v-> IsQQUnimodularPair(OQ,L[K3[v][1]])
or IsQQUnimodularPair(OQ,L[K3[v][2]])
or IsQQUnimodularPair(OQ,L[K3[v][3]]) );
#Need to check that this is OK here

S:=K3{DD};
Points:=Points{DD};

V:=List([1..Length(S)],x->[S[x][1],S[x][2],S[x][3], Points[x] ] );

V:=Filtered(V,v->not v[4]=fail);

V:=List(V,v-> [v[1],v[2],v[3],v[4],HAP_HeightOfPointOnSphere(OQ,v[4],L[v[1]])]);
V:=Filtered(V,v->v[5]>=0);

VV:=[];
for v in V do
#h:=HAP_HeightOfPointOnSphere(OQ,v[4],L[v[1]]) ;
h:=v[5];
bool:=true; ;
#NEXT LINE IS TIME CONSUMING
#DD:=Filtered(K!.simplicesLst[3],x-> (v[1] in x and v[2] in x) or (v[1] in x and v[3] in x) or (v[3] in x and v[2] in x));  #?????????
DD:=Filtered(K!.simplicesLst[3],x-> (v[1] in x and v[2] in x) );

DD:=Concatenation(DD);
DD:=SSortedList(DD);
for i in DD do
w:=L[i];
#for w in L do
if  HAP_HeightOfPointOnSphere(OQ,v[4],w) - h > 0  then
bool:=false; break; fi;
od;
if bool then Add(VV,v); fi;
od;



V:=List(VV,x->[SortedList([x[1],x[2],x[3]]),x[4],x[5]]);
D:=K!.vertices; ##??
DD:=[];
for v in D do
F:=Filtered([1..Length(V)],i-> v in V[i][1]);
F:=List(F,i->[V[i][2],V[i][3]]);
F:=SSortedList(F);
if Length(F)>2 then Add(DD,v);  fi;
od;

L:=L{DD};
L:=QQNeighbourhoodOfUnimodularPairs(OQ,L); 
L:=SSortedList(L);


VV:=Filtered(VV,v->v[1] in DD and v[2] in DD and v[3] in DD);
M:=List(VV,v->Position(K!.simplicesLst[3],[v[1],v[2],v[3]]));
M:=Difference([1..Length(K!.Points)], M);
for i in M do
K!.Points[i]:=fail;
#K!.Heights:=-infinity;
od;


return [L,K];                                    #####
end);                                            #####
##################################################
##################################################

####################################################
####################################################
InstallGlobalFunction(UnimodularPairsReduced,
function(OQ,LL)
local K,L;

L:=QNeighbourhoodOfUnimodularPairs(OQ,LL);
if OQ!.bianchiInteger=-5 then
L:=Filtered(L,x->IsQUnimodularPair(OQ,x));
else
L:=Filtered(L,x->IsQQUnimodularPair(OQ,x));
fi;
K:=UnimodularPairsReduced_NN(OQ,L);
K:=QQNeighbourhoodOfUnimodularPairs(OQ,K[1]);
K:=UnimodularPairsReduced_NN(OQ,K);
return K;
end);
####################################################
####################################################

####################################################
####################################################
InstallGlobalFunction(CoverOfUnimodularPairs,  #####
function(arg)                                  #####
local OQ,N, NRMS, L, LL, K, Y, bool, BI, A,     
ABI, LST, UU, u, w, a, b, I, i, pos, HAPRECORD;

OQ:=arg[1];
N:=1;
BI:=OQ!.bianchiInteger;
ABI:=AbsInt(BI);
###############################

HAPRECORD:=[
[ 1, 1 ],[ 2, 1 ],[ 3, 1 ],[ 5, 20 ],[ 6, 24 ],[ 7, 1 ],[ 10, 40 ],[ 11, 1 ],[ 13, 52 ],[ 14, 56 ],[ 15, 15 ],[ 17, 68 ],[ 19, 5 ],[ 21, 84 ],[ 22, 88 ],[ 23, 18 ],[ 26, 121 ],[ 29, 169 ],[ 30, 169 ],[ 31, 20 ],[ 33, 528 ],[ 34, 225 ],[ 35, 35 ],[ 37, 289 ],[ 38, 4 ],[ 39, 39 ],[ 41, 361 ],[ 42, 672 ],[ 43, 9 ],
#[ 46, 25 ],
[ 47, 36 ],[ 51, 51 ],
#[ 53, 25 ],
[ 55, 56 ],
#[ 57, 49 ],
[ 59, 36 ],[ 67, 23 ],[ 71, 64 ],[ 79, 64 ],[ 83, 36 ],[ 87, 87 ],[ 91, 91 ],[ 95, 95 ],[ 103, 64 ],[ 107, 64 ],[ 111, 111 ],[ 115, 115 ],
#[ 119, 25 ],
[ 123, 123 ],[ 127, 121 ],[ 131, 100 ],[ 139, 100 ],[ 143, 576 ],[ 151, 110 ],[ 155, 155 ],[159,240],[ 163, 53 ],[167,384],[ 179, 144], [199,196]
];;
                 #HAPRECORD contains some precomuted [d,r] for which Swan's
                 #criterion is satisfied for the given norm r.
#HAPRECORD:=[];  #Uncomment this line to calculate HAPRECORD entries from 
                 #scratch.  

if Length(arg)=2 then pos:=arg[2];
else

pos:=Position(List(HAPRECORD,x->x[1]),ABI);
if pos =fail or pos=0 then pos:=infinity;
if Length(arg)=1 then
Print("Try \n  P:=BianchiPolyhedron(OQ,N);\nfor some guessed positive integer value of N and then try\n  SwanBianchiCriterion(P);\nto test if the value of N was large enough. If the test returns false then you'll need to try a larger value of N.\n\nA successful value of N can be stored as a pair [d,N] in the list HAPRECORD which can be edited manually in the file hap/lib/Congruence/bianchi.gi .\n\n");
fi;
else pos:=HAPRECORD[pos][2]; fi;

fi;
###############################
NRMS:=QuadraticIntegersByNorm(OQ,1000);  #We'll never need anything higher!!!
NRMS:=List(NRMS,x->HAPNorm(OQ,x));
NRMS:=SSortedList(NRMS);
bool:=true;

L:=[];

while bool do
N:=N+1; 
if Length(arg)>1 then
#Print("Adding hemispheres of squared radius ",1/NRMS[N],"\n");
fi;

L:=UnimodularPairs(OQ,NRMS[N],true,L);
if pos=infinity then
K:=SimplicialComplexOfUnimodularPairs(OQ,
                 QNeighbourhoodOfUnimodularPairs(OQ,L));
Y:=RegularCWComplex(K);
CocriticalCellsOfRegularCWComplex(Y,1);
if Homology(Y,0)=[0] and Homology(Y,1)=[] then break; fi;
fi;
if NRMS[N]>=pos then break; fi;
od;

K:=UnimodularPairsReduced(OQ,L);

L:=K[1]; K:=K[2];

###########CHECK SWAN'S CONDITION REALLY IS MET
if pos=infinity then 
A:=BianchiPolyhedron(OQ!.bianchiInteger,L);
A:=SwanBianchiCriterion_alt(A);
if not Length(A)=0 then 
return CoverOfUnimodularPairs(OQ,N+1,true);
fi;
fi;
##################################################

L:=Filtered(L,x->IsQQUnimodularPair(OQ,x));   
L:=QQNeighbourhoodOfUnimodularPairs(OQ,L);
L:=Filtered(L,x->IsQQUnimodularPair(OQ,x));
return [L,K];                                    #####
end);                                        #####
##################################################
##################################################

##################################################
##################################################
InstallGlobalFunction(IsUnimodularCollection,
function(OQ,L)
local K,Y;
K:=SimplicialComplexOfUnimodularPairs(OQ,L);
Y:=RegularCWComplex(K);
CocriticalCellsOfRegularCWComplex(Y,1);
if not Homology(Y,0)=[0] then return false; fi;
if not Homology(Y,1)=[] then return false; fi;
return true; 
end);
##################################################
###############################################

##################################################
##################################################
InstallGlobalFunction(SwanBianchiCriterion,
function(P)
local mr, mv, R, L,LL, i, u, Points;

mr:=P!.minRadius;
mv:=P!.minVertexHeight;
if IsInt(1/mv) then R:=1/mv;
else R:= 1+Int(1/mv);
fi;

L:=UnimodularPairs(P!.ring,R);;
LL:=UnimodularPairs(P!.ring,mr);;
L:=Difference(L,LL);;
L:=Filtered(L,x->IsQQUnimodularPair(P!.ring,x));;
L:=Filtered(L,x->not HAP_IsRedundantUnimodularPair(P!.ring,P!.unimodularPairs,x));

Points:=Filtered([1..Length(P!.points)],i->P!.visibleHeights[i]>0);

for u in L do
for i in Points do
if P!.visibleHeights[i] < HAP_HeightOfPointOnSphere(P!.ring,P!.points[i],u) then return false; fi;
od;
od;

return true;

end);
##################################################
##################################################


##################################################
##################################################
InstallGlobalFunction(SwanBianchiCriterion_alt,
function(arg)
local  PP,P,F,A,K,C,CC,D,H, nrmL, L, S, S1, v,b,x,i,h,s1,s2,t1,t2;

P:=arg[1];

if not IsUnimodularCollection(P!.ring,P!.unimodularPairs) then 
return [false]; fi;

H:=P!.heights;
H:=Filtered(H,x->x>0);
H:=1/Minimum(H);
if IsHapQuadraticNumber(H) then H:=H!.rational; fi;

L:=P!.unimodularPairs;
L:=List(L,x->HAPNorm(P!.ring,x[1]));;
nrmL:=Maximum(L);

if nrmL>=H then return []; fi;

L:=UnimodularPairs(P!.ring,nrmL);

if Length(arg)=2 then 
   H:=UnimodularPairs(P!.ring,arg[2]);
   H:=QNeighbourhoodOfUnimodularPairs(P!.ring,H);
else
   H:=UnimodularPairs(P!.ring,H);
   H:=Filtered(H,h->not h in L);
   H:=QNeighbourhoodOfUnimodularPairs(P!.ring,H);
fi;


S:=QuadraticNumber(0,1,-P!.bianchiInteger);
S1:=1/S;

L:=HAP_BianchiFundamentalRectangle(P!.ring);
s1:=0; t1:=L[2]; s2:=0; t2:=L[4];
#s1:=L[1];t1:=L[2];; s2:=L[3]; t2:=L[4];


D:=[];
for i in [1..Length(P!.points)] do
if not P!.points[i]=fail then
if P!.heights[i]>0 
and P!.points[i][1]>=s1 and P!.points[i][1]<=t1 and P!.points[i][2]*S1>=s2
and P!.points[i][2]*S1<=t2
then Add(D,i);
fi;
fi;
od;

C:=[]; CC:=[];
for i in D do
for h  in H do
if HAP_HeightOfPointOnSphere(P!.ring,P!.points[i],h) > P!.heights[i]
then 
Add(C,h);
Add(CC,i);
fi;
od;
od;
CC:=SSortedList(CC);

K:=P!.simplicialComplex;
A:=[];
for v in K!.vertices do
F:=Filtered(K!.simplicesLst[3],x-> v in x);
F:=Concatenation(F);
F:=SSortedList(F);
if IsSubset(CC,F) then Add(A,K!.unimodularPairs[v]); fi;
od;
if Length(A)>0 then return A; fi;

C:=Filtered(C,x->x[3]>nrmL);
PP:=BianchiPolyhedron(P!.bianchiInteger,Concatenation(P!.unimodularPairs,C));
return Filtered(PP!.unimodularPairs,x->not x in P!.unimodularPairs);

end);
##################################################
##################################################

#####################################################
#####################################################
InstallGlobalFunction(BianchiPolyhedron,
function(arg)
local d,K, P,C, OQ, F, H, L,M,i,x,N,s;

d:=arg[1];
if d>=0 then
Print("Input must be a negative square free integer.\n");
return fail;
fi;

F:=Factors(AbsInt(d));

if not Length(F)=Length(SSortedList(F)) then
Print("Input is not a square free integer.\n");
return fail;
fi;

P:=Objectify(HapBianchiPolyhedron,rec());
P!.bianchiInteger:=d;
OQ:=RingOfIntegers(QuadraticNumberField(d));
P!.ring:=OQ;

if AbsInt(d) in [1,3] then
Print("Warning: the polyhedron is a union of fundamental domains for PSL( ",OQ, ").\n");
fi; 

if Length(arg)=1 then
    K:=CoverOfUnimodularPairs(OQ); P!.unimodularPairs:=K[1]; K:=K[2];
else

    if IsInt(arg[2]) then
        K:=CoverOfUnimodularPairs(OQ,arg[2]); P!.unimodularPairs:=K[1]; K:=K[2];
    else
        K:=UnimodularPairsReduced(OQ,arg[2]); P!.unimodularPairs:=K[1]; K:=K[2];
    fi;
fi;

N:=4;
while Length(P!.unimodularPairs)=0 do 
#Print("Try again with a larger choice of limit on the norms or a larger list of unimodular pairs\n");
N:=N+1;     #Bit of a botch job!
K:=CoverOfUnimodularPairs(OQ,N); P!.unimodularPairs:=K[1]; K:=K[2];
od;


K:=HAP_VertexHeights(OQ,P!.unimodularPairs);

P!.unimodularPairs:=K[2]!.unimodularPairs;
P!.simplicialComplex:=K[2];
P!.vertices:=K[2]!.Points;
P!.heights:=K[2]!.Heights;
C:=[];
for x in K[1] do
Add(C,x);
Add(C,[-x[1],x[2]]);
Add(C,[x[1],-x[2]]);
Add(C,[-x[1],-x[2]]);
od;
C:=SSortedList(C);
P!.cusps:=C;




L:=K[2]!.unimodularPairs;
H:=P!.heights;
############
for i in [1..Length(H)] do
if H[i]>0 then
for s in L do 
if HAP_HeightOfPointOnSphere(OQ,K[2]!.Points[i],s)!.rational>H[i] then
H[i]:=-infinity;
fi;
od;
fi;
od;
############
P!.visibleHeights:=1*H;
H:=Filtered(H,x->x>0);
if Length(H)>0 then
H:=Minimum(H);
else
H:=0;
fi;

L:=P!.unimodularPairs;
L:=List(L,x->HAPNorm(P!.ring,x[1]));;
L:=1/Maximum(L);

P!.minVertexHeight:=H;
P!.minRadius:=L;
P!.points:=K[2]!.Points;

P!.unimodularPairs:=QNeighbourhoodOfUnimodularPairs(P!.ring,P!.unimodularPairs);
return P;

end);
####################################################
####################################################

#################################################################
#################################################################
InstallGlobalFunction(Display3DUnimodularPairs_extra,
function(K)
local Y,ORBS,OQ,colours,coloursLst,spheres,isint,s,t,i,j,tmp;

#######################
isint:=function(v)
local x;
for x in v do
if not IsInt(x) then return false; fi;
od;
return true;
end;
#######################
Y:=K!.cwSpace;
ORBS:=Y!.ORBS;
spheres:=Y!.sphereCentres;

colours:=List(ORBS[3],x->[]);

OQ:=K!.ring;
OQ:=OQ!.bianchiInteger;
if 1 = OQ mod 4 then
for s in [1..Length(ORBS[3])] do
for i in ORBS[3][s] do
    for j in [1..Length(spheres)] do
    t:=spheres[i]-spheres[j];
    if isint(2*t) then Add(colours[s],j);  fi;
    od;
od;
od;
else
for s in [1..Length(ORBS[3])] do
for i in ORBS[3][s] do
    for j in [1..Length(spheres)] do
    if isint(spheres[i]-spheres[j]) then Add(colours[s],j);  fi;
    od;
od;
od;
fi;
colours:=List(colours,x->Set(x));
coloursLst:=List([1..Length(Y!.unimodularPairsSingletons)],i->PositionProperty(colours,x->i in x));
colours:=["red","cyan","green","yellow","blue","magenta","palered", "palecyan","palegreen","paleyellow","paleblue","pink","orange","fuchsia","purple","royalblue"] ;
for i in [1..100] do
tmp:=Concatenation("rgb(",String(Random([0..255])),",",String(Random([0..255])),",",String(Random([0..255])),")");
Add(colours,tmp);
od;
coloursLst:=List([1..Length(spheres)],i->colours[coloursLst[i]]);
return coloursLst;
end);
#################################################################
#################################################################

