/******************************************************************************
* File: FibonacciIterator.hh
* Author: Keith Schwarz (htiek@cs.stanford.edu)
*
* An STL-style iterator for iterating across the Fibonacci numbers.  This
* iterator stores two Fibonacci numbers, F(n) and F(n+1), and can move around
* the entries of the Fibonacci sequence one at a time by using the two
* values to produce either the next value in the sequence or the preivous
* value in the sequence.  For example, given F(n) and F(n+1) we can produce
* an iterator for F(n+1), F(n+2) by using the fact that
*
*    (F(n+1), F(n+2)) = (F(n+1), F(n) + F(n+1))
*
* Similarly, we can get an iterator to (F(n-1), F(n)) by noting that
*
*    (F(n-1), F(n)) = (F(n+1) - F(n), F(n))
*
* At each point, the iterator acts as though it is iterating over the first
* value in the tuple, so the iterator (F(3), F(4)) looks like an iterator over
* the value F(3) = 2.
*
* The implementation of a Fibonacci iterator provided in this header file
* allows the type of the numbers used by the iterator to be customized along
* with the binary addition and subtraction operators used.  This means that
* the implementation can be customized so that it computes Fibonacci numbers
* with a custom integer type, or to allow for computation of Fibonacci numbers
* in a group other than the integers.  For example, if the addition and
* subtraction operators are implemented as multiplication and division, then
* the Fibonacci iterator will visit Fibonacci powers of a number, which can be
* used to take logarithms efficiently.
*/

#ifndef FibonacciIterator_Included
#define FibonacciIterator_Included

#include <functional> // For std::plus, std::minus
#include <iterator>   // For std::bidirectional_iterator_tag, std::iterator

/**
* An iterator class capable of navigating across the Fibonacci sequence using
* a user-specified integer type.
*/

template <typename Integer, typename Plus = std::plus<Integer>,
typename Minus = std::minus<Integer> >
class FibonacciIterator: public std::iterator<std::bidirectional_iterator_tag,
const Integer> {
public:
/**
* Constructor: FibonacciIterator(Integer zero = Integer(0),
*                                Integer one = Integer(1),
*                                Plus p = Plus(), Minus m = Minus())
* Usage: FibonacciIterator<int> itr;
* --------------------------------------------------------------------------
* Constructs a new Fibonacci iterator traversing the Fibonacci sequence
* whose first two terms are zero and one and that uses the specified plus
* and minus function objects to navigate the sequence.
*/

explicit FibonacciIterator(Integer zero = Integer(0),
Integer one  = Integer(1),
Plus p = Plus(), Minus m = Minus());

/**
* operator*  () const;
* operator-> () const;
* Usage: cout << *itr << endl;
* --------------------------------------------------------------------------
* Dereferences and returns the current integer in the sequence.  You should
* not modify the values returned as they are not guaranteed to be valid
* after the iterator advances.  Moreover, you should not hold pointers or
* references to these values, as the memory will be recycled after the
* iterator is incremented or decremented.
*/

const Integer& operator*  () const;
const Integer* operator-> () const;

/**
* operator++ ();
* operator++ (int);
* operator-- ();
* operator-- (int);
* Usage: ++itr; --itr; itr++; itr--;
* --------------------------------------------------------------------------
* Moves the iterator one step forward or backward in the Fibonacci sequence.
* If integer overflow occurs, the results depend on the type of the integer
* being used as a counter.  If the iterator is backed up while at 0, the
* results are mathematically well-defined but depend on the underlying type
* of the integer for correctness.
*/

FibonacciIterator&      operator++ ();
const FibonacciIterator operator++ (int);

FibonacciIterator&      operator-- ();
const FibonacciIterator operator-- (int);

private:
/* The current and next Fibonacci values in the sequence. */
Integer curr, next;

/* The plus and minus operators. */
Plus plus;
Minus minus;
};

/* Comparison functions for FibonacciIterator. */
template <typename Integer, typename Plus, typename Minus>
bool operator== (const FibonacciIterator<Integer, Plus, Minus>& lhs,
const FibonacciIterator<Integer, Plus, Minus>& rhs);
template <typename Integer, typename Plus, typename Minus>
bool operator!= (const FibonacciIterator<Integer, Plus, Minus>& lhs,
const FibonacciIterator<Integer, Plus, Minus>& rhs);

/* * * * * Implementation Below This Point * * * * */

/* Constructor sets up the internal fields based on the parameters. */
template <typename Integer, typename Plus, typename Minus>
FibonacciIterator<Integer, Plus, Minus>::FibonacciIterator(Integer zero,
Integer one,
Plus plus,
Minus minus)
: curr(zero), next(one), plus(plus), minus(minus) {
// Handled in initializer list.
}

/* Dereferencing to a value just returns the current value in the sequence. */
template <typename Integer, typename Plus, typename Minus>
const Integer& FibonacciIterator<Integer, Plus, Minus>::operator*  () const {
return curr;
}
template <typename Integer, typename Plus, typename Minus>
const Integer* FibonacciIterator<Integer, Plus, Minus>::operator-> () const {
return &**this;
}

/* Incrementing the Fibonacci iterator walks forward one step in the Fibonacci
* series.
*/

template <typename Integer, typename Plus, typename Minus>
FibonacciIterator<Integer, Plus, Minus>&
FibonacciIterator<Integer, Plus, Minus>::operator++ () {
Integer newNext = plus(curr, next);
curr = next;
next = newNext;

return *this;
}
template <typename Integer, typename Plus, typename Minus>
const FibonacciIterator<Integer, Plus, Minus>
FibonacciIterator<Integer, Plus, Minus>::operator++ (int) {
FibonacciIterator result = *this;
++ *this;
return result;
}

/* Decrementing the Fibonacci iterator backs it up one step in the sequence. */
template <typename Integer, typename Plus, typename Minus>
FibonacciIterator<Integer, Plus, Minus>&
FibonacciIterator<Integer, Plus, Minus>::operator-- () {
Integer prev = minus(next, curr);
next = curr;
curr = prev;

return *this;
}
template <typename Integer, typename Plus, typename Minus>
const FibonacciIterator<Integer, Plus, Minus>
FibonacciIterator<Integer, Plus, Minus>::operator-- (int) {
FibonacciIterator result = *this;
-- *this;
return result;
}

/* Equality comparisons just check if the two values are equal. */
template <typename Integer, typename Plus, typename Minus>
bool operator== (const FibonacciIterator<Integer, Plus, Minus>& lhs,
const FibonacciIterator<Integer, Plus, Minus>& rhs) {
return lhs.curr == rhs.curr && lhs.next == rhs.next;
}

/* Disequality implemented in terms of equality. */
template <typename Integer, typename Plus, typename Minus>
bool operator!= (const FibonacciIterator<Integer, Plus, Minus>& lhs,
const FibonacciIterator<Integer, Plus, Minus>& rhs) {
return !(lhs == rhs);
}

#endif