File: canonize_part_2.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 (628 lines) | stat: -rw-r--r-- 19,485 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
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
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
/*
 *	canonize_part_2.c
 *
 *	This file provides the function
 *
 *		void canonical_retriangulation(Triangulation *manifold);
 *
 *	which accepts an arbitrary subdivision of a canonical cell decomposition
 *	into Tetrahedra, and replaces it with a canonical retriangulation
 *	(defined below).  If the canonical cell decomposition is itself a
 *	triangulation (as is typically the case) then the canonical retriangulation
 *	is just the canonical cell decomposition itself.  If the canonical cell
 *	decomposition is not a triangulation, the canonical retriangulation will
 *	introduce a finite vertex at the center of each 3-cell.
 *	canonical_retriangulation() is intended to follow a call to proto_canonize()
 *	(cf. the function canonize() in canonize.c);  it assumes the tet->tilt[]
 *	fields are correct.
 *
 *	If *manifold has a hyperbolic structure and/or VertexCrossSections,
 *	they are discarded.
 *
 *
 *	Definition of the canonical retriangulation.
 *
 *	The canonical cell decomposition of a cusped hyperbolic 3-manifold
 *	is defined in
 *
 *		J. Weeks, Convex hulls and isometries of cusped hyperbolic
 *			3-manifolds, Topology Appl. 52 (1993) 127-149.
 *
 *	and
 *
 *		M. Sakuma and J. Weeks, The generalized tilt formula,
 *			Geometriae Dedicata 55 (1995) 115-123.
 *
 *	If the canonical cell decomposition is a triangulation (as is
 *	typically the case), then the canonical retriangulation is just
 *	the canonical cell decomposition itself.  Otherwise, the canonical
 *	retriangulation is defined by the following two step procedure.
 *
 *	Step #1.	Subdivide each 3-cell by coning its boundary to a point
 *				in its interior.
 *
 *	Step #2.	Each 2-cell in the canonical cell decomposition (just
 *				the original 2-cells, not the 2-cells introduced in Step #1)
 *				is the boundary between two 3-cells created at Step #1.
 *				The union of the two 3-cells is the suspension of the
 *				2-cell between two of the vertices introduced in Step #1.
 *				Triangulate this suspension in the "obvious" symmetrical way,
 *				as n tetrahedra surrounding a common edge, where n is the
 *				number of sides of the 2-cell, and the common edge runs
 *				from one finite vertex to the other.
 *
 *	This two-step procedure defines a canonical retriangulation of
 *	the canonical cell decomposition.  Note that it's not a subdivision.
 *
 *
 *	Computing the canonical retriangulation.
 *
 *	The following is a mathematical explanation of the algorithm.
 *	The details of the implementation are documented in the code itself.
 *
 *	We begin with an arbitrary subdivision of the canonical cell
 *	decomposition into Tetrahedra.
 *
 *	Definition.  A 2-cell in the Triangulation is called "opaque" if
 *	it lies in the 2-skeleton of the canonical cell decomposition,
 *	and "transparent" if lies in the interior of a 3-cell of the
 *	canonical decomposition.
 *
 *	Step #1.
 *	To cone a 3-cell to a point in its interior, first do a one-to-four
 *	move to cone a single Tetrahedron to a point in its interior.
 *	If that was the only Tetrahedron in the 3-cell, we're done.
 *	Otherwise, perform a two-to-three move across a transparent face of
 *	the coned tetrahedron.  This yields a coned hexahedron.  Continue
 *	in this fashion until all the Tetrahedra in the 3-cell have been
 *	absorbed into the coned polyhedron.  This coned polyhedron may have
 *	some pair of faces on its boundary identified to yield the original
 *	3-cell.  Where this occurs, call cancel_tetrahedra() to simplify the
 *	coned polyhedron.  (This operation is documented more thoroughly
 *	in the code itself.)
 *
 *	Step #2.
 *	Do a two-to-three move across each opaque face, then cancel
 *	all pairs of Tetrahedra surrounding EdgeClasses of order 2.
 *	You may take it as an exercise for the reader to prove that this
 *	has the desired effect, or you may read the explanation provided
 *	in the function step_two() below.
 *
 *
 *	Programming note:  I have coded this algorithm for simplicity
 *	rather than speed.  But even though the code is "less efficient"
 *	because it does, e.g., some unnecessary rescanning of lists, I don't
 *	think this will make a measurable difference in practice, and
 *	in any case I think it's a small price to pay to keep the logic
 *	of the code clean and simple.
 */

#include "kernel.h"
#include "canonize.h"


static void		remove_vertex_cross_sections(Triangulation *manifold);
static void		attach_canonize_info(Triangulation *manifold);
static void		free_canonize_info(Triangulation *manifold);
static void		label_opaque_faces(Triangulation *manifold);
static void		step_one(Triangulation *manifold);
static void		initialize_tet_status(Triangulation *manifold);
static Boolean	cone_3_cell(Triangulation *manifold, int *num_finite_vertices);
static Boolean	find_unconed_tet(Triangulation *manifold, Tetrahedron **tet0);
static Boolean	expand_coned_region(Triangulation *manifold);
static Boolean	attempt_cancellation(Triangulation *manifold);
static Boolean	verify_coned_region(Triangulation *manifold);
static void		step_two(Triangulation *manifold);
static Boolean	eliminate_opaque_face(Triangulation *manifold);


void canonical_retriangulation(
	Triangulation	*manifold)
{
	/*
	 *	Remove the hyperbolic structures and VertexCrossSections, if any.
	 */

	remove_hyperbolic_structures(manifold);
	remove_vertex_cross_sections(manifold);

	/*
	 *	If the canonical cell decomposition is a triangulation, we're done.
	 *	(Note:  Comment out this line if you want to invoke the more
	 *	elaborate canonical retriangulation scheme for all manifolds, not
	 *	just those whose canonical cell decompositions contain cells other
	 *	than tetrahedra.)
	 */

	if (is_canonical_triangulation(manifold) == TRUE)
		return;

	/*
	 *	Add a CanonizeInfo field to each Tetrahedron to hold local variables.
	 *	These variables must be accessible to the low-level retriangulation
	 *	routines in simplify_triangulation.c, which is why we use the
	 *	special purpose pointer canonize_info in the Tetrahedron data
	 *	structure, rather than using the general purpose "extra" pointer.
	 */

	attach_canonize_info(manifold);

	/*
	 *	Note which 2-cells are opaque and which are transparent.
	 */

	label_opaque_faces(manifold);

	/*
	 *	Carry out the two step retriangulation algorithm described above.
	 */

	step_one(manifold);
	step_two(manifold);

	/*
	 *	Free the CanonizeInfo fields.
	 */

	free_canonize_info(manifold);

	/*
	 *	We can't possibly compute the Chern-Simons invariant
	 *	for a manifold with finite vertices, so we set
	 *	CS_fudge_is_known to FALSE.  However, we can leave
	 *	CS_value_is_known as TRUE if it is already TRUE, since
	 *	the manifold is still the same.
	 */

	manifold->CS_fudge_is_known = FALSE;
}


static void remove_vertex_cross_sections(
	Triangulation	*manifold)
{
	Tetrahedron	*tet;

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

		if (tet->cross_section != NULL)
		{
			my_free(tet->cross_section);
			tet->cross_section = NULL;
		}
}


static void attach_canonize_info(
	Triangulation	*manifold)
{
	Tetrahedron	*tet;

	for (tet = manifold->tet_list_begin.next;
		 tet != &manifold->tet_list_end;
		 tet = tet->next)
	{
		/*
		 *	Just to be safe . . .
		 */
		if (tet->canonize_info != NULL)
			uFatalError("attach_canonize_info", "canonize_part_2");

		/*
		 *	Attach the CanonizeInfo.
		 */
		tet->canonize_info = NEW_STRUCT(CanonizeInfo);
	}
}


static void free_canonize_info(
	Triangulation	*manifold)
{
	Tetrahedron	*tet;

	for (tet = manifold->tet_list_begin.next;
		 tet != &manifold->tet_list_end;
		 tet = tet->next)
	{
		/*
		 *	Free the CanonizeInfo structure.
		 */
		my_free(tet->canonize_info);

		/*
		 *	Set the canonize_info pointer to NULL just to be safe.
		 */
		tet->canonize_info = NULL;
	}
}


static void label_opaque_faces(
	Triangulation	*manifold)
{
	Tetrahedron	*tet,
				*nbr_tet;
	FaceIndex	f,
				nbr_f;
	double		sum_of_tilts;

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

		for (f = 0; f < 4; f++)
		{
			nbr_tet	= tet->neighbor[f];
			nbr_f	= EVALUATE(tet->gluing[f], f);

			sum_of_tilts = tet->tilt[f] + nbr_tet->tilt[nbr_f];

			tet->canonize_info->face_status[f] =
				sum_of_tilts < - CONCAVITY_EPSILON ?
				opaque_face :
				transparent_face;
		}
}


static void step_one(
	Triangulation	*manifold)
{
	int	num_finite_vertices;

	/*
	 *	Initialize each part_of_coned_cell flag to FALSE.
	 */

	initialize_tet_status(manifold);

	/*
	 *	Keep track of the number of finite vertices that
	 *	have been introduced, so they can be given consecutive
	 *	negative integers as indices.
	 */

	num_finite_vertices = 0;

	/*
	 *	Cone each 3-cell of the canonical cell decomposition to a point
	 *	in its interior.  Each call to cone_3_cell() cones a single 3-cell,
	 *	so we must keep calling cone_3_cell() until it returns FALSE.
	 */

	while (cone_3_cell(manifold, &num_finite_vertices) == TRUE)
		;
}


static void initialize_tet_status(
	Triangulation	*manifold)
{
	Tetrahedron	*tet;

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

		tet->canonize_info->part_of_coned_cell = FALSE;
}


static Boolean cone_3_cell(
	Triangulation	*manifold,
	int				*num_finite_vertices)
{
	Tetrahedron	*tet0;

	/*
	 *	Is there a Tetrahedron which is not yet part of a coned cell?
	 *	If so, proceed.
	 *	If not, return FALSE.
	 */

	if (find_unconed_tet(manifold, &tet0) == FALSE)
		return FALSE;

	/*
	 *	Cone tet0 to its center.  This will introduce a finite vertex.
	 *	Each of the four new Tetrahedra will have part_of_coned_cell
	 *	set to TRUE.  The face_status for the "exterior" faces will
	 *	be preserved, and the face_status for the "interior" faces
	 *	will be set to inside_cone_face.
	 */

	one_to_four(tet0, &manifold->num_tetrahedra, -++*num_finite_vertices);

	/*
	 *	Expand the coned region.  Whenever a Tetrahedron with
	 *	part_of_coned_cell == TRUE borders a Tetrahedron with
	 *	part_of_coned_cell == FALSE across a transparent face,
	 *	do a two-to-three move across the face to include the
	 *	(space occupied by the) latter Tetrahedron in the (growing)
	 *	coned region.  Keep doing this as long as progress is
	 *	being made.  The two-to-three move will set
	 *	part_of_coned_cell = TRUE for the three new Tetrahedra
	 *	it creates.  It will preserve the face_status of the
	 *	"exterior" faces of the new Tetrahedra, and set the face_status
	 *	of the "interior" faces to inside_cone_face.
	 *
	 *	(Yes, I know this isn't the optimally efficient way to do this,
	 *	but I don't think that's important.  Please see the programming
	 *	note at the end of the documentation at the top of this file.)
	 */

	while (expand_coned_region(manifold) == TRUE)
		;

	/*
	 *	If the initial, arbitrary subdivision of the 3-cell contained
	 *	edges in its interior (as would be the case with an octahedron,
	 *	for example), then the coned 3-cell we just produced will have
	 *	some identifications on its boundary (for example, in the case
	 *	of the octahedron we'd have a coned decahedron with two adjacent
	 *	boundary faces identified to yield the octahedron).  Assuming at
	 *	least one pair of identified faces are adjacent, we can call
	 *	cancel_tetrahedra() to simplify the structure of the coned region
	 *	from a coned n-hedron with k pairs of faces identified to a coned
	 *	(n-2)-hedron with (k-1) pairs of faces identified.  As long as some
	 *	pair of identified faces are adjacent (and identified in the obvious
	 *	way) we can keep repeating the simplification to arrive at a coned
	 *	(n-2k)-hedron with no faces identified, which is exactly what
	 *	we want in Step #1 of this algorithm.
	 *
	 *	It's theoretically possible to have faces of a coned polyehdron
	 *	identified with no pair of identified faces adjacent to each other.
	 *	For example, consider the complement of the house-with-two-rooms
	 *	in the 3-sphere.  Fortunately such examples are so rare and so
	 *	complicated that I doubt any will ever show up as triangulations
	 *	of 3-cells in canonical cell decompositions.  If one did show up,
	 *	verify_coned_region() would detect the condition and call uFatalError().
	 *
	 *	Proposition.  Identifying two adjacent faces (in the "obvious" way)
	 *	creates an EdgeClass of order 2, and this is the only way
	 *	EdgeClasses of order 2 arise.
	 *
	 *	Proof.  It's obvious that such an identification creates an
	 *	EdgeClass of order 2.  We must prove that this is the only way
	 *	they may arise.  Consider separately EdgeClasses from the
	 *	original Triangulation (i.e. from the original arbitrary subdivision
	 *	of the canonical cell decomposition), which connect one ideal
	 *	vertex to another, and EdgeClasses which are added during the
	 *	coning process, which connect an ideal vertex to a finite vertex.
	 *
	 *	Case 1.  Original EdgeClasses, which connect one ideal vertex
	 *	to another.  There are no EdgeClasses of order 2 in the initial
	 *	Triangulation, because it is geometric.  In a coned polyhedron
	 *	(ignoring boundary identifications for the moment) each
	 *	EdgeClass connecting one ideal vertex to another is incident
	 *	to precisely two of the coned polyhedron's Tetrahedra.  If the
	 *	EdgeClass lies on the boundary of the 3-cell (of the canonical
	 *	cell decomposition) then its true order will be greater than two.
	 *	If it lies in the interior of the 3-cell, then the only way for
	 *	the order to be exactly two is to have the two adjacent faces
	 *	identified.
	 *
	 *	Case 2.  EdgeClasses added during the coning process, which
	 *	connect an ideal vertex to a finite vertex.  We assume such
	 *	an EdgeClass has order 2, and will deduce a contradiction.
	 *	Consider the coned polyhedron, ignoring boundary identifications
	 *	for the moment.  Consider the two faces on the boundary of
	 *	incident to the ideal endpoint of the given EdgeClass.  These
	 *	faces are part of the original (geometric!) subdivision of the
	 *	canonical cell decomposition.  They have all three ideal vertices
	 *	in common, therefore they must coincide.  But this implies that
	 *	the  boundary component at the ideal vertex of the given EdgeClass
	 *	is topologically a sphere.
	 *
	 *	Q.E.D.
	 */

	while (attempt_cancellation(manifold) == TRUE)
		;

	/*
	 *	As explained above, once we've cancelled all Tetrahedra incident
	 *	to EdgeClasses of order 2, we will almost surely have the required
	 *	coning of the 3-cell's boundary to a point in its interior, unless
	 *	we encounter a situation like the complement of the
	 *	house-with-two-rooms.  verify_coned_region() checks that we
	 *	do in fact have a coning of the original 3-cell;  that is,
	 *	no Tetrahedron with part_of_coned_cell == TRUE has a face with
	 *	face_status transparent_face.
	 */

	if (verify_coned_region(manifold) == FALSE)
		uFatalError("cone_3_cell", "canonize_part_2");

	return TRUE;
}


static Boolean find_unconed_tet(
	Triangulation	*manifold,
	Tetrahedron		**tet0)
{
	Tetrahedron		*tet;

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

		if (tet->canonize_info->part_of_coned_cell == FALSE)
		{
			*tet0 = tet;
			return TRUE;
		}

	return FALSE;
}


static Boolean expand_coned_region(
	Triangulation	*manifold)
{
	Tetrahedron		*tet;
	FaceIndex		f;

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

		if (tet->canonize_info->part_of_coned_cell == TRUE)

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

				if (tet->canonize_info->face_status[f] == transparent_face)

					if (tet->neighbor[f]->canonize_info->part_of_coned_cell == FALSE)
					{
						if (two_to_three(tet, f, &manifold->num_tetrahedra) == func_OK)

							return TRUE;

						else	/* this should never occur */

							uFatalError("expand_coned_region", "canonize_part_2");
					}

	return FALSE;
}


static Boolean attempt_cancellation(
	Triangulation	*manifold)
{
	EdgeClass	*edge,
				*where_to_resume;

	for (edge = manifold->edge_list_begin.next;
		 edge != &manifold->edge_list_end;
		 edge = edge->next)

		if (edge->order == 2)
		{
			if (cancel_tetrahedra(edge, &where_to_resume, &manifold->num_tetrahedra) == func_OK)

				return TRUE;

			else
				/*
				 *	I don't think failure is possible, but if it is
				 *	I want to know about it.
				 */
				uFatalError("attempt_cancellation", "canonize_part_2");
		}

	return FALSE;
}


static Boolean verify_coned_region(
	Triangulation	*manifold)
{
	/*
	 *	Because one_to_four(), two_to_three() and cancel_tetrahedra()
	 *	all maintain the coned polyhedron as some sort of coned
	 *	polyhedron, all we need to check is that it has no remaining
	 *	identifications on its boundary.  That is, we need to check
	 *	that the faces of each Tetrahedron with part_of_coned_cell == TRUE
	 *	have face_status opaque_face or inside_cone_face, but never
	 *	transparent_face.
	 */

	Tetrahedron		*tet;
	FaceIndex		f;

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

		if (tet->canonize_info->part_of_coned_cell == TRUE)

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

				if (tet->canonize_info->face_status[f] == transparent_face)

					return FALSE;

	return TRUE;
}


static void step_two(
	Triangulation	*manifold)
{
	/*
	 *	Step #1 of the algorithm has already been carried out, so
	 *	we know that every 3-cell in the canonical cell decomposition
	 *	has been subdivided by coning the boundary to a point in
	 *	the interior.  There are three types of EdgeClasses:
	 *
	 *	(A)	Those which lie in the 1-skeleton of the canonical
	 *		cell decomposition.  They have order at least 6.
	 *
	 *	(B)	Those which lie in the 2-skeleton of the canonical
	 *		cell decomposition, but not in the 1-skeleton.  They
	 *		serve to (artificially) subdivide the faces of the 3-cells
	 *		into triangles.  Each has order precisely 4.
	 *
	 *	(C)	Those in the interior of the 3-cells.  They connect ideal
	 *		vertices to finite vertices, and have order at least 3.
	 *
	 *	Step #2 of the algorithm consists of two substeps:
	 *
	 *	Substep A.	Do a two-to-three move across every opaque face.
	 *
	 *	Substep B.	Perform all possible cancellations of Tetrahedra
	 *				surrounding EdgeClasses of order 2.
	 *
	 *	After Substep A is complete, we know that
	 *
	 *		EdgeClasses of type A will have order at least 3.
	 *		EdgeClasses of type B will have order precisely 2.
	 *		EdgeClasses of type C will have order at least 6.
	 *		The new EdgeClasses introduced in Substep A will have
	 *			order precisely 3.
	 *
	 *	Therefore Substep B will eliminate precisely the EdgeClasses
	 *	of type B.  It's easy to see that removing these EdgeClasses
	 *	creates the Triangulation specified in Step #2 of the definition
	 *	of the canonical retriangulation at the top of this file.
	 */

	while (eliminate_opaque_face(manifold) == TRUE)
		;

	while (attempt_cancellation(manifold) == TRUE)
		;
}


static Boolean eliminate_opaque_face(
	Triangulation	*manifold)
{
	Tetrahedron		*tet;
	FaceIndex		f;

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

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

			if (tet->canonize_info->face_status[f] == opaque_face)
			{
				if (two_to_three(tet, f, &manifold->num_tetrahedra) == func_OK)

					return TRUE;

				else	/* this should never occur */

					uFatalError("expand_coned_region", "canonize_part_2");
			}

	return FALSE;
}