示例#1
0
void data_to_triangulation(
	TriangulationData	*data,
	Triangulation		**manifold_ptr)
{
	/*
	 *	We assume the UI has done some basic error checking
	 *	on the data, so we don't repeat it here.
	 */

	Triangulation	*manifold;
	Tetrahedron		**tet_array;
	Cusp			**cusp_array;
	Boolean			cusps_are_given;
	int				i,
					j,
					k,
					l,
					m;
	Boolean			all_peripheral_curves_are_zero,
					finite_vertices_are_present;

	/*
	 *	Initialize *manifold_ptr to NULL.
	 *	We'll do all our work with manifold, and then copy
	 *	manifold to *manifold_ptr at the very end.
	 */
	*manifold_ptr = NULL;

	/*
	 *	Allocate and initialize the Triangulation structure.
	 */
	manifold = NEW_STRUCT(Triangulation);
	initialize_triangulation(manifold);

	/*
	 *	Allocate and copy the name.
	 */
	manifold->name = NEW_ARRAY(strlen(data->name) + 1, char);
	strcpy(manifold->name, data->name);

	/*
	 *	Set up the global information.
	 *
	 *	The hyperbolic structure is included in the file only for
	 *	human readers;  here we recompute it from scratch.
	 */
	manifold->num_tetrahedra			= data->num_tetrahedra;
	manifold->solution_type[complete]	= not_attempted;
	manifold->solution_type[ filled ]	= not_attempted;
	manifold->orientability				= data->orientability;
	manifold->num_or_cusps				= data->num_or_cusps;
	manifold->num_nonor_cusps			= data->num_nonor_cusps;
	manifold->num_cusps					= manifold->num_or_cusps
										+ manifold->num_nonor_cusps;

	/*
	 *	Allocate the Tetrahedra.
	 *	Keep pointers to them on a temporary array, so we can
	 *	find them by their indices.
	 */
	tet_array = NEW_ARRAY(manifold->num_tetrahedra, Tetrahedron *);
	for (i = 0; i < manifold->num_tetrahedra; i++)
	{
		tet_array[i] = NEW_STRUCT(Tetrahedron);
		initialize_tetrahedron(tet_array[i]);
		INSERT_BEFORE(tet_array[i], &manifold->tet_list_end);
	}

	/*
	 *	If num_or_cusps or num_nonor_cusps is nonzero, allocate the Cusps.
	 *		Keep pointers to them on temporary arrays, so we can find them
	 *		by their indices.
	 *	Otherwise we will create arbitrary Cusps later.
	 */
	cusps_are_given = (data->num_or_cusps != 0) || (data->num_nonor_cusps != 0);
	if (cusps_are_given == TRUE)
	{
		cusp_array = NEW_ARRAY(manifold->num_cusps, Cusp *);
		for (i = 0; i < manifold->num_cusps; i++)
		{
			cusp_array[i] = NEW_STRUCT(Cusp);
			initialize_cusp(cusp_array[i]);
			INSERT_BEFORE(cusp_array[i], &manifold->cusp_list_end);
		}
	}
示例#2
0
/* DJH */
void my_drill_tube(
	Triangulation	*manifold,
	int		singular_index )
{
	/*
	 *	Insert a triangular-pillow-with-tunnel (as described at the top
	 *	of this file) so as to connect the boundary component at one
	 *	end of the given edge to the boundary component at the other end.
	 *	The orientation on the triangular pillow will match the
	 *	orientation on tet, so that the orientation on the manifold
	 *	(if there is one) will be preserved.  Edge orientations are also
	 *	respected.
	 */
	EdgeIndex		e;	
	VertexIndex		v0,
					v1,
					v2,
					vv0,
					vv1,
					vv2;
	FaceIndex		f,
					ff;
	Tetrahedron		*nbr_tet, *tet, *tet0,
					*new_tet0,
					*new_tet1;
	Permutation		gluing;
	EdgeClass		*edge0,
					*edge1,
					*edge2, *edge,
					*new_edge,
					*edge3;
	Orientation		edge_orientation0,
					edge_orientation1,
					edge_orientation2;
	PeripheralCurve	c;
	Orientation		h;
	int				num_strands,
					i,
					intersection_number[2],
					the_gcd;
	Cusp			*unique_cusp, *merged_cusp, *dead_cusp, *cusp;
	MatrixInt22		*basis_change = NULL;
	Boolean			creating_new_cusp, same_cusp;
	int			*cone_points, num_cone_points, dead_index;

	for(	edge = manifold->edge_list_begin.next;
		edge!=&manifold->edge_list_end;
		edge = edge->next )
	if (edge->is_singular)
		if (edge->singular_index == singular_index)
			break;

	if (edge == &manifold->edge_list_end)
		uFatalError("my_drill_tube", "finite_vertices");

	tet = edge->incident_tet;
	e   = edge->incident_edge_index;

	/*
	 *	Relative to the orientation of tet, the vertices v0, v1 and v2
	 *	are arranged in counterclockwise order around the face f.
	 */
	v0 = one_vertex_at_edge[e];
	v1 = other_vertex_at_edge[e];

	if (	tet->cusp[v0] == tet->cusp[v1]
		&& tet->cusp[v1]->num_cone_points ==2		/* if it's a $\S^2(n,n) orbifold then we are creating a torus cusp */
		&& tet->cusp[v1]->euler_characteristic==2)
		creating_new_cusp = TRUE;
	else	creating_new_cusp = FALSE;

	v2 = remaining_face[v1][v0];
	f  = remaining_face[v0][v1];

	/*
	 *	Note the matching face and its vertices.
	 */
	nbr_tet	= tet->neighbor[f];
	gluing	= tet->gluing[f];
	ff		= EVALUATE(gluing, f);
	vv0		= EVALUATE(gluing, v0);
	vv1		= EVALUATE(gluing, v1);
	vv2		= EVALUATE(gluing, v2);
	
	/*
	 *	Note the incident EdgeClasses (which may or may not be distinct).
	 */
	edge0 = tet->edge_class[e];
	edge1 = tet->edge_class[edge_between_vertices[v1][v2]];
	edge2 = tet->edge_class[edge_between_vertices[v2][v0]];
	
	/*
	 *	Construct the triangular-pillow-with-tunnel, as described
	 *	at the top of this file.
	 */

	new_tet0 = NEW_STRUCT(Tetrahedron);
	new_tet1 = NEW_STRUCT(Tetrahedron);
	initialize_tetrahedron(new_tet0);
	initialize_tetrahedron(new_tet1);
	INSERT_BEFORE(new_tet0, &manifold->tet_list_end);
	INSERT_BEFORE(new_tet1, &manifold->tet_list_end);
	manifold->num_tetrahedra += 2;

	new_edge = NEW_STRUCT(EdgeClass);
	initialize_edge_class(new_edge);
	INSERT_BEFORE(new_edge, &manifold->edge_list_end);

	new_tet0->neighbor[0] = new_tet1;
	new_tet0->neighbor[1] = NULL;	/* assigned below */
	new_tet0->neighbor[2] = NULL;	/* assigned below */
	new_tet0->neighbor[3] = new_tet1;

	new_tet1->neighbor[0] = new_tet0;
	new_tet1->neighbor[1] = new_tet1;
	new_tet1->neighbor[2] = new_tet1;
	new_tet1->neighbor[3] = new_tet0;

	new_tet0->gluing[0] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet0->gluing[1] = 0x00;	/* assigned below */
	new_tet0->gluing[2] = 0x00;	/* assigned below */
	new_tet0->gluing[3] = CREATE_PERMUTATION(0, 1, 1, 0, 2, 2, 3, 3);

	new_tet1->gluing[0] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[1] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[2] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[3] = CREATE_PERMUTATION(0, 1, 1, 0, 2, 2, 3, 3);

	new_tet0->edge_class[0] = edge1;
	new_tet0->edge_class[1] = edge1;
	new_tet0->edge_class[2] = edge0;
	new_tet0->edge_class[3] = edge2;
	new_tet0->edge_class[4] = edge0;
	new_tet0->edge_class[5] = edge0;

	new_tet1->edge_class[0] = edge1;
	new_tet1->edge_class[1] = edge1;
	new_tet1->edge_class[2] = edge0;
	new_tet1->edge_class[3] = new_edge;
	new_tet1->edge_class[4] = edge0;
	new_tet1->edge_class[5] = edge0;

	edge0->order += 6;
	edge1->order += 4;
	edge2->order += 1;

	new_edge->order					= 1;
	new_edge->incident_tet			= new_tet1;
	new_edge->incident_edge_index	= 3;
	
	edge_orientation0 = tet->edge_orientation[e];
	edge_orientation1 = tet->edge_orientation[edge_between_vertices[v1][v2]];
	edge_orientation2 = tet->edge_orientation[edge_between_vertices[v2][v0]];

	new_tet0->edge_orientation[0] = edge_orientation1;
	new_tet0->edge_orientation[1] = edge_orientation1;
	new_tet0->edge_orientation[2] = edge_orientation0;
	new_tet0->edge_orientation[3] = edge_orientation2;
	new_tet0->edge_orientation[4] = edge_orientation0;
	new_tet0->edge_orientation[5] = edge_orientation0;

	new_tet1->edge_orientation[0] = edge_orientation1;
	new_tet1->edge_orientation[1] = edge_orientation1;
	new_tet1->edge_orientation[2] = edge_orientation0;
	new_tet1->edge_orientation[3] = right_handed;
	new_tet1->edge_orientation[4] = edge_orientation0;
	new_tet1->edge_orientation[5] = edge_orientation0;
	
	new_tet0->cusp[0] = tet->cusp[v0];
	new_tet0->cusp[1] = tet->cusp[v0];
	new_tet0->cusp[2] = tet->cusp[v0];
	new_tet0->cusp[3] = tet->cusp[v2];
	
	new_tet1->cusp[0] = tet->cusp[v0];
	new_tet1->cusp[1] = tet->cusp[v0];
	new_tet1->cusp[2] = tet->cusp[v0];
	new_tet1->cusp[3] = tet->cusp[v2];
	
	/*
	 *	Install the triangular-pillow-with-tunnel.
	 */
	
	tet->neighbor[f]		= new_tet0;
	tet->gluing[f]			= CREATE_PERMUTATION(f, 2, v0, 0, v1, 1, v2, 3);
	new_tet0->neighbor[2]	= tet;
	new_tet0->gluing[2]		= inverse_permutation[tet->gluing[f]];
	
	nbr_tet->neighbor[ff]	= new_tet0;
	nbr_tet->gluing[ff]		= CREATE_PERMUTATION(ff, 1, vv0, 0, vv1, 2, vv2, 3);
	new_tet0->neighbor[1]	= nbr_tet;
	new_tet0->gluing[1]		= inverse_permutation[nbr_tet->gluing[ff]];

	for(c=0;c<2;c++)
		for(h=0;h<2;h++)
	{
		new_tet0->curve[c][h][3][2] = -nbr_tet->curve[c][h][v2][f];
		new_tet0->curve[c][h][3][1] = -nbr_tet->curve[c][h][vv2][ff];
	}

	/*
	 *	Typically creating_new_cusp is FALSE, meaning that we are
	 *	connecting a spherical boundary component to a torus or
	 *	Klein bottle boundary component, and we simply extend the
	 *	existing peripheral curves across the new tetrahedra.
	 *
	 *	In the exceptional case that creating_new_cusp is TRUE,
	 *	meaning that the manifold has no real cusps and we are
	 *	connecting the "special fake cusp" to itself, we must
	 *	install a meridian and longitude, and set up the Dehn filling.
	 */
	if (creating_new_cusp == FALSE)
	{
		if (tet->cusp[v0]->index<tet->cusp[v1]->index)
		{
			merged_cusp	= tet->cusp[v0];
			dead_cusp	= tet->cusp[v1];
		}
		else
		{
			merged_cusp	= tet->cusp[v1];
			dead_cusp	= tet->cusp[v0];
		}

		if (merged_cusp==dead_cusp)
			same_cusp = TRUE;
		else	same_cusp = FALSE;

		if (same_cusp)
			merged_cusp->euler_characteristic = merged_cusp->euler_characteristic - 2;
		else if (merged_cusp->euler_characteristic==2 || dead_cusp->euler_characteristic==2)
			merged_cusp->euler_characteristic = merged_cusp->euler_characteristic
								+dead_cusp->euler_characteristic - 2;
		else	merged_cusp->euler_characteristic = merged_cusp->euler_characteristic
								+dead_cusp->euler_characteristic;

		merged_cusp->topology = unknown_topology;
		if (same_cusp)
			num_cone_points = merged_cusp->num_cone_points - 2;
		else	num_cone_points = merged_cusp->num_cone_points + dead_cusp->num_cone_points -2;
		cone_points = NEW_ARRAY( num_cone_points, int );

		num_cone_points = 0;
		for(i=0;i<merged_cusp->num_cone_points;i++)
		if (merged_cusp->cone_points[i]!=edge->singular_index)
			cone_points[num_cone_points++] = merged_cusp->cone_points[i];
		my_free(merged_cusp->cone_points);

		if (!same_cusp)
		{
			dead_index = dead_cusp->index;

			/* fix cusp fields */
                        for(    tet0 = manifold->tet_list_begin.next;
                                tet0!=&manifold->tet_list_end;
                                tet0 = tet0->next )
                                for( i = 0; i < 4; i++ )
                                if (tet0->cusp[i] == dead_cusp)
                                        tet0->cusp[i] = merged_cusp;

			for(i=0;i<dead_cusp->num_cone_points;i++)
			if (dead_cusp->cone_points[i]!=edge->singular_index)
				cone_points[num_cone_points++] = dead_cusp->cone_points[i];

			my_free(dead_cusp->cone_points);
			REMOVE_NODE(dead_cusp);
			my_free(dead_cusp);
		}

		/* install new cone points */
		merged_cusp->cone_points = cone_points;
		merged_cusp->num_cone_points = num_cone_points;

		for(	cusp = manifold->cusp_list_begin.next;
			cusp!=&manifold->cusp_list_end;
			cusp = cusp->next )
		{
			for (i=0;i < cusp->num_cone_points;i++)
			if (cusp->cone_points[i] > edge->singular_index)
				cusp->cone_points[i] = cusp->cone_points[i] - 1;

			if (!same_cusp && cusp->index > dead_index)
				cusp->index--;
		}
		
               	for(    edge3 = manifold->edge_list_begin.next;
                        edge3!=&manifold->edge_list_end;
                        edge3 = edge3->next )
                if (edge3->is_singular && edge3->singular_index > edge->singular_index)
                        edge3->singular_index--;

                edge->singular_index    = -1;
                edge->is_singular       = FALSE;
                edge->singular_order    = 1;
		manifold->num_singular_arcs--;

		if (!same_cusp)
		{
			manifold->num_cusps--;
			manifold->num_or_cusps--;
		}
	}
	else /* creating_new_cusp == TRUE */
	{
示例#3
0
static Triangulation *try_Dirichlet_to_triangulation(
	WEPolyhedron	*polyhedron)
{
	/*
	 *	Implement Plan A as described above.
	 */
	
	Triangulation	*triangulation;
	WEEdge			*edge,
					*nbr_edge,
					*mate_edge;
	WEEdgeEnd		end;
	WEEdgeSide		side;
	Tetrahedron		*new_tet;
	FaceIndex		f;

	/*
	 *	Don't attempt to triangulate an orbifold.
	 */

	if (singular_set_is_empty(polyhedron) == FALSE)
		return NULL;
	
	/*
	 *	Set up the Triangulation.
	 */

	triangulation = NEW_STRUCT(Triangulation);
	initialize_triangulation(triangulation);

	/*
	 *	Allocate and copy the name.
	 */

	triangulation->name = NEW_ARRAY(strlen(DEFAULT_NAME) + 1, char);
	strcpy(triangulation->name, DEFAULT_NAME);

	/*
	 *	Allocate the Tetrahedra.
	 */

	triangulation->num_tetrahedra = 4 * polyhedron->num_edges;

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

		for (end = 0; end < 2; end++)	/* = tail, tip */

			for (side = 0; side < 2; side++)	/* = left, right */
			{
				new_tet = NEW_STRUCT(Tetrahedron);
				initialize_tetrahedron(new_tet);
				INSERT_BEFORE(new_tet, &triangulation->tet_list_end);
				edge->tet[end][side] = new_tet;
			}

	/*
	 *	Initialize neighbors.
	 */

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

		for (end = 0; end < 2; end++)	/* = tail, tip */

			for (side = 0; side < 2; side++)	/* = left, right */
			{
				/*
				 *	Neighbor[0] is associated to this same WEEdge.
				 *	It lies on the same side (left or right), but
				 *	at the opposite end (tail or tip).
				 */
				edge->tet[end][side]->neighbor[0] = edge->tet[!end][side];

				/*
				 *	Neighbor[1] lies on the same face of the Dirichlet
				 *	domain, but at the "next side" of that face.
				 */
				nbr_edge = edge->e[end][side];
				if (nbr_edge->v[!end] == edge->v[end])
					/* edge and nbr_edge point in the same direction */
					edge->tet[end][side]->neighbor[1] = nbr_edge->tet[!end][side];
				else if (nbr_edge->v[end] == edge->v[end])
					/* edge and nbr_edge point in opposite directions */
					edge->tet[end][side]->neighbor[1] = nbr_edge->tet[end][!side];
				else
					uFatalError("Dirichlet_to_triangulation", "Dirichlet_conversion");

				/*
				 *	Neighbor[2] is associated to this same WEEdge.
				 *	It lies at the same end (tail or tip), but
				 *	on the opposite side (left or right).
				 */
				edge->tet[end][side]->neighbor[2] = edge->tet[end][!side];

				/*
				 *	Neighbor[3] lies on this face's "mate" elsewhere
				 *	on the Dirichlet domain.
				 */
				mate_edge = edge->neighbor[side];
				edge->tet[end][side]->neighbor[3] = mate_edge->tet
					[edge->preserves_direction[side] ? end  : !end ]
					[edge->preserves_sides[side]     ? side : !side];
			}

	/*
	 *	Initialize all gluings to the identity.
	 */

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

		for (end = 0; end < 2; end++)	/* = tail, tip */

			for (side = 0; side < 2; side++)	/* = left, right */

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

					edge->tet[end][side]->gluing[f] = IDENTITY_PERMUTATION;

	/*
	 *	Set up the EdgeClasses.
	 */
	create_edge_classes(triangulation);
	orient_edge_classes(triangulation);

	/*
	 *	Attempt to orient the manifold.
	 */
	orient(triangulation);

	/*
	 *	Set up the Cusps, including "fake cusps" for the finite vertices.
	 *	Then locate and remove the fake cusps.  If the manifold is closed,
	 *	drill out an arbitrary curve to express it as a Dehn filling.
	 *	Finally, determine the topology of each cusp (torus or Klein bottle)
	 *	and count them.
	 */
	create_cusps(triangulation);
	mark_fake_cusps(triangulation);
	peripheral_curves(triangulation);
	remove_finite_vertices(triangulation);
	count_cusps(triangulation);
	
	/*
	 *	Try to compute a hyperbolic structure, first for the unfilled
	 *	manifold, and then for the closed manifold if appropriate.
	 */
	find_complete_hyperbolic_structure(triangulation);
	do_Dehn_filling(triangulation);

	/*
	 *	If the manifold is hyperbolic, install a shortest basis on each cusp.
	 */
	if (	triangulation->solution_type[complete] == geometric_solution
	 	 || triangulation->solution_type[complete] == nongeometric_solution)
		install_shortest_bases(triangulation);

	/*
	 *	All done!
	 */
	return triangulation;
}
示例#4
0
static void drill_tube(
	Triangulation	*manifold,
	Tetrahedron		*tet,
	EdgeIndex		e,
	Boolean			creating_new_cusp)
{
	/*
	 *	Insert a triangular-pillow-with-tunnel (as described at the top
	 *	of this file) so as to connect the boundary component at one
	 *	end of the given edge to the boundary component at the other end.
	 *	The orientation on the triangular pillow will match the
	 *	orientation on tet, so that the orientation on the manifold
	 *	(if there is one) will be preserved.  Edge orientations are also
	 *	respected.
	 */
	
	VertexIndex		v0,
					v1,
					v2,
					vv0,
					vv1,
					vv2;
	FaceIndex		f,
					ff;
	Tetrahedron		*nbr_tet,
					*new_tet0,
					*new_tet1;
	Permutation		gluing;
	EdgeClass		*edge0,
					*edge1,
					*edge2,
					*new_edge;
	Orientation		edge_orientation0,
					edge_orientation1,
					edge_orientation2;
	PeripheralCurve	c;
	Orientation		h;
	int				num_strands,
					intersection_number[2],
					the_gcd;
	Cusp			*unique_cusp;
	MatrixInt22		basis_change[1];
	
	/*
	 *	Relative to the orientation of tet, the vertices v0, v1 and v2
	 *	are arranged in counterclockwise order around the face f.
	 */
	v0 = one_vertex_at_edge[e];
	v1 = other_vertex_at_edge[e];
	v2 = remaining_face[v1][v0];
	f  = remaining_face[v0][v1];

	/*
	 *	Note the matching face and its vertices.
	 */
	nbr_tet	= tet->neighbor[f];
	gluing	= tet->gluing[f];
	ff		= EVALUATE(gluing, f);
	vv0		= EVALUATE(gluing, v0);
	vv1		= EVALUATE(gluing, v1);
	vv2		= EVALUATE(gluing, v2);
	
	/*
	 *	Note the incident EdgeClasses (which may or may not be distinct).
	 */
	edge0 = tet->edge_class[e];
	edge1 = tet->edge_class[edge_between_vertices[v1][v2]];
	edge2 = tet->edge_class[edge_between_vertices[v2][v0]];
	
	/*
	 *	Construct the triangular-pillow-with-tunnel, as described
	 *	at the top of this file.
	 */

	new_tet0 = NEW_STRUCT(Tetrahedron);
	new_tet1 = NEW_STRUCT(Tetrahedron);
	initialize_tetrahedron(new_tet0);
	initialize_tetrahedron(new_tet1);
	INSERT_BEFORE(new_tet0, &manifold->tet_list_end);
	INSERT_BEFORE(new_tet1, &manifold->tet_list_end);
	manifold->num_tetrahedra += 2;

	new_edge = NEW_STRUCT(EdgeClass);
	initialize_edge_class(new_edge);
	INSERT_BEFORE(new_edge, &manifold->edge_list_end);

	new_tet0->neighbor[0] = new_tet1;
	new_tet0->neighbor[1] = NULL;	/* assigned below */
	new_tet0->neighbor[2] = NULL;	/* assigned below */
	new_tet0->neighbor[3] = new_tet1;

	new_tet1->neighbor[0] = new_tet0;
	new_tet1->neighbor[1] = new_tet1;
	new_tet1->neighbor[2] = new_tet1;
	new_tet1->neighbor[3] = new_tet0;

	new_tet0->gluing[0] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet0->gluing[1] = 0x00;	/* assigned below */
	new_tet0->gluing[2] = 0x00;	/* assigned below */
	new_tet0->gluing[3] = CREATE_PERMUTATION(0, 1, 1, 0, 2, 2, 3, 3);

	new_tet1->gluing[0] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[1] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[2] = CREATE_PERMUTATION(0, 0, 1, 2, 2, 1, 3, 3);
	new_tet1->gluing[3] = CREATE_PERMUTATION(0, 1, 1, 0, 2, 2, 3, 3);

	new_tet0->edge_class[0] = edge1;
	new_tet0->edge_class[1] = edge1;
	new_tet0->edge_class[2] = edge0;
	new_tet0->edge_class[3] = edge2;
	new_tet0->edge_class[4] = edge0;
	new_tet0->edge_class[5] = edge0;

	new_tet1->edge_class[0] = edge1;
	new_tet1->edge_class[1] = edge1;
	new_tet1->edge_class[2] = edge0;
	new_tet1->edge_class[3] = new_edge;
	new_tet1->edge_class[4] = edge0;
	new_tet1->edge_class[5] = edge0;

	edge0->order += 6;
	edge1->order += 4;
	edge2->order += 1;

	new_edge->order					= 1;
	new_edge->incident_tet			= new_tet1;
	new_edge->incident_edge_index	= 3;
	
	edge_orientation0 = tet->edge_orientation[e];
	edge_orientation1 = tet->edge_orientation[edge_between_vertices[v1][v2]];
	edge_orientation2 = tet->edge_orientation[edge_between_vertices[v2][v0]];

	new_tet0->edge_orientation[0] = edge_orientation1;
	new_tet0->edge_orientation[1] = edge_orientation1;
	new_tet0->edge_orientation[2] = edge_orientation0;
	new_tet0->edge_orientation[3] = edge_orientation2;
	new_tet0->edge_orientation[4] = edge_orientation0;
	new_tet0->edge_orientation[5] = edge_orientation0;

	new_tet1->edge_orientation[0] = edge_orientation1;
	new_tet1->edge_orientation[1] = edge_orientation1;
	new_tet1->edge_orientation[2] = edge_orientation0;
	new_tet1->edge_orientation[3] = right_handed;
	new_tet1->edge_orientation[4] = edge_orientation0;
	new_tet1->edge_orientation[5] = edge_orientation0;
	
	new_tet0->cusp[0] = tet->cusp[v0];
	new_tet0->cusp[1] = tet->cusp[v0];
	new_tet0->cusp[2] = tet->cusp[v0];
	new_tet0->cusp[3] = tet->cusp[v2];
	
	new_tet1->cusp[0] = tet->cusp[v0];
	new_tet1->cusp[1] = tet->cusp[v0];
	new_tet1->cusp[2] = tet->cusp[v0];
	new_tet1->cusp[3] = tet->cusp[v2];
	
	/*
	 *	Install the triangular-pillow-with-tunnel.
	 */
	
	tet->neighbor[f]		= new_tet0;
	tet->gluing[f]			= CREATE_PERMUTATION(f, 2, v0, 0, v1, 1, v2, 3);
	new_tet0->neighbor[2]	= tet;
	new_tet0->gluing[2]		= inverse_permutation[tet->gluing[f]];
	
	nbr_tet->neighbor[ff]	= new_tet0;
	nbr_tet->gluing[ff]		= CREATE_PERMUTATION(ff, 1, vv0, 0, vv1, 2, vv2, 3);
	new_tet0->neighbor[1]	= nbr_tet;
	new_tet0->gluing[1]		= inverse_permutation[nbr_tet->gluing[ff]];

	/*
	 *	Typically creating_new_cusp is FALSE, meaning that we are
	 *	connecting a spherical boundary component to a torus or
	 *	Klein bottle boundary component, and we simply extend the
	 *	existing peripheral curves across the new tetrahedra.
	 *
	 *	In the exceptional case that creating_new_cusp is TRUE,
	 *	meaning that the manifold has no real cusps and we are
	 *	connecting the "special fake cusp" to itself, we must
	 *	install a meridian and longitude, and set up the Dehn filling.
	 */
	if (creating_new_cusp == FALSE)
	{
		/*
		 *	Extend the peripheral curves across the boundary of the
		 *	triangular-pillow-with-tunnel.
		 *
		 *	Note:  The orientations of new_tet0 and new_tet1 match that
		 *	of tet, so the right_handed and left_handed sheets match up
		 *	in the obvious way.
		 */
	
		for (c = 0; c < 2; c++)		/* c = M, L                      */
			for (h = 0; h < 2; h++)	/* h = right_handed, left_handed */
			{
				num_strands = tet->curve[c][h][v0][f];
				new_tet0->curve[c][h][0][2] = -num_strands;
				new_tet0->curve[c][h][0][1] = +num_strands;
	
				num_strands = tet->curve[c][h][v1][f];
				new_tet0->curve[c][h][1][2] = -num_strands;
				new_tet0->curve[c][h][1][0] = +num_strands;
				new_tet1->curve[c][h][2][0] = -num_strands;
				new_tet1->curve[c][h][2][1] = +num_strands;
				new_tet1->curve[c][h][1][2] = -num_strands;
				new_tet1->curve[c][h][1][0] = +num_strands;
				new_tet0->curve[c][h][2][0] = -num_strands;
				new_tet0->curve[c][h][2][1] = +num_strands;
	
				num_strands = tet->curve[c][h][v2][f];
				new_tet0->curve[c][h][3][2] = -num_strands;
				new_tet0->curve[c][h][3][1] = +num_strands;
			}
	}
	else /* creating_new_cusp == TRUE */
	{
		/*
		 *	We have just installed a tube connecting the (unique)
		 *	spherical "cusp" to itself, to convert it to a torus or
		 *	Klein bottle.
		 */
		unique_cusp = tet->cusp[v0]->matching_cusp;
		unique_cusp->is_complete	= TRUE;	/* to be filled below */
		unique_cusp->index			= 0;
		unique_cusp->is_finite		= FALSE;
		manifold->num_cusps			= 1;
		
		/*
		 *	Install an arbitrary meridian and longitude.
		 */
		peripheral_curves(manifold);
		count_cusps(manifold);
		
		/*
		 *	Two sides of the (truncated) vertex 0 of new_tet0
		 *	(namely the sides incident to faces 1 and 2 of new_tet0)
		 *	define the Dehn filling curve by which we can recover
		 *	the closed manifold.  Count how many times the newly
		 *	installed meridian and longitude cross this Dehn filling curve.
		 *	To avoid messy questions about which sheet of the cusp's
		 *	double cover we're on, use two (parallel) copies of the
		 *	Dehn filling curve, one on each sheet of the cover.
		 *	Ultimately we're looking for a linear combination of the
		 *	meridian and longitude whose intersection number with
		 *	the Dehn filling curve is zero, so it won't matter if
		 *	we're off by a factor of two.
		 */
		for (c = 0; c < 2; c++)		/* c = M, L                      */
		{
			intersection_number[c] = 0;
			
			for (h = 0; h < 2; h++)	/* h = right_handed, left_handed */
			{
				intersection_number[c] += new_tet0->curve[c][h][0][1];
				intersection_number[c] += new_tet0->curve[c][h][0][2];
			}
		}
		
		/*
		 *	Use the intersection numbers to deduce
		 *	the desired Dehn filling coefficients.
		 */
		the_gcd = gcd(intersection_number[M], intersection_number[L]);
		unique_cusp->is_complete	= FALSE;
		unique_cusp->m				= -intersection_number[L] / the_gcd;
		unique_cusp->l				= +intersection_number[M] / the_gcd;

		/*
		 *	Switch to a basis in which the Dehn filling curve is a meridian.
		 */
		unique_cusp->cusp_shape[initial] = Zero;	/* force current_curve_basis() to ignore the cusp shape */
		current_curve_basis(manifold, unique_cusp->index, basis_change[0]);
		if (change_peripheral_curves(manifold, basis_change) != func_OK)
			uFatalError("drill_tube", "finite_vertices");
	}
}