module Fibonaccia
The Fibonaccia module simply provides three things to Ruby code:
-
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 usingBigDecimal
semantics; -
The Fibonacci sequence, to however many terms you desire (and your resources can support);
-
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
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
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
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
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
@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
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
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 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
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 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
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
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
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
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
@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