Skip to content

Commit

Permalink
1.0.3: --linear option; version/licence info
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
jagerman committed Apr 30, 2014
1 parent bb3971f commit e527067
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 136 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# 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.
Expand Down
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <[email protected]>")
set(fracdist_homepage "https://github.com/jagerman/fracdist")
Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand All @@ -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)
Expand Down
112 changes: 112 additions & 0 deletions cli-common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#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;
}

77 changes: 42 additions & 35 deletions fdcrit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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))
Expand Down
Loading

0 comments on commit e527067

Please sign in to comment.