######################################################################
# The declarations below allow us to specify the types of arguments
# to functions that we will define later.
# Although type RR is intended to refer to real numbers, Maple
# will not test this very stringently. It will reject sets, lists,
# vectors and so on, but it will accept any algebraic expression.
# Type RR0 will accept expressions that refer to real constants,
# such as sqrt(2) + exp(5) / 1.23. It will reject expressions
# that involve variables, or that refer to non-real complex
# constants.
# Type RR1 will accept numeric constants like 1.2345 or 1/13, but
# reject symbolic expressions like Pi or sqrt(3).
`type/RR` := (u) -> type(u,algebraic); #@ RR
`type/RR0` := (u) -> type(u,realcons); #@ RR0
`type/RR1` := (u) -> type(u,numeric); #@ RR1
# Types CC, CC0 and CC1 are analogous to RR, RR0 and RR1, but
# for complex numbers.
`type/CC` := (u) -> type(u,algebraic); #@ CC
`type/CC0` := (u) -> type(u,constant); #@ CC0
`type/CC1` := (u) -> type(u,complex(numeric)); #@ CC1
# Types RR_2, RR0_2 and RR1_2 are for elements of R^2, which
# we represent as lists. (Note that Maple also has Vectors,
# which are different from lists, but for technical reasons we
# generally prefer to use lists.)
`type/RR_2` := (u) -> type(u,[algebraic,algebraic]); #@ RR_2
`type/RR0_2` := (u) -> type(u,[realcons,realcons]); #@ RR0_2
`type/RR1_2` := (u) -> type(u,[numeric,numeric]); #@ RR1_2
# Types RR_3, RR0_3 and RR1_3 are for elements of R^3
`type/RR_3` := (u) -> type(u,[algebraic,algebraic,algebraic]); #@ RR_3
`type/RR0_3` := (u) -> type(u,[realcons,realcons,realcons]); #@ RR0_3
`type/RR1_3` := (u) -> type(u,[numeric,numeric,numeric]); #@ RR1_3
# Types RR_4, RR0_4 and RR1_4 are for elements of R^4
`type/RR_4` := (u) -> type(u,[algebraic,algebraic,algebraic,algebraic]); #@ RR_4
`type/RR0_4` := (u) -> type(u,[realcons,realcons,realcons,realcons]); #@ RR0_4
`type/RR1_4` := (u) -> type(u,[numeric,numeric,numeric,numeric]); #@ RR1_4
protect(
'RR','RR0','RR1',
'CC','CC0','CC1',
'RR_2','RR0_2','RR1_2',
'RR_3','RR0_3','RR1_3',
'RR_4','RR0_4','RR1_4'
);
######################################################################
# Norms, dot products, cross products
# Distance between points in R^2, R^3 and R^4
nm2 := (u::RR_2) -> sqrt(add(u[i]^2,i=1..2)); #@ nm2
nm3 := (u::RR_3) -> sqrt(add(u[i]^2,i=1..3)); #@ nm3
nm4 := (u::RR_4) -> sqrt(add(u[i]^2,i=1..4)); #@ nm4
dist := (d,a::list(RR),b::list(RR)) -> sqrt(add((a[i]-b[i])^2,i=1..d)): #@ dist
d2 := proc(u::RR_2 ,v::RR_2 ) sqrt(add((u[i]-v[i])^2,i=1..2)); end: #@ d2
d2f := proc(u::RR0_2,v::RR0_2) evalf(d2(u,v)); end: #@ d2f
d3 := proc(u::RR_3 ,v::RR_3 ) sqrt(add((u[i]-v[i])^2,i=1..3)); end: #@ d3
d3f := proc(u::RR0_3,v::RR0_3) evalf(d3(u,v)); end: #@ d3f
d4 := proc(u::RR_4 ,v::RR_4 ) sqrt(add((u[i]-v[i])^2,i=1..4)); end: #@ d4
d4f := proc(u::RR0_4,v::RR0_4) evalf(d4(u,v)); end: #@ d4f
# Dot products in R^2, R^3 and R^4
dp := (d,a::list(RR),b::list(RR)) -> add(a[i]*b[i],i=1..d): #@ dp
dp2 := (a::RR_2,b::RR_2) -> a[1]*b[1] + a[2]*b[2]: #@ dp2
dp3 := (a::RR_3,b::RR_3) -> a[1]*b[1] + a[2]*b[2] + a[3]*b[3]: #@ dp3
dp4 := (a::RR_4,b::RR_4) -> a[1]*b[1] + a[2]*b[2] + a[3]*b[3] + a[4]*b[4]: #@ dp4
dpv := (a::list(RR),b::list(RR)) -> `+`(op(a *~ b)); #@ dpv
distv := (a::list(RR),b::list(RR)) -> sqrt(dpv(a -~ b,a -~ b)); #@ distv
# Ordinary cross product of three-dimensional vectors.
#@ cross_product
cross_product := (u::RR_3,v::RR_3) -> [
u[2] * v[3] - u[3] * v[2],
u[3] * v[1] - u[1] * v[3],
u[1] * v[2] - u[2] * v[1]
]:
# cross_product4(u,v,w) is a vector in R^4 depending on vectors
# u,v,w in R^4. It is orthogonal to each of u, v and w. If
# u,v and w are othogonal then |cross_product4(u,v,w)| = |u||v||w|.
#@ cross_product4
cross_product4 := (u::RR_4,v::RR_4,w::RR_4) -> [
-u[2]*v[3]*w[4]+u[2]*v[4]*w[3]-v[2]*w[3]*u[4]+v[2]*u[3]*w[4]-w[2]*u[3]*v[4]+w[2]*v[3]*u[4],
u[1]*v[3]*w[4]-u[1]*v[4]*w[3]+v[1]*w[3]*u[4]-v[1]*u[3]*w[4]+w[1]*u[3]*v[4]-w[1]*v[3]*u[4],
-u[1]*v[2]*w[4]+u[1]*v[4]*w[2]-v[1]*w[2]*u[4]+v[1]*u[2]*w[4]-w[1]*u[2]*v[4]+w[1]*v[2]*u[4],
u[1]*v[2]*w[3]-u[1]*v[3]*w[2]+v[1]*w[2]*u[3]-v[1]*u[2]*w[3]+w[1]*u[2]*v[3]-w[1]*v[2]*u[3]
]:
######################################################################
# Isometric simplices
# Map from the 2-simplex to the equilateral triangle in R^2 with
# vertices [1,0] and [-1/2,+/- sqrt(3)/2].
# Arguments can be given as (t1,t2) or ([t1,t2]) or (t0,t1,t2) or
# ([t0,t1,t2]). If t0 is missing then it is taken to be 1-t1-t2.
#@ triangle_proj
triangle_proj := proc()
local tt;
if nargs = 1 then tt := args[1] else tt := [args]; fi;
if nops(tt) = 2 then tt = [1 - tt[1] - tt[2],tt[1],tt[2]]; fi;
return([tt[1]-tt[2]/2-tt[3]/2,(tt[2]-tt[3])*sqrt(3)/2]);
end:
#@ triangle_lift
triangle_lift := (xy) -> [1/3+(2/3)*xy[1],
1/3-(1/3)*xy[1]+(1/3)*sqrt(3)*xy[2],
1/3-(1/3)*xy[1]-(1/3)*sqrt(3)*xy[2]]:
######################################################################
# Conversion between complex numbers and pairs of real numbers.
C_to_R2 := (z) -> [Re(z),Im(z)]; #@ C_to_R2
R2_to_C := (xy) -> xy[1] + I * xy[2]; #@ R2_to_C
C_mult := (u,v) -> [u[1]*v[1]-u[2]*v[2], u[1]*v[2]+u[2]*v[1]]; #@ C_mult
# Any polynomial f : R -> R extends in an obvious way to give a
# function C -> C, and by identifying C with R^2 we obtain a
# function g : R^2 -> R^2. The function below converts f to g.
#@ series_on_R2
series_on_R2 := proc(f)
local t,u,ft,d,up,ans,i,c;
ft := f(t);
d := degree(ft,t);
up := [1,0];
ans := [0,0];
for i from 0 to d do
c := coeff(ft,t,i);
ans := expand(ans +~ c *~ up);
up := expand([u[1] * up[1] - u[2] * up[2],u[1] * up[2] + u[2] * up[1]]);
od;
return unapply(ans,u);
end:
######################################################################
# Several versions of stereographic projection
# stereo() is the stereographic homeomorphism from
# S^3 minus { [0,0,0,1] } to R^3. The inverse homeomorphism is
# unstereo().
#@ S3_to_R3
S3_to_R3 := (x::RR_4) -> [x[1]/(1-x[4]),x[2]/(1-x[4]),x[3]/(1-x[4])];
#@ R3_to_S3
R3_to_S3 := (u::RR_3) -> [seq(2*u[i]/(add(u[j]^2,j=1..3)+1),i=1..3),
(add(u[j]^2,j=1..3)-1)/(add(u[j]^2,j=1..3)+1)];
stereo := eval(S3_to_R3); #@ stereo
unstereo := eval(R3_to_S3); #@ unstereo
# Some convenience functions for plotting via stereographic projection.
#@ stereo_point
stereo_point := proc(x::RR_4) point(stereo(x),args[2..-1]); end:
#@ stereo_curve
stereo_curve := proc(xs::list(RR_4)) curve(map(stereo,xs),args[2..-1]); end:
#@ stereo_polygon
stereo_polygon := proc(xs::list(RR_4)) polygon(map(stereo,xs),args[2..-1]); end:
#@ stereo_spacecurve
stereo_spacecurve := proc(x::RR_4) spacecurve(stereo(x),args[2..-1]); end:
#@ R_to_S1
R_to_S1 := proc(x::RR)
if x = infinity then
return([-1,0]);
else
return([(1-x^2)/(1+x^2),2*x/(1+x^2)]);
fi;
end:
#@ S1_to_R
S1_to_R := proc(u::RR_2)
if u[1] = -1 then
return infinity;
else
return u[2]/(1 + u[1]);
fi;
end:
# SC1 is the unit circle in C (rather than R^2)
#@ R_to_SC1
R_to_SC1 := proc(x::RR)
if x = infinity then
return(-1);
else
return((1-x^2+2*I*x)/(1+x^2));
fi;
end:
#@ RP1_to_SC1
RP1_to_SC1 := proc(xy::RR_2)
return (xy[2]^2-xy[1]^2+2*I*xy[1]*xy[2])/(xy[1]^2+xy[2]^2);
end:
# SC1_to_R and SC1_to_R_alt are in theory the same. However,
# SC1_to_R(z) is guaranteed to be real, even if |z| is slightly
# different from 1. On the other hand, SC1_to_R_alt
#@ SC1_to_R
SC1_to_R := proc(u::CC)
if u = -1 then
return infinity;
else
return Im(u)/(1 + Re(u));
fi;
end:
SC1_to_R_alt := (z) -> I*(1-z)/(1+z); #@ SC1_to_R_alt
# I is the interval [-1,1]
R_to_I := (t) -> t/sqrt(1+t^2); #@ R_to_I
I_to_R := (t) -> t/sqrt(1-t^2); #@ I_to_R
S1_to_I := u -> u[2]/sqrt(2*(1+u[1])); #@ S1_to_I
I_to_S1 := t -> [1-2*t^2,2*t*sqrt(1-t^2)]; #@ I_to_S1
SC1_to_I := z -> Im(z)/sqrt(2*(1+Re(z))); #@ SC1_to_I
# tan_sum satisfies tan_sum(tan(s),tan(t)) = tan(s+t).
tan_sum := (a,b) -> (a + b)/(1 - a*b); #@ tan_sum
# C_to_S2 is the usual homeomorphism from the Riemann sphere to
# S^2, normalised to send the unit circle in C to the equator.
#@ C_to_S2
C_to_S2 := proc(z::CC)
if z = infinity then
return([0,0,1]);
else
return
[2*Re(z)/(1+abs(z)^2),2*Im(z)/(1+abs(z)^2),(abs(z)^2-1)/(1+abs(z)^2)];
fi;
end:
#@ S2_to_C
S2_to_C := proc(u::RR_3)
if u[3] = 1 then
return(infinity);
else
return(simplify((u[1]+I*u[2])/(1-u[3])));
fi;
end:
#@ S2_tangent_frame
S2_tangent_frame := proc(a::RR1_3)
local e,b,c,i,j,m;
e[1] := [1,0,0];
e[2] := [0,1,0];
e[3] := [0,0,1];
i := 0;
m := 0;
for j from 1 to 3 do
e[j] := e[j] - dp3(e[j],a) *~ a;
if nm3(e[j]) > m then
m := nm3(e[j]);
i := j;
fi;
od;
b := e[i] /~ nm3(e[i]);
c := cross_product(b,a);
return [b,c];
end:
# stereo_shift(x,a) is a version of stereographic projection that
# sends a to zero and -a to infinity.
#@ stereo_shift
stereo_shift := (x::RR_4, a::RR_4) ->
[(-x[1]*a[4]+x[2]*a[3]-x[3]*a[2]+x[4]*a[1])/(1+x[1]*a[1]+x[2]*a[2]+x[3]*a[3]+x[4]*a[4]),
(-x[1]*a[3]-x[2]*a[4]+x[3]*a[1]+x[4]*a[2])/(1+x[1]*a[1]+x[2]*a[2]+x[3]*a[3]+x[4]*a[4]),
(x[1]*a[2]-x[2]*a[1]-x[3]*a[4]+x[4]*a[3])/(1+x[1]*a[1]+x[2]*a[2]+x[3]*a[3]+x[4]*a[4])]:
#@ unstereo_shift
unstereo_shift := (u::RR_3,a::RR_4) -> [
-(2*u[1]*a[4]+2*u[2]*a[3]-2*u[3]*a[2]+a[1]*u[1]^2+a[1]*u[2]^2+a[1]*u[3]^2-a[1])/(u[1]^2+u[2]^2+u[3]^2+1),
-(-2*u[1]*a[3]+2*u[2]*a[4]+2*u[3]*a[1]+a[2]*u[1]^2+a[2]*u[2]^2+a[2]*u[3]^2-a[2])/(u[1]^2+u[2]^2+u[3]^2+1),
(-2*u[1]*a[2]+2*u[2]*a[1]-2*u[3]*a[4]-a[3]*u[1]^2-a[3]*u[2]^2-a[3]*u[3]^2+a[3])/(u[1]^2+u[2]^2+u[3]^2+1),
(2*u[1]*a[1]+2*u[2]*a[2]+2*u[3]*a[3]-a[4]*u[1]^2-a[4]*u[2]^2-a[4]*u[3]^2+a[4])/(u[1]^2+u[2]^2+u[3]^2+1)]:
######################################################################
# A Mobius function that preserves the unit circle
mob := (z,a) -> (z - a)/(1 - conjugate(a)*z); #@ mob
# This assumes that u and v lie in the unit disc, and returns the
# unique holomorphic involution of the unit disc that exchanges
# u and v.
#@ switcher
switcher := proc(u,v)
local a,z;
a := (u+v+conjugate(u+v)*u*v)/(1-abs(u*v)^2);
return unapply((a-z)/(1-conjugate(a)*z),z);
end:
#@ midpoint_Delta
midpoint_Delta := proc(u,v)
local a;
a := (u+v+conjugate(u+v)*u*v)/(1-abs(u*v)^2);
if a = 0 then
return 0;
else
return ((1-sqrt(1-abs(a)^2))/conjugate(a));
fi;
end:
######################################################################
# Polar coordinates on S^3
#@ S3_polar
S3_polar := (abc::RR_3) ->
[cos(abc[1])*cos(abc[2]),cos(abc[1])*sin(abc[2]),
sin(abc[1])*cos(abc[3]),sin(abc[1])*sin(abc[3])];
#@ S3_polar_inv
S3_polar_inv := (x::RR_4) ->
[arcsin(sqrt(x[3]^2+x[4]^2)),
arcsin(x[2]/sqrt(x[1]^2+x[2]^2)),
arcsin(x[4]/sqrt(x[3]^2+x[4]^2))];
######################################################################
# Various different models for the 2-torus:
#
# T refers to points s in R^4 with s[1]^2+s[2]^2 = s[3]^2+s[4]^2 = 1.
# TA refers to pairs of angles
# TC refers to pairs of unit complex numbers
# TP refers to (R u infinity)^2
TA_to_TC := (theta) -> [exp(I*theta[1]),exp(I*theta[2])]; #@ TA_to_TC
TA_to_TP := (theta) -> [tan(theta[1]/2),tan(theta[2]/2)]; #@ TA_to_TP
TA_to_T := (theta) -> [cos(theta[1]),sin(theta[1]),cos(theta[2]),sin(theta[2])]; #@ TA_to_T
TC_to_TA := (z) -> [argument(z[1]),argument(z[2])]; #@ TC_to_TA
TC_to_TP := (z) -> [Im(z[1])/(1+Re(z[1])),Im(z[2])/(1+Re(z[2]))]; #@ TC_to_TP
TC_to_T := (z) -> [Re(z[1]),Im(z[1]),Re(z[2]),Im(z[2])]; #@ TC_to_T
TP_to_TA := (t) -> [2*arctan(t[1]),2*arctan(t[2])]; #@ TP_to_TA
TP_to_TC := (t) -> [(1+I*t[1])/(1-I*t[1]),(1+I*t[2])/(1-I*t[2])]; #@ TP_to_TC
TP_to_T := (t) -> [(1-t[1]^2)/(1+t[1]^2),2*t[1]/(1+t[1]^2),
(1-t[2]^2)/(1+t[2]^2),2*t[2]/(1+t[2]^2)]; #@ TP_to_T
T_to_TA := (s) -> [arctan(s[2],s[1]),arctan(s[4],s[3])]; #@ T_to_TA
T_to_TC := (s) -> [s[1]+I*s[2],s[3]+I*s[4]]; #@ T_to_TC
T_to_TP := (s) -> [s[2]/(1+s[1]),s[4]/(1+s[3])]; #@ T_to_TP
# T_to_S2 gives a homeomorphism T/C_2 -> S^2.
T_to_S2 := (s) -> [s[3]-s[1],s[2]*s[4],-s[3]-s[1]]/~ sqrt((1+s[1]^2)*(1+s[3]^2)); #@ T_to_S2
TP_to_S2 := (a) -> [a[1]^2-a[2]^2, 2*a[1]*a[2], a[1]^2*a[2]^2-1] /~ sqrt((1+a[1]^4)*(1+a[2]^4)); #@ TP_to_S2
TA_to_S2 := (theta) -> T_to_S2(TA_to_T(theta)); #@ TA_to_S2
TC_to_S2 := (z) -> T_to_S2(TC_to_T(z)); #@ TC_to_S2
# This is a generically defined section for T_to_S2
#@ S2_to_TP
S2_to_TP := (u) -> [
abs(u[2])/sqrt((sqrt(1-u[1]^2)-u[3])*(sqrt(1-u[3]^2)-u[1])),
u[2] /sqrt((sqrt(1-u[1]^2)-u[3])*(sqrt(1-u[3]^2)+u[1]))
];
# T_to_R3 is the standard embedding of T in R^3.
T_to_R3 := (t) -> [t[1]*(2+t[3]),t[2]*(2+t[3]),t[4]]; #@ T_to_R3
TA_to_R3 := (theta) -> T_to_R3(TA_to_T(theta)); #@ TA_to_R3
TC_to_R3 := (z) -> T_to_R3(TC_to_T(z)); #@ TC_to_R3
TP_to_R3 := (t) -> T_to_R3(TP_to_T(t)); #@ TP_to_R3
# We next have similar functions for the 4-torus
TTA_to_TTC := (theta) -> [seq(exp(I*theta[i]),i=1..4)]; #@ TTA_to_TTC
TTA_to_TTP := (theta) -> [seq(tan(theta[i]/2),i=1..4)]; #@ TTA_to_TTP
TTA_to_TT := (theta) -> map(op,[seq([cos(theta[i]),sin(theta[i])],i=1..4)]); #@ TTA_to_TT
TTC_to_TTA := (z) -> [seq(argument(z[i]),i=1..4)]; #@ TTC_to_TTA
TTC_to_TTP := (z) -> [seq(Im(z[i])/(1+Re(z[i])),i=1..4)]; #@ TTC_to_TTP
TTC_to_TT := (z) -> map(op,[seq([Re(z[i]),Im(z[i])],i=1..4)]); #@ TTC_to_TT
TTP_to_TTA := (t) -> [seq(2*arctan(t[i]),i=1..4)]; #@ TTP_to_TTA
TTP_to_TTC := (t) -> [seq((1+I*t[i])/(1-I*t[i]),i=1..4)]; #@ TTP_to_TTC
TTP_to_TT := (t) -> map(op,[seq([(1-t[i]^2)/(1+t[i]^2),2*t[i]/(1+t[i]^2)],i=1..4)]); #@ TTP_to_TT
TT_to_TTA := (s) -> [seq(arctan(s[2*i],s[2*i-1]),i=1..4)]; #@ TT_to_TTA
TT_to_TTC := (s) -> [seq(s[2*i-1]+I*s[2*i],i=1..4)]; #@ TT_to_TTC
TT_to_TTP := (s) -> [seq(s[2*i]/(1+s[2*i-1]),i=1..4)]; #@ TT_to_TTP
######################################################################
# Quaternions
# We identify R^4 with the quaternions by the rule
# [x1,x2,x3,x4] |-> x1 i + x2 j + x3 k + x4.
#@ H_mult
H_mult := proc(a::RR_4,b::RR_4)
[ a[1]*b[4] + a[2]*b[3] - a[3]*b[2] + a[4]*b[1] ,
- a[1]*b[3] + a[2]*b[4] + a[3]*b[1] + a[4]*b[2] ,
a[1]*b[2] - a[2]*b[1] + a[3]*b[4] + a[4]*b[3] ,
- a[1]*b[1] - a[2]*b[2] - a[3]*b[3] + a[4]*b[4]
];
end:
#@ H_conjugate
H_conjugate := proc(a::RR_4)
[-a[1],-a[2],-a[3],a[4]];
end:
#@ H_quotient
H_quotient := proc(a,b)
H_mult(a,H_conjugate(b)) /~ nm4(b)^2;
end:
######################################################################
# The Hopf fibration
# The Hopf fibration from R^4\{0} to S^2
#@ hopf_map
hopf_map := (x::RR_4) ->
[2*x[1]*x[3]+2*x[2]*x[4],
2*x[1]*x[4]-2*x[2]*x[3],
x[1]^2+x[2]^2-x[3]^2-x[4]^2]/~(rho(x));
#@ find_hopf_preimages
find_hopf_preimages := proc(y::RR_3)
local eqs,sol,fsol,is_real,x,xx;
xx := [x[1],x[2],x[3],x[4]];
is_real := (u) -> (Im(u[1]) = 0 and Im(u[2]) = 0 and Im(u[3]) = 0 and Im(u[4]) = 0):
eqs := {g0(x)=0,hopf_proj(x)[1]=y[1],hopf_proj(x)[2]=y[2],hopf_proj(x)[3]=y[3]};
sol := [solve(eqs,{op(xx)})]:
fsol := select(is_real,evalf(map(s -> subs(s,xx),sol)));
return(fsol);
end:
#@ act_hopf
act_hopf[1] := (u) -> [ u[1], u[2], u[3]];
act_hopf[LL] := (u) -> [-u[1],-u[2], u[3]];
act_hopf[LM] := (u) -> [ u[2], u[1], u[3]];
act_hopf[LLLM] := (u) -> [-u[2],-u[1], u[3]];
act_hopf[LN] := (u) -> [-u[2],-u[1], u[3]];
act_hopf[LLLN] := (u) -> [ u[2], u[1], u[3]];
act_hopf[MN] := (u) -> [-u[1],-u[2], u[3]];
act_hopf[LLMN] := (u) -> [ u[1], u[2], u[3]];
######################################################################
# Random points on spheres
#@ random_S3_point
random_S3_point := proc()
local x,rr,nx;
rr := rand(-1000..1000);
nx := 0;
while nx < 0.1 or nx > 1 do
x := [rr(),rr(),rr(),rr()] /~ 1000.;
nx := sqrt(add(x[i]^2,i=1..4));
od;
x := x /~ nx;
return(x);
end:
random_S2_point := () -> hopf_map(random_S3_point());
######################################################################
# R2_zone(x,y) returns an integer between 0 and 16 depending on the
# argument theta of (x,y). If theta = k * Pi/4 then the result is
# 2k+1. If k * Pi/4 < theta < (k+1) * Pi/4 then the result is 2k+2.
# If (x,y) = (0,0) then theta is undefined and the result is 0.
#
# We also define C_zone(x + I*y) = R2_zone(x,y).
#@ R2_zone
R2_zone := proc(x0,y0)
local x,y;
x := evalf(x0);
y := evalf(y0);
if x = 0 and y = 0 then
return(0);
elif y = 0 and 0 < x then
return(1);
elif 0 < y and y < x then
return(2);
elif 0 < x and x = y then
return(3);
elif 0 < x and x < y then
return(4);
elif 0 = x and 0 < y then
return(5);
elif 0 < -x and -x < y then
return(6);
elif 0 < -x and -x = y then
return(7);
elif 0 < y and y < -x then
return(8);
elif 0 = y and 0 < -x then
return(9);
elif 0 < -y and -y < -x then
return(10);
elif 0 < -y and -y = -x then
return(11);
elif 0 < -x and -x < -y then
return(12);
elif 0 = -x and 0 < -y then
return(13);
elif 0 < x and x < -y then
return(14);
elif 0 < x and x = -y then
return(15)
elif 0 < -y and -y < x then
return(16);
else
return(FAIL);
fi;
end:
#@ C_zone
C_zone := (z::CC) -> R2_zone(Re(z),Im(z)):
######################################################################
# Winding numbers
# This calculates the winding number of a map u : [0,2*Pi] -> C\{0}.
# The second parameter n_ defaults to 24 if it is omitted. The
# result may be incorrect if n_ is too small, but 24 is ample for
# the functions that occur in practice in this project.
#@ C_winding_number
C_winding_number := proc(u,n_)
local n,uu,a;
n := `if`(nargs > 1,n_,24);
uu := [seq(evalf(u(2*Pi*i/n)),i=0..(n-1))];
uu := [op(uu),uu[1]];
a := add(argument(uu[i+1]/uu[i]),i=1..n);
return round(evalf(a/(2*Pi)));
end:
#@ R2_winding_number
R2_winding_number := proc(u,n_)
C_winding_number((t) -> (u(t)[1] + u(t)[2]*I),args[2..-1]);
end:
######################################################################
# Miscellaneous other things
# rational_sphere point defines a map Q x Q -> S^2 n Q^4 with dense image.
#@ rational_sphere_point
rational_sphere_point := (s::RR,t::RR) -> [
2*s/(1+s^2)*2*t/(1+t^2),
2*s/(1+s^2)*(1-t^2)/(1+t^2),
(1-s^2)/(1+s^2)
];
# Midpoints of line segments
midpoint_R2 := (a::RR_2,b::RR_2) -> [(a[1]+b[1])/2,(a[2]+b[2])/2]; #@ midpoint_R2
midpoint_R3 := (a::RR_3,b::RR_3) -> [(a[1]+b[1])/2,(a[2]+b[2])/2,(a[3]+b[3])/2]; #@ midpoint_R3
midpoint_R4 := (a::RR_4,b::RR_4) -> [(a[1]+b[1])/2,(a[2]+b[2])/2,(a[3]+b[3])/2,(a[4]+b[4])/2]; #@ midpoint_R4
# Midpoints of arcs in the spheres S^2 and S^3
#@ midpoint_S2
midpoint_S2 := proc(a::RR_3,b::RR_3)
local c,r;
c := midpoint_R3(a,b);
r := sqrt(c[1]^2+c[2]^2+c[3]^2);
c := [c[1]/r,c[2]/r,c[3]/r];
return(c);
end:
#@ midpoint_S3
midpoint_S3 := proc(a::RR_4,b::RR_4)
local c,r;
c := midpoint_R4(a,b);
r := sqrt(c[1]^2+c[2]^2+c[3]^2+c[4]^2);
c := [c[1]/r,c[2]/r,c[3]/r,c[4]/r];
return(c);
end:
# The area of a flat triangle with vertices in S^3
#@ triangle_area
triangle_area := proc(a::RR_4,b::RR_4,c::RR_4)
local p,q;
p := [seq(b[i]-a[i],i=1..4)];
q := [seq(c[i]-a[i],i=1..4)];
sqrt(expand(dp4(p,p)*dp4(q,q)-dp4(p,q)^2))/2;
end:
# xx is a generic vector in R^4
xx := [x[1],x[2],x[3],x[4]]; #@ xx
yy := [y[1],y[2]]; #@ yy
zz := [z[1],z[2]]; #@ zz
tt := [t[1],t[2]]; #@ tt
assume(x[1]::real);
assume(x[2]::real);
assume(x[3]::real);
assume(x[4]::real);
assume(y[1]::real);
assume(y[2]::real);
protect('x','xx','y','yy','z','zz','t','tt');