You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

221 lines
6.2 KiB

/*
* tests/pslq_test.cpp
*
* This work was supported by the Director, Office of Science, Division
* of Mathematical, Information, and Computational Sciences of the
* U.S. Department of Energy under contract number DE-AC03-76SF00098.
*
* Copyright (c) 2000-2001
*
* A driver for the pslq program which exercises the double-double and
* quad-double library.
*/
#include <cmath>
#include <iostream>
#include <ctime>
#include <cstring>
#include <limits>
#include <iomanip>
#include <qd/fpu.h>
#include "tictoc.h"
#include "pslq.h"
using std::cout;
using std::cerr;
using std::endl;
using std::strcmp;
int g_verbose = 0;
bool flag_double_pslq = false;
bool flag_dd_pslq = false;
bool flag_qd_pslq = false;
/* Computes the value of the given n-th degree polynomial at point x
where the (n+1) coefficients of the polynomial is given in a. */
template <class T>
T polyeval(T *a, int n, T &x, double &err_bnd) {
/* Use Horner's evaluation scheme. */
T t = a[n];
err_bnd = std::abs(to_double(t)) * 0.5;
for (int i = n-1; i >= 0; i--) {
t *= x;
t += a[i];
err_bnd *= std::abs(to_double(x));
err_bnd += std::abs(to_double(t));
}
err_bnd = (2.0 * err_bnd - to_double(t)) * std::numeric_limits<T>::epsilon();
return t;
}
double nroot(double x, int n) {
return std::pow(x, 1.0 / n);
}
bool is_int(double x) {
return (std::abs(x) <= std::numeric_limits<int>::max() &&
static_cast<int>(x) == x);
}
/* Sets r = 2^(1/p) + 3^(1/q) and tries to recover the algebraic
* polynomial of degree pq performing PSLQ on 1, r, r^2, ..., r^n. */
template <class T>
bool pslq_test(int p, int q, double eps, int max_itr = 100000) {
T *x, *b;
T r = nroot(T(2.0), p) + nroot(T(3.0), q);
T t;
int err;
tictoc tv;
double tm;
int n = p * q + 1;
std::ios_base::fmtflags fmt = cout.flags();
b = new T[n];
x = new T[n];
/* Fill in vector x with powers of r. */
x[0] = 1.0;
x[1] = r;
t = r*r;
for (int i = 2; i < n; i++, t *= r) x[i] = t;
cout << " testing pslq_test(" << p << ", " << q << ") ..." << endl;
if (g_verbose) cout << std::setprecision(std::numeric_limits<T>::digits10) << " r = " << r << endl;
/* Construct algebraic relation */
tic(&tv);
err = pslq<T>(x, n, b, eps, max_itr);
tm = toc(&tv);
cout << " elapsed time = " << std::setprecision(4) << tm << " seconds." << endl;
cout << std::right << std::setprecision(2) << std::fixed;
if (!err) {
if (g_verbose) {
cout << " polynomial: ";
for (int i = 0; i < n; i++) {
if (i > 0) cout << " ";
cout << std::setprecision(0) << std::setw(24) << b[i] << endl;
}
}
/* Check if r satisfies the polynomial. */
double err_bnd;
t = abs(polyeval<T>(b, n-1, r, err_bnd));
err = t > 10.0 * err_bnd;
cout << std::scientific << std::setprecision(4);
if (err || g_verbose) {
cout << " residual = " << t << endl;
cout << " error bound = " << err_bnd << endl;
}
}
delete [] x;
delete [] b;
if (err)
cout << " test FAILED." << endl;
else
cout << " test passed." << endl;
cout << endl;
cout.flags(fmt);
return !err;
}
/* We need this since Sun C++ compiler seems to miscompile when
* eps parameter is given default (templated) argument. */
template <class T>
bool pslq_test(int p, int q) {
return pslq_test<T>(p, q, std::numeric_limits<T>::epsilon());
}
void print_usage() {
cout << "pslq_test [-h] [-n N] [-d] [-dd] [-qd] [-all] [-verbose]" << endl;
cout << " Performs the PSLQ algorithm on 1, r, r^2, ..., r^{n-1}" << endl;
cout << " where r is a root of a constructed integer coefficient" << endl;
cout << " polynomial. PSLQ algorithm should reconstruct the polynomial" << endl;
cout << " in most cases where the degree is not too high and the" << endl;
cout << " polynomial is irreducible over the rationals." << endl;
cout << endl;
cout << " -h -help Print this usage message and exit." << endl;
cout << " -d Perform PSLQ with double precision (53 bit mantissa)." << endl;
cout << " -dd Perform PSLQ with double-double precision." << endl;
cout << " (about 106 bits of significand)." << endl;
cout << " -qd Perform PSLQ with quad-double precision." << endl;
cout << " (about 212 bits of significand). This is the default." << endl;
cout << " -all Perform PSLQ with all three precisions above." << endl;
cout << " -verbose" << endl;
cout << " -v Increase verbosity." << endl;
}
int main(int argc, char **argv) {
char *arg;
/* Parse the command-line arguments. */
for (int i = 1; i < argc; i++) {
arg = argv[i];
if (strcmp(arg, "-h") == 0 || strcmp(arg, "-help") == 0) {
print_usage();
return 0;
} else if (strcmp(arg, "-d") == 0) {
flag_double_pslq = true;
} else if (strcmp(arg, "-dd") == 0) {
flag_dd_pslq = true;
} else if (strcmp(arg, "-qd") == 0) {
flag_qd_pslq = true;
} else if (strcmp(arg, "-all") == 0) {
flag_double_pslq = flag_dd_pslq = flag_qd_pslq = true;
} else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0) {
g_verbose++;
} else {
cerr << "Unknown flag `" << arg << "'." << endl;
}
}
if (!flag_double_pslq && !flag_dd_pslq && !flag_qd_pslq) {
flag_dd_pslq = true;
flag_qd_pslq = true;
}
unsigned int old_cw;
fpu_fix_start(&old_cw);
bool pass = true;
if (flag_double_pslq) {
cout << "Performing double-precision PSLQ." << endl;
pass &= pslq_test<double>(2, 2);
pass &= pslq_test<double>(3, 2);
}
if (flag_dd_pslq) {
cout << "Performing double-double precision PSLQ." << endl;
pass &= pslq_test<dd_real>(2, 2);
pass &= pslq_test<dd_real>(2, 3);
pass &= pslq_test<dd_real>(2, 4);
pass &= pslq_test<dd_real>(3, 3);
pass &= pslq_test<dd_real>(2, 5);
}
if (flag_qd_pslq) {
cout << "Performing quad-double precision PSLQ." << endl;
pass &= pslq_test<qd_real>(3, 3);
pass &= pslq_test<dd_real>(2, 5);
pass &= pslq_test<qd_real>(4, 3);
pass &= pslq_test<qd_real>(2, 6);
pass &= pslq_test<qd_real>(2, 7);
pass &= pslq_test<qd_real>(3, 5);
}
if (pass)
cout << "All tests passed." << endl;
else
cout << "Some tests FAILED." << endl;
fpu_fix_end(&old_cw);
return (pass ? 0 : 1);
}