/* 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); }
/* 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; }
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; }
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; } } }