File: core_geodesics.c

package info (click to toggle)
snappea 3.0d3-20.1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 5,896 kB
  • ctags: 3,582
  • sloc: ansic: 33,469; sh: 8,293; python: 7,623; makefile: 240
file content (232 lines) | stat: -rw-r--r-- 6,678 bytes parent folder | download | duplicates (12)
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;

	}
}