1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
/*
* core_geodesics.c
*
* This file provides the function
*
* void core_geodesic( Triangulation *manifold,
* int cusp_index,
* int *singularity_index,
* Complex *core_length,
* int *precision);
*
* which computes the core_length and singularity_index for the
* Cusp of index cusp_index in Triangulation *manifold. The
* singularity_index describes the nature of the cusp:
*
* If the Cusp is unfilled or the Dehn filling coefficients are
* not integers, then
*
* *singularity_index is set to zero,
* *core_length is undefined.
*
* If the Cusp is filled and the Dehn filling coefficients are
* relatively prime integers (so we have a manifold locally), then
*
* *singularity_index is set to one,
* *core_length is set to the complex length
* of the central geodesic,
*
* If the Cusp is filled and the Dehn filling coefficients are
* non relatively prime integers (so we have an orbifold locally), then
*
* *singularity_index is set to the index of the singular locus,
* *core_length is set to the complex length of the central
* geodesic in the smallest manifold cover of
* a neighborhood of the singular set.
*
* If the precision pointer is not NULL, *precision is set to the
* number of decimal places of accuracy in the computed value of
* core_length.
*
* Klein bottle cusps are OK. Their torsion will always be zero.
*
* This file also provides the function
*
* void compute_core_geodesic( Cusp *cusp,
* int *singularity_index,
* Complex length[2]);
*
* for use within the kernel. It is similar to core_geodesic(),
* only it takes a Cusp pointer as input, and outputs the complex
* lengths relative to the ultimate and penultimate hyperbolic
* structures, rather than reporting a precision.
*
*
* The Algorithm.
*
* Say we're doing (p, q) Dehn filling on some Cusp. The (closed) core
* geodesic lifts to a set of (infinite) geodesics in the universal cover.
* Let L be one such (infinite) geodesic in the univeral cover, and
* consider the group G of covering transformations fixing L (setwise).
* G is generated by the holonomies of the meridian and longitude,
* which we denote H(m) and H(l), subject to the single relation
* p H(m) + q H(l) = 0. We would like to find new generators g and h
* for the group such that the relation takes the form n g + 0 h = 0.
* The generator g will be the purely rotational part of the group G
* (n will be the order of the singular locus), and h will generate
* the translational part of G.
*
* One approach to finding g and h would be to proceed in the general
* context of finitely generated abelian groups, and apply the Euclidean
* algorithm to the relation p H(m) + q H(l) = 0, changing bases until
* the relation simplifies down to something of the form n g + 0 h = 0.
* But rather than messing with the bookkeeping of this approach, we'll
* use a more pedestrian method.
*
* Let (a, b; c, d) be the matrix expressing (g, h) in terms
* of (H(m), H(l)):
*
* | g | | a b | | H(m) |
* | | = | | | |
* | h | | c d | | H(l) |
*
* Because 0 = n g = n (a H(m) + b H(l)) = na H(m) + nb H(l) is
* the identity, and p H(m) + q H(l) is the only relation in the
* presentation of the group, it follows that (a, b) must be
* proportional to (p, q). Furthermore, since the matrix (a, b; c, d)
* has determinant one, a and b must be relatively prime, so therefore
* (a, b) = (p, q)/gcd(p,q). It now follows that c and d are integers
* satisfying
* 1 = a d - b c = d p/gcd(p,q) - c q/gcd(p,q)
* <=>
* d p - c q = gcd(p,q)
*
* We can find such integers using SnapPea's standard
* euclidean_algorithm() function.
*/
#include "kernel.h"
#define TORSION_EPSILON 1e-5
void core_geodesic(
Triangulation *manifold,
int cusp_index,
int *singularity_index,
Complex *core_length,
int *precision)
{
Cusp *cusp;
Complex length[2];
cusp = find_cusp(manifold, cusp_index);
/*
* Compute the complex length relative to the ultimate
* and penultimate hyperbolic structures.
*/
compute_core_geodesic(cusp, singularity_index, length);
/*
* Package up the results.
*/
if (*singularity_index != 0)
{
*core_length = length[ultimate];
if (precision != NULL)
*precision = complex_decimal_places_of_accuracy(
length[ultimate], length[penultimate]);
}
else
{
*core_length = Zero;
if (precision != NULL)
*precision = 0;
}
}
void compute_core_geodesic(
Cusp *cusp,
int *singularity_index,
Complex length[2])
{
int i;
long int positive_d,
negative_c;
double pi_over_n;
/*
* If the Cusp is unfilled or the Dehn filling coefficients aren't
* integers, then just write in some zeros (as explained at the top
* of this file) and return.
*/
if (cusp->is_complete == TRUE
|| Dehn_coefficients_are_integers(cusp) == FALSE)
{
*singularity_index = 0;
length[ultimate] = Zero;
length[penultimate] = Zero;
return;
}
/*
* The euclidean_algorithm() will give the singularity index
* directly (as the g.c.d.), and the coefficients lead to the
* complex length (cf. the explanation at the top of this file).
*/
*singularity_index = euclidean_algorithm(
(long int) cusp->m,
(long int) cusp->l,
&positive_d,
&negative_c);
for (i = 0; i < 2; i++) /* i = ultimate, penultimate */
{
/*
* length[i] = c H(m) + d H(l)
*
* (The holonomies are already in logarithmic form.)
*/
length[i] = complex_plus(
complex_real_mult(
(double) (- negative_c),
cusp->holonomy[i][M]
),
complex_real_mult(
(double) positive_d,
cusp->holonomy[i][L]
)
);
/*
* Make sure the length is positive.
*/
if (length[i].real < 0.0)
length[i] = complex_negate(length[i]);
/*
* We want to normalize the torsion to the range
* [-pi/n + epsilon, pi/n + epsilon], where n is
* the order of the singular locus.
*/
pi_over_n = PI / *singularity_index;
while (length[i].imag < - pi_over_n + TORSION_EPSILON)
length[i].imag += 2 * pi_over_n;
while (length[i].imag > pi_over_n + TORSION_EPSILON)
length[i].imag -= 2 * pi_over_n;
/*
* In the case of a Klein bottle cusp, H(m) will be purely
* rotational and H(l) will be purely translational
* (cf. the documentation at the top of holonomy.c).
* But the longitude used in practice is actually the
* double cover of the true longitude, so we have to
* divide the core_length by two to compensate.
*/
if (cusp->topology == Klein_cusp)
length[i].real /= 2.0;
}
}
|