void install_current_curve_bases( Triangulation *manifold) { Cusp *cusp; MatrixInt22 *change_matrices; /* * Allocate an array to store the change of basis matrices. */ change_matrices = NEW_ARRAY(manifold->num_cusps, MatrixInt22); /* * Compute the change of basis matrices. */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) { if (cusp->index < 0 || cusp->index >= manifold->num_cusps) uFatalError("install_current_curve_bases", "current_curve_basis"); current_curve_basis_on_cusp(cusp, change_matrices[cusp->index]); } /* * Install the change of basis matrices. */ if (change_peripheral_curves(manifold, change_matrices) != func_OK) uFatalError("install_current_curve_bases", "current_curve_basis"); /* * Free the array used to store the change of basis matrices. */ my_free(change_matrices); }
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"); } }
FuncResult compute_closed_symmetry_group( Triangulation *manifold, SymmetryGroup **symmetry_group, Triangulation **symmetric_triangulation, Boolean *is_full_group) { FuncResult result; /* * Make sure the variables used to pass back our results * are all initially empty. */ if (*symmetry_group != NULL || *symmetric_triangulation != NULL) uFatalError("compute_closed_symmetry_group", "symmetry_group"); /* * compute_symmetry_group() should have passed us a 1-cusp * manifold with a Dehn filling on its cusp. */ if (get_num_cusps(manifold) != 1 || all_cusps_are_filled(manifold) == FALSE || all_Dehn_coefficients_are_relatively_prime_integers(manifold) == FALSE) { uFatalError("compute_closed_symmetry_group", "symmetry_group_closed"); } /* * For later convenience, change the basis on the cusp * so that the Dehn filling curve becomes a meridian. */ { MatrixInt22 basis_change[1]; current_curve_basis(manifold, 0, basis_change[0]); change_peripheral_curves(manifold, basis_change); } /* * At the very least, we can try to establish a (possibly trivial) * lower bound on the symmetry group by computing the group * which preserves the given core geodesic. */ { SymmetryGroup *dummy = NULL; if (compute_cusped_symmetry_group(manifold, &dummy, symmetry_group) == func_OK) { copy_triangulation(manifold, symmetric_triangulation); free_symmetry_group(dummy); /* we don't need dummy */ } else { /* * The only way compute_cusped_symmetry_group() may fail * is if a canonical cell decomposition cannot be found, * e.g. because the manifold is not hyperbolic. */ return func_failed; } } /* * For small to medium sized manifolds we should have * no trouble getting a Dirichlet domain. But if we can't, * then we want to muddle along as best we can without one. */ { WEPolyhedron *polyhedron; polyhedron = compute_polyhedron(manifold); if (polyhedron != NULL) { result = compute_symmetry_group_using_polyhedron( manifold, symmetry_group, symmetric_triangulation, is_full_group, polyhedron); free_Dirichlet_domain(polyhedron); } else result = compute_symmetry_group_without_polyhedron( manifold, symmetry_group, symmetric_triangulation, is_full_group); } return result; }