📄 egmp.c
字号:
/*********************************************************************** File: egmp.c Rev: b-1 Date: 11/25/2000 Copyright (c) 2000, 2001 by David M. Warme************************************************************************ Support routines for the EFST generator that use the GNU Multi-Precision arithmetic library (GMP -- if we have it) to compute certain items with high accuracy.************************************************************************ Modification Log: b-1: 11/25/2000 warme : Created.************************************************************************/#include "config.h"#ifdef HAVE_GMP#include "egmp.h"#include "efst.h"#include "gmp.h"#include "steiner.h"/* * Global Routines */double compute_EFST_length (struct einfo * eip, struct eqp_t * eqpt);void qr3_clear (qr3_t * p);void qr3_init (qr3_t * p);void update_eqpoint_and_displacement (struct einfo * eip, struct eqp_t * eqpk);extern int Multiple_Precision;/* * Local Equates */#define DEBUG_PRINT 0/* * Local Routines */static void compute_eqpoint (struct qr3_point * p, struct einfo * eip, struct eqp_t * eqpk);static void qr3_mul (qr3_t * dst, qr3_t * p1, qr3_t * p2);static double qr3_to_double (qr3_t *);static void r_to_q (mpq_t q_dst, double r_src);/* * A routine to recompute the coordinates of the given eq-point and its * corresponding displacement vector. Using purely floating point * arithmetic, errors tend to accumulate as the eq-point order grows. * Once we are pretty sure that we are going to save this eq-point, we * call this routine that re-computes these quantities correct to within * 1/2 ULP of the floating point arithmetic -- thus preventing the * accumulation of such errors. */ voidupdate_eqpoint_and_displacement (struct einfo * eip, /* IN - EFST generation info */struct eqp_t * eqpk /* IN - eq-point to recompute */){double nx;double ny;double ex;double ey;mpq_t rtmp;struct point * tp;struct qr3_point * ep;qr3_t dv_tmp; mpq_init (rtmp); qr3_init (&dv_tmp); ep = &(eip -> cur_eqp); compute_eqpoint (ep, eip, eqpk);#if DEBUG_PRINT printf ("\nPoint %3d: original = (%24.20f, %24.20f)\n", eqpk -> E.pnum, eqpk -> E.x, eqpk -> E.y);#endif nx = qr3_to_double (&(ep -> x)); ny = qr3_to_double (&(ep -> y));#if DEBUG_PRINT printf ("\tNew: (%24.20f, %24.20f)\n", nx, ny); ex = fabs (nx - eqpk -> E.x) / nx; ey = fabs (ny - eqpk -> E.y) / ny; printf ("\tErrors:\t%d\t(%14g, %14g)\n", eqpk -> S, ex, ey); printf ("\tSymbolic: X = "); mpz_out_str (stdout, 10, mpq_numref (ep -> x.a)); printf (" / "); mpz_out_str (stdout, 10, mpq_denref (ep -> x.a)); printf (" + "); mpz_out_str (stdout, 10, mpq_numref (ep -> x.b)); printf (" * sqrt (3) / "); mpz_out_str (stdout, 10, mpq_denref (ep -> x.b)); printf ("\n"); printf ("\tSymbolic: Y = "); mpz_out_str (stdout, 10, mpq_numref (ep -> y.a)); printf (" / "); mpz_out_str (stdout, 10, mpq_denref (ep -> y.a)); printf (" + "); mpz_out_str (stdout, 10, mpq_numref (ep -> y.b)); printf (" * sqrt (3) / "); mpz_out_str (stdout, 10, mpq_denref (ep -> y.b)); printf ("\n");#endif eqpk -> E.x = nx; eqpk -> E.y = ny; tp = &(eip -> eqp [eqpk -> DV.pnum].E); r_to_q (rtmp, tp -> x); mpq_sub (dv_tmp.a, ep -> x.a, rtmp); mpq_set (dv_tmp.b, ep -> x.b); eqpk -> DV.x = qr3_to_double (&dv_tmp); r_to_q (rtmp, tp -> y); mpq_sub (dv_tmp.a, ep -> y.a, rtmp); mpq_set (dv_tmp.b, ep -> y.b); eqpk -> DV.y = qr3_to_double (&dv_tmp); qr3_clear (&dv_tmp); mpq_clear (rtmp);}/* * Compute the exact coordinates of the given eq-point as a member * of the field Q(sqrt(3)) -- i.e., as (A + B*sqrt(3), C + D*sqrt(3)) * where A, B, C and D are exact rational numbers. */ voidcompute_eqpoint (struct qr3_point * out, /* OUT - eq-point coordinates */struct einfo * eip, /* IN - EFST generation info */struct eqp_t * eqpk /* IN - eq-point to calculate */){int c;int n;struct qr3_point P, Q, R;mpq_t c2, c3, t1, t2; if (eqpk -> L EQ NULL) { /* Base case -- a terminal. */ r_to_q (out -> x.a, eqpk -> E.x); r_to_q (out -> y.a, eqpk -> E.y); mpq_set_ui (out -> x.b, 0, 1); mpq_set_ui (out -> y.b, 0, 1); return; } /* Recurse, let P be the right point, and Q the left eq-point. */ qr3_init (&P.x); qr3_init (&P.y); qr3_init (&Q.x); qr3_init (&Q.y); qr3_init (&R.x); qr3_init (&R.y); compute_eqpoint (&P, eip, eqpk -> R); compute_eqpoint (&Q, eip, eqpk -> L); mpq_sub (R.x.a, Q.x.a, P.x.a); mpq_sub (R.x.b, Q.x.b, P.x.b); mpq_sub (R.y.a, Q.y.a, P.y.a); mpq_sub (R.y.b, Q.y.b, P.y.b); mpz_init_set_ui (mpq_numref (c2), 2); mpz_init_set_ui (mpq_denref (c2), 1); mpz_init_set_ui (mpq_numref (c3), 3); mpz_init_set_ui (mpq_denref (c3), 1); mpq_init (t1); mpq_init (t2); mpq_mul (t1, c3, R.y.b); mpq_sub (t2, R.x.a, t1); mpq_div (t1, t2, c2); mpq_add (out -> x.a, P.x.a, t1); mpq_sub (t1, R.x.b, R.y.a); mpq_div (t2, t1, c2); mpq_add (out -> x.b, P.x.b, t2); mpq_mul (t1, c3, R.x.b); mpq_add (t2, R.y.a, t1); mpq_div (t1, t2, c2); mpq_add (out -> y.a, P.y.a, t1); mpq_add (t1, R.y.b, R.x.a); mpq_div (t2, t1, c2); mpq_add (out -> y.b, P.y.b, t2); mpq_clear (t2); mpq_clear (t1); mpq_clear (c3); mpq_clear (c2); qr3_clear (&R.y); qr3_clear (&R.x); qr3_clear (&Q.y); qr3_clear (&Q.x); qr3_clear (&P.y); qr3_clear (&P.x);}/* * Convert a double to a rational number. */ static voidr_to_q (mpq_t q_dst, /* OUT - rational number */double r_src /* IN - double to convert */){int mant_size;int expon;mpf_t x;mpz_t tmp;mpz_ptr np;mpz_ptr dp; /* First, convert to MP-floating point. */ mpf_init2 (x, 64); mpf_set_d (x, r_src); /* Access mantissa as an mpz_t... */ tmp [0]._mp_alloc = x [0]._mp_prec + 1; tmp [0]._mp_size = x [0]._mp_size; tmp [0]._mp_d = x [0]._mp_d; np = mpq_numref (q_dst); dp = mpq_denref (q_dst); /* Since mantissa is fractional (not an integer), we */ /* must include the mantissa size in the exponent... */ mant_size = x [0]._mp_size; if (mant_size < 0) { mant_size = -mant_size; } expon = x [0]._mp_exp - mant_size; if (expon < 0) { /* Set numerator */ mpz_set (np, tmp); /* Divide by limb-base**(- expon) */ mpz_set_ui (dp, 1); mpz_mul_2exp (dp, dp, 32 * (- expon)); } else { /* Set denominator */ mpz_set_ui (dp, 1); /* Set numerator to proper shift factor. */ mpz_set_ui (np, 1); mpz_mul_2exp (np, np, 32 * expon); /* multiply mantissa in... */ mpz_mul (np, np, tmp); } mpq_canonicalize (q_dst); mpf_clear (x); /* DO NOT clear tmp! */}/* * Initialize an Q(sqrt(3)) number. */ voidqr3_init (qr3_t * p){ mpq_init (p -> a); mpq_init (p -> b);}/* * Clear a Q(sqrt(3)) number. */ voidqr3_clear (qr3_t * p){ mpq_clear (p -> a); mpq_clear (p -> b);}/* * Accurately translate an element Z of Q(sqrt(3)) into a double. * Let Z = A + B * sqrt(3), where A and B are rational. * * Z - A = B * sqrt(3) ==> Z^2 - 2*A*Z + A^2 - 3*B^2 = 0 * * We solve start with a floating point approximation of Z, convert * it to a rational and then apply Newton iterations until we * achieve the desired precision. The termination test is as * follows: * * |Z - (A + B*sqrt(3))| < eps * (A + B*sqrt(3)), * * Z^2 - 2*(A + B*sqrt(3))*Z + A^2 + 2*A*B*sqrt(3) + 3*B^2 * < eps^2 * (A^2 + 2*A*B*sqrt(3) + 3*B^2), * * Z^2 - 2*A*Z + A^2 + 3*B^2 - eps^2*(A^2 + 3*B^2) * < (2*B*Z - (1 - eps^2) 2*A*B) * sqrt(3), * * Z^2 - 2*A*Z + (1 - eps^2)*(A^2 + 3*B^2) * < (Z - (1 - eps^2)*A) * 2 * B * sqrt(3), * * The obvious thing here is to square both sides and compare. * However, one must be careful regarding the signs of the two * sides when doing this. */ static doubleqr3_to_double (qr3_t * p /* IN - A + B*sqrt(3) to convert */){int flag;mpq_t z;mpq_t t1;mpq_t t2;mpq_t t3;mpq_t t4;mpq_t t5;mpq_t t6;mpq_t c_1_minus_eps2;double xf;#define EPS 64 /* relative error of 2^(-EPS) */ if (mpz_sgn (mpq_numref (p -> b)) EQ 0) { return (mpq_get_d (p -> a)); } mpq_init (z); mpq_init (t1); mpq_init (t2); mpq_init (t3); mpq_init (t4); mpq_init (t5); mpq_init (t6); mpq_init (c_1_minus_eps2); /* c_1_minus_eps2 is now 0 / 1. Set it to be */ /* 1 - 2**( - 2 * EPS). */ mpz_mul_2exp (mpq_denref (c_1_minus_eps2), mpq_denref (c_1_minus_eps2), 2*EPS); mpz_sub_ui (mpq_numref (c_1_minus_eps2), mpq_denref (c_1_minus_eps2), 1); /* Compute t1 = (A^2 - 3*B^2) */ mpq_mul (t3, p -> a, p -> a); mpq_mul (t4, p -> b, p -> b); mpz_mul_ui (mpq_numref (t4), mpq_numref (t4), 3); mpq_canonicalize (t4); mpq_sub (t1, t3, t4); /* Compute t2 = (1 - eps^2) * (A^2 + 3*B^2). */ mpq_add (t2, t3, t4); mpq_mul (t2, t2, c_1_minus_eps2); /* Compute t3 = (1 - eps^2)*A. */ mpq_mul (t3, c_1_minus_eps2, p -> a); /* Compute t4 = 12 * B^2 */ mpz_mul_ui (mpq_numref (t4), mpq_numref (t4), 3); mpq_canonicalize (t4); /* Constants for the termination test are now ready. */ /* Time to start up the Newton iteration. */ /* Approximate solution using floating point as a start... */ xf = mpq_get_d (p -> a) + mpq_get_d (p -> b) * sqrt(3.0); /* Put into rational form. */ r_to_q (z, xf); for (;;) { /* Compute Z = new Z, via Newton */ mpq_mul (t5, z, z); mpq_sub (t5, t5, t1); mpq_sub (t6, z, p -> a); mpz_mul_ui (mpq_numref (t6), mpq_numref (t6), 2); mpq_canonicalize (t6); mpq_div (z, t5, t6);#if DEBUG_PRINT printf (" New z is %24.20f\n", mpq_get_d (z));#endif if (Multiple_Precision <= 1) { /* Termination test takes time. At level 1, */ /* we just do 1 Newton iteration and quit. */ break; } /* Now test termination... */ mpq_set (t6, p -> a); mpz_mul_ui (mpq_numref (t6), mpq_numref (t6), 2); mpq_canonicalize (t6); mpq_sub (t5, z, t6); mpq_mul (t5, t5, z); mpq_add (t5, t5, t2); mpq_sub (t6, z, t3); mpq_mul (t6, t6, p -> b); /* Time to check the signs... */ flag = 1; if (mpz_sgn (mpq_numref (t5)) < 0) { if (mpz_sgn (mpq_numref (t6)) >= 0) break; flag = -1; } else if (mpz_sgn (mpq_numref (t6)) < 0) { continue; } mpq_mul (t5, t5, t5); mpq_mul (t6, t6, t6); mpz_mul_ui (mpq_numref (t6), mpq_numref (t6), 12); mpq_canonicalize (t6);#if DEBUG_PRINT printf (" t5 = %24g, t6 = %24g\n", mpq_get_d (t5), mpq_get_d (t6));#endif if (mpq_cmp (t5, t6) * flag < 0) break; } xf = mpq_get_d (z); mpq_clear (c_1_minus_eps2); mpq_clear (t6); mpq_clear (t5); mpq_clear (t4); mpq_clear (t3); mpq_clear (t2); mpq_clear (t1); mpq_clear (z); return (xf);}/* * Routine to compute the length of a given EFST (i.e., Simpson line) * to within 1/2 ULP. (Modulo good behavior of the sqrt() function...) * * The exact position of the eq-point end of the Simpson line has already * been stored in eip -> cur_eqp. */ doublecompute_EFST_length (struct einfo * eip, /* IN - EFST generation info */struct eqp_t * eqpt /* IN - terminal end of Simpson line */){qr3_t x;qr3_t y;double len; qr3_init (&x); qr3_init (&y); r_to_q (x.a, - eqpt -> E.x); r_to_q (y.a, - eqpt -> E.y); mpq_add (x.a, x.a, eip -> cur_eqp.x.a); mpq_add (y.a, y.a, eip -> cur_eqp.y.a); mpq_set (x.b, eip -> cur_eqp.x.b); mpq_set (y.b, eip -> cur_eqp.y.b); qr3_mul (&x, &x, &x); qr3_mul (&y, &y, &y); mpq_add (x.a, x.a, y.a); mpq_add (x.b, x.b, y.b); len = sqrt (qr3_to_double (&x)); qr3_clear (&y); qr3_clear (&x); return (len);}/* * Multiply two elements of Q(sqrt(3)). */ static voidqr3_mul (qr3_t * dst, /* OUT - destination */qr3_t * p1, /* IN - first operand */qr3_t * p2 /* IN - second operand */){mpq_t res_a;mpq_t tmp1;mpq_t tmp2; mpq_init (res_a); mpq_init (tmp1); mpq_init (tmp2); mpq_mul (tmp1, p1 -> a, p2 -> a); mpq_mul (tmp2, p1 -> b, p2 -> b); mpz_mul_ui (mpq_numref (tmp2), mpq_numref (tmp2), 3); mpq_canonicalize (tmp2); mpq_add (res_a, tmp1, tmp2); mpq_mul (tmp1, p1 -> a, p2 -> b); mpq_mul (tmp2, p1 -> b, p2 -> a); mpq_add (dst -> b, tmp1, tmp2); mpq_set (dst -> a, res_a); mpq_clear (tmp2); mpq_clear (tmp1); mpq_clear (res_a);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -