fibonaccia 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.tito/custom/gemtagger.py +112 -0
- data/.tito/custom/gemtagger.pyc +0 -0
- data/.tito/packages/.readme +3 -0
- data/.tito/packages/rubygem-fibonaccia +1 -0
- data/.tito/tito.props +6 -0
- data/.travis.yml +30 -0
- data/CONTRIBUTORS.md +7 -0
- data/Changelog +3 -0
- data/Details.md +304 -0
- data/Gemfile +44 -0
- data/LICENCE.md +203 -0
- data/LICENCE.txt +201 -0
- data/README.md +158 -0
- data/Rakefile +53 -0
- data/features/constants.feature +23 -0
- data/features/enumeration.feature +22 -0
- data/features/exceptions.feature +44 -0
- data/features/is_fibonacci.feature +128 -0
- data/features/sizing.feature +70 -0
- data/features/slicing-growth.feature +18 -0
- data/features/slicing-sequence.feature +39 -0
- data/features/slicing.feature +35 -0
- data/features/step_definitions/exceptions.rb +23 -0
- data/features/step_definitions/fibonaccia-specific.rb +36 -0
- data/features/step_definitions/streams.rb +53 -0
- data/features/step_definitions/utility.rb +58 -0
- data/features/support/env.rb +165 -0
- data/features/support/hooks.rb +12 -0
- data/fibonaccia.gemspec +121 -0
- data/lib/fibonaccia.rb +573 -0
- data/lib/fibonaccia/classmethods.rb +35 -0
- data/lib/fibonaccia/exceptions.rb +82 -0
- data/lib/fibonaccia/module-doc.rb +39 -0
- data/lib/fibonaccia/version.rb +86 -0
- data/rubygem-fibonaccia.spec +118 -0
- data/tasks/markdown-to-html.rake +9 -0
- metadata +200 -0
data/lib/fibonaccia.rb
ADDED
@@ -0,0 +1,573 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# @internal_comment
|
3
|
+
#
|
4
|
+
# Copyright © 2015 Ken Coar
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
# :nocov:
|
20
|
+
if (RUBY_VERSION =~ %r!\b1.9\b!)
|
21
|
+
Encoding.default_external = Encoding::UTF_8
|
22
|
+
Encoding.default_internal = Encoding::UTF_8
|
23
|
+
end
|
24
|
+
# :nocov:
|
25
|
+
|
26
|
+
require('rubygems')
|
27
|
+
require('bundler')
|
28
|
+
Bundler.setup
|
29
|
+
|
30
|
+
require('fibonaccia/module-doc')
|
31
|
+
require('fibonaccia/version')
|
32
|
+
require('fibonaccia/exceptions')
|
33
|
+
require('bigdecimal')
|
34
|
+
require('bigdecimal/math')
|
35
|
+
|
36
|
+
module Fibonaccia
|
37
|
+
|
38
|
+
include BigMath
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
#
|
42
|
+
# The number of digits of precision we want for our <tt>BigDecimal</tt> operations.
|
43
|
+
#
|
44
|
+
BDPrecision = BigDecimal::double_fig
|
45
|
+
|
46
|
+
unless (Fibonaccia.const_defined?('PHI_Float'))
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
# Phi (φ), the golden ratio. φ can be simply expressed by a
|
50
|
+
# formula, but it's an irrational number, meaning that the default
|
51
|
+
# precision is implementation-specific.
|
52
|
+
#
|
53
|
+
# Provide a <tt>Float</tt> value which uses the default precision.
|
54
|
+
#
|
55
|
+
# @note
|
56
|
+
# Use <tt>Fibonaccia.PHI</tt> or <tt>Fibonaccia.PHI(false)</tt>
|
57
|
+
# to access this value.
|
58
|
+
#
|
59
|
+
# @see PHI
|
60
|
+
#
|
61
|
+
PHI_Float = (1.0 + Math.sqrt(5)) / 2.0
|
62
|
+
|
63
|
+
# @!macro PHIconst
|
64
|
+
#
|
65
|
+
# Default value of φ as a <tt>Float</tt>.
|
66
|
+
#
|
67
|
+
# Referencing <tt>PHI</tt> as a constant
|
68
|
+
# (<tt>Fibonaccia::PHI</tt>) is equivalent to:
|
69
|
+
#
|
70
|
+
# Fibonaccia.PHI(false)
|
71
|
+
#
|
72
|
+
# Use {Fibonaccia.PHI}<tt>(true)</tt> to obtain the
|
73
|
+
# <tt>BigDecimal</tt> representation.
|
74
|
+
#
|
75
|
+
|
76
|
+
# @macro PHIconst
|
77
|
+
PHI = PHI_Float
|
78
|
+
|
79
|
+
# @api private
|
80
|
+
#
|
81
|
+
# Provide a value for φ using an arbitrarily large precision.
|
82
|
+
#
|
83
|
+
# @note
|
84
|
+
# Use <tt>Fibonaccia.PHI(true)</tt> to access this value.
|
85
|
+
#
|
86
|
+
# @see PHI
|
87
|
+
#
|
88
|
+
PHI_BigDecimal = (1.0 + BigDecimal.new(5).sqrt(BDPrecision)) / 2.0
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# This bit of jiggery-pokery exists because we don't want to expose
|
93
|
+
# the *real* definition of PHI (which is PHI_Float) in the
|
94
|
+
# documentation. So we wrap a fake one in a never-successful
|
95
|
+
# conditional, and Yard uses that.
|
96
|
+
#
|
97
|
+
# :nocov:
|
98
|
+
if (false)
|
99
|
+
# @macro PHIconst
|
100
|
+
PHI = calculated_constant
|
101
|
+
end
|
102
|
+
# :nocov:
|
103
|
+
|
104
|
+
unless (::Fibonaccia.const_defined?('B_Float'))
|
105
|
+
# @!macro SpiralFactors
|
106
|
+
# Constant used to construct a Golden Spiral. See the
|
107
|
+
# {https://en.wikipedia.org/wiki/Golden_spiral Wikipedia article}
|
108
|
+
# for details of its definition and use.
|
109
|
+
|
110
|
+
# @api private
|
111
|
+
#
|
112
|
+
# @macro SpiralFactors
|
113
|
+
#
|
114
|
+
# This constant is a <tt>Float</tt> value. For greater precision,
|
115
|
+
# use {B_BigDecimal}.
|
116
|
+
#
|
117
|
+
B_Float = (2.0 * Math.log(PHI_Float)) / Math::PI
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
# @macro SpiralFactors
|
122
|
+
#
|
123
|
+
# This constant is a <tt>BigDecimal</tt> value. If you don't need
|
124
|
+
# arbitrarily great precision, use {B_BigDecimal} instead.
|
125
|
+
#
|
126
|
+
B_BigDecimal = ((2.0 * BigMath.log(PHI_BigDecimal, BDPrecision)) / BigMath.PI(BDPrecision))
|
127
|
+
|
128
|
+
# (see B_Float)
|
129
|
+
C_Float = Math.exp(B_Float)
|
130
|
+
|
131
|
+
# (see B_BigDecimal)
|
132
|
+
C_BigDecimal = BigMath.exp(B_BigDecimal, BDPrecision)
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Since this module is *not* intended for use as a mix-in, all the
|
137
|
+
# methods and 'private' data are kept in the eigenclass.
|
138
|
+
#
|
139
|
+
class << self
|
140
|
+
|
141
|
+
include BigMath
|
142
|
+
|
143
|
+
unless (const_defined?('SERIES'))
|
144
|
+
#
|
145
|
+
# First three terms of the Fibonacci sequence, which is our seed
|
146
|
+
# and our minimum internal series.
|
147
|
+
#
|
148
|
+
SEED = [ 0, 1, 1 ].freeze
|
149
|
+
|
150
|
+
# @api private
|
151
|
+
#
|
152
|
+
# Minimum number of terms in the series -- the seed values.
|
153
|
+
#
|
154
|
+
MIN_TERMS = SEED.count
|
155
|
+
|
156
|
+
# @api private
|
157
|
+
#
|
158
|
+
# Array of Fibonacci numbers to however many terms have been
|
159
|
+
# evolved. Defined as a constant because the underlying array
|
160
|
+
# structure is mutable even for constants, and it's pre-seeded
|
161
|
+
# with the first three terms.
|
162
|
+
#
|
163
|
+
SERIES = SEED.dup
|
164
|
+
end
|
165
|
+
|
166
|
+
include Enumerable
|
167
|
+
|
168
|
+
# @private
|
169
|
+
#
|
170
|
+
# Method invoked when a scope does an <b><tt>include Fibonaccia</tt></b>. We
|
171
|
+
# simply emit a warning message and do nothing else.
|
172
|
+
#
|
173
|
+
# @param [Module,Class] klass
|
174
|
+
# Object in whose scope the <tt>include</tt> appeared.
|
175
|
+
#
|
176
|
+
# @return [void]
|
177
|
+
#
|
178
|
+
def included(klass)
|
179
|
+
warn("#warning: #{self.name} " +
|
180
|
+
'is not intended for use as a mix-in, but it does no harm')
|
181
|
+
return nil
|
182
|
+
end # def included
|
183
|
+
|
184
|
+
#
|
185
|
+
# Constant Phi (φ), the golden ratio.
|
186
|
+
#
|
187
|
+
# φ can be simply expressed by a formula, but it's an irrational
|
188
|
+
# number, meaning that the default precision is
|
189
|
+
# implementation-specific. {PHI} allows you to access the value
|
190
|
+
# either at the implementation precision, or the
|
191
|
+
# <tt>BigDecimal</tt> extended precision.
|
192
|
+
#
|
193
|
+
# @!macro PHIdoc
|
194
|
+
# @param [Boolean] extended
|
195
|
+
#
|
196
|
+
# @return [Float]
|
197
|
+
# when <tt>extended</tt> is false (or at least not a true value).
|
198
|
+
# @return [BigDecimal]
|
199
|
+
# when <tt>extended</tt> is true.
|
200
|
+
#
|
201
|
+
# @example Using conventional 'constant' semantics
|
202
|
+
# irb> Fibonaccia::PHI
|
203
|
+
# => 1.618033988749895
|
204
|
+
# irb> Fibonaccia::PHI(false)
|
205
|
+
# => 1.618033988749895
|
206
|
+
# irb> Fibonaccia::PHI(true)
|
207
|
+
# => #<BigDecimal:198e990,'0.1618033988 7498948482 0458683433 33333335E1',54(72)>
|
208
|
+
#
|
209
|
+
# @example Using module method semantics
|
210
|
+
# irb> Fibonaccia.PHI
|
211
|
+
# => 1.618033988749895
|
212
|
+
# irb> Fibonaccia.PHI(false)
|
213
|
+
# => 1.618033988749895
|
214
|
+
# irb> Fibonaccia.PHI(true)
|
215
|
+
# => #<BigDecimal:198e990,'0.1618033988 7498948482 0458683433 33333335E1',54(72)>
|
216
|
+
#
|
217
|
+
# @macro PHIdoc
|
218
|
+
#
|
219
|
+
def PHI(extended=false)
|
220
|
+
result = (extended \
|
221
|
+
? PHI_BigDecimal \
|
222
|
+
: PHI_Float)
|
223
|
+
return result
|
224
|
+
end # def PHI
|
225
|
+
|
226
|
+
# @internal_comment
|
227
|
+
#
|
228
|
+
# I can't get Yard to process the phi as a method name, so it's
|
229
|
+
# just undocumented. Meh.
|
230
|
+
#
|
231
|
+
|
232
|
+
# @api private
|
233
|
+
#
|
234
|
+
# Alias name (probably not universally useable) for the {PHI} method.
|
235
|
+
#
|
236
|
+
# @macro PHIdoc
|
237
|
+
#
|
238
|
+
define_method(:'φ', self.instance_method(:PHI))
|
239
|
+
|
240
|
+
# @api private
|
241
|
+
#
|
242
|
+
# We return a <i>copy</i> so our actual array can't be accidentally
|
243
|
+
# polluted by whatever the caller may do to what we give it.
|
244
|
+
#
|
245
|
+
|
246
|
+
#
|
247
|
+
# Copy of the internal series.
|
248
|
+
#
|
249
|
+
# Return a <tt>dup</tt> of the internal series, to however many
|
250
|
+
# terms it has grown.
|
251
|
+
#
|
252
|
+
# @note
|
253
|
+
# Since this is a duplicate of the module-internal array, it can
|
254
|
+
# have a significant impact on memory usage if the series has
|
255
|
+
# been extended to any great length.
|
256
|
+
#
|
257
|
+
# @see reset
|
258
|
+
# @see shrink
|
259
|
+
# @see terms=
|
260
|
+
#
|
261
|
+
# @example
|
262
|
+
# irb> require('fibonaccia')
|
263
|
+
# irb> Fibonaccia.series
|
264
|
+
# => [0, 1, 1]
|
265
|
+
# irb> Fibonaccia[10]
|
266
|
+
# => 55
|
267
|
+
# irb> Fibonaccia.series
|
268
|
+
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
269
|
+
#
|
270
|
+
# @return [Array<Integer>]
|
271
|
+
# Returns the list of Fibonacci numbers as far as they've been
|
272
|
+
# calculated by the module.
|
273
|
+
#
|
274
|
+
def series
|
275
|
+
return SERIES.dup
|
276
|
+
end # def series
|
277
|
+
|
278
|
+
# @api private
|
279
|
+
#
|
280
|
+
# This method is called to extend the {SERIES} array if necessary.
|
281
|
+
#
|
282
|
+
# @param [Integer] nterms
|
283
|
+
# If the value of this parameter is greater than the number of
|
284
|
+
# terms in the {SERIES} array, new terms are calculated until
|
285
|
+
# the series is long enough.
|
286
|
+
#
|
287
|
+
# @return [void]
|
288
|
+
#
|
289
|
+
def extend_series(nterms)
|
290
|
+
nterms = [ 0, nterms.to_i ].max
|
291
|
+
n = [ 0, nterms - self.terms ].max
|
292
|
+
n.times do
|
293
|
+
SERIES << (SERIES[-2] + SERIES[-1])
|
294
|
+
end
|
295
|
+
return nil
|
296
|
+
end # def extend_series
|
297
|
+
protected(:extend_series)
|
298
|
+
|
299
|
+
#
|
300
|
+
# Extend the internal series by the specified number of terms.
|
301
|
+
#
|
302
|
+
# @param [Integer] nterms
|
303
|
+
# Number of terms by which to grow the internal series.
|
304
|
+
# @return [Integer]
|
305
|
+
# the number of terms in the series after the operation.
|
306
|
+
#
|
307
|
+
# @raise [Fibonaccia::NotPositiveInteger]
|
308
|
+
# if the argument isn't an integer greater than or equal to zero.
|
309
|
+
#
|
310
|
+
def grow(nterms)
|
311
|
+
unless (nterms.kind_of?(Integer) && (nterms >= 0))
|
312
|
+
msg = 'argument must be a non-negative integer'
|
313
|
+
raise(Fibonaccia::NotPositiveInteger, msg)
|
314
|
+
end
|
315
|
+
self.extend_series(self.terms + nterms)
|
316
|
+
return self.terms
|
317
|
+
end # def grow
|
318
|
+
|
319
|
+
#
|
320
|
+
# Shrink the internal series by the specified number of terms.
|
321
|
+
#
|
322
|
+
# @!macro minsize
|
323
|
+
# @note
|
324
|
+
# The series <b><i>cannot</i></b> be shrunk to fewer than the
|
325
|
+
# {SEED} elements.
|
326
|
+
#
|
327
|
+
# @macro minsize
|
328
|
+
# @param [Integer] nterms
|
329
|
+
# Number of terms by which to shrink the internal series.
|
330
|
+
# @return [Integer]
|
331
|
+
# the number of terms in the series after the operation.
|
332
|
+
#
|
333
|
+
# @raise [Fibonaccia::NotPositiveInteger]
|
334
|
+
# if the argument isn't an integer greater than or equal to zero.
|
335
|
+
#
|
336
|
+
def shrink(nterms)
|
337
|
+
unless (nterms.kind_of?(Integer) && (nterms >= 0))
|
338
|
+
msg = 'argument must be a non-negative integer'
|
339
|
+
raise(Fibonaccia::NotPositiveInteger, msg)
|
340
|
+
end
|
341
|
+
nterms = [ MIN_TERMS, self.terms - nterms ].max
|
342
|
+
SERIES.replace(SERIES.take(nterms))
|
343
|
+
return self.terms
|
344
|
+
end # def shrink
|
345
|
+
|
346
|
+
#
|
347
|
+
# The number of terms in the internal series.
|
348
|
+
#
|
349
|
+
# Similar to the #count method provided by the <tt>Enumerable</tt>
|
350
|
+
# mix-in, but a more direct approach -- and complementary to
|
351
|
+
# the {terms=} method.
|
352
|
+
#
|
353
|
+
# @return [Integer]
|
354
|
+
# number of terms in the internal series.
|
355
|
+
#
|
356
|
+
def terms
|
357
|
+
result = SERIES.count
|
358
|
+
return result
|
359
|
+
end # def terms
|
360
|
+
|
361
|
+
#
|
362
|
+
# Set the internal series to a specific number of terms.
|
363
|
+
#
|
364
|
+
# @macro minsize
|
365
|
+
# @param [Integer] nterms
|
366
|
+
# Number of terms to which the series should be grown or shrunk.
|
367
|
+
# @return [Integer]
|
368
|
+
# the number of terms in the series after the operation.
|
369
|
+
#
|
370
|
+
# @raise [Fibonaccia::NotPositiveInteger]
|
371
|
+
# if the argument isn't an integer greater than or equal to zero.
|
372
|
+
#
|
373
|
+
def terms=(nterms)
|
374
|
+
unless (nterms.kind_of?(Integer) && (nterms >= 0))
|
375
|
+
msg = 'argument must be a non-negative integer'
|
376
|
+
raise(Fibonaccia::NotPositiveInteger, msg)
|
377
|
+
end
|
378
|
+
nterms = [ MIN_TERMS, nterms ].max
|
379
|
+
if (nterms > self.terms)
|
380
|
+
self.grow(nterms - self.terms)
|
381
|
+
elsif (nterms < self.terms)
|
382
|
+
self.shrink(self.terms - nterms)
|
383
|
+
end
|
384
|
+
return self.terms
|
385
|
+
end # def terms=
|
386
|
+
|
387
|
+
#
|
388
|
+
# Reset the internal series to just the seed value.
|
389
|
+
#
|
390
|
+
# This can be used to free up memory.
|
391
|
+
#
|
392
|
+
# @return [void]
|
393
|
+
#
|
394
|
+
def reset
|
395
|
+
SERIES.replace(SEED)
|
396
|
+
return nil
|
397
|
+
end # def reset
|
398
|
+
|
399
|
+
#
|
400
|
+
# Iterate over the current internal series, yielding each value in
|
401
|
+
# turn.
|
402
|
+
#
|
403
|
+
# @yield [Integer]
|
404
|
+
# Each element of the internal series is yielded in turn.
|
405
|
+
#
|
406
|
+
# @return [Array<Integer>]
|
407
|
+
# if a block has been passed.
|
408
|
+
# @return [Enumerator]
|
409
|
+
# when invoked without a block.
|
410
|
+
#
|
411
|
+
def each(&block)
|
412
|
+
result = SERIES.each(&block)
|
413
|
+
return result
|
414
|
+
end # each
|
415
|
+
|
416
|
+
# @internal_comment
|
417
|
+
#
|
418
|
+
# The following method is *not* part of <tt>Enumerable</tt>, so we
|
419
|
+
# add it explicitly.
|
420
|
+
#
|
421
|
+
|
422
|
+
#
|
423
|
+
# Return the last value in the internal series.
|
424
|
+
#
|
425
|
+
# This is equivalent to
|
426
|
+
#
|
427
|
+
# Fibonaccia[-1]
|
428
|
+
#
|
429
|
+
# @return [Integer]
|
430
|
+
# the last term in the internal series.
|
431
|
+
#
|
432
|
+
def last
|
433
|
+
result = SERIES.last
|
434
|
+
return result
|
435
|
+
end # def last
|
436
|
+
|
437
|
+
#
|
438
|
+
# Obtain a slice (see Array#slice) of the Fibonacci series.
|
439
|
+
#
|
440
|
+
# The internal series will be extended, if necessary, to include
|
441
|
+
# all terms requested.
|
442
|
+
#
|
443
|
+
# @note
|
444
|
+
# The internal series is *zero-based*, which means the first
|
445
|
+
# term is numbered <tt>0</tt>.
|
446
|
+
#
|
447
|
+
# @param [Integer] first_term
|
448
|
+
# The first term of the slice from the series.
|
449
|
+
# @param [Integer] nterms
|
450
|
+
# The number of elements in the slice to be returned.
|
451
|
+
#
|
452
|
+
# @return [Integer]
|
453
|
+
# if the result is a valid slice containing only one term
|
454
|
+
# (<i>i.e.</i>, <tt>nterms</tt> is 1). Returns the Fibonacci term at
|
455
|
+
# the specified (zero-based) position in the sequence.
|
456
|
+
# @return [Array<Integer>]
|
457
|
+
# if the result is a valid multi-element slice (<i>e.g.</i>, <tt>nterms</tt>
|
458
|
+
# is greater than 1). Returns the specified slice.
|
459
|
+
# @return [nil]
|
460
|
+
# if the slice parameters are not meaningful (<i>e.g.</i>, <tt>slice(1, -1)</tt>).
|
461
|
+
#
|
462
|
+
# @raise [ArgumentError]
|
463
|
+
# if the arguments are not all integers.
|
464
|
+
#
|
465
|
+
def slice(first_term, nterms=1)
|
466
|
+
args = {
|
467
|
+
'first_term' => first_term,
|
468
|
+
'nterms' => nterms,
|
469
|
+
}
|
470
|
+
#
|
471
|
+
# Sanity-check our arguments; be more informative than the default
|
472
|
+
#
|
473
|
+
# TypeError: no implicit conversion of <class> into Integer
|
474
|
+
#
|
475
|
+
args.each do |argname,argval|
|
476
|
+
unless (argval.kind_of?(Integer))
|
477
|
+
raise(ArgumentError, "#{argname} must be an integer")
|
478
|
+
end
|
479
|
+
end
|
480
|
+
nterms = [ 1, nterms ].max
|
481
|
+
if (first_term < 0)
|
482
|
+
endpoint = [ 0, self.terms + first_term + nterms ].max
|
483
|
+
else
|
484
|
+
endpoint = first_term + nterms
|
485
|
+
end
|
486
|
+
Fibonaccia.extend_series(endpoint)
|
487
|
+
#
|
488
|
+
# We're going to pass this along to the array's own #slice
|
489
|
+
# method, so build its argument list appropriately.
|
490
|
+
#
|
491
|
+
args = [ first_term ]
|
492
|
+
args << nterms unless (nterms == 1)
|
493
|
+
result = SERIES.slice(*args)
|
494
|
+
#
|
495
|
+
# If we got a multi-element slice, make sure we don't return our
|
496
|
+
# master sequence! Ruby shouldn't let it happen, but defensive
|
497
|
+
# programing is all.
|
498
|
+
#
|
499
|
+
result = result.dup if (result === SERIES)
|
500
|
+
return result
|
501
|
+
end
|
502
|
+
|
503
|
+
# @internal_comment
|
504
|
+
#
|
505
|
+
# <tt>Module</tt> doesn't have <tt>#alias_method</tt>, so we have
|
506
|
+
# to work sideways to make the equivalent functionality happen.
|
507
|
+
# And Yard doesn't pick up on <tt>define_method</tt> invocations,
|
508
|
+
# so we have to add the method documentation manually.
|
509
|
+
#
|
510
|
+
|
511
|
+
# @!method [](first_term, nterms=1)
|
512
|
+
#
|
513
|
+
# Alias for {slice} ( *q.v.*).
|
514
|
+
#
|
515
|
+
# This is included because it's probably more human-readable to
|
516
|
+
# find the *n*-th term of the sequence using the syntax
|
517
|
+
#
|
518
|
+
# Fibonaccia[n]
|
519
|
+
#
|
520
|
+
# @see slice
|
521
|
+
#
|
522
|
+
define_method(:[], self.instance_method(:slice))
|
523
|
+
|
524
|
+
# @internal_comment
|
525
|
+
#
|
526
|
+
# We need to use the <tt>BigDecimal</tt> module to deal with the
|
527
|
+
# extra precision required for #is_fibonacci?.
|
528
|
+
#
|
529
|
+
# In addition, we don't use #member? or #include? for this
|
530
|
+
# functionality because it would onflict with the Enumerable
|
531
|
+
# semantics for those, which apply to the internal series.
|
532
|
+
#
|
533
|
+
|
534
|
+
#
|
535
|
+
# See if value appears in the Fibonacci series.
|
536
|
+
#
|
537
|
+
# Check to see if the given value is found in the Fibonacci series,
|
538
|
+
# using the transform described at
|
539
|
+
# {https://en.wikipedia.org/wiki/Fibonacci_number#Recognizing_Fibonacci_numbers}.
|
540
|
+
#
|
541
|
+
# @see https://en.wikipedia.org/wiki/Fibonacci_number#Recognizing_Fibonacci_numbers
|
542
|
+
#
|
543
|
+
# @param [Integer] val
|
544
|
+
# Value to be checked for membership in the Fibonacci series.
|
545
|
+
#
|
546
|
+
# @return [Boolean]
|
547
|
+
# <tt>true</tt> if the given value is a Fibonacci number, else <tt>false</tt>.
|
548
|
+
#
|
549
|
+
def is_fibonacci?(val)
|
550
|
+
#
|
551
|
+
# Needs to be an integer.
|
552
|
+
#
|
553
|
+
return false unless (val.respond_to?(:floor) && (val.floor == val))
|
554
|
+
#
|
555
|
+
# Needs to be non-negative.
|
556
|
+
#
|
557
|
+
return false if (val < 0)
|
558
|
+
return true if (SERIES.include?(val))
|
559
|
+
#
|
560
|
+
# Easy checks are done, time for some math-fu.
|
561
|
+
#
|
562
|
+
val = BigDecimal.new(val)
|
563
|
+
[ +4, -4 ].each do |c|
|
564
|
+
eqterm = 5 * (val**2) + c
|
565
|
+
root = eqterm.sqrt(BDPrecision)
|
566
|
+
return true if (root.floor == root)
|
567
|
+
end
|
568
|
+
return false
|
569
|
+
end # is_fibonacci?
|
570
|
+
|
571
|
+
end # module Fibonaccia eigenclass
|
572
|
+
|
573
|
+
end # module Fibonaccia
|