File: cusp_shapes.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 (455 lines) | stat: -rw-r--r-- 14,599 bytes parent folder | download | duplicates (11)
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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
/*
 *	cusp_shapes.c
 *
 *	This file provides the function
 *
 *		void	compute_cusp_shapes(Triangulation	*manifold,
 *									FillingStatus	which_structure);
 *
 *	which computes the shape of each unfilled cusp.  The shape of an
 *	orientable cusp is defined to be the ratio of the complex numbers
 *	representing the longitudinal and meridional translations
 *	(i.e. longitude/meridian).  The shape of a nonorientable cusp is
 *	defined to be the shape of the cusp's orientation double cover;
 *	it is always pure imaginary.  (Another reasonable definition for
 *	the shape of a nonorientable cusp would be to divide the shape
 *	of the orientation double cover by two, to correspond more closely
 *	to the geometry of the nonorientable cusp's fundamental domain,
 *	but I decided not to do this.)
 *
 *	compute_cusp_shapes() computes the cusp shapes using the which_structure
 *	(initial or current) hyperbolic structure, and stores each computed cusp
 *	shape in the cusp_shape[which_structure] field of the Cusp data structure.
 *
 *	To estimate the computed cusp shape's precision, compute_cusp_shapes()
 *	computes the cusp shape using both the ultimate and penultimate values of
 *	the Tetrahedron shapes.  The number of digits to which the answers agree
 *	is stored in the shape_precision[which_structure] field of the Cusp data
 *	structure.
 *
 *	do_Dehn_filling() calls compute_cusp_shapes() to maintain correct values
 *	in the cusp_shape[current] field of each unfilled cusp whenever a
 *	hyperbolic structure is present.  find_complete_hyperbolic_structure()
 *	takes responsibility for copying the original shape of each cusp into
 *	the cusp_shape[initial] field.
 *
 *	Cusp shapes are computed iff manifold->solution_type[which_structure]
 *	is geometric_solution, nongeometric_solution, flat_solution, or
 *	(tentatively) other_solution.  For other solution types (degenerate_solution,
 *	etc.) all cusp shapes are set to zero.
 *
 *	Technical note:  with the usual orientation conventions for the
 *	longitude and meridian, we must view the cusp from the fat part
 *	of the manifold looking out towards infinity in order to have the
 *	imaginary part of the cusp shape be positive.  The code
 *	in compute_translation() views the cusp from infinity looking in
 *	towards the fat part of the manifold (as usual), and then
 *	compute_one_cusp_shape() takes the complex conjugate of the shape
 *	at the very end.
 *
 *	The function shortest_cusp_basis() in shortest_cusp_basis.c converts
 *	the (meridian, longitude) basis to the (shortest, second shortest)
 *	basis.  cusp_modulus() uses the latter to compute the cusp modulus
 *	as (second shortest translation)/(shortest translation).
 *
 *	96/9/27  Added the which_structure parameter to compute_cusp_shapes()
 *	so it can compute the cusp shapes for either the initial (complete)
 *	or the current (filled) hyperbolic structure.  Previously it used only
 *	the current structure.
 */


#include "kernel.h"

static void				compute_the_cusp_shapes(Triangulation *manifold, FillingStatus which_structure);
static void				set_all_shapes_to_zero(Triangulation *manifold, FillingStatus which_structure);
static void				compute_one_cusp_shape(Triangulation *manifold, Cusp *cusp, FillingStatus which_structure);
static void				compute_translation(PositionedTet *initial_ptet,
							PeripheralCurve which_curve, TraceDirection which_direction,
							Complex translation[2], FillingStatus which_structure);
static PositionedTet	find_start(Triangulation *manifold, Cusp *cusp);


void compute_cusp_shapes(
	Triangulation	*manifold,
	FillingStatus	which_structure)
{
	/*
	 *	If we have some reasonable (or semi-reasonable) hyperbolic
	 *	structure, then compute the cusp shapes.  Otherwise, fill in
	 *	all shapes with zeros.
	 */

	switch (manifold->solution_type[which_structure])
	{
		case geometric_solution:
		case nongeometric_solution:
		case flat_solution:
		case other_solution:	/* we'll give the cusps of other_solution a try	*/
								/* other_solution can be moved below if its		*/
								/* cusp shapes can't be computed meaningfully	*/

			compute_the_cusp_shapes(manifold, which_structure);

			break;

		case not_attempted:
		case degenerate_solution:
		case no_solution:

			set_all_shapes_to_zero(manifold, which_structure);

			break;
	}
}


static void compute_the_cusp_shapes(
	Triangulation	*manifold,
	FillingStatus	which_structure)
{
	Cusp	*cusp;

	/*
	 *	Call compute_one_cusp_shape() for each unfilled cusp.
	 */

	for (cusp = manifold->cusp_list_begin.next;
		 cusp != &manifold->cusp_list_end;
		 cusp = cusp->next)

		if ( which_structure == initial
		 || (which_structure == current && cusp->is_complete))

			compute_one_cusp_shape(manifold, cusp, which_structure);

		else
		{
			cusp->cusp_shape[which_structure]		= Zero;
			cusp->shape_precision[which_structure]	= 0;
		}
}


static void set_all_shapes_to_zero(
	Triangulation	*manifold,
	FillingStatus	which_structure)
{
	Cusp	*cusp;

	for (cusp = manifold->cusp_list_begin.next;
		 cusp != &manifold->cusp_list_end;
		 cusp = cusp->next)
	{
		cusp->cusp_shape[which_structure]		= Zero;
		cusp->shape_precision[which_structure]	= 0;
	}
}


static void compute_one_cusp_shape(
	Triangulation	*manifold,
	Cusp			*cusp,
	FillingStatus	which_structure)
{
	PositionedTet	initial_ptet;
	TraceDirection	direction[2];		/*	direction[M/L]							*/
	Complex			translation[2][2],	/*	translation[M/L][ultimate/penultimate]	*/
					shape[2];			/*	shape[ultimate/penultimate]				*/
	int				i;

	/*
	 *	Compute the longitudinal and meridional translations, and
	 *	divide them to get the cusp shape.
	 *
	 *	Do parallel computations for the ultimate and penultimate shapes,
	 *	to estimate the accuracy of the final answer.
	 */

	/*
	 *	Find and position a tetrahedron so that the near edge of the top
	 *	vertex intersects both the meridian and the longitude.
	 */
	initial_ptet = find_start(manifold, cusp);

	for (i = 0; i < 2; i++)		/* which curve */
	{
		/*
		 *	Decide whether the meridian and longitude cross the near edge of the
		 *	top vertex in a forwards or backwards direction.
		 */
		direction[i] =
			(initial_ptet.tet->curve[i][initial_ptet.orientation] [initial_ptet.bottom_face] [initial_ptet.near_face] > 0) ?
			trace_forwards:
			trace_backwards;

		/*
		 *	Compute the translation.
		 */
		compute_translation(&initial_ptet, i, direction[i], translation[i], which_structure);
	}

	/*
	 *	Compute the cusp shape.
	 */
	for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
		shape[i] = complex_div(translation[L][i], translation[M][i]);   /* will handle division by Zero correctly */

	/*
	 *	Record the cusp shape and its accuracy.
	 */
	cusp->cusp_shape[which_structure]		= shape[ultimate];
	cusp->shape_precision[which_structure]	= complex_decimal_places_of_accuracy(shape[ultimate], shape[penultimate]);

	/*
	 *	Adjust for the fact that the meridian and/or the longitude may have
	 *	been traced backwards.
	 */
	if (direction[M] != direction[L])
	{
		cusp->cusp_shape[which_structure].real = - cusp->cusp_shape[which_structure].real;
		cusp->cusp_shape[which_structure].imag = - cusp->cusp_shape[which_structure].imag;
	}

	/*
	 *	As explained at the top of this file, the usual convention for the
	 *	cusp shape requires viewing the cusp from the fat part of
	 *	the manifold looking out, rather than from the cusp looking in, as
	 *	in done in the rest of SnapPea.  For this reason, we must take the
	 *	complex conjugate of the final cusp shape.
	 */
	cusp->cusp_shape[which_structure].imag = - cusp->cusp_shape[which_structure].imag;
}


static void compute_translation(
	PositionedTet	*initial_ptet,
	PeripheralCurve	which_curve,
	TraceDirection	which_direction,
	Complex			translation[2],	/* returns translations based on ultimate	*/
									/* and penultimate shapes					*/
	FillingStatus	which_structure)
{
	PositionedTet	ptet;
	int				i,
					initial_strand,
					strand,
					*this_vertex,
					near_strands,
					left_strands;
	Complex			left_endpoint[2],	/*	left_endpoint[ultimate/penultimate]		*/
					right_endpoint[2],	/*	right_endpoint[ultimate/penultimate]	*/
					old_diff,
					new_diff,
					rotation;

	/*
	 *	Place the near edge of the top vertex of the initial_ptet in the
	 *	complex plane with its left endpoint at zero and its right endpoint at one.
	 *	Trace the curve which_curve in the direction which_direction, using the
	 *	shapes of the ideal tetrahedra to compute the position of endpoints of
	 *	each edge we cross.  When we return to our starting point in the manifold,
	 *	the position of the left endpoint (or the position of the right endpoint
	 *	minus one) will tell us the translation.
	 *
	 *	Note that we are working in the orientation double cover of the cusp.
	 *
	 *	Here's how we keep track of where we are.  At each step, we are always
	 *	at the near edge of the top vertex (i.e. the truncated vertex opposite
	 *	the bottom face) of the PositionedTet ptet.  The curve (i.e. the
	 *	meridian or longitude) may cross that edge several times.  The variable
	 *	"strand" keeps track of which intersection we are at;  0 means we're at
	 *	the strand on the far left, 1 means we're at the next strand, etc.
	 */

	ptet			= *initial_ptet;
	initial_strand	= 0;
	strand			= initial_strand;
	for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
	{
		left_endpoint[i]	= Zero;
		right_endpoint[i]	= One;
	}

	do
	{
		/*
		 *	Note the curve's intersection numbers with the near side and left side.
		 */
		this_vertex =	ptet.tet->curve[which_curve][ptet.orientation][ptet.bottom_face];
		near_strands = this_vertex[ptet.near_face];
		left_strands = this_vertex[ptet.left_face];

		/*
		 *	If we are tracing the curve backwards, negate the intersection numbers
		 *	so the rest of compute_translation() can enjoy the illusion that we
		 *	are tracing the curve forwards.
		 */
		if (which_direction == trace_backwards)
		{
			near_strands = - near_strands;
			left_strands = - left_strands;
		}

		/*
		 *	Does the current strand bend to the left or to the right?
		 */

		if (strand < FLOW(near_strands, left_strands))
		{
			/*
			 *	The current strand bends to the left.
			 */

			/*
			 *	The left_endpoint remains fixed.
			 *	Update the right_endpoint.
			 *
			 *	The plan is to compute the vector old_diff which runs
			 *	from left_endpoint to right_endpoint, multiply it by the
			 *	complex edge parameter to get the vector new_diff which
			 *	runs from left_endpoint to the new value of right_endpoint,
			 *	and then add new_diff to left_endpoint to get the new
			 *	value of right_endpoint itself.
			 *
			 *	Note that the complex edge parameters are always expressed
			 *	relative to the right_handed Orientation, so if we are
			 *	viewing this Tetrahedron relative to the left_handed
			 *	Orientation, we must take the conjugate-inverse of the
			 *	edge parameter.
			 */
			for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
			{
				old_diff = complex_minus(right_endpoint[i], left_endpoint[i]);
				rotation = ptet.tet->shape[which_structure]->cwl[i][edge3_between_faces[ptet.near_face][ptet.left_face]].rect;
				if (ptet.orientation == left_handed)
				{
					rotation		= complex_div(One, rotation);	/* invert . . .			*/
					rotation.imag	= - rotation.imag;				/* . . . and conjugate	*/
				}
				new_diff = complex_mult(old_diff, rotation);
				right_endpoint[i] = complex_plus(left_endpoint[i], new_diff);
			}

			/*
			 *	strand remains unchanged.
			 */

			/*
			 *	Move the PositionedTet onward, following the curve.
			 */
			veer_left(&ptet);

		}
		else
		{
			/*
			 *	The current strand bends to the right.
			 *
			 *	Proceed as above, but note that
			 *
			 *	(1)	We now divide by the complex edge parameter
			 *		instead of multiplying by it.
			 *
			 *	(2)	We must adjust the variable "strand".  Some of the strands
			 *		from the near edge may be peeling off to the left (in which
			 *		case left_strands is negative), or some strands from the left
			 *		edge may be joining those from the near edge in passing to
			 *		the right edge (in which case left_strands is positive).
			 *		Either way, the code "strand += left_strands" is correct.
			 */

			for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
			{
				old_diff = complex_minus(left_endpoint[i], right_endpoint[i]);
				rotation = ptet.tet->shape[which_structure]->cwl[i][edge3_between_faces[ptet.near_face][ptet.right_face]].rect;
				if (ptet.orientation == left_handed)
				{
					rotation		= complex_div(One, rotation);
					rotation.imag	= - rotation.imag;
				}
				new_diff = complex_div(old_diff, rotation);
				left_endpoint[i] = complex_plus(right_endpoint[i], new_diff);
			}

			strand += left_strands;

			veer_right(&ptet);

		}
	}
	while ( ! same_positioned_tet(&ptet, initial_ptet) || strand != initial_strand);

	/*
	 *	Write the computed translations, and return.
	 */

	for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
		translation[i] = left_endpoint[i];
}


static PositionedTet find_start(
	Triangulation	*manifold,
	Cusp			*cusp)
{
	Tetrahedron		*tet;
	VertexIndex		vertex;
	Orientation		orientation;
	FaceIndex		side;
	PositionedTet	ptet;

	for (tet = manifold->tet_list_begin.next;
		 tet != &manifold->tet_list_end;
		 tet = tet->next)

		for (vertex = 0; vertex < 4; vertex++)
		{
			if (tet->cusp[vertex] != cusp)
				continue;

			for (orientation = right_handed; orientation <= left_handed; orientation++)

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

					if (side != vertex
					 && tet->curve[M][orientation][vertex][side] != 0
					 && tet->curve[L][orientation][vertex][side] != 0)
					{
						/*
						 *	Record the current position of the Tetrahedron in
						 *	a PositionedTet structure . . .
						 */
						ptet.tet			= tet;
						ptet.bottom_face	= vertex;
						ptet.near_face		= side;
						if (orientation == right_handed)
						{
							ptet.left_face	= remaining_face[ptet.bottom_face][ptet.near_face];
							ptet.right_face	= remaining_face[ptet.near_face][ptet.bottom_face];
						}
						else
						{
							ptet.left_face	= remaining_face[ptet.near_face][ptet.bottom_face];
							ptet.right_face	= remaining_face[ptet.bottom_face][ptet.near_face];
						}
						ptet.orientation	= orientation;

						/*
						 *	. . . and return it.
						 */
						return ptet;
					}
		}

	/*
	 *	The program should never get to this point.
	 */
	uFatalError("find_start", "cusp_shapes");

	/*
	 *	The C++ compiler would like a return value, even though
	 *	we never return from the uFatalError() call.
	 */
	return ptet;
}