Commit e527067b authored by Jason Rhinelander's avatar Jason Rhinelander

1.0.3: --linear option; version/licence info

- Added --linear flag to fdpval and fdcrit that uses linear B
  interpolation between the two nearest dataset B values instead of the
  5-9 nearest points for a quadratic interpolation.
- Added version and licence info to fdpval and fdcrit output
- Added version variables to library (in fracdist/version.hpp, .cpp)
- Removed md5sum check from mn-files.zip download; it broke every time
  the data set download changed (from the recent fracdist.f fixes),
  making the package unbuildable.
- Renamed parse-vals.hpp to cli-common.hpp, and moved some more
  fdpval.cpp/fdcrit.cpp common code into it.
parent bb3971f0
# Major version changes (for change details, see https://github.com/jagerman/fracdist)
## 1.0.3
- Added --linear flag to fdpval and fdcrit that uses linear B interpolation
between the two nearest dataset B values instead of the 5-9 nearest points
for a quadratic interpolation.
- Added version and licence info to fdpval and fdcrit output
- Added version variables to library (in fracdist/version.hpp, .cpp)
- Removed md5sum check from mn-files.zip download; it broke every time the data
set download changed (from the recent fracdist.f fixes), making the package
unbuildable.
## 1.0.2
- Added versioning to libfracdist shared library.
......
......@@ -4,7 +4,7 @@ project(fracdist CXX)
set(fracdist_VMAJ 1)
set(fracdist_VMIN 0)
set(fracdist_VPAT 2)
set(fracdist_VPAT 3)
set(fracdist_description "fractional unit roots/cointegration pvalue and critical value finder")
set(fracdist_author "Jason Rhinelander <jason@imaginary.ca>")
set(fracdist_homepage "https://github.com/jagerman/fracdist")
......@@ -47,7 +47,7 @@ if (MINGW)
endif()
message(STATUS "Downloading ${fracdist_data_url}...")
file(DOWNLOAD "${fracdist_data_url}" "${fracdist_data_zip}" STATUS zipdl_stat EXPECTED_MD5 "506e607bbabc99ba796bd80c766e8f27")
file(DOWNLOAD "${fracdist_data_url}" "${fracdist_data_zip}" STATUS zipdl_stat)
list(GET zipdl_stat 0 errcode)
if (errcode)
......@@ -71,7 +71,7 @@ add_custom_command(OUTPUT ${fracdist_datafiles}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data"
COMMENT "Extracting data/frcapp*.txt, frmapp*.txt from ${fracdist_data_zip_basename}")
foreach(hpp fracdist/common.hpp fracdist/pvalue.hpp fracdist/critical.hpp)
foreach(hpp fracdist/common.hpp fracdist/pvalue.hpp fracdist/critical.hpp fracdist/version.hpp)
list(APPEND fracdist_headers "${CMAKE_CURRENT_SOURCE_DIR}/${hpp}")
endforeach()
list(APPEND fracdist_headers "${CMAKE_CURRENT_BINARY_DIR}/fracdist/data.hpp")
......@@ -87,6 +87,9 @@ add_custom_command(OUTPUT ${fracdist_data_generated}
)
add_custom_target(data DEPENDS ${fracdist_data_generated})
configure_file("${CMAKE_SOURCE_DIR}/fracdist/version.cpp.in" "${CMAKE_BINARY_DIR}/fracdist/version.cpp")
list(APPEND fracdist_source "${CMAKE_BINARY_DIR}/fracdist/version.cpp")
# Try to find Eigen3 on the system first; if that fails, use the included copy
find_package(Eigen3)
if (NOT EIGEN3_FOUND)
......
#pragma once
#include <cerrno>
#include <cstring>
#include <fracdist/version.hpp>
#include <unordered_set>
#include <list>
#include <string>
#include <algorithm>
#include <cctype>
#include <utility>
#include <iostream>
#define PRINT_ERROR(fmt, ...) do { fprintf(stderr, "\n" fmt "\n\n", ##__VA_ARGS__); help(argv[0]); return 3; } while(0)
#define RETURN_ERROR(fmt, ...) do { PRINT_ERROR(fmt, ##__VA_ARGS__); return 3; } while(0)
// Calls a std::stod, std::stoul, etc. type function and returns false if the parse fails. VAR,
// which must be predeclared, will be set to the parsed value after this macro's code.
#define PARSE(VAR, STOTYPE, ...) \
try {\
size_t endpos;\
VAR = STOTYPE(arg, &endpos, ##__VA_ARGS__);\
if (endpos != arg.length()) throw std::invalid_argument("Match did not use whole string");\
}\
catch (...) { return false; }
// Parses a double. Returns true if the entire argument could be parsed as a double, false otherwise.
inline bool parse_double(const std::string &arg, double &result) {
PARSE(result, std::stod);
return true;
}
// Parses an unsigned int. 0 if the entire argument could be parsed as an unsigned int, 1 otherwise.
inline bool parse_uint(const std::string &arg, unsigned int &result) {
unsigned long parsed;
PARSE(parsed, std::stoul, 10);
if (parsed > std::numeric_limits<unsigned int>::max())
return false;
result = (unsigned int) parsed;
return true;
}
#undef PARSE
inline void uc(std::string &lc) {
std::transform(lc.begin(), lc.end(), lc.begin(), std::ptr_fun<int, int>(std::toupper));
}
// Parses a boolean value. Accepted values: 0, 1, (case-insensitive) t, true, f, false
// Returns true on successful parse, false on failure.
inline bool parse_bool(std::string arg, bool &result) {
uc(arg);
if (arg == "1" || arg == "TRUE" || arg == "T") {
result = true;
return true;
}
else if (arg == "0" || arg == "FALSE" || arg == "F") {
result = false;
return true;
}
return false;
}
// Parses the Q, B, and C values and removes them from the argument list
#define PARSE_Q_B_C \
success = parse_uint(args.front().c_str(), q);\
if (not success or q < 1 or q > fracdist::q_length)\
RETURN_ERROR("Invalid q value ``%s''", args.front().c_str());\
args.pop_front();\
\
success = parse_double(args.front().c_str(), b);\
if (not success or b < fracdist::bvalues.front() or b > fracdist::bvalues.back())\
RETURN_ERROR("Invalid b value ``%s''", args.front().c_str());\
args.pop_front();\
\
success = parse_bool(args.front().c_str(), constant);\
if (not success)\
RETURN_ERROR("Invalid constant value ``%s''", args.front().c_str());\
args.pop_front();
inline int print_version(const char *program) {
fprintf(stderr,
"%s (fracdist) %s\n"
"Copyright (C) 2014 Jason Rhinelander\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n\n",
program, fracdist::version_string);
return 10;
}
/// Returns true if the given `args` contains any of the values in `find`
inline bool arg_match(const std::list<std::string> &args, const std::unordered_set<std::string> &find) {
for (auto &arg : args) {
if (find.count(arg))
return true;
}
return false;
}
/// Like arg_match, but removes the first matched argument from `args`.
inline bool arg_remove(std::list<std::string> &args, const std::unordered_set<std::string> &remove) {
for (auto it = args.begin(); it != args.end(); it++) {
if (remove.count(*it)) {
args.erase(it);
return true;
}
}
return false;
}
......@@ -7,21 +7,13 @@
* This is a simple wrapper around the fracdist_critical call and does not support the alternative
* functionality available through fracdist::critical_advanced().
*/
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <fracdist/critical.hpp>
#include "parse-vals.hpp"
#include <iostream>
#define HELP help(argv[0])
#define ERROR(fmt, ...) error_with_help(fmt, argv[0], ##__VA_ARGS__)
#include "cli-common.hpp"
/** Prints a help message to stderr, returns 1 (to be returned by main()). */
int help(const char *arg0) {
fprintf(stderr, "\n"
"Usage: %s Q B C P [P ...]\n\n"
"Usage: %s Q B C P [P ...] [--linear|-l]\n\n"
"Estimates a p-value for the test statistic(s) T.\n\n"
"Q is the q value, which must be an integer between 1 and %zd, inclusive.\n\n"
......@@ -36,55 +28,70 @@ int help(const char *arg0) {
"least one test level is required, and all test levels must satisfy 0 <= P <= 1.\n\n"
"Critical values will be output one-per-line in the same order as the given\n"
"values of P\n\n",
"values of P\n\n"
"If the optional --linear (or -l) argument is given, linear interpolation of the\n"
"two closest dataset B values is used and exact values are used for exact B\n"
"value matches. The default, when --linear is not given, uses a quadratic\n"
"approximation of nearby B values (even when the value of B exactly matches the\n"
"data set).\n\n",
arg0, fracdist::q_length, fracdist::bvalues.front(), fracdist::bvalues.back());
print_version("fdcrit");
return 2;
}
int error_with_help(const char *errfmt, const char* arg0, ...) {
va_list arg;
va_start(arg, arg0);
fprintf(stderr, "\n");
vfprintf(stderr, errfmt, arg);
va_end(arg);
help(arg0);
return 3;
}
int main(int argc, char *argv[]) {
double b;
std::vector<double> levels;
size_t num_levels;
std::list<double> levels;
unsigned int q;
bool constant;
if (argc >= 5) {
int fail;
num_levels = argc-4;
std::list<std::string> args;
for (int i = 1; i < argc; i++) {
args.push_back(argv[i]);
}
if (arg_match(args, {"--help", "-h", "-?"}))
return help(argv[0]);
else if (arg_match(args, {"--version", "-v"}))
return print_version("fdcrit");
bool linear_interp = arg_remove(args, {"--linear", "-l"});
if (args.size() >= 4) {
bool success;
PARSE_Q_B_C;
for (size_t i = 0; i < num_levels; i++) {
while (not args.empty()) {
std::string arg = args.front();
args.pop_front();
double d;
fail = parse_double(argv[4+i], d);
if (fail)
return ERROR("Invalid test level ``%s''", argv[4+i]);
success = parse_double(arg.c_str(), d);
if (not success)
RETURN_ERROR("Invalid test level ``%s''", arg.c_str());
if (d < 0 || d > 1)
return ERROR("Invalid test level ``%s'': value must be between 0 and 1", argv[4+i]);
RETURN_ERROR("Invalid test level ``%s'': value must be between 0 and 1", arg.c_str());
levels.push_back(d);
}
}
// No arguments; output the help.
else if (args.empty()) {
return help(argv[0]);
}
else {
return ERROR("Invalid number of arguments");
RETURN_ERROR("Invalid arguments");
}
for (auto &d : levels) {
double r;
try {
r = fracdist::critical(d, q, b, constant);
r = fracdist::critical_advanced(d, q, b, constant,
linear_interp ? fracdist::interpolation::linear : fracdist::interpolation::JGMMON14, 9);
} catch (std::exception &e) {
return ERROR("An error occured: %s", e.what());
RETURN_ERROR("An error occured: %s", e.what());
}
if (std::isinf(r))
......
......@@ -7,20 +7,13 @@
* This is a simple wrapper around the fracdist_pvalue call and does not support the alternative
* functionality available through fracdist_pvalue_advanced().
*/
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <fracdist/pvalue.hpp>
#include "parse-vals.hpp"
#define HELP help(argv[0])
#define ERROR(fmt, ...) error_with_help(fmt, argv[0], ##__VA_ARGS__)
#include "cli-common.hpp"
/** Prints a help message to stderr, returns 1 (to be returned by main()). */
int help(const char *arg0) {
fprintf(stderr, "\n"
"Usage: %s Q B C T [T ...]\n\n"
"Usage: %s Q B C T [T ...] [--linear|-l]\n\n"
"Estimates a p-value for the test statistic(s) T.\n\n"
"Q is the q value, which must be an integer between 1 and %zd, inclusive.\n\n"
......@@ -35,56 +28,69 @@ int help(const char *arg0) {
"At least one test statistic is required, an all T values must be >= 0.\n\n"
"P-values will be output one-per-line in the same order as the given values of\n"
"T.\n\n",
"T.\n\n"
"If the optional --linear (or -l) argument is given, linear interpolation of the\n"
"two closest dataset B values is used and exact values are used for exact B\n"
"value matches. The default, when --linear is not given, uses a quadratic\n"
"approximation of nearby B values (even when the value of B exactly matches the\n"
"data set).\n\n",
arg0, fracdist::q_length, fracdist::bvalues.front(), fracdist::bvalues.back());
print_version("fdpval");
return 2;
}
int error_with_help(const char *errfmt, const char* arg0, ...) {
va_list arg;
va_start(arg, arg0);
fprintf(stderr, "\n");
vfprintf(stderr, errfmt, arg);
va_end(arg);
help(arg0);
return 3;
}
int main(int argc, char *argv[]) {
double b;
std::vector<double> tests;
size_t num_tests;
std::list<double> tests;
unsigned int q;
bool constant;
if (argc >= 5) {
int fail;
num_tests = argc-4;
std::list<std::string> args;
for (int i = 1; i < argc; i++) {
args.push_back(argv[i]);
}
if (arg_match(args, {"--help", "-h", "-?"}))
return help(argv[0]);
else if (arg_match(args, {"--version", "-v"}))
return print_version("fdpval");
bool linear_interp = arg_remove(args, {"--linear", "-l"});
if (args.size() >= 4) {
bool success;
PARSE_Q_B_C;
for (size_t i = 0; i < num_tests; i++) {
while (not args.empty()) {
std::string arg = args.front();
args.pop_front();
double d;
fail = parse_double(argv[4+i], d);
if (fail)
return ERROR("Invalid test statistic ``%s''", argv[4+i]);
success = parse_double(arg.c_str(), d);
if (not success)
RETURN_ERROR("Invalid test statistic ``%s''", arg.c_str());
if (d < 0)
return ERROR("Invalid test statistic ``%s'': value must be >= 0", argv[4+i]);
RETURN_ERROR("Invalid test statistic ``%s'': value must be >= 0", arg.c_str());
tests.push_back(d);
}
}
// No arguments; output the help.
else if (args.empty()) {
return help(argv[0]);
}
else {
return ERROR("Invalid number of arguments");
RETURN_ERROR("Invalid arguments");
}
for (size_t i = 0; i < num_tests; i++) {
for (auto &t : tests) {
double r;
try {
r = fracdist::pvalue(tests[i], q, b, constant);
r = fracdist::pvalue_advanced(t, q, b, constant,
linear_interp ? fracdist::interpolation::linear : fracdist::interpolation::JGMMON14, 9);
} catch (std::exception &e) {
return ERROR("An error occured: %s", e.what());
RETURN_ERROR("An error occured: %s", e.what());
}
printf("%.7g\n", r);
......
#include <fracdist/version.hpp>
namespace fracdist {
const unsigned int version_major = @fracdist_VMAJ@;
const unsigned int version_minor = @fracdist_VMIN@;
const unsigned int version_patch = @fracdist_VPAT@;
const char *version_string = "@fracdist_VMAJ@.@fracdist_VMIN@.@fracdist_VPAT@";
}
#pragma once
namespace fracdist {
/** The major version of this fracdist release. `1` for version 1.2.3. */
extern const unsigned int version_major;
/** The minor version of this fracdist release. `2` for version 1.2.3. */
extern const unsigned int version_minor;
/** The minor version of this fracdist release. `3` for version 1.2.3. */
extern const unsigned int version_patch;
/** The full, three-part version value as a string. The string `1.2.3` for version 1.2.3. */
extern const char *version_string;
}
#pragma once
#include <cmath>
#include <cerrno>
#include <climits>
#include <cstdbool>
#include <cstdio>
#include <cstdlib>
#include <cstring>
// Calls a strtod, strtoul, etc. type function and returns 1 if parse fails. parsed will be set to
// the parsed value after this macro's code.
#define PARSE(TYPE, strtotype, ...) \
errno = 0;\
char *endptr;\
TYPE parsed = strtotype(arg, &endptr, ##__VA_ARGS__);\
if (errno || endptr[0] != 0) {\
return 1;\
}
// Parses a double. Returns 0 if the entire argument could be parsed as a double, 1 otherwise.
inline int parse_double(const char *arg, double &result) {
PARSE(double, strtod);
result = parsed;
return 0;
}
// Parses an unsigned int. 0 if the entire argument could be parsed as an unsigned int, 1 otherwise.
inline int parse_uint(const char *arg, unsigned int &result) {
PARSE(unsigned long, strtoul, 10);
if (parsed > UINT_MAX) {
return 1;
}
result = (unsigned int) parsed;
return 0;
}
// Parses a boolean value. Accepted values: 0, 1, (case-insensitive) t, true, f, false
inline int parse_bool(const char *arg, bool &result) {
if (!strcmp("1", arg) || !strcasecmp("TRUE", arg) || !strcasecmp("T", arg)) {
result = true;
return 0;
}
else if (!strcmp("0", arg) || !strcasecmp("FALSE", arg) || !strcasecmp("F", arg)) {
result = false;
return 0;
}
return 1;
}
#undef PARSE
#define PARSE_Q_B_C \
fail = parse_uint(argv[1], q);\
if (fail || q < 1 || q > fracdist::q_length)\
return ERROR("Invalid q value ``%s''", argv[1]);\
\
fail = parse_double(argv[2], b);\
if (fail || b < fracdist::bvalues.front() || b > fracdist::bvalues.back())\
return ERROR("Invalid b value ``%s''", argv[2]);\
\
fail = parse_bool(argv[3], constant);\
if (fail)\
return ERROR("Invalid constant value ``%s''", argv[3]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment