module Fibonaccia

The Fibonaccia module simply provides three things to Ruby code:

  1. Access to a constant, Fibonaccia.PHI (φ), which is the value of the Golden Ratio (see the {en.wikipedia.org/wiki/Golden_ratio Wikipedia article}) either to whatever precision Ruby is using, or to an arbitrarily great precision using BigDecimal semantics;

  2. The Fibonacci sequence, to however many terms you desire (and your resources can support);

  3. Coördinates to construct a {en.wikipedia.org/wiki/Golden_spiral golden spiral} (not the Fibonacci spiral, which is an approximation of the golden spiral). Not yet implemented.

Constants

BDPrecision

@api private

The number of digits of precision we want for our BigDecimal operations.

B_BigDecimal

@api private

@macro SpiralFactors

This constant is a BigDecimal value. If you don’t need arbitrarily great precision, use {B_BigDecimal} instead.

B_Float

@api private

@macro SpiralFactors

This constant is a Float value. For greater precision, use {B_BigDecimal}.

C_BigDecimal

(see B_BigDecimal)

C_Float

(see B_Float)

MINIMUM_RUBY_VERSION

Minimum version of Ruby we support.

MIN_TERMS

@api private

Minimum number of terms in the series – the seed values.

PHI

@macro PHIconst

PHI_BigDecimal

@api private

Provide a value for φ using an arbitrarily large precision.

@note

Use <tt>Fibonaccia.PHI(true)</tt> to access this value.

@see PHI

PHI_Float

@api private

Phi (φ), the golden ratio. φ can be simply expressed by a formula, but it’s an irrational number, meaning that the default precision is implementation-specific.

Provide a Float value which uses the default precision.

@note

Use <tt>Fibonaccia.PHI</tt> or <tt>Fibonaccia.PHI(false)</tt>
to access this value.

@see PHI

SEED

First three terms of the Fibonacci sequence, which is our seed and our minimum internal series.

SERIES

@api private

Array of Fibonacci numbers to however many terms have been evolved. Defined as a constant because the underlying array structure is mutable even for constants, and it’s pre-seeded with the first three terms.

VERSION

Frozen string representation of the module version number.

Public Class Methods

PHI(extended=false) click to toggle source

Constant Phi (φ), the golden ratio.

φ can be simply expressed by a formula, but it’s an irrational number, meaning that the default precision is implementation-specific. {PHI} allows you to access the value either at the implementation precision, or the BigDecimal extended precision.

@!macro PHIdoc

@param [Boolean] extended

@return [Float]
  when <tt>extended</tt> is false (or at least not a true value).
@return [BigDecimal]
  when <tt>extended</tt> is true.

@example Using conventional 'constant' semantics
    irb> Fibonaccia::PHI
    => 1.618033988749895
    irb> Fibonaccia::PHI(false)
    => 1.618033988749895
    irb> Fibonaccia::PHI(true)
    => #<BigDecimal:198e990,'0.1618033988 7498948482 0458683433 33333335E1',54(72)>

@example Using module method semantics
    irb> Fibonaccia.PHI
    => 1.618033988749895
    irb> Fibonaccia.PHI(false)
    => 1.618033988749895
    irb> Fibonaccia.PHI(true)
    => #<BigDecimal:198e990,'0.1618033988 7498948482 0458683433 33333335E1',54(72)>

@macro PHIdoc

# File lib/fibonaccia.rb, line 218
def PHI(extended=false)
  result            = (extended \
                       ? PHI_BigDecimal \
                       : PHI_Float)
  return result
end
VERSION() click to toggle source

Returns the package version number as a string.

@return [String]

Package version number.
# File lib/fibonaccia/version.rb, line 81
def self.VERSION
  return self.const_get('VERSION')
end
each(&block) click to toggle source

Iterate over the current internal series, yielding each value in turn.

@yield [Integer]

Each element of the internal series is yielded in turn.

@return [Array<Integer>]

if a block has been passed.

@return [Enumerator]

when invoked without a block.
# File lib/fibonaccia.rb, line 410
def each(&block)
  result            = SERIES.each(&block)
  return result
end
grow(nterms) click to toggle source

Extend the internal series by the specified number of terms.

@param [Integer] nterms

Number of terms by which to grow the internal series.

@return [Integer]

the number of terms in the series after the operation.

@raise [Fibonaccia::NotPositiveInteger]

if the argument isn't an integer greater than or equal to zero.
# File lib/fibonaccia.rb, line 309
def grow(nterms)
  unless (nterms.kind_of?(Integer) && (nterms >= 0))
    msg             = 'argument must be a non-negative integer'
    raise(Fibonaccia::NotPositiveInteger, msg)
  end
  self.extend_series(self.terms + nterms)
  return self.terms
end
included(klass) click to toggle source

@private

Method invoked when a scope does an include Fibonaccia. We simply emit a warning message and do nothing else.

@param [Module,Class] klass

Object in whose scope the <tt>include</tt> appeared.

@return [void]

# File lib/fibonaccia.rb, line 177
def included(klass)
  warn("#warning: #{self.name} " +
       'is not intended for use as a mix-in, but it does no harm')
  return nil
end
is_fibonacci?(val) click to toggle source

See if value appears in the Fibonacci series.

Check to see if the given value is found in the Fibonacci series, using the transform described at {en.wikipedia.org/wiki/Fibonacci_number#Recognizing_Fibonacci_numbers}.

@see en.wikipedia.org/wiki/Fibonacci_number#Recognizing_Fibonacci_numbers

@param [Integer] val

Value to be checked for membership in the Fibonacci series.

@return [Boolean]

<tt>true</tt> if the given value is a Fibonacci number, else <tt>false</tt>.
# File lib/fibonaccia.rb, line 548
def is_fibonacci?(val)
  #
  # Needs to be an integer.
  #
  return false unless (val.respond_to?(:floor) && (val.floor == val))
  #
  # Needs to be non-negative.
  #
  return false if (val < 0)
  return true if (SERIES.include?(val))
  #
  # Easy checks are done, time for some math-fu.
  #
  val               = BigDecimal.new(val)
  [ +4, -4 ].each do |c|
    eqterm          = 5 * (val**2) + c
    root            = eqterm.sqrt(BDPrecision)
    return true if (root.floor == root)
  end
  return false
end
last() click to toggle source

Return the last value in the internal series.

This is equivalent to

Fibonaccia[-1]

@return [Integer]

the last term in the internal series.
# File lib/fibonaccia.rb, line 431
def last
  result            = SERIES.last
  return result
end
reset() click to toggle source

Reset the internal series to just the seed value.

This can be used to free up memory.

@return [void]

# File lib/fibonaccia.rb, line 393
def reset
  SERIES.replace(SEED)
  return nil
end
series() click to toggle source

Copy of the internal series.

Return a dup of the internal series, to however many terms it has grown.

@note

Since this is a duplicate of the module-internal array, it can
have a significant impact on memory usage if the series has
been extended to any great length.

@see reset @see shrink @see terms=

@example

irb> require('fibonaccia')
irb> Fibonaccia.series
=> [0, 1, 1]
irb> Fibonaccia[10]
=> 55
irb> Fibonaccia.series
=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

@return [Array<Integer>]

Returns the list of Fibonacci numbers as far as they've been
calculated by the module.
# File lib/fibonaccia.rb, line 273
def series
  return SERIES.dup
end
shrink(nterms) click to toggle source

Shrink the internal series by the specified number of terms.

@!macro minsize

@note
  The series <b><i>cannot</i></b> be shrunk to fewer than the
  {SEED} elements.

@macro minsize @param [Integer] nterms

Number of terms by which to shrink the internal series.

@return [Integer]

the number of terms in the series after the operation.

@raise [Fibonaccia::NotPositiveInteger]

if the argument isn't an integer greater than or equal to zero.
# File lib/fibonaccia.rb, line 335
def shrink(nterms)
  unless (nterms.kind_of?(Integer) && (nterms >= 0))
    msg             = 'argument must be a non-negative integer'
    raise(Fibonaccia::NotPositiveInteger, msg)
  end
  nterms            = [ MIN_TERMS, self.terms - nterms ].max
  SERIES.replace(SERIES.take(nterms))
  return self.terms
end
slice(first_term, nterms=1) click to toggle source

Obtain a slice (see Array#slice) of the Fibonacci series.

The internal series will be extended, if necessary, to include all terms requested.

@note

The internal series is *zero-based*, which means the first
term is numbered <tt>0</tt>.

@param [Integer] first_term

The first term of the slice from the series.

@param [Integer] nterms

The number of elements in the slice to be returned.

@return [Integer]

if the result is a valid slice containing only one term
(<i>i.e.</i>, <tt>nterms</tt> is 1).  Returns the Fibonacci term at
the specified (zero-based) position in the sequence.

@return [Array<Integer>]

if the result is a valid multi-element slice (<i>e.g.</i>, <tt>nterms</tt>
is greater than 1).  Returns the specified slice.

@return [nil]

if the slice parameters are not meaningful (<i>e.g.</i>, <tt>slice(1, -1)</tt>).

@raise [ArgumentError]

if the arguments are not all integers.
# File lib/fibonaccia.rb, line 464
def slice(first_term, nterms=1)
  args              = {
    'first_term'    => first_term,
    'nterms'        => nterms,
  }
  #
  # Sanity-check our arguments; be more informative than the default
  #
  #    TypeError: no implicit conversion of <class> into Integer
  #
  args.each do |argname,argval|
    unless (argval.kind_of?(Integer))
      raise(ArgumentError, "#{argname} must be an integer")
    end
  end
  nterms            = [ 1, nterms ].max
  if (first_term < 0)
    endpoint        = [ 0, self.terms + first_term + nterms ].max
  else
    endpoint        = first_term + nterms
  end
  Fibonaccia.extend_series(endpoint)
  #
  # We're going to pass this along to the array's own #slice
  # method, so build its argument list appropriately.
  #
  args              = [ first_term ]
  args              << nterms unless (nterms == 1)
  result            = SERIES.slice(*args)
  #
  # If we got a multi-element slice, make sure we don't return our
  # master sequence!  Ruby shouldn't let it happen, but defensive
  # programing is all.
  #
  result            = result.dup if (result === SERIES)
  return result
end
terms() click to toggle source

The number of terms in the internal series.

Similar to the count method provided by the Enumerable mix-in, but a more direct approach – and complementary to the {terms=} method.

@return [Integer]

number of terms in the internal series.
# File lib/fibonaccia.rb, line 355
def terms
  result            = SERIES.count
  return result
end
terms=(nterms) click to toggle source

Set the internal series to a specific number of terms.

@macro minsize @param [Integer] nterms

Number of terms to which the series should be grown or shrunk.

@return [Integer]

the number of terms in the series after the operation.

@raise [Fibonaccia::NotPositiveInteger]

if the argument isn't an integer greater than or equal to zero.
# File lib/fibonaccia.rb, line 372
def terms=(nterms)
  unless (nterms.kind_of?(Integer) && (nterms >= 0))
    msg             = 'argument must be a non-negative integer'
    raise(Fibonaccia::NotPositiveInteger, msg)
  end
  nterms            = [ MIN_TERMS, nterms ].max
  if (nterms > self.terms)
    self.grow(nterms - self.terms)
  elsif (nterms < self.terms)
    self.shrink(self.terms - nterms)
  end
  return self.terms
end
version() click to toggle source

Returns the {rubygems.org/gems/versionomy Versionomy} representation of the package version number.

@return [Versionomy]

# File lib/fibonaccia/version.rb, line 71
def self.version
  return @version
end

Protected Class Methods

extend_series(nterms) click to toggle source

@api private

This method is called to extend the {SERIES} array if necessary.

@param [Integer] nterms

If the value of this parameter is greater than the number of
terms in the {SERIES} array, new terms are calculated until
the series is long enough.

@return [void]

# File lib/fibonaccia.rb, line 288
def extend_series(nterms)
  nterms            = [ 0, nterms.to_i ].max
  n                 = [ 0, nterms - self.terms ].max
  n.times do
    SERIES          << (SERIES[-2] + SERIES[-1])
  end
  return nil
end