File: double_cover.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 (395 lines) | stat: -rw-r--r-- 10,867 bytes parent folder | download | duplicates (8)
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/*
 *	double_cover.c
 *
 *	This file provides the function
 *
 *		Triangulation *double_cover(Triangulation *manifold);
 *
 *	which computes the orientable double cover of a nonorientable manifold.
 */

#include "kernel.h"

typedef Tetrahedron *TetPtrPair[2];
typedef int			IntPair[2];

static void	allocate_storage(Triangulation *manifold, Triangulation **the_cover, TetPtrPair **new_tet);
static void	set_neighbors_and_gluings(Triangulation *manifold, TetPtrPair *new_tet);
static void	lift_peripheral_curves(Triangulation *manifold, TetPtrPair *new_tet);
static void	lift_tet_shapes(Triangulation *manifold, TetPtrPair *new_tet);
static void	set_cusp_data(Triangulation *manifold, Triangulation *the_cover, TetPtrPair *new_tet);


Triangulation *double_cover(
	Triangulation	*manifold)
{
	Triangulation	*the_cover;
	TetPtrPair		*new_tet;

	/*
	 *	Make sure the manifold is nonorientable.
	 */
	if (manifold->orientability != nonorientable_manifold)
		uFatalError("double_cover", "double_cover");

	/*
	 *	Number the manifold's Tetrahedra for easy reference.
	 */
	number_the_tetrahedra(manifold);

	/*
	 *	Allocate space for the_cover.
	 *
	 *	The i-th tetrahedron in manifold will have two preimages in
	 *	the_cover, which we'll record as new_tet[i][0] and new_tet[i][1].
	 */
	allocate_storage(manifold, &the_cover, &new_tet);

	/*
	 *	Set the neighbor and gluing fields of the Tetrahedra.
	 */
	set_neighbors_and_gluings(manifold, new_tet);

	/*
	 *	Lift the peripheral curves to the double cover.
	 *	The lifted {meridian, longitude} will adhere to the right hand
	 *	rule relative to the local orientation on each Cusp's double
	 *	cover (cf. peripheral_curves.c).  The manifold does not yet
	 *	have a global orientation.
	 */
	lift_peripheral_curves(manifold, new_tet);

	/*
	 *	Copy the TetShapes and shape histories.
	 */
	lift_tet_shapes(manifold, new_tet);

	/*
	 *	Set some global information.
	 */
	the_cover->num_tetrahedra = 2 * manifold->num_tetrahedra;
	the_cover->solution_type[complete]	= manifold->solution_type[complete];
	the_cover->solution_type[filled]	= manifold->solution_type[filled];

	/*
	 *	Create Cusps for the_cover, make sure they're in the same order
	 *	as their images in the manifold, copy in the Dehn filling
	 *	coefficients, and set the CuspTopology to torus_cusp.
	 *	We must do this before orienting the_cover, or else the
	 *	vertex indices on the_cover will no longer coincide with
	 *	those on the manifold.
	 *	Count the cusps.
	 */
	create_cusps(the_cover);
	set_cusp_data(manifold, the_cover, new_tet);
	count_cusps(the_cover);

	/*
	 *	Orient the_cover.
	 */
	orient(the_cover);
	if (the_cover->orientability != oriented_manifold)
		uFatalError("double_cover", "double_cover");

	/*
	 *	orient() moves all peripheral curves to the right_handed sheets
	 *	of the Cusps local double covers.  Thus some {meridian, longitude}
	 *	pairs may no longer adhere to the right-hand rule.  Correct them,
	 *	and adjust the Dehn filling coefficients accordingly.
	 */
	fix_peripheral_orientations(the_cover);

	/*
	 *	Create and orient the EdgeClasses.
	 */
	create_edge_classes(the_cover);
	orient_edge_classes(the_cover);

	/*
	 *	In principle we could lift the holonomies and cusp shapes from
	 *	the manifold to the_cover, but the code would be delicate, difficult
	 *	to write, and bug-prone (it would be sensitive to the details of
	 *	which orientation the_cover was given and which meridians or
	 *	longitudes were reversed, so changing other parts of the algorithm
	 *	would most likely introduce errors here).  A more robust approach
	 *	is to let polish_hyperbolic_structures() "unnecessarily" refine
	 *	the hyperbolic structure.  It will automatically compute the
	 *	holonomies and cusp shapes.  The price we pay is that in all cases
	 *	we'll have one "unnecessary" iteration of Newton's method, and
	 *	in cases where the shape histories are nontrivial, we'll end up
	 *	computing the hyperbolic structure from scratch.
	 */
	polish_hyperbolic_structures(the_cover);

	/*
	 *	Free local arrays.
	 */
	my_free(new_tet);

	return the_cover;
}


static void allocate_storage(
	Triangulation	*manifold,
	Triangulation	**the_cover,
	TetPtrPair		**new_tet)
{
	int	i,
		j;

	/*
	 *	Allocate space for the new Triangulation.
	 */

	*the_cover = NEW_STRUCT(Triangulation);
	initialize_triangulation(*the_cover);

	/*
	 *	Allocate space for the new Tetrahedra, and the array
	 *	which organizes them.
	 */

	*new_tet = NEW_ARRAY(manifold->num_tetrahedra, TetPtrPair);

	for (i = 0; i < manifold->num_tetrahedra; i++)
		for (j = 0; j < 2; j++)
		{
			(*new_tet)[i][j] = NEW_STRUCT(Tetrahedron);
			initialize_tetrahedron((*new_tet)[i][j]);
			INSERT_BEFORE((*new_tet)[i][j], &(*the_cover)->tet_list_end);
		}
}


static void set_neighbors_and_gluings(
	Triangulation	*manifold,
	TetPtrPair		*new_tet)
{
	int			i,
				j;
	Tetrahedron	*old_tet;
	VertexIndex	v;

	for (old_tet = manifold->tet_list_begin.next, i = 0;
		 old_tet != &manifold->tet_list_end;
		 old_tet = old_tet->next, i++)
	{
		if (old_tet->index != i)
			uFatalError("set_neighbors_and_gluings", "double_cover");

		for (j = 0; j < 2; j++)

			for (v = 0; v < 4; v++)
			{
				new_tet[i][j]->neighbor[v]	= new_tet
					[old_tet->neighbor[v]->index]
					[parity[old_tet->gluing[v]] == orientation_preserving ? j : !j];

				new_tet[i][j]->gluing[v] = old_tet->gluing[v];
			}
	}
}


static void lift_peripheral_curves(
	Triangulation	*manifold,
	TetPtrPair		*new_tet)
{
	int				i;
	Tetrahedron		*old_tet;
	PeripheralCurve	c;
	VertexIndex		v;
	FaceIndex		f;

	/*
	 *	Lift the peripheral curves from manifold to the_cover.
	 *
	 *	The curves on a torus cusp lift to both preimages in the_cover.
	 *	In the original nonorientable manifold, the curves are stored on
	 *	one sheet of the cusp's double cover (cf. the top-of-file
	 *	documentation in peripheral curves.c).  We don't know a priori
	 *	which sheet it is, but the other sheet will be empty, so we can
	 *	safely lift both sheets to the_cover.  We lift the right_handed
	 *	(resp. left_handed) sheet at each vertex in the manifold to the
	 *	right_handed (resp. left_handed) sheet at the corresponding vertex
	 *	in each of the corresponding tetrahedra in the_cover to insure that
	 *	the peripheral curves obey the right hand rule relative to the
	 *	orientation of each cusp's double cover (cf. the top-of-file
	 *	documentation in peripheral curves.c).
	 *
	 *	The curves on a Klein bottle cusp are already described as a single
	 *	lift to the double cover (cf. the top-of-file ocumentation in
	 *	peripheral curves.c).  So we copy the contents of the
	 *
	 *		old_tet->curve[][right_handed][][]
	 *			to new_tet[][0]->curve[][right_handed][][],
	 *	and
	 *		old_tet->curve[][ left_handed][][]
	 *			to new_tet[][1]->curve[][ left_handed][][].
	 *
	 *	The fields
	 *				new_tet[][1]->curve[][right_handed][][]
	 *	and
	 *				new_tet[][0]->curve[][ left_handed][][]
	 *
	 *	are left zero.
	 *
	 *	Note that the curves match up across gluings (compare the convention
	 *	for matching curves in peripheral_curves.c with the convention for
	 *	assigning neighbors in set_neighbors_and_gluings() above).
	 */

	for (old_tet = manifold->tet_list_begin.next, i = 0;
		 old_tet != &manifold->tet_list_end;
		 old_tet = old_tet->next, i++)

		for (c = 0; c < 2; c++)

			for (v = 0; v < 4; v++)

				for (f = 0; f < 4; f++)

					if (old_tet->cusp[v]->topology == torus_cusp)
					{
						new_tet[i][0]->curve[c][right_handed][v][f] =
						new_tet[i][1]->curve[c][right_handed][v][f] =
							old_tet->curve[c][right_handed][v][f];

						new_tet[i][0]->curve[c][ left_handed][v][f] =
						new_tet[i][1]->curve[c][ left_handed][v][f] =
							old_tet->curve[c][ left_handed][v][f];
					}
					else
					{
						new_tet[i][0]->curve[c][right_handed][v][f] =
							old_tet->curve[c][right_handed][v][f];

						new_tet[i][1]->curve[c][ left_handed][v][f] =
							old_tet->curve[c][ left_handed][v][f];
					}
}


static void lift_tet_shapes(
	Triangulation	*manifold,
	TetPtrPair		*new_tet)
{
	int				i,
					j,
					k;
	Tetrahedron		*old_tet;

	for (old_tet = manifold->tet_list_begin.next, i = 0;
		 old_tet != &manifold->tet_list_end;
		 old_tet = old_tet->next, i++)

		for (k = 0; k < 2; k++)		/* k = complete, filled */

			if (old_tet->shape[k] != NULL)

				for (j = 0; j < 2; j++)		/* j = which lift */
				{
					new_tet[i][j]->shape[k] = NEW_STRUCT(TetShape);
					*new_tet[i][j]->shape[k] = *old_tet->shape[k];

					copy_shape_history(old_tet->shape_history[k], &new_tet[i][j]->shape_history[k]);
				}
}


static void set_cusp_data(
	Triangulation	*manifold,
	Triangulation	*the_cover,
	TetPtrPair		*new_tet)
{
	int				i,
					j,
					cusp_count;
	IntPair			*old_to_new;
	CuspTopology	topology;
	Boolean			is_complete;
	double			m,
					l;
	Tetrahedron		*old_tet;
	VertexIndex		v;

	/*
	 *	Set up the correspondence between the old indices and the new ones.
	 *	Old tori will lift to two new tori, while old Klein bottles each
	 *	lift to a single new one.
	 */

	old_to_new = NEW_ARRAY(manifold->num_cusps, IntPair);

	cusp_count = 0;

	for (i = 0; i < manifold->num_cusps; i++)
	{
		get_cusp_info(manifold, i, &topology,
			NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

		if (topology == torus_cusp)
		{
			old_to_new[i][0] = cusp_count++;
			old_to_new[i][1] = cusp_count++;
		}
		else
		{
			old_to_new[i][0] = cusp_count++;
			old_to_new[i][1] = old_to_new[i][0];
		}
	}

	/*
	 *	Now assign the indices to the newly computed cusps.
	 *	This algorithm is highly redundant -- each index will be set
	 *	once for every ideal vertex incident to the cusp -- but the
	 *	inefficiency is insignificant.  A cusp and its mate (i.e.
	 *	the two cusps in the double cover which project down to a given
	 *	cusp in the original manifold) may swap indices many times during
	 *	this redundant procedure, as their indices get overwritten.
	 *
	 *	While we're here, let's set the topology of each Cusp to torus_cusp.
	 */

	for (old_tet = manifold->tet_list_begin.next, i = 0;
		 old_tet != &manifold->tet_list_end;
		 old_tet = old_tet->next, i++)

		for (v = 0; v < 4; v++)

			for (j = 0; j < 2; j++)
			{
				new_tet[i][j]->cusp[v]->index = old_to_new
					[old_tet->cusp[v]->index] [j];

				new_tet[i][j]->cusp[v]->topology = torus_cusp;
			}

	/*
	 *	Set the is_complete, m and l fields of the new cusps.
	 */

	for (i = 0; i < manifold->num_cusps; i++)
	{
		get_cusp_info(manifold, i, &topology, &is_complete, &m, &l,
								NULL, NULL, NULL, NULL, NULL, NULL);

		if (topology == torus_cusp)
		{
			set_cusp_info(the_cover, old_to_new[i][0], is_complete, m, l);
			set_cusp_info(the_cover, old_to_new[i][1], is_complete, m, l);
		}
		else
		{
			set_cusp_info(the_cover, old_to_new[i][0], is_complete, m, l);
		}
	}

	/*
	 *	Free the local storage.
	 */

	my_free(old_to_new);
}