Beispiel #1
0
static Boolean singular_set_is_empty(
	WEPolyhedron	*polyhedron)
{
	/*
	 *	Check whether the singular set of this orbifold is empty.
	 */

	WEVertexClass	*vertex_class;
	WEEdgeClass		*edge_class;
	WEFaceClass		*face_class;

	for (vertex_class = polyhedron->vertex_class_begin.next;
		 vertex_class != &polyhedron->vertex_class_end;
		 vertex_class = vertex_class->next)

		if (vertex_class->singularity_order >= 2)

			return FALSE;

	/*
	 *	Dirichlet_construction.c subdivides Dirichlet domains for
	 *	orbifolds so that the k-skeleton of the singular set lies
	 *	in the k-skeleton of the Dirichlet domain (k = 0,1,2).
	 *	Thus if there are no singular VertexClasses, there can't be
	 *	any singular EdgeClasses or FaceClasses either.
	 */
	
	for (edge_class = polyhedron->edge_class_begin.next;
		 edge_class != &polyhedron->edge_class_end;
		 edge_class = edge_class->next)

		if (edge_class->singularity_order >= 2)

			uFatalError("singular_set_is_empty", "Dirichlet_conversion");

	for (face_class = polyhedron->face_class_begin.next;
		 face_class != &polyhedron->face_class_end;
		 face_class = face_class->next)

		if (face_class->num_elements != 2)

			uFatalError("singular_set_is_empty", "Dirichlet_conversion");

	/*
	 *	No singularities are present.
	 */

	return TRUE;
}
Beispiel #2
0
Boolean mark_fake_cusps(
    Triangulation   *manifold)
{
    int     real_cusp_count,
            fake_cusp_count;
    Cusp    *cusp;

    compute_cusp_Euler_characteristics(manifold);

    real_cusp_count = 0;
    fake_cusp_count = 0;

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

        switch (cusp->euler_characteristic)
        {
        case 0:
            cusp->is_finite = FALSE;
            cusp->index = real_cusp_count++;
            break;

        case 2:
            cusp->is_finite = TRUE;
            cusp->index = --fake_cusp_count;
            break;

        default:
            uFatalError("mark_fake_cusps", "cusps");
        }

    return (fake_cusp_count < 0);
}
Beispiel #3
0
void count_cusps(
    Triangulation   *manifold)
{
    Cusp    *cusp;

    manifold->num_cusps         = 0;
    manifold->num_or_cusps      = 0;
    manifold->num_nonor_cusps   = 0;

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

        switch (cusp->topology)
        {
        case torus_cusp:
            manifold->num_or_cusps++;
            break;

        case Klein_cusp:
            manifold->num_nonor_cusps++;
            break;

        default:
            uFatalError("count_cusps", "cusps");
        }
    }
}
void uAutoReleaseObject(uObject* obj)
{
    uThreadData* thread = uGetThreadData();

    if (thread->AutoReleaseStack.Length() == 0)
        uFatalError(XLI_FUNCTION);

    if (obj)
    {
        thread->AutoReleasePool.Add(obj);

#ifdef U_DEBUG_MEM
        int releaseCount = 0;

        for (int i = 0; i < thread->AutoReleasePool.Length(); i++)
            if (thread->AutoReleasePool[i] == obj)
                releaseCount++;

        int retainCount = obj->__obj_retains - releaseCount;

        if (retainCount < 0)
            Xli::Error->WriteFormat("*** BAD RELEASE: '%s', object id: %d (%d retains) ***\n", obj->__obj_type->TypeName, obj->__obj_id, retainCount);
#endif
    }
}
Beispiel #5
0
static void pick_base_tet(
    Triangulation   *manifold,
    Cusp            *cusp,
    Tetrahedron     **base_tet,
    VertexIndex     *base_vertex)
{
    Tetrahedron *tet;
    VertexIndex v;

    for (tet = manifold->tet_list_begin.next;
         tet != &manifold->tet_list_end;
         tet = tet->next)
        for (v = 0; v < 4; v++)
            if (tet->cusp[v] == cusp)
            {
                *base_tet       = tet;
                *base_vertex    = v;
                return;
            }

    /*
     *  If pick_base_tet() didn't find any vertex belonging
     *  to the specified cusp, we're in big trouble.
     */
    uFatalError("pick_base_tet", "peripheral_curves");
}
uRuntime::uRuntime()
{
    if (uMainThreadPtr != NULL)
        uFatalError("There is only room for one Uno Runtime object in this process.");






    uWeakMutex = Xli::CreateMutex();


#ifdef U_DEBUG_MEM
    uHeapObjects = new Xli::HashMap<uObject*, bool>();
#endif

    uMainThreadPtr = Xli::GetCurrentThread();
    uThreadDataTls = Xli::CreateTls(uFreeThreadData);

    uAutoReleasePool pool;



    uInitSupport();
    uInitObjectModel();
}
Beispiel #7
0
static void make_isometry_array(
	IsometryList	*isometry_list,
	Isometry		*the_linked_list)
{
	int			i;
	Isometry	*an_isometry;

	if (isometry_list->num_isometries == 0)

		isometry_list->isometry = NULL;

	else
	{
		isometry_list->isometry = NEW_ARRAY(isometry_list->num_isometries, Isometry *);

		for (	an_isometry = the_linked_list, i = 0;
				an_isometry != NULL && i < isometry_list->num_isometries;
				an_isometry = an_isometry->next, i++)

			isometry_list->isometry[i] = an_isometry;

		/*
		 *	A quick error check.
		 */
		if (an_isometry != NULL || i != isometry_list->num_isometries)
			uFatalError("make_isometry_array", "isometry");
	}
}
Beispiel #8
0
static Boolean attempt_two_to_three(
    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 (concave_face(tet, f) == TRUE
             && would_create_negatively_oriented_tetrahedra(tet, f) == FALSE)
            {
                if (two_to_three(tet, f, &manifold->num_tetrahedra) == func_OK)

                    return TRUE;

                else

                    /*
                     *  We should never get to this point.
                     */
                    uFatalError("attempt_two_to_three", "canonize_part_1.c");
            }

    return FALSE;
}
static FuncResult fill_first_cusp(
    Triangulation   **manifold)
{
    Triangulation   *new_manifold;
    int             count;
    Boolean         fill_cusp[2] = {TRUE, FALSE};

    if (get_num_cusps(*manifold) != 2)
        uFatalError("fill_first_cusp", "symmetry_group_closed");

    new_manifold = fill_cusps(*manifold, fill_cusp, get_triangulation_name(*manifold), FALSE);
    if (new_manifold == NULL)
        return func_failed; /* this seems unlikely */

    /*
     *  Usually the complete solution will be geometric, even if
     *  the filled solution is not.  But occasionally we'll get
     *  a new_manifold which didn't simplify sufficiently, and
     *  we'll need to rattle it around to get a decent triangulation.
     */
    count = MAX_RANDOMIZATIONS;
    while (--count >= 0
            && get_complete_solution_type(new_manifold) != geometric_solution)
        randomize_triangulation(new_manifold);

    free_triangulation(*manifold);
    *manifold = new_manifold;
    new_manifold = NULL;

    return func_OK;
}
void uWeakStateIntercept::SetCallback(uWeakObject* weak, uWeakStateIntercept::Callback cb)
{
    if (!weak || !cb || weak->ZombieState != uWeakObject::Healthy)
        uFatalError(XLI_FUNCTION);

    weak->ZombieState = uWeakObject::Infected;
    weak->ZombieStateIntercept = cb;
}
Beispiel #11
0
FuncResult compute_symmetry_group(
    Triangulation   *manifold,
    SymmetryGroup   **symmetry_group_of_manifold,
    SymmetryGroup   **symmetry_group_of_link,
    Triangulation   **symmetric_triangulation,
    Boolean         *is_full_group)
{
    Triangulation   *simplified_manifold;
    FuncResult      result;

    /*
     *  Make sure the variables used to pass back our results
     *  are all initially empty.
     */
    if (*symmetry_group_of_manifold != NULL
     || *symmetry_group_of_link     != NULL
     || *symmetric_triangulation    != NULL)
        uFatalError("compute_symmetry_group", "symmetry_group");

    /*
     *  If the space isn't a manifold, return func_bad_input.
     */
    if (all_Dehn_coefficients_are_relatively_prime_integers(manifold) == FALSE)
        return func_bad_input;

    /*
     *  Whether the manifold is cusped or not, we want to begin
     *  by getting rid of "unnecessary" cusps.
     */
    simplified_manifold = fill_reasonable_cusps(manifold);
    if (simplified_manifold == NULL)
        return func_failed;

    /*
     *  Split into cases according to whether the manifold is
     *  closed or cusped (i.e. whether all cusps are filled or not).
     */
    if (all_cusps_are_filled(simplified_manifold) == TRUE)
        result = compute_closed_symmetry_group(
                                    simplified_manifold,
                                    symmetry_group_of_manifold,
                                    symmetric_triangulation,
                                    is_full_group);
    else
    {
        result = compute_cusped_symmetry_group(
                                    simplified_manifold,
                                    symmetry_group_of_manifold,
                                    symmetry_group_of_link);
        *is_full_group = TRUE;
    }

    free_triangulation(simplified_manifold);

    return result;
}
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);
}
Beispiel #13
0
static Boolean same_homology(
	Triangulation	*manifold0,
	Triangulation	*manifold1)
{
	AbelianGroup	*g0,
					*g1;
	Boolean			groups_are_isomorphic;
	int				i;

	/*
	 *	Compute the homology groups.
	 */
	g0 = homology(manifold0);
	g1 = homology(manifold1);

	/*
	 *	compute_isometries() has already checked that both manifolds
	 *	really are manifolds, so neither g0 nor g1 should be NULL.
	 */
	if (g0 == NULL  ||  g1 == NULL)
		uFatalError("same_homology", "isometry");

	/*
	 *	Put the homology groups into a canonical form.
	 */
	compress_abelian_group(g0);
	compress_abelian_group(g1);

	/*
	 *	Compare the groups.
	 */

	if (g0->num_torsion_coefficients != g1->num_torsion_coefficients)

		groups_are_isomorphic = FALSE;

	else
	{
		groups_are_isomorphic = TRUE;	/* innocent until proven guilty */

		for (i = 0; i < g0->num_torsion_coefficients; i++)
			if (g0->torsion_coefficients[i] != g1->torsion_coefficients[i])
				groups_are_isomorphic = FALSE;
	}

	/*
	 *	Free the Abelian groups.
	 */
	free_abelian_group(g0);
	free_abelian_group(g1);

	return groups_are_isomorphic;
}
Beispiel #14
0
static void verify_gen_list(
	MatrixPairList	*gen_list,
	int				num_matrix_pairs)
{
	MatrixPair	*matrix_pair;

	/*
	 *	Does the list have at least two elements beyond the identity?
	 *	Algebraically, we'll need an objective function
	 *		and at least one constraint.
	 *	Geometrically, the Dirichlet domain which provided these
	 *		generators must have at least two pairs of faces.
	 */

	if (num_matrix_pairs < 2)
		uFatalError("verify_gen_list", "Dirichlet_basepoint");

	/*
	 *	The first MatrixPair on gen_list should be the identity.
	 */

	if (gen_list->begin.next->height > 1.0 + IDENTITY_EPSILON)
		uFatalError("verify_gen_list", "Dirichlet_basepoint");

	/*
	 *	We want the MatrixPairs to be in order of increasing image height.
	 *	(Note that this loop starts at the second MatrixPair on the list.)
	 */

	for (	matrix_pair = gen_list->begin.next->next;
			matrix_pair != &gen_list->end;
			matrix_pair = matrix_pair->next)

		if (matrix_pair->height < matrix_pair->prev->height)

			uFatalError("verify_gen_list", "Dirichlet_basepoint");
}
Beispiel #15
0
void error_check_for_create_cusps(
    Triangulation   *manifold)
{
    Tetrahedron *tet;
    VertexIndex v;

    if (manifold->num_cusps         != 0
            || manifold->num_or_cusps      != 0
            || manifold->num_nonor_cusps   != 0
            || manifold->cusp_list_begin.next != &manifold->cusp_list_end)

        uFatalError("error_check_for_create_cusps", "cusps");


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

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

            if (tet->cusp[v] != NULL)

                uFatalError("error_check_for_create_cusps", "cusps");
}
Beispiel #16
0
static void attach_extra(
    Triangulation   *manifold)
{
    Tetrahedron *tet;

    for (tet = manifold->tet_list_begin.next;
         tet != &manifold->tet_list_end;
         tet = tet->next)
    {
        /*
         *  Make sure no other routine is using the "extra"
         *  field in the Tetrahedron data structure.
         */
        if (tet->extra != NULL)
            uFatalError("attach_extra", "peripheral_curves");

        /*
         *  Attach the locally defined struct extra.
         */
        tet->extra = NEW_ARRAY(4, Extra);
    }
}
Beispiel #17
0
static Boolean attempt_three_to_two(
    Triangulation   *manifold)
{
    EdgeClass   *edge,
                *where_to_resume;

    /*
     *  Note:  It's easy to prove that if the three original Tetrahedra
     *  are positively oriented, then the two new Tetrahedra must be
     *  positively oriented as well. So we needn't worry about negatively
     *  oriented Tetrahedra here.
     */

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

        if (edge->order == 3)

            if (concave_edge(edge) == TRUE)
            {
                if (three_to_two(edge, &where_to_resume, &manifold->num_tetrahedra) == func_OK)

                    return TRUE;

                else
                    /*
                     *  The only reason three_to_two() can fail is that the
                     *  three Tetrahedra surrounding the EdgeClass are not
                     *  distinct.  But by Corolloary 3 of "Convex hulls..."
                     *  this cannot happen where the hull is concave.
                     */
                    uFatalError("attempt_three_to_two", "canonize_part_1");
            }

    return FALSE;
}
Beispiel #18
0
FuncResult compute_isometries(
	Triangulation	*manifold0,
	Triangulation	*manifold1,
	Boolean			*are_isometric,
	IsometryList	**isometry_list,
	IsometryList	**isometry_list_of_links)
{
	Triangulation	*simplified_manifold0,
					*simplified_manifold1;
	IsometryList	*the_isometry_list,
					*the_isometry_list_of_links;
	FuncResult		result;

	/*
	 *	Make sure the variables used to pass back our results
	 *	are initially empty.
	 */

	if ((isometry_list          != NULL && *isometry_list          != NULL)
	 || (isometry_list_of_links != NULL && *isometry_list_of_links != NULL))
		uFatalError("compute_isometries", "isometry");

	/*
	 *	If one of the spaces isn't a manifold, return func_bad_input.
	 */

	if (all_Dehn_coefficients_are_relatively_prime_integers(manifold0) == FALSE
	 || all_Dehn_coefficients_are_relatively_prime_integers(manifold1) == FALSE)
		return func_bad_input;

	/*
	 *	Check whether the manifolds are obviously nonhomeomorphic.
	 *
	 *	(In the interest of a robust, beyond-a-shadow-of-a-doubt algorithm,
	 *	stick to discrete invariants like the number of cusps and the
	 *	first homology group, and avoid real-valued invariants like
	 *	the volume which require judging when two floating point numbers
	 *	are equal.)  [96/12/6  Comparing canonical triangulations
	 *	relies on having at least a vaguely correct hyperbolic structure,
	 *	so it should be safe to reject manifolds whose volumes differ
	 *	by more than, say, 0.01.]
	 */

	if (count_unfilled_cusps(manifold0) != count_unfilled_cusps(manifold1)
	 || same_homology(manifold0, manifold1) == FALSE
	 || (	manifold0->solution_type[filled] == geometric_solution
		 && manifold1->solution_type[filled] == geometric_solution
	 	 && fabs(volume(manifold0, NULL) - volume(manifold1, NULL)) > CRUDE_VOLUME_EPSILON))
	{
		*are_isometric = FALSE;
		return func_OK;
	}

	/*
	 *	Whether the actual manifolds (after taking into account Dehn
	 *	fillings) have cusps or not, we want to begin by getting rid
	 *	of "unnecessary" cusps.
	 */

	simplified_manifold0 = fill_reasonable_cusps(manifold0);
	if (simplified_manifold0 == NULL)
		return func_failed;

	simplified_manifold1 = fill_reasonable_cusps(manifold1);
	if (simplified_manifold1 == NULL)
		return func_failed;

	/*
	 *	Split into cases according to whether the manifolds are
	 *	closed or cusped (i.e. whether all cusps are filled or not).
	 *	The above tests insure that either both are closed or both
	 *	are cusped.
	 */

	if (all_cusps_are_filled(simplified_manifold0) == TRUE)

		result = compute_closed_isometry(	simplified_manifold0,
											simplified_manifold1,
											are_isometric);
	else
	{
		result = compute_cusped_isometries(	simplified_manifold0,
											simplified_manifold1,
											&the_isometry_list,
											&the_isometry_list_of_links);
		if (result == func_OK)
		{
			*are_isometric = the_isometry_list->num_isometries > 0;

			if (isometry_list != NULL)
				*isometry_list = the_isometry_list;
			else
				free_isometry_list(the_isometry_list);

			if (isometry_list_of_links != NULL)
				*isometry_list_of_links = the_isometry_list_of_links;
			else
				free_isometry_list(the_isometry_list_of_links);
		}
	}

	/*
	 *	We no longer need the simplified manifolds.
	 */
	free_triangulation(simplified_manifold0);
	free_triangulation(simplified_manifold1);

	return result;
}
Beispiel #19
0
static void linear_programming(
	ObjectiveFunction	objective_function,
	int					num_constraints,
	Constraint			*constraints,
	Solution			solution)
{
	int			i,
				j,
				k;
	Constraint	*active_constraints[3],
				*new_constraints[3];
	Solution	apex,
				new_apex,
				max_apex;
	double		apex_height,
				new_height,
				max_height;
	int			inactive_constraint_index;

	/*
	 *	Initialize the three active_constraints to be the first three
	 *	constraints on the list.  (In the present context these are the
	 *	step size constraints, but let's write the code so as not to
	 *	rely on this knowledge.)  Visually, we think of the intersection
	 *	of the halfspaces defined by the three active_constraints as
	 *	a pyramid, oriented so that the gradient of objective function
	 *	points up.
	 */
	for (i = 0; i < 3; i++)
		active_constraints[i] = constraints + i;

	/*
	 *	Initialize the apex to be the vertex defined by the intersection
	 *	of the three step size constraints.
	 *
	 *	Important note:  We assume the maximum value for the objective
	 *	function, subject to the active_constraints, occurs at the apex.
	 *	(In the present context this is true by virtue of the way the
	 *	step size constraints were written.)
	 */
	if (solve_three_equations(active_constraints, apex) == func_failed)
		uFatalError("linear_programming", "Dirichlet_basepoint");

	/*
	 *	For future reference, set apex_height to the value of the
	 *	objective function at the apex.
	 */
	apex_height = EVALUATE_EQN(objective_function, apex);

	/*
	 *	Go down the full list of constraints and see whether the apex
	 *	satisfies all of them.
	 *
	 *	If it does, we've solved the linear programming problem
	 *	and we're done.
	 *
	 *	If it doesn't, then slice the pyramid defined by the
	 *	active_constraints with the constraint which isn't satisfied.
	 *	Let the new apex be the highest point on the truncated pyramid,
	 *	and the new active_constraints be the three faces of the truncated
	 *	pyramid incident to the new apex.  Repeat this procedure until
	 *	all constraints are satisfied.  Note that we have to start from
	 *	the beginning of the constraint list each time, since even if the
	 *	old apex satisfied a given constraint, the new apex might not.
	 *
	 *	If candidate apexes are always at distinct heights, then it's easy
	 *	to prove that this algorithm will terminate in a finite number of
	 *	steps.  But if different candidate apexes sometimes lie at the
	 *	same height (as would happen if a constraint function were parallel
	 *	to the level sets of the objective function, for example) then we
	 *	cannot prove that the algorithm terminates.  Indeed, the example
	 *	given in the documentation at the end of this function shows how
	 *	the algorithm might get into an infinite loop.  To avoid this
	 *	problem, we add an infinitessimal perturbation to the objective
	 *	function.  Say we add a term epsilon*dx to the objective function,
	 *	where epsilon is a true mathematical infinitessimal, not just a very
	 *	small number.  Then if two heights are precisely equal as floating
	 *	point numbers, the one with the greater dx coordinate will be
	 *	considered greater than the one with the smaller dx coordinate.
	 *	But what if their dx coordinates are equal, too?  And dy and dz?
	 *	Our official theoretical definition for the objection function is
	 *
	 *		(objective function as computed)	+ dx*epsilon
	 *											+ dy*(epsilon^2)
	 *											+ dz*(epsilon^3)
	 *
	 *	In practice, this means that we first compare heights based on the
	 *	objective function as computed.  If they come out exactly equal,
	 *	then we compare based on dx coordinates.  If the dx's are equal,
	 *	then we compare dy's.  If the dy's are equal we compare dz's.  If
	 *	all those things are equal, then the points coincide, and it makes
	 *	sense that their heights should be equal.  Since there are only
	 *	finite number of possible apexes (one for each triple of constraints),
	 *	it follows that if the height is reduced at each step, then the
	 *	algorithm must terminate after a finite number of steps.
	 */

	for (i = 0; i < num_constraints; i++)

		if (EVALUATE_EQN(constraints[i], apex) > CONSTRAINT_EPSILON)
		{
			/*
			 *	Uh-oh.  The apex doesn't satisfy constraints[i].
			 *	Slice the pyramid with constraints[i], and see which
			 *	new vertex is highest.  The new set of active constraints
			 *	will include two of the old active constraints, plus
			 *	constraints[i].
			 */

			/*
			 *	Initialize max_height to -1.0.
			 */
			max_height = -1.0;
			for (j = 0; j < 3; j++)
				max_apex[j] = 0.0;

			/*
			 *	Replace each active_constraint, in turn, with constraints[i].
			 *	The variable j will be the index of the active_constraint
			 *	currently being replaced with constraints[i].
			 */ 

			for (j = 0; j < 3; j++)
			{
				/*
				 *	Assemble the candidate set of new_constraints.
				 */
				for (k = 0; k < 3; k++)
					new_constraints[k] =
//	Cater to a DEC compiler error that chokes on &(array)[i]
//						(k == j ? &constraints[i] : active_constraints[k]);
						(k == j ? constraints + i : active_constraints[k]);

				/*
				 *	Find the common intersection of the new_constraints.
				 *
				 *	The equations can't possibly be underdetermined, because
				 *	if the solution set were 1-dimensional it would have to
				 *	include the apex, which is known not to satisfy
				 *	constraints[i].
				 *
				 *	The equations might, however, be inconsistent.  In
				 *	this case, we continue with the loop, as if new_height
				 *	were greater than apex_height (cf. below).  If the
				 *	equations are in principle inconsistent but roundoff
				 *	error gives a solution, that's OK too:  the solution
				 *	will yield a new_height near +infinity or -infinity,
				 *	and new_apex will be ignored or fail to be maximal,
				 *	respectively.
				 */
				if (solve_three_equations(new_constraints, new_apex) == func_failed)
					continue;

				/*
				 *	Compute the value of the objective function
				 *	at the new apex.
				 */
				new_height = EVALUATE_EQN(objective_function, new_apex);

				/*
				 *	If new_height is greater than apex_height, then new_apex
				 *	is above apex, and is not actually a vertex of the
				 *	truncated pyramid.  We ignore it and move on.  (It's
				 *	easy to prove that *some* j will yield a valid maximum
				 *	height.  When we slice the pyramid with constraints[i]
				 *	the resulting solid will somewhere obtain a maximum
				 *	height.  The old apex is gone, so it can't be there.
				 *	And the origin is still present, so it must be higher
				 *	than the origin.  The infinitessimal correction to the
				 *	objective function insures that no planes or lines are
				 *	ever truly horizontal, so the maximum must occur at a
				 *	vertex, where constraints[i] and two of the old
				 *	constraints intersect.)
				 *
				 *	If new_height == apex_height, apex_is_higher() applies
				 *	the infinitessimal correction to the objective function,
				 *	as explained above.
				 */
				if (apex_is_higher(new_height, apex_height, new_apex, apex) == TRUE)
					continue;

				/*
				 *	Is new_height greater than max_height?
				 */
				if (apex_is_higher(new_height, max_height, new_apex, max_apex) == TRUE)
				{
					inactive_constraint_index = j;
					max_height = new_height;
					for (k = 0; k < 3; k++)
						max_apex[k] = new_apex[k];
				}
			}

			/*
			 *	Swap constraints[i] into the active_constraints array
			 *	at index inactive_constraint_index.
			 */
//	Cater to a DEC compiler error that chokes on &(array)[i]
//			active_constraints[inactive_constraint_index] =	&constraints[i];
			active_constraints[inactive_constraint_index] =	constraints + i;

			/*
			 *	Set the apex to max_apex and apex_height to max_height.
			 *
			 *	Note that we preserve the condition (cf. above) that the
			 *	maximum value of the objective function on the pyramid
			 *	occurs at the apex.
			 */
			for (j = 0; j < 3; j++)
				apex[j] = max_apex[j];
			apex_height = max_height;

			/*
			 *	We've fixed up the active_constraints and the apex,
			 *	so now recheck the other constraints.  Set i = -1,
			 *	so that after the i++ in the for(;;) statement it will
			 *	be back to i = 0.
			 */
			i = -1;
		 }

	/*
	 *	Hooray.  All the constraints are satisfied.
	 *	Set the solution equal to the apex and we're done.
	 */
	for (i = 0; i < 3; i++)
		solution[i] = apex[i];

	return;

	/*
	 *	Here's an example in which an unperturbed objective function
	 *	may lead to an infinite loop.
	 *
	 *	Make a sketch (in (dx, dy, dz) space) showing the following points:
	 *
	 *		s0	= (2, 0, 0)
	 *		s1	= (-1,  sqrt(3), 0)	[twice a primitive cube root of unity]
	 *		s2	= (-1, -sqrt(3), 0)
	 *
	 *		t0	= (1, 0, 1)
	 *		t1	= (-1,  sqrt(3)/2, 1)
	 *		t2	= (-1, -sqrt(3)/2, 1)
	 *
	 *		u	= (0, 0, 2)
	 *
	 *	The objective function is 0*dx + 0*dy + 1*dz + constant.
	 *
	 *	The constraints are defined by the following planes.  (I'll give
	 *	a spanning set for each plane.  The constraint itself will be an
	 *	equation saying that (dx, dy, dz) must lie on the same side of the
	 *	plane as the origin (0, 0, 0) (or on the plane itself is OK too).)
	 *	Sketch each of these planes in your picture.
	 *
	 *		a0	= {s1, s2, u}
	 *		a1	= {s2, s0, u}
	 *		a2	= {s0, s1, u}
	 *
	 *		b	= {t0, t1, t2}
	 *
	 *		c0	= {s0, t1, t2}
	 *		c1	= {t0, s1, t2}
	 *		c2	= {t0, t1, s2}
	 *
	 *	Now look what happens in the naive linear programming algorithm.
	 *	Say we start with constraints a0, a1 and a2, so the apex is at
	 *	point u.  Then we consider constraint b.  Constraint b will
	 *	replace one of {a0, a1, a2};  w.l.o.g. say it's a2.  So we're
	 *	left with constraints {a0, a1, b} and the apex is at t2.  The point
	 *	t2 satisfies all the constraints except c2, so eventually the
	 *	algorithm will swap c2 for either a0 or a1 (w.l.o.g. say its a1)
	 *	and we're left with equations {a0, c2, b}, and the apex moves to
	 *	t1.  The point t1 satisfies all the constraints except c1, so
	 *	c1 gets swapped for either a0 or c2, the constraint set becomes
	 *	either {c1, c2, b} or {a0, c1, b}, and the apex moves to either
	 *	t0 or t2.  This is the critical juncture for the algorithm.  If
	 *	it swapped c1 for a0, then on the next iteration of the algorithm
	 *	it swaps c0 for b, and the constraint set {c1, c2, c0} gives us
	 *	the true solution.  But if it swapped c1 for c2, then we run the
	 *	risk of getting into an infinite loop.  But with the perturbed
	 *	objective function this will never happen, because we'll never
	 *	visit the same vertex twice.
	 */
}
Beispiel #20
0
static void compute_singular_action(
        Triangulation   *manifold,
        Isometry                *isometry)
{
	Boolean		lower_index_is_at_top;
	EdgeIndex	index, image_index;
	VertexIndex	image_v1,image_v2,v1,v2;
	Tetrahedron	*tet, *image_tet;
	EdgeClass	*edge, *image_edge;
	PositionedTet	ptet, ptet0;

	for(	edge = manifold->edge_list_begin.next;
		edge!=&manifold->edge_list_end;
		edge = edge->next )
	if (edge->is_singular)
	{
               if (edge->singular_index<0)
                        uFatalError("compute_singular_action","isometry_cusped");

                tet     = edge->incident_tet;
                index   = edge->incident_edge_index;
                v1      = MIN(one_vertex_at_edge[index], other_vertex_at_edge[index]);
                v2      = MAX(one_vertex_at_edge[index], other_vertex_at_edge[index]);

                image_tet       = tet->image;
                image_v1        = EVALUATE(tet->map,v1);
                image_v2        = EVALUATE(tet->map,v2);
                image_index     = edge_between_vertices[image_v1][image_v2];
                image_edge      = image_tet->edge_class[image_index];

                if (image_edge->singular_index<0 || image_edge->singular_order != edge->singular_order )
                        uFatalError("compute_singular_action","isometry_cusped");

                /* i have never kept track of a direction only the singular edges.  prehaps this is something i should be doing...
                 * for now, here is how we'll kept track of orientation of these maps from singular edge to singular edge:
                 *      each edge has an incident tet and an incident edge index, define an ordering on the ends of the singular
                 *      edge by the ordering of the vertex indices of this edge.
                 *
                 * there may be some clever  way of doing this with the handedness of the edge classes. i am not sure.
                 */

                set_left_edge( image_edge, &ptet0 );
                ptet = ptet0;

                lower_index_is_at_top = ( ptet.bottom_face < ptet.right_face );
                isometry->singular_image[edge->singular_index] = 0;
                do{
                        if (ptet.tet == image_tet && edge_between_faces[ptet.near_face][ptet.left_face] == image_index)
                        {
                                if (lower_index_is_at_top)
                                {
                                        if (image_v1 == ptet.bottom_face)
                                                isometry->singular_image[edge->singular_index] =    image_edge->singular_index + 1;
                                        else    isometry->singular_image[edge->singular_index] =  -(image_edge->singular_index + 1);
                                }
                                else
                                {
                                        if (image_v1 == ptet.bottom_face)
                                                isometry->singular_image[edge->singular_index] =  -(image_edge->singular_index + 1);
                                        else    isometry->singular_image[edge->singular_index] =    image_edge->singular_index + 1;
                                }

                                break;
                        }

                        veer_left( &ptet );

                }while(!same_positioned_tet( &ptet0, &ptet ));

                if (isometry->singular_image[edge->singular_index] == 0)
                        uFatalError("compute_singular_action","isometry_cusped");
	}
}
Beispiel #21
0
static FuncResult attempt_isometry(
	Triangulation	*manifold0,
	Tetrahedron		*initial_tet0,
	Tetrahedron		*initial_tet1,
	Permutation		initial_map,
	int			*singular_map )
{
	Tetrahedron	*tet0,
				*tet1,
				*nbr0,
				*nbr1,
				**queue;
	EdgeClass		*edge0,
				*edge1;
	int			first,
				last,
				i,
				j;
	FaceIndex	face0,
				face1;
	Permutation	gluing0,
				gluing1,
				nbr0_map;

	/*
	 *	initial_tet1 and initial_map are arbitrary, so
	 *	the vast majority of calls to attempt_isometry()
	 *	will fail.  Therefore it's worth including a quick
	 *	plausibility check at the beginning, to make the
	 *	algorithm run faster.
	 */
	if (is_isometry_plausible(initial_tet0, initial_tet1, initial_map) == FALSE)
		return func_failed;

	/*
	 *	Initialize all the image fields of manifold0
	 *	to NULL to show they haven't been set.
	 */
	for (	tet0 = manifold0->tet_list_begin.next;
		 	tet0 != &manifold0->tet_list_end;
			tet0 = tet0->next)
		tet0->image = NULL;

	/*
	 *	Allocate space for a queue which is large enough
	 *	to hold pointers to all the Tetrahedra in manifold0.
	 */
	queue = NEW_ARRAY(manifold0->num_tetrahedra, Tetrahedron *);

	/*
	 *	At all times, the Tetrahedra on the queue will be those which
	 *
	 *		(1) have set their image and map fields, but
	 *
	 *		(2)	have not checked their neighbors.
	 */

	/*
	 *	Set the image and map fields for initial_tet0.
	 */
	initial_tet0->image	= initial_tet1;
	initial_tet0->map	= initial_map;

	/*
	 *	Put initial_tet0 on the queue.
	 */
	first = 0;
	last  = 0;
	queue[first] = initial_tet0;

	/*
	 *	While there are Tetrahedra on the queue . . .
	 */
	while (last >= first)
	{
		/*
		 *	Pull the first Tetrahedron off the queue and call it tet0.
		 */
		tet0 = queue[first++];

		/*
		 *	tet0 maps to some Tetrahedron tet1 in manifold1.
		 */
		tet1 = tet0->image;

		/* we will keep track on how the singular edges in manifold0 map into manifold1 */
		for(i=0;i<4;i++)
		for(j=i+1;j<4;j++)
		{
			edge0 = tet0->edge_class[edge_between_vertices[i][j]];
			edge1 = tet1->edge_class[edge_between_vertices[EVALUATE(tet0->map, i)]
							[EVALUATE(tet0->map, j)]];

                        if (edge0->order != edge1->order )
			{
				my_free(queue);
				return func_failed;
			}


                        if (edge0->is_singular != edge1->is_singular )
			{
				my_free(queue);
				return func_failed;
			}

			if (edge0->is_singular && edge0->singular_order != edge1->singular_order )
			{
				my_free(queue);
				return func_failed;
			}

			if (singular_map != NULL && edge0->is_singular )
				singular_map[edge0->singular_index] = edge1->singular_index;
		}

		/*
		 *	For each face of tet0 . . .
		 */
		for (face0 = 0; face0 < 4; face0++)
		{
			/*
			 *	Let nbr0 be the Tetrahedron which meets tet0 at face0.
			 */
			nbr0 = tet0->neighbor[face0];

			/*
			 *	tet0->map takes face0 of tet0 to face1 of tet1.
			 */
			face1 = EVALUATE(tet0->map, face0);

			/*
			 *	Let nbr1 be the Tetrahedron which meets tet1 at face1.
			 */
			nbr1 = tet1->neighbor[face1];

			/*
			 *	Let gluing0 be the gluing which identifies face0 of
			 *	tet0 to nbr0, and similarly for gluing1.
			 */
			gluing0 = tet0->gluing[face0];
			gluing1 = tet1->gluing[face1];

			/*
			 *						 gluing0
			 *				   tet0  ------>  nbr0
			 *					|				|
			 *		  tet0->map |				| nbr0->map
			 *					|				|
			 *					V    gluing1	V
			 *				   tet1  ------>  nbr1
			 *
			 *	We want to ensure that tet1 and nbr1 enjoy the same
			 *	relationship to each other in manifold1 that tet0 and
			 *	nbr0 do in manifold0.  The conditions
			 *
			 *					nbr0->image == nbr1
			 *	and
			 *		nbr0->map == gluing1 o tet0->map o gluing0^-1
			 *
			 *	are necessary and sufficient to insure that we have a
			 *	combinatorial equivalence between the Triangulations.
			 *	(The proof relies on the fact that we've already checked
			 *	(near the beginning of compute_cusped_isometries() above)
			 *	that the Triangulations have the same number of Tetrahedra;
			 *	otherwise one Triangulation could be a (possibly branched)
			 *	covering of the other.)
			 */

			/*
			 *	Compute the required value for nbr0->map.
			 */
			nbr0_map = compose_permutations(
							compose_permutations(
								gluing1,
								tet0->map
							),
							inverse_permutation[gluing0]
						);

			/*
			 *	If nbr0->image and nbr0->map have already been set,
			 *	check that they satisfy the above conditions.
			 */
			if (nbr0->image != NULL)
			{
				if (nbr0->image != nbr1  ||  nbr0->map != nbr0_map)
				{
					/*
					 *	This isn't an isometry.
					 */
					my_free(queue);
					return func_failed;
				}
			}
			/*
			 *	else . . .
			 *	nbr0->image and nbr0->map haven't been set.
			 *	Set them, and put nbr0 on the queue.
			 */
			else
			{
				if (!is_isometry_plausible(nbr0,nbr1,nbr0_map))
				{
					my_free(queue);
					return func_failed;
				}

				nbr0->image	= nbr1;
				nbr0->map	= nbr0_map;
				queue[++last] = nbr0;
			}
		}
	}

	/*
	 *	A quick error check.
	 *	Is it plausible that each Tetrahedron
	 *	has been on the queue exactly once?
	 */
	if (first != manifold0->num_tetrahedra
	 || last  != manifold0->num_tetrahedra - 1)
		uFatalError("attempt_isometry", "isometry");

	/*
	 *	Free the queue, and report success.
	 */
	my_free(queue);
	return func_OK;
}
Beispiel #22
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;
}
static void uDumpObjectAndStrongRefs(FILE *fp, uObject *obj)
{
    uType *type = obj->GetType();

    uDumpObject(fp, obj, type->TypeName);

    switch (type->TypeType)
    {
    case uTypeTypeClass:
        do
        {
	    uDumpStrongRefs(fp, obj, obj, type);
            type = type->BaseType;
        } while (type);
	    break;

    case uTypeTypeEnum:
        break;

    case uTypeTypeStruct:
    {
        uByte* valueAddr = (uByte*)obj + sizeof(uObject);
        uDumpStrongRefs(fp, obj, valueAddr, type);
	    break;
    }

    case uTypeTypeDelegate:
    {
        uDelegate *delegate = (uDelegate*)obj;
        uDumpStrongRef(fp, obj, delegate->_obj);
        uDumpStrongRef(fp, obj, delegate->_prev);
        break;
    }

    case uTypeTypeArray:
    {
        uArray *array = (uArray *)obj;
        uArrayType *arrayType = (uArrayType *)type;
        uType *elmType = arrayType->ElementType;

        switch (elmType->TypeType)
        {
            case uTypeTypeClass:
            case uTypeTypeInterface:
            case uTypeTypeDelegate:
            case uTypeTypeArray:
            {
	        for (int i = 0; i < array->_len; ++i)
		{
		    uObject *target = ((uObject **)array->_ptr)[i];
                    uDumpStrongRef(fp, obj, target);
		}
            }
            break;

            case uTypeTypeEnum:
                break;
            case uTypeTypeStruct:
            {
                for (int i = 0; i < array->_len; ++i)
                {
                    uByte *valueAddr = (uByte *)array->_ptr + i * elmType->ValueSize;
                    uDumpStrongRefs(fp, obj, valueAddr, elmType);
                }
            }
            break;

            default:
                uFatalError(XLI_FUNCTION);
            }
        }
        break;

    default:
        uFatalError(XLI_FUNCTION);
    }
}
Beispiel #24
0
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 compiler would like a return value, even though
     *  we never return from the uFatalError() call.
     *  The Metrowerks compiler would, in addition, like
     *  the data to be initialized before use.
     */
    ptet.tet            = NULL;
    ptet.near_face      = 0;
    ptet.left_face      = 0;
    ptet.right_face     = 0;
    ptet.bottom_face    = 0;
    ptet.orientation    = unknown_orientation;
    return ptet;
}
void uReleaseObject(uObject* obj)
{
    if (obj)
    {
        if (Xli::AtomicDecrement(&obj->__obj_retains) == 0)
        {
            if (!uTryClearWeakObject(obj))
                return;

#ifdef U_DEBUG_MEM
            uThreadData* thread = uGetThreadData();

            if (thread->AutoReleaseStack.Length() > 0 &&
                thread->AutoReleaseStack.Last().AllocCount > 0)
            {
                thread->AutoReleaseStack.Last().FreeCount++;
                thread->AutoReleaseStack.Last().FreeSize += obj->__obj_alloc_size;
            }
#endif
            uType* type = obj->__obj_type;

            switch (type->TypeType)
            {
            case uTypeTypeClass:
                do
                {
                    if (type->__fp_Finalize)
                    {
                        try { (*type->__fp_Finalize)(obj); }
                        catch (...) { Xli::Error->WriteFormat("Runtime Error: Unhandled exception in finalizer for '%s'\n", type->TypeName); }
                    }

                    uReleaseValue(type, obj);
                    type = type->BaseType;

                } while (type);

                break;

            case uTypeTypeEnum:
                break;

            case uTypeTypeStruct:
                // Note: This must be a boxed value, so append size of object header
                uReleaseValue(type, (uByte*)obj + sizeof(uObject));
                break;

            case uTypeTypeDelegate:
                uReleaseObject(((uDelegate*)obj)->_obj);
                uReleaseObject(((uDelegate*)obj)->_prev);
                break;

            case uTypeTypeArray:
                {
                    uArray* array = (uArray*)obj;
                    uArrayType* arrayType = (uArrayType*)type;
                    uType* elmType = arrayType->ElementType;

                    switch (elmType->TypeType)
                    {
                    case uTypeTypeClass:
                    case uTypeTypeInterface:
                    case uTypeTypeDelegate:
                    case uTypeTypeArray:
                        for (uObject** objAddr = (uObject**)array->_ptr; array->_len--; objAddr++)
                            uReleaseObject(*objAddr);
                        break;

                    case uTypeTypeEnum:
                        break;

                    case uTypeTypeStruct:
                        for (uByte* valueAddr = (uByte*)array->_ptr; array->_len--; valueAddr += elmType->ValueSize)
                            uReleaseValue(elmType, valueAddr);
                        break;

                    default:
                        uFatalError(XLI_FUNCTION);
                    }
                }
                break;

            default:
                uFatalError(XLI_FUNCTION);
            }

#if U_DEBUG_MEM >= 2
            Xli::Error->WriteFormat("Freeing '%s' (%d bytes)\n", obj->__obj_type->TypeName, obj->__obj_alloc_size);
#endif

#ifdef U_DEBUG_MEM
            uEnterCritical();
            uHeapObjects->Remove(obj);
            uExitCritical();
#endif

            U_FREE_OBJECT(obj);
        }
        else if ((*(uUInt*)&obj->__obj_retains) & 0xF0000000)
        {
            // Object must be corrupt if one of the four first bits are set
            uFatalError(XLI_FUNCTION);
        }
        else
        {
#if U_DEBUG_MEM >= 3
            Xli::Error->WriteFormat("Released '%s' (%d bytes) (%d retains)\n", obj->__obj_type->TypeName, obj->__obj_alloc_size, obj->__obj_retains);
#endif
        }
    }
}
Beispiel #26
0
void fix_peripheral_orientations(
	Triangulation	*manifold)
{
	Tetrahedron	*tet;
	VertexIndex	v;
	FaceIndex	f;
	Cusp		*cusp;

	/*
	 *	This function should get called only for orientable manifolds.
	 */
	if (manifold->orientability != oriented_manifold && 
 		manifold->orientability != oriented_orbifold)
		uFatalError("fix_peripheral_orientations", "orient");

	/*
	 *	Compute the intersection number of the meridian and longitude.
	 */
	copy_curves_to_scratch(manifold, 0, FALSE);
	copy_curves_to_scratch(manifold, 1, FALSE);
	compute_intersection_numbers(manifold);

	/*
	 *	Reverse the meridian on cusps with intersection_number[L][M] == -1.
	 */

	/* which Tetrahedron */
	for (tet = manifold->tet_list_begin.next;
		 tet != &manifold->tet_list_end;
		 tet = tet->next)

		/* which ideal vertex */
		for (v = 0; v < 4; v++)

			if (tet->cusp[v]->intersection_number[L][M] == -1)

				/* which side of the vertex */
				for (f = 0; f < 4; f++)

					if (v != f)
					{
						tet->curve[M][right_handed][v][f] = - tet->curve[M][right_handed][v][f];

						if (tet->curve[M][left_handed][v][f] != 0.0
						 || tet->curve[L][left_handed][v][f] != 0.0)
							uFatalError("fix_peripheral_orientations", "orient");
					}

	/*
	 *	When we reverse the meridian we must also negate the meridional
	 *	Dehn filling coefficient in order to maintain the same (oriented)
	 *	Dehn filling curve as before.  However, this Dehn filling curve
	 *	will wind clockwise around the core geodesics, relative to
	 *	the global orientation on the manifold (because the global
	 *	orientation disagrees with the local orientation we had been using
	 *	on the nonorientable manifold's torus cusp).  This forces a whole
	 *	new solution to be found to the gluing equations.  To avoid this,
	 *	we reverse the direction of the Dehn filling curve (i.e. we
	 *	negate both the m and l coefficients).  The net effect is that
	 *	we negate the l coefficient.
	 *
	 *	This reversal of the Dehn filling curve is not really
	 *	necessary, and could be eliminated if it's ever causes problems.
	 */

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

		if (cusp->intersection_number[L][M] == -1)

			cusp->l = - cusp->l;
}
Beispiel #27
0
void extend_orientation(
	Triangulation	*manifold,
	Tetrahedron		*initial_tet)
{
	Tetrahedron	**queue,
				*tet;
	int			queue_first,
				queue_last;
	FaceIndex	f;
	/*
	 *	Set all the tet->flag fields to FALSE,
	 *	to show that no Tetrahedra have been visited.
	 */

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

		tet->flag = FALSE;

	/*
	 *	Tentatively assume the manifold is orientable.
	 */
	manifold->orientability = oriented_manifold;

	/*
	 *	Allocate space for a queue of pointers to the Tetrahedra.
	 *	Each Tetrahedron will appear on the queue exactly once,
	 *	so an array of length manifold->num_tetrahedra will be just right.
	 */
	queue = NEW_ARRAY(manifold->num_tetrahedra, Tetrahedron *);

	/*
	 *	Put the initial Tetrahedron on the queue,
	 *	and mark it as visited.
	 */
	queue_first = 0;
	queue_last  = 0;
	queue[0] = initial_tet;
	queue[0]->flag = TRUE;

	/*
	 *	Start processing the queue.
	 */
	do
	{
		/*
		 *	Pull a Tetrahedron off the front of the queue.
		 */
		tet = queue[queue_first++];

		/*
		 *	Look at the four neighboring Tetrahedra.
		 */
		for (f = 0; f < 4; f++)
		{
			/*
			 *	If the neighbor hasn't been visited...
			 */
			if (tet->neighbor[f]->flag == FALSE)
			{
				/*
				 *	...reverse its orientation if necessary...
				 */
				if (parity[tet->gluing[f]] == orientation_reversing)
					reverse_orientation(tet->neighbor[f]);

				/*
				 *	...mark it as visited...
				 */
				tet->neighbor[f]->flag = TRUE;

				/*
				 *	...and put it on the back of the queue.
				 */
				queue[++queue_last] = tet->neighbor[f];
			}
			/*
			 *	If the neighbor has been visited . . .
			 */
			else
			{
				/*
				 *	...check whether its orientation is consistent.
				 */
				if (parity[tet->gluing[f]] == orientation_reversing)
					manifold->orientability = nonorientable_manifold;
			}
		}
	}
	/*
	 *	Keep going until either we discover the manifold is nonorientable,
	 *	or the queue is exhausted.
	 */
	while (manifold->orientability == oriented_manifold
		&& queue_first <= queue_last);

	/*
	 *	Free the memory used for the queue.
	 */
	my_free(queue);	

	/*
	 *	An "unnecessary" (but quick) error check.
	 */
	if (manifold->orientability == oriented_manifold
	 && (	queue_first != manifold->num_tetrahedra
		 || queue_last  != manifold->num_tetrahedra - 1))
		uFatalError("orient", "orient");

	/*
	 *	Another error check.
	 *	We should have oriented a manifold before attempting to
	 *	compute the Chern-Simons invariant.
	 */
/*
	if (manifold->CS_value_is_known || manifold->CS_fudge_is_known)
		uFatalError("orient", "orient");
*/
	/*
	 *	Respect the conventions for peripheral curves and
	 *	edge orientations in oriented manifolds.
	 */
	if (manifold->orientability == oriented_manifold)
	{
		transfer_peripheral_curves(manifold);
		make_all_edge_orientations_right_handed(manifold);
	}

}
Beispiel #28
0
int
main(int argc, char *argv[])
{
    Widget toplevel;
    XtAppContext app_con;
    Widget panel;
    Widget base[XkbNumModifiers];
    Widget latched[XkbNumModifiers];
    Widget locked[XkbNumModifiers];
    Widget effective[XkbNumModifiers];
    Widget compat[XkbNumModifiers];
    Widget baseBox, latchBox, lockBox, effBox, compatBox;
    register int i;
    unsigned bit;
    XkbEvent ev;
    XkbStateRec state;
    static Arg hArgs[] = { {XtNorientation, (XtArgVal) XtorientHorizontal} };
    static Arg vArgs[] = { {XtNorientation, (XtArgVal) XtorientVertical} };
    static Arg onArgs[] = { {XtNon, (XtArgVal) True} };
    static Arg offArgs[] = { {XtNon, (XtArgVal) False} };
    static char *fallback_resources[] = {
        "*Box*background: grey50",
        "*Box*borderWidth: 0",
        "*Box*vSpace: 1",
        NULL
    };

    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-version") == 0) {
            printf("xkbwatch (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
            exit(0);
        }
    }

    uSetErrorFile(NullString);
    toplevel = XtOpenApplication(&app_con, "XkbWatch",
                                 options, XtNumber(options), &argc, argv,
                                 fallback_resources,
                                 sessionShellWidgetClass, NULL, ZERO);
    if (toplevel == NULL) {
        uFatalError("Couldn't create application top level\n");
        exit(1);
    }
    inDpy = outDpy = XtDisplay(toplevel);
    if (inDpy) {
        int i1, mn, mj;

        mj = XkbMajorVersion;
        mn = XkbMinorVersion;
        if (!XkbQueryExtension(inDpy, &i1, &evBase, &errBase, &mj, &mn)) {
            uFatalError("Server doesn't support a compatible XKB\n");
            exit(1);
        }
    }
    panel =
        XtCreateManagedWidget("xkbwatch", boxWidgetClass, toplevel, vArgs, 1);
    if (panel == NULL) {
        uFatalError("Couldn't create top level box\n");
        exit(1);
    }
    baseBox = XtCreateManagedWidget("base", boxWidgetClass, panel, hArgs, 1);
    if (baseBox == NULL)
        uFatalError("Couldn't create base modifiers box\n");
    latchBox =
        XtCreateManagedWidget("latched", boxWidgetClass, panel, hArgs, 1);
    if (latchBox == NULL)
        uFatalError("Couldn't create latched modifiers box\n");
    lockBox = XtCreateManagedWidget("locked", boxWidgetClass, panel, hArgs, 1);
    if (lockBox == NULL)
        uFatalError("Couldn't create locked modifiers box\n");
    effBox =
        XtCreateManagedWidget("effective", boxWidgetClass, panel, hArgs, 1);
    if (effBox == NULL)
        uFatalError("Couldn't create effective modifiers box\n");
    compatBox =
        XtCreateManagedWidget("compat", boxWidgetClass, panel, hArgs, 1);
    if (compatBox == NULL)
        uFatalError("Couldn't create compatibility state box\n");
    XkbSelectEvents(inDpy, XkbUseCoreKbd, XkbStateNotifyMask,
                    XkbStateNotifyMask);
    XkbGetState(inDpy, XkbUseCoreKbd, &state);
    for (i = XkbNumModifiers - 1, bit = 0x80; i >= 0; i--, bit >>= 1) {
        ArgList list;

        char buf[30];

        sprintf(buf, "base%d", i);
        if (state.base_mods & bit)
            list = onArgs;
        else
            list = offArgs;
        base[i] = XtCreateManagedWidget(buf, ledWidgetClass, baseBox, list, 1);
        sprintf(buf, "latched%d", i);
        if (state.latched_mods & bit)
            list = onArgs;
        else
            list = offArgs;
        latched[i] =
            XtCreateManagedWidget(buf, ledWidgetClass, latchBox, list, 1);
        sprintf(buf, "locked%d", i);
        if (state.locked_mods & bit)
            list = onArgs;
        else
            list = offArgs;
        locked[i] =
            XtCreateManagedWidget(buf, ledWidgetClass, lockBox, list, 1);
        sprintf(buf, "effective%d", i);
        if (state.mods & bit)
            list = onArgs;
        else
            list = offArgs;
        effective[i] =
            XtCreateManagedWidget(buf, ledWidgetClass, effBox, list, 1);
        sprintf(buf, "compat%d", i);
        if (state.compat_state & bit)
            list = onArgs;
        else
            list = offArgs;
        compat[i] =
            XtCreateManagedWidget(buf, ledWidgetClass, compatBox, list, 1);
    }
    XtRealizeWidget(toplevel);
    while (1) {
        XtAppNextEvent(app_con, &ev.core);
        if (ev.core.type == evBase + XkbEventCode) {
            if (ev.any.xkb_type == XkbStateNotify) {
                unsigned changed;

                if (ev.state.changed & XkbModifierBaseMask) {
                    changed = ev.state.base_mods ^ state.base_mods;
                    state.base_mods = ev.state.base_mods;
                    for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
                        if (changed & bit) {
                            ArgList list;

                            if (state.base_mods & bit)
                                list = onArgs;
                            else
                                list = offArgs;
                            XtSetValues(base[i], list, 1);
                        }
                    }
                }
                if (ev.state.changed & XkbModifierLatchMask) {
                    changed = ev.state.latched_mods ^ state.latched_mods;
                    state.latched_mods = ev.state.latched_mods;
                    for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
                        if (changed & bit) {
                            ArgList list;

                            if (state.latched_mods & bit)
                                list = onArgs;
                            else
                                list = offArgs;
                            XtSetValues(latched[i], list, 1);
                        }
                    }
                }
                if (ev.state.changed & XkbModifierLockMask) {
                    changed = ev.state.locked_mods ^ state.locked_mods;
                    state.locked_mods = ev.state.locked_mods;
                    for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
                        if (changed & bit) {
                            ArgList list;

                            if (state.locked_mods & bit)
                                list = onArgs;
                            else
                                list = offArgs;
                            XtSetValues(locked[i], list, 1);
                        }
                    }
                }
                if (ev.state.changed & XkbModifierStateMask) {
                    changed = ev.state.mods ^ state.mods;
                    state.mods = ev.state.mods;
                    for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
                        if (changed & bit) {
                            ArgList list;

                            if (state.mods & bit)
                                list = onArgs;
                            else
                                list = offArgs;
                            XtSetValues(effective[i], list, 1);
                        }
                    }
                }
                if (ev.state.changed & XkbCompatStateMask) {
                    changed = ev.state.compat_state ^ state.compat_state;
                    state.compat_state = ev.state.compat_state;
                    for (i = 0, bit = 1; i < XkbNumModifiers; i++, bit <<= 1) {
                        if (changed & bit) {
                            ArgList list;

                            if (state.compat_state & bit)
                                list = onArgs;
                            else
                                list = offArgs;
                            XtSetValues(compat[i], list, 1);
                        }
                    }
                }
            }
        }
        else
            XtDispatchEvent(&ev.core);
    }
/* BAIL: */
    if (inDpy)
        XCloseDisplay(inDpy);
    if (outDpy != inDpy)
        XCloseDisplay(outDpy);
    inDpy = outDpy = NULL;
    return 0;
}
FuncResult change_peripheral_curves(
	      Triangulation	*manifold,
	CONST MatrixInt22	change_matrices[])
{
	int			i,
				v,
				f,
				old_m,
				old_l;
	double		old_m_coef,	/* changed from int to double, JRW 2000/01/18 */
				old_l_coef;
	Tetrahedron	*tet;
	Cusp		*cusp;
	Complex		old_Hm,
				old_Hl;

	/*
	 *	First make sure all the change_matrices have determinant +1.
	 */

	for (i = 0; i < manifold->num_cusps; i++)
		if (DET2(change_matrices[i]) != +1)
			return func_bad_input;

	/*
	 *	The change_matrices for Klein bottle cusps must have zeros in the
	 *	off-diagonal entries.  (Nothing else makes sense topologically.)
	 */

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

		if (cusp->topology == Klein_cusp)

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

				if (change_matrices[cusp->index][i][!i] != 0)

					uFatalError("change_peripheral_curves", "change_peripheral_curves");

	/*
	 *	Change the peripheral curves according to the change_matrices.
	 *	As stated at the top of this file, the transformation rule is
	 *
	 *		| new m |   |                    | | old m |
	 *		|       | = | change_matrices[i] | |       |
	 *		| new l |   |                    | | old l |
	 */

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

		for (i = 0; i < 2; i++)				/*	which orientation	*/

			for (v = 0; v < 4; v++)			/*	which vertex		*/

				for (f = 0; f < 4; f++)		/*	which side			*/
				{
					old_m = tet->curve[M][i][v][f];
					old_l = tet->curve[L][i][v][f];

					tet->curve[M][i][v][f]
						= change_matrices[tet->cusp[v]->index][0][0] * old_m
						+ change_matrices[tet->cusp[v]->index][0][1] * old_l;
					tet->curve[L][i][v][f]
						= change_matrices[tet->cusp[v]->index][1][0] * old_m
						+ change_matrices[tet->cusp[v]->index][1][1] * old_l;
				}

	/*
	 *	Change the Dehn filling coefficients to reflect the new
	 *	peripheral curves.  That is, we want the Dehn filling curves
	 *	to be topologically the same as before, even though the
	 *	coefficients will be different because we changed the basis.
	 *
	 *	To keep our thinking straight, let's imagine all peripheral
	 *	curves -- old and new -- in terms of some arbitrary but
	 *	fixed basis for pi_1(T^2) = Z + Z.  We never actually compute
	 *	such a basis, but it helps keep our thinking straight.
	 *	Relative to this fixed basis, we have
	 *
	 *				old m = (old m [0],  old m [1])
	 *				old l = (old l [0],  old l [1])
	 *				new m = (new m [0],  new m [1])
	 *				new l = (new l [0],  new l [1])
	 *
	 *	Note that these m's and l's are curves, not coefficients!
	 *	They are elements of pi_1(T^2) = Z + Z.
	 *
	 *	We can then rewrite the above transformation rule, with
	 *	each peripheral curve (old m, old l, new m and new l)
	 *	appearing as a row in a 2 x 2 matrix:
	 *
	 *		| <--new m--> |   |                    | | <--old m--> |
	 *		|             | = | change_matrices[i] | |             |
	 *		| <--new l--> |   |                    | | <--old l--> |
	 *
	 *	We can invert the change_matrix to solve for the old curves
	 *	in terms of the new ones:
	 *												-1
	 *		| <--old m--> |   |                    |   | <--new m--> |
	 *		|             | = | change_matrices[i] |   |             |
	 *		| <--old l--> |   |                    |   | <--new l--> |
	 *
	 *	The Dehn filling curve is
	 *
	 *		old_m_coef * old_m  +  old_l_coef * old_l
	 *
	 *		= old_m_coef * [  change_matrices[i][1][1] * new_m
	 *						- change_matrices[i][0][1] * new_l]
	 *		+ old_l_coef * [- change_matrices[i][1][0] * new_m
	 *						+ change_matrices[i][0][0] * new_l]
	 *
	 *		= new_m * [  old_m_coef * change_matrices[i][1][1]
	 *				   - old_l_coef * change_matrices[i][1][0] ]
	 *		+ new_l * [- old_m_coef * change_matrices[i][0][1]
	 *				   + old_l_coef * change_matrices[i][0][0] ]
	 *
	 *	Therefore
	 *
	 *		new_m_coef =   old_m_coef * change_matrices[i][1][1]
	 *					 - old_l_coef * change_matrices[i][1][0]
	 *		new_l_coef = - old_m_coef * change_matrices[i][0][1]
	 *					 + old_l_coef * change_matrices[i][0][0]
	 */

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

		if (cusp->is_complete == FALSE)
		{
			old_m_coef = cusp->m;
			old_l_coef = cusp->l;

			cusp->m =   old_m_coef * change_matrices[cusp->index][1][1]
					  - old_l_coef * change_matrices[cusp->index][1][0];
			cusp->l = - old_m_coef * change_matrices[cusp->index][0][1]
					  + old_l_coef * change_matrices[cusp->index][0][0];
		}

	/*
	 *	Update the holonomies according to the rule
	 *
	 *		| new H(m) |   |                    | | old H(m) |
	 *		|          | = | change_matrices[i] | |          |
	 *		| new H(l) |   |                    | | old H(l) |
	 *
	 *	(These are actually logs of holonomies, so it's correct to
	 *	add -- not multiply -- them.)
	 *
	 *	For complete Cusps the holonomies should be zero, but that's OK.
	 */

	for (cusp = manifold->cusp_list_begin.next;
		 cusp != &manifold->cusp_list_end;
		 cusp = cusp->next)
 	if ( cusp->topology == torus_cusp || cusp->topology == Klein_cusp ) /* DJH */
		for (i = 0; i < 2; i++)		/* i = ultimate, penultimate */
		{
			old_Hm = cusp->holonomy[i][M];
			old_Hl = cusp->holonomy[i][L];

			cusp->holonomy[i][M] = complex_plus(
				complex_real_mult(
					change_matrices[cusp->index][0][0],
					old_Hm
				),
				complex_real_mult(
					change_matrices[cusp->index][0][1],
					old_Hl
				)
			);

			cusp->holonomy[i][L] = complex_plus(
				complex_real_mult(
					change_matrices[cusp->index][1][0],
					old_Hm
				),
				complex_real_mult(
					change_matrices[cusp->index][1][1],
					old_Hl
				)
			);
		}

	/*
	 *	Update the cusp_shapes.
	 */

	for (cusp = manifold->cusp_list_begin.next;
		 cusp != &manifold->cusp_list_end;
		 cusp = cusp->next)
 	if ( cusp->topology == torus_cusp || cusp->topology == Klein_cusp ) /* DJH */
	{
		cusp->cusp_shape[initial] = transformed_cusp_shape(
									cusp->cusp_shape[initial],
									change_matrices[cusp->index]);

		if (cusp->is_complete == TRUE)
			cusp->cusp_shape[current] = transformed_cusp_shape(
									cusp->cusp_shape[current],
									change_matrices[cusp->index]);
		/*
		 *	else cusp->cusp_shape[current] == Zero, and needn't be changed.
		 */
	}

	return func_OK;
}
Beispiel #30
0
static void compute_cusp_Euler_characteristics(
    Triangulation   *manifold)
{
    Cusp        *cusp;
    EdgeClass   *edge;
    Tetrahedron *tet;
    VertexIndex v,
                v0,
                v1;

    /*
     *  Initialize all Euler characteristics to zero.
     */

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

        cusp->euler_characteristic = 0;

    /*
     *  It'll be easier to count the edges twice (once from each side)
     *  so compute twice the Euler characteristic and divide by two
     *  at the end.
     */

    /*
     *  Count the vertices in the triangulation of the boundary,
     *  which come from edges in the manifold itself.
     */

    for (edge = manifold->edge_list_begin.next;
            edge != &manifold->edge_list_end;
            edge = edge->next)
    {
        tet = edge->incident_tet;
        v0  =   one_vertex_at_edge[edge->incident_edge_index];
        v1  = other_vertex_at_edge[edge->incident_edge_index];
        tet->cusp[v0]->euler_characteristic += 2;
        tet->cusp[v1]->euler_characteristic += 2;
    }

    /*
     *  Count the edges in the triangulation of the boundary,
     *  which come from faces in the manifold itself.
     */

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

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

            tet->cusp[v]->euler_characteristic -= 3;

    /*
     *  Count the faces in the triangulation of the boundary,
     *  which come from tetrahedra in the manifold itself.
     */

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

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

            tet->cusp[v]->euler_characteristic += 2;

    /*
     *  Divide by two (cf. above).
     */

    for (cusp = manifold->cusp_list_begin.next;
            cusp != &manifold->cusp_list_end;
            cusp = cusp->next)
    {
        if (cusp->euler_characteristic % 2 != 0)
            uFatalError("compute_cusp_Euler_characteristics", "cusps");
        cusp->euler_characteristic /= 2;
    }
}