I implemented this console application that generates Fibonacci and Lucas sequence numbers using boost and GMP for multiprecision.
There is an iterator-like class which can do any sequence based on the recursive rule F(n) = F(n-1) + F(n-2)
.
Then there are two functions which can compute Fibonacci and Lucas numbers given their position as for random access of the sequences.
I am using operator<<
to feed the iterator with one of these functions so it can align itself with given start position in the given sequence.
The iterator can actually iterate backwards, because there is nothing that prevents it. You can invoke the behaviour by passing negative limit to the console app. This actually allows to go below zeroth element of the sequences.
I welcome any constructive criticism on my code and also suggestions how to make it more general in terms providing potentialy more sequences, using different number representations and having the iterator actualy do the random access (+=,-=,...) using those functions without loosing the ability to act on them and not knowing which function is used for the random access... And of course whatever other improvement comes to your mind :)
I am not sure whether I am using anything C++17 specific, but that is as high as I can go.
#include <iostream> #include <algorithm> #include <boost/multiprecision/gmp.hpp> /** * Provides iterator over fibonacci family sequences. * That is those sequences generated by recursive formula: * F(n) = F(n-1) + F(n-2) */ template <class T = long, class TIndex = T> struct pair_sum_iterator { public: typedef std::bidirectional_iterator_tag iterator_category; typedef T value_type; typedef TIndex difference_type; typedef T* pointer; typedef T& reference; T current, next; TIndex index; pair_sum_iterator() : current(0), next(0), index(0) {} pair_sum_iterator(const TIndex& index) : current(0), next(0), index(index) {} pair_sum_iterator(const T& first, const T& second) : current(first), next(second), index(0) {} pair_sum_iterator(const T& first, const T& second, const TIndex& index) : current(first), next(second), index(index) {} const T& operator*() const { return current; } pair_sum_iterator & operator++() { ++index; T tmp = current; current = next; next += tmp;; return *this; } pair_sum_iterator operator++(int) { pair_sum_iterator old(*this); ++(*this); return old; } pair_sum_iterator & operator--() { --index; T tmp = current; current = next - current; next = tmp; return *this; }; pair_sum_iterator operator--(int) { pair_sum_iterator old(*this); --(*this); return old; } pair_sum_iterator & operator+=(const TIndex& diff) { TIndex d(diff); while (d > 0) { ++(*this); --d; } while (d < 0) { --(*this); d += 1; //bug in boost - cannot use ++d } return *this; } pair_sum_iterator & operator-=(const TIndex& diff) { return *this += -diff; } pair_sum_iterator operator+(const TIndex& diff) const { pair_sum_iterator result(*this); return result += diff; } pair_sum_iterator operator-(const TIndex& diff) const { pair_sum_iterator result(*this); return result -= diff; } pair_sum_iterator& operator<<(T (*f)(const TIndex&)) { current = f(index); next = f(index + 1); return *this; } }; template <class T, class TIndex> bool operator==(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index == b.index; } template <class T, class TIndex> bool operator!=(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index != b.index; } template <class T, class TIndex> bool operator<(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index < b.index; } template <class T, class TIndex> bool operator<=(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index <= b.index; } template <class T, class TIndex> bool operator>(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index > b.index; } template <class T, class TIndex> bool operator>=(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index >= b.index; } template <class T, class TIndex> TIndex operator-(const pair_sum_iterator<T, TIndex> & a, const pair_sum_iterator<T, TIndex> & b) { return a.index - b.index; } /** * Fibonacci and lucas sequence random accessors and related stuff */ typedef boost::multiprecision::mpz_int app_int; typedef boost::multiprecision::mpf_float_500 app_float; const app_float sqrt5 = boost::multiprecision::sqrt(app_float(5)); const app_float phi = (app_float(1) + sqrt5) / app_float(2); const app_float nphi = app_float(1) - phi; app_int fibonacci(const app_int & n) { app_float n_f(n); return boost::multiprecision::round(((boost::multiprecision::pow(phi,n_f) - boost::multiprecision::pow(nphi, n_f)) / sqrt5)).convert_to<app_int>(); } app_int lucas(const app_int & n) { app_float n_f(n); return boost::multiprecision::round(boost::multiprecision::pow(nphi, n_f) + boost::multiprecision::pow(phi, n_f)).convert_to<app_int>(); } /** * Console application parameters */ struct app_params { public: app_int start; app_int limit; app_int (*sequence)(const app_int &); bool show_usage; bool success; app_params() : start(0), limit(10), sequence(fibonacci), show_usage(false), success(true) {} void usage(bool success) { show_usage = true; this->success = success; } static app_params parse(int argc, char* argv[]) { unsigned int arg_offset = 1; app_params params; try { if (argc > 1) { std::string arg1(argv[1]); if (arg1 == "-h" || arg1 == "--help") { params.usage(argc == 2); } else { if (arg1 == "lucas") { ++arg_offset; params.sequence = lucas; } if (argc == 1 + arg_offset) { // custom limit params.limit = app_int(argv[arg_offset]); } else if (argc == 2 + arg_offset) { // custom limit and offset params.start = app_int(argv[arg_offset]); params.limit = app_int(argv[arg_offset + 1]); } else if (argc > arg_offset) { //argc==arg_offset -> default limit and offest for lucas, otherwise invalid params params.usage(false); } } } } catch (std::runtime_error error) { params.usage(false); } return params; } }; /** * Displays application usage information */ void print_usage(std::ostream & stream) { stream << "Usage:\n"; stream << "\tfibonacci [lucas] [[<start=0>] <limit=10>]\n"; } /** * Application entry point */ int main(int argc, char* argv[]) { auto params = app_params::parse(argc, argv); if (params.show_usage) { print_usage(params.success ? std::cout : std::cerr); return params.success ? EXIT_SUCCESS : EXIT_FAILURE; } pair_sum_iterator<app_int> f(params.start); pair_sum_iterator<app_int> end(params.start + params.limit); f << params.sequence; std::ostream_iterator<app_int> out(std::cout, "\n"); if (params.limit.sign() < 0) { std::reverse_copy(end, f, out); } else { std::copy(f, end, out); } return EXIT_SUCCESS; }
To Compile:
g++ -std=c++17 -g main.cc -lboost_math_c99 -lgmp -o fibonacci
$g++ --version g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
To Run:
fibonacci [lucas] [[<start=0>] <limit=10>]