Ejemplo n.º 1
0
/* Based on http://en.wikipedia.org/wiki/Quartic_function#Quick_and_memorable_solution_from_first_principles */
static int solve_depressed_quartic(const complex_t* poly, complex_t* results) {
    complex_t helper_cubic[4];
    complex_t helper_results[3];
    complex_t quadratic_factor[3];
	complex_t p, c_plus_p_sqr, d_div_p;
    const complex_t e = poly[0];
    const complex_t d = poly[1];
    const complex_t c = poly[2];
	int num_results;
    if (complex_eq(d, complex_from_real(0.0))) {
		int i, num_quad_results;
        complex_t quadratic[3];
        complex_t quadratic_results[2];
        quadratic[0] = e;
        quadratic[1] = c;
        quadratic[2] = complex_from_real(1.0);
        num_quad_results = solve_poly(2, quadratic, quadratic_results);
        for (i = 0; i < num_quad_results; ++i) {
            const complex_t s = complex_sqrt(quadratic_results[i]);
            results[2*i] = complex_negate(s);
            results[2*i + 1] = s;
        }
        return 2 * num_quad_results;
    }
    helper_cubic[0] = complex_negate(complex_mult(d, d));
    helper_cubic[1] = complex_add(complex_mult(c, c), complex_mult_real(-4.0, e));
    helper_cubic[2] = complex_mult_real(2.0, c);
    helper_cubic[3] = complex_from_real(1.0);
    if (solve_poly(3, helper_cubic, helper_results) < 1)
        return 0;
    p = complex_sqrt(helper_results[0]);
    c_plus_p_sqr = complex_add(c, complex_mult(p, p));
    d_div_p = complex_div(d, p);
    quadratic_factor[0] = complex_add(c_plus_p_sqr, complex_negate(d_div_p));
    quadratic_factor[1] = complex_mult_real(2.0, p);
    quadratic_factor[2] = complex_from_real(2.0);
    num_results = solve_poly(2, quadratic_factor, results);
    quadratic_factor[0] = complex_add(c_plus_p_sqr, d_div_p);
    quadratic_factor[1] = complex_negate(quadratic_factor[1]);
    return num_results + solve_poly(2, quadratic_factor, results + num_results);
}
Ejemplo n.º 2
0
/* Based on http://en.wikipedia.org/wiki/Cubic_equation#Cardano.27s_method */
static int solve_depressed_cubic(const complex_t* poly, complex_t* results) {
    const complex_t q = poly[0];
    const complex_t p = poly[1];
    complex_t t, u, cubic_root_of_unity;
    int i;
    if (complex_eq(p, complex_from_real(0.0))) {
        results[0] = complex_pow_real(complex_negate(q), 1.0/3.0);
        return 1;
    }
    t = complex_add(
        complex_mult_real(0.25, complex_mult(q, q)),
        complex_mult_real(1.0/27.0, complex_mult(p, complex_mult(p, p))));
    cubic_root_of_unity.real = -0.5;
    cubic_root_of_unity.imag = 0.5 * sqrt(3.0);
    for (i = 0; i < 3; ++i) {
        if (i == 0)
            u = complex_pow_real(complex_add(complex_mult_real(-0.5, q), complex_sqrt(t)), 1.0/3.0);
        else
            u = complex_mult(u, cubic_root_of_unity);
        results[i] = complex_add(u, complex_div(p, complex_mult_real(-3.0, u)));
    }
    return 3;
}
Ejemplo n.º 3
0
static int solve_depressed_quadratic(const complex_t* poly, complex_t* results) {
    const complex_t t = complex_sqrt(complex_negate(poly[0]));
    results[0] = complex_negate(t);
    results[1] = t;
    return 2;
}
Ejemplo n.º 4
0
void compute_core_geodesic(
    Cusp    *cusp,
    int     *singularity_index,
    Complex length[2])
{
    int         i;
    long int    positive_d,
                negative_c;
    Real      pi_over_n;

    /*
     *  If the Cusp is unfilled or the Dehn filling coefficients aren't
     *  integers, then just write in some zeros (as explained at the top
     *  of this file) and return.
     */

    if (cusp->is_complete == TRUE
     || Dehn_coefficients_are_integers(cusp) == FALSE)
    {
        *singularity_index  = 0;
        length[ultimate]    = Zero;
        length[penultimate] = Zero;

        return;
    }

    /*
     *  The euclidean_algorithm() will give the singularity index
     *  directly (as the g.c.d.), and the coefficients lead to the
     *  complex length (cf. the explanation at the top of this file).
     */

    *singularity_index = euclidean_algorithm(
                            (long int) cusp->m,
                            (long int) cusp->l,
                            &positive_d,
                            &negative_c);

    for (i = 0; i < 2; i++)     /* i = ultimate, penultimate */
    {
        /*
         *  length[i] = c H(m) + d H(l)
         *
         *  (The holonomies are already in logarithmic form.)
         */
        length[i] = complex_plus(
                complex_real_mult(
		    (Real) (double)(- negative_c),
                    cusp->holonomy[i][M]
                ),
                complex_real_mult(
		    (Real) (double) positive_d,
                    cusp->holonomy[i][L]
                )
            );

        /*
         *  Make sure the length is positive.
         */
        if (length[i].real < 0.0)
            length[i] = complex_negate(length[i]);

        /*
         *  We want to normalize the torsion to the range
         *  [-pi/n + epsilon, pi/n + epsilon], where n is
         *  the order of the singular locus.
         */

        pi_over_n = PI / *singularity_index;

        while (length[i].imag < - pi_over_n + TORSION_EPSILON)
            length[i].imag += 2.0 * pi_over_n;

        while (length[i].imag >   pi_over_n + TORSION_EPSILON)
            length[i].imag -= 2.0 * pi_over_n;

        /*
         *  In the case of a Klein bottle cusp, H(m) will be purely
         *  rotational and H(l) will be purely translational
         *  (cf. the documentation at the top of holonomy.c).
         *  But the longitude used in practice is actually the
         *  double cover of the true longitude, so we have to
         *  divide the core_length by two to compensate.
         */
        if (cusp->topology == Klein_cusp)
            length[i].real /= 2.0;

    }
}
Ejemplo n.º 5
0
static void initial_tetrahedron(
	Triangulation	*manifold,
	Tetrahedron		**initial_tet,
	Boolean			compute_corners,
	Boolean			centroid_at_origin)
{
	VertexIndex	v[4];
	Complex		z,
				sqrt_z,
				w[4];
	Tetrahedron	*tet;
	EdgeIndex	best_edge,
				edge;

	/*
	 *	Set a default choice of tetrahedron and edge.
	 */
	*initial_tet = manifold->tet_list_begin.next;
	best_edge = 0;

	/*
	 *	2000/02/11 JRW  Can we choose the initial tetrahedron in such
	 *	a way that if we happen to have the canonical triangulation
	 *	of a 2-bridge knot or link complement, the basepoint falls
	 *	at a center of D2 symmetry?  That is, can we find a Tetrahedron
	 *	that looks like the "top of the tower" in the canonical
	 *	triangulation of a 2-bridge knot or link complement?
	 */
	for (tet = manifold->tet_list_begin.next;
		 tet != &manifold->tet_list_end;
		 tet = tet->next)
		for (edge = 0; edge < 6; edge++)
			if (tet->neighbor[one_face_at_edge  [edge]]
			 == tet->neighbor[other_face_at_edge[edge]])
			{
				*initial_tet	= tet;
				best_edge		= edge;
			}

	if (compute_corners)
	{
		if (centroid_at_origin == TRUE)
		{
			/*
			 *	Proposition.  For any value of w, positioning the corners at
			 *
			 *				corner[0] =  w
			 *				corner[1] =  w^-1
			 *				corner[2] = -w^-1
			 *				corner[3] = -w
			 *
			 *	defines a tetrahedron with its centroid at the "origin" and
			 *	the common perpendiculars between pairs of opposite edges
			 *	coincident with the "coordinate axes".  [In the Klein model,
			 *	the tetrahedron is inscribed in a rectangular box whose faces
			 *	are parallel to the coordinate axes.]
			 *
			 *	Proof:  Use the observation that the line from a0 to a1 will
			 *	intersect the line from b0 to b1 iff the cross ratio
			 *
			 *				(b0 - a0) (b1 - a1)
			 *				-------------------
			 *				(b1 - a0) (b0 - a1)
			 *
			 *	of the tetrahedron they span is real, and they will be
			 *	orthogonal iff the cross ratio is -1.
			 *
			 *	[-w, w] is orthogonal to [0, infinity] because
			 *
			 *				(0 - -w) (infinity - w)
			 *				----------------------- = -1
			 *				(infinity - -w) (0 - w)
			 *
			 *	and similarly for [-w^-1, w^-1] and [0, infinity].
			 *
			 *	[w^-1, w] is orthogonal to [-1, 1] because
			 *
			 *				(-1 - w^-1) (1 - w)
			 *				------------------- = -1
			 *				(1 - w^-1) (-1 - w)
			 *
			 *	and similarly for [-w^-1, -w] and [-1, 1].
			 *
			 *	[-w^-1, w] is orthogonal to [-i, i] because
			 *
			 *				(-i - -w^-1) (i - w)
			 *				-------------------- = -1
			 *				(i - -w^-1) (-i - w)
			 *
			 *	and similarly for [w^-1, -w] and [-i, i].
			 *
			 *	Q.E.D.
			 *
			 *
			 *	The tetrahedron will have the correct cross ratio z iff
			 *
			 *			(w - -w^-1) (w^-1 -   -w )    (w + w^-1)^2
			 *		z = -------------------------- = --------------
			 *			(w -   -w ) (w^-1 - -w^-1)         4
			 *
			 *	Solving for w in terms of z gives the four possibilities
			 *
			 *		w = +- (sqrt(z) +- sqrt(z - 1))
			 *
			 *	Note that sqrt(z) + sqrt(z - 1) and sqrt(z) - sqrt(z - 1) are
			 *	inverses of one another.  We can choose any of the four solutions
			 *	to be "w", and the other three will automatically become w^-1,
			 *	-w, and -w^-1.
			 *
			 *	Comment:  This position for the initial corners brings out
			 *	nice numerical properties in the O(3,1) matrices for manifolds
			 *	composed of regular ideal tetrahedra (cf. the proofs in the
			 *	directory "Tilings of H^3", which aren't part of SnapPea, but
			 *	I could give you a copy).
			 */

			z = (*initial_tet)->shape[filled]->cwl[ultimate][0].rect;

			w[0] = complex_plus(
					complex_sqrt(z),
					complex_sqrt(complex_minus(z, One))
				);
			w[1] = complex_div(One, w[0]);
			w[2] = complex_negate(w[1]);
			w[3] = complex_negate(w[0]);

			(*initial_tet)->corner[0] = w[0];
			(*initial_tet)->corner[1] = w[1];
			(*initial_tet)->corner[2] = w[2];
			(*initial_tet)->corner[3] = w[3];
		}
		else
		{
			/*
			 *	Originally this code positioned the Tetrahedron's vertices
			 *	at {0, 1, z, infinity}.  As of 2000/02/04 I modified it
			 *	to put the vertices at {0, 1/sqrt(z), sqrt(z), infinity} instead,
			 *	so that the basepoint (0,0,1) falls at the midpoint
			 *	of the edge extending from 0 to infinity, and the
			 *	tetrahedron's symmetry axis lies parallel to the x-axis.
			 *	To convince yourself that the tetrahedron's axis of
			 *	symmetry does indeed pass through that point, note
			 *	that a half turn around the axis of symmetry factors
			 *	as a reflection in the plane |z| = 1 followed by
			 *	a reflection in the vertical plane sitting over x-axis.
			 */

			/*
			 *	Order the vertices so that the tetrahedron is positively
			 *	oriented, and the selected edge is between vertices
			 *	v[0] and v[1].
			 */
			v[0] = one_vertex_at_edge[best_edge];
			v[1] = other_vertex_at_edge[best_edge];
			v[2] = remaining_face[v[1]][v[0]];
			v[3] = remaining_face[v[0]][v[1]];

			/*
			 *	Set the coordinates of the corners.
			 */

			z = (*initial_tet)->shape[filled]->cwl[ultimate][edge3[best_edge]].rect;
			sqrt_z = complex_sqrt(z);

			(*initial_tet)->corner[v[0]] = Infinity;
			(*initial_tet)->corner[v[1]] = Zero;
			(*initial_tet)->corner[v[2]] = complex_div(One, sqrt_z);
			(*initial_tet)->corner[v[3]] = sqrt_z;
		}
	}
}