A
Ambush Commander
A while back I posted a Fibonacci sequence calculator on this group,
and asked people to critique it. I received many helpful comments, and
made the program a lot better.
Now, I've converted it to use GMP so there's a bit of pointer work and
dynamic memory allocation going on. Can I, once again, have comments
on whether or not the code does "good practices", and where it could
be better? Thanks!
P.S. I am especially interested in comments for the fibonacci()
function; not so much for the processArgs() one.
----
/**
* Exercise #2.1
*
* Create a program that takes one argument and prints the
corresponding
* term from the Fibonacci series. There are also a number of extra
* features.
*/
#include <iostream>
#include <vector>
#include <boost/program_options.hpp>
#include <gmp.h>
/**
* Defines a whole number type (non-negative integer) that indexes the
* Fibonacci sequence. This type determines the nth term we can
calculate,
* though it has no bearing on the actual value of that term.
*/
typedef unsigned long WholeNumber;
/**
* Recursively calculates a term of the Fibonacci series.
* @param result Variable to write result to, no pointer necessary
* @param term Index of term in sequence
* @todo Convert code to use C++ GMP API
* @warning There is no way to free the internal cache
* @note I'm doing something very funky here: I'm using malloc in
order
to get the pointers to the arrays to persist... but GMP
also
has this sort of stuff going behind the scenes! So malloc
is being called twice! I don't know how to get around
this.
*/
void fibonacci(mpz_t result, WholeNumber term) {
static std::vector<mpz_t*> cache;
if (cache.empty()) {
// initialize cache with some (required) preloaded values
mpz_t *val0 = (mpz_t*) malloc(sizeof(mpz_t));
mpz_t *val1 = (mpz_t*) malloc(sizeof(mpz_t));
mpz_init(*val0);
cache.push_back(val0);
mpz_init(*val1);
mpz_set_ui(*val1, 1);
cache.push_back(val1);
}
if (cache.size() > term) {
// cache hit, return that value
mpz_init(result);
mpz_set(result, *cache[term]);
} else {
// cache miss, generate it
mpz_t val1, val2; // previous two values
fibonacci(val2, term - 2);
fibonacci(val1, term - 1);
mpz_t *valr = (mpz_t*) malloc(sizeof(mpz_t));
mpz_init(*valr);
mpz_add(*valr, val2, val1);
cache.push_back(valr);
mpz_init(result);
mpz_set(result, *valr);
}
}
/**
* Parses command line options, possibly returning messages
* @param vm Pointer to map to store argument information in
* @param argc Argument count
* @param argv Argument values
* @return Error code, or 1 to continue
*/
int processArgs(boost:rogram_options::variables_map *vm, int argc,
char *argv[]) {
using namespace boost:rogram_options;
using std::cout;
options_description desc("Options");
desc.add_options()
("help,h", "produce help message")
("list,l", value<WholeNumber>(), "list several Fibonnaci
numbers")
("term,t", value<WholeNumber>(), "print the nth term of the
series")
("nlsep,n", "format output with newlines, not commas")
;
positional_options_description p;
p.add("term", -1);
store(command_line_parser(argc,
argv).options(desc).positional(p).run(), *vm);
notify(*vm);
if (argc == 1 || vm->count("help")) {
cout << "\nFibonacci Numbers Generator\n By Edward Z. Yang\n
\n";
cout << "Usage: fibonacci [options] [term]\n";
cout << desc << "\n";
return 1;
}
return 0;
}
/**
* Main body
*/
int main(int argc, char *argv[]) {
using std::cout;
using std::string;
boost:rogram_options::variables_map vm;
int failed = processArgs(&vm, argc, argv);
if (failed) return failed;
if (vm.count("list")) {
std::string sep;
if (vm.count("nlsep")) {
sep = "\n";
} else {
sep = ", ";
}
WholeNumber max = vm["list"].as<WholeNumber>();
if (max == 0) max = 20; // set default value
mpz_t result;
for (WholeNumber i = 1; i < max; i++) {
fibonacci(result, i);
mpz_out_str(stdout, 10, result);
if (i != max - 1) cout << sep;
}
return 0;
}
// default behavior
WholeNumber term = vm["term"].as<WholeNumber>();
mpz_t result;
fibonacci(result, term);
mpz_out_str(stdout, 10, result);
return 0;
}
and asked people to critique it. I received many helpful comments, and
made the program a lot better.
Now, I've converted it to use GMP so there's a bit of pointer work and
dynamic memory allocation going on. Can I, once again, have comments
on whether or not the code does "good practices", and where it could
be better? Thanks!
P.S. I am especially interested in comments for the fibonacci()
function; not so much for the processArgs() one.
----
/**
* Exercise #2.1
*
* Create a program that takes one argument and prints the
corresponding
* term from the Fibonacci series. There are also a number of extra
* features.
*/
#include <iostream>
#include <vector>
#include <boost/program_options.hpp>
#include <gmp.h>
/**
* Defines a whole number type (non-negative integer) that indexes the
* Fibonacci sequence. This type determines the nth term we can
calculate,
* though it has no bearing on the actual value of that term.
*/
typedef unsigned long WholeNumber;
/**
* Recursively calculates a term of the Fibonacci series.
* @param result Variable to write result to, no pointer necessary
* @param term Index of term in sequence
* @todo Convert code to use C++ GMP API
* @warning There is no way to free the internal cache
* @note I'm doing something very funky here: I'm using malloc in
order
to get the pointers to the arrays to persist... but GMP
also
has this sort of stuff going behind the scenes! So malloc
is being called twice! I don't know how to get around
this.
*/
void fibonacci(mpz_t result, WholeNumber term) {
static std::vector<mpz_t*> cache;
if (cache.empty()) {
// initialize cache with some (required) preloaded values
mpz_t *val0 = (mpz_t*) malloc(sizeof(mpz_t));
mpz_t *val1 = (mpz_t*) malloc(sizeof(mpz_t));
mpz_init(*val0);
cache.push_back(val0);
mpz_init(*val1);
mpz_set_ui(*val1, 1);
cache.push_back(val1);
}
if (cache.size() > term) {
// cache hit, return that value
mpz_init(result);
mpz_set(result, *cache[term]);
} else {
// cache miss, generate it
mpz_t val1, val2; // previous two values
fibonacci(val2, term - 2);
fibonacci(val1, term - 1);
mpz_t *valr = (mpz_t*) malloc(sizeof(mpz_t));
mpz_init(*valr);
mpz_add(*valr, val2, val1);
cache.push_back(valr);
mpz_init(result);
mpz_set(result, *valr);
}
}
/**
* Parses command line options, possibly returning messages
* @param vm Pointer to map to store argument information in
* @param argc Argument count
* @param argv Argument values
* @return Error code, or 1 to continue
*/
int processArgs(boost:rogram_options::variables_map *vm, int argc,
char *argv[]) {
using namespace boost:rogram_options;
using std::cout;
options_description desc("Options");
desc.add_options()
("help,h", "produce help message")
("list,l", value<WholeNumber>(), "list several Fibonnaci
numbers")
("term,t", value<WholeNumber>(), "print the nth term of the
series")
("nlsep,n", "format output with newlines, not commas")
;
positional_options_description p;
p.add("term", -1);
store(command_line_parser(argc,
argv).options(desc).positional(p).run(), *vm);
notify(*vm);
if (argc == 1 || vm->count("help")) {
cout << "\nFibonacci Numbers Generator\n By Edward Z. Yang\n
\n";
cout << "Usage: fibonacci [options] [term]\n";
cout << desc << "\n";
return 1;
}
return 0;
}
/**
* Main body
*/
int main(int argc, char *argv[]) {
using std::cout;
using std::string;
boost:rogram_options::variables_map vm;
int failed = processArgs(&vm, argc, argv);
if (failed) return failed;
if (vm.count("list")) {
std::string sep;
if (vm.count("nlsep")) {
sep = "\n";
} else {
sep = ", ";
}
WholeNumber max = vm["list"].as<WholeNumber>();
if (max == 0) max = 20; // set default value
mpz_t result;
for (WholeNumber i = 1; i < max; i++) {
fibonacci(result, i);
mpz_out_str(stdout, 10, result);
if (i != max - 1) cout << sep;
}
return 0;
}
// default behavior
WholeNumber term = vm["term"].as<WholeNumber>();
mpz_t result;
fibonacci(result, term);
mpz_out_str(stdout, 10, result);
return 0;
}