• # Example: Fibonacci Rabbits

based on the problem statement at http://rosalind.info/problems/fib/

`require 'aqueductron'`
• The rabbits, see, they reproduce. Generation n is a function of the size of the previous two generations.

F(n) = F(n - 1) + k * F(n - 2)

The game is to predict future generation sizes. Send the known generation sizes down the duct, and it learns a value of k. It needs at least 3 generations. Send :unknown and the size of the next generation is predicted.

## Custom ductwork

Custom duct pieces contain functions that construct the next duct piece out of other functions. Each function knows a little more than the one in the previous iteration.

`class Fibonacci`
• The duct is short: one custom piece, and then a collector that outputs the last number it has seen. That is the last known value in the sequence of rabbit counts. The interesting parts are the custom function definitions.

```  def rabbit_predictor
Aqueductron::Duct.new.custom(empty_fib_function).last
end```
• First, we know nothing. The initial piece only knows how to accept some data and change the piece to incorporate that data.

```  def empty_fib_function
->(piece,msg) do
piece.pass_on(msg, one_data_fib_function(msg),"#{msg}..")
end
end```
• Second, we know one generation's size. That isn't enough to do anything except look for the second generation.

```  def one_data_fib_function(first)
->(piece,msg) do
piece.pass_on(msg, two_data_fib_function(first, msg),"#{first},#{msg}..")
end
end```
• Third, we know two generations. Once we get an additional piece of data, we can calculate an initial guess at k.

```  def two_data_fib_function(first,second)
->(piece,msg) do
k = (msg - second + 0.0) / first # initial guess at k
piece.pass_on(msg, learning_fib_function(msg, second, k, 1),
"..#{second},#{msg}.. starting k~#{k}")
end
end```
• Fourth, as long as we keep getting data, we can refine the value of k. Keep using this function, with different arguments, as long as we keep getting observed data points. As long as someone is still counting the rabbits. Once we get :unknown, it's time to start predicting.

```  def learning_fib_function(prev_number, prev_prev_number, k, data_points_in_k)
->(piece,msg) do
if (msg == :unknown)
fib_function(prev_number, prev_prev_number, k).call(piece,msg)
else
this_k = estimate_k(prev_prev_number, prev_number, msg)
average_k = add_data_point(k, data_points_in_k, this_k)
piece.pass_on(msg, learning_fib_function(msg, prev_number, average_k, data_points_in_k + 1),
"..#{prev_number},#{msg}.. learning k~#{average_k}")
end
end
end```
• Fifth and finally, predict future generations based on the value of k we've learned. This one keeps setting up a new version of itself, with different arguments, every time.

```  def fib_function(prev_number, prev_prev_number, k)
->(piece, _) do # msg is ignored
current_val = (prev_number + ( prev_prev_number * k )).round
piece.pass_on(current_val, fib_function(current_val, prev_number, k),
"..#{prev_number},#{current_val}.. k=#{k}")
end
end```
• helper methods do some calculation

```  def estimate_k(first, second, third) #consecutive terms
(third - second) / first
end

def add_data_point(average_so_far, data_points_included, new_data_point)
(average_so_far * data_points_included + new_data_point) / (data_points_included + 1)
end
end```
• ## To use this

get a new prediction duct

`rabbits = Fibonacci.new.rabbit_predictor`
• Seed it with the known data

`seeded = rabbits.keep_flowing([2,5,8,11])`
• send :unknown to get the next output

=> 17

`seeded.drip(:unknown).eof`
• and more :unknowns to get subsequent generations

=> 25

`seeded.drip(:unknown).drip(:unknown)`
• for added fun, drip the known generations in one at a time to watch the duct learn.