magician 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -8
- data/CHANGELOG.md +33 -5
- data/Gemfile +7 -7
- data/LICENSE.txt +1 -1
- data/README.md +15 -9
- data/VERSION +1 -1
- data/lib/magician/array.rb +133 -92
- data/lib/magician/integer.rb +44 -50
- data/lib/magician/math.rb +167 -116
- data/lib/magician/numeric.rb +38 -26
- data/lib/magician/random.rb +31 -0
- data/lib/magician/shortcuts.rb +5 -2
- data/lib/magician/string.rb +7 -7
- data/magician.gemspec +20 -18
- data/spec/array_spec.rb +87 -70
- data/spec/integer_spec.rb +44 -30
- data/spec/math_spec.rb +90 -72
- data/spec/numeric_spec.rb +36 -18
- data/spec/random_spec.rb +21 -0
- data/spec/shortcuts_spec.rb +7 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/string_spec.rb +10 -9
- metadata +20 -18
data/lib/magician/math.rb
CHANGED
@@ -1,121 +1,172 @@
|
|
1
1
|
# Magician's extensions to the Math module.
|
2
2
|
module Math
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
4
|
+
# If we don't do this, our new methods won't get added onto Math.
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Solves a quadratic formula of the form "ax^2+bx+c=0" for x, where a is not
|
8
|
+
# 0. It asks for the three coefficients of the function (a, b, and c), and
|
9
|
+
# returns the two possible values for x. Complex number results are not
|
10
|
+
# supported yet.
|
11
|
+
#
|
12
|
+
# @param [Numeric] a the first coefficient (must not be 0)
|
13
|
+
# @param [Numeric] b the second coefficient
|
14
|
+
# @param [Numeric] c the third coefficient
|
15
|
+
#
|
16
|
+
# @return [Array] a sorted array of two Floats, the two possible values for x
|
17
|
+
#
|
18
|
+
# @raise [ArgumentError] if a is 0
|
19
|
+
def quadratic(a, b, c)
|
20
|
+
raise ArgumentError, 'a cannot be zero' if a.zero?
|
21
|
+
|
22
|
+
left = -b
|
23
|
+
right = Math.sqrt(b**2 - 4*a*c)
|
24
|
+
bottom = 2*a
|
25
|
+
|
26
|
+
[ (left+right)/bottom, (left-right)/bottom ].sort
|
27
|
+
end
|
28
|
+
|
29
|
+
# The number of size k ordered subsets of a set of size n. Equivalent to
|
30
|
+
# n!/(n-k)!.
|
31
|
+
#
|
32
|
+
# @param [Integer] n the size of the set to pick from
|
33
|
+
# @param [Integer] k the size of the ordered subsets
|
34
|
+
#
|
35
|
+
# @return [Integer] the number of permutations
|
36
|
+
#
|
37
|
+
# @raise [ArgumentError] if either argument is negative, or if n < k
|
38
|
+
def permutations(n, k)
|
39
|
+
raise ArgumentError, 'n cannot be negative' if n < 0
|
40
|
+
raise ArgumentError, 'k cannot be negative' if k < 0
|
41
|
+
raise ArgumentError, 'n must be at least as large as k' if n < k
|
42
|
+
|
43
|
+
n.factorial / (n-k).factorial
|
44
|
+
end
|
45
|
+
|
46
|
+
# The number of size k unordered subsets of a set of size n. Equivalent to
|
47
|
+
# n!/(k!(n-k)!).
|
48
|
+
#
|
49
|
+
# @param [Integer] n the size of the set to pick from
|
50
|
+
# @param [Integer] k the size of the unordered subsets
|
51
|
+
#
|
52
|
+
# @return [Integer] the number of combinations
|
53
|
+
#
|
54
|
+
# @raise [ArgumentError] if either argument is negative, or if n < k
|
55
|
+
def combinations(n, k)
|
56
|
+
raise ArgumentError, 'n cannot be negative' if n < 0
|
57
|
+
raise ArgumentError, 'k cannot be negative' if k < 0
|
58
|
+
raise ArgumentError, 'n must be at least as large as k' if n < k
|
59
|
+
|
60
|
+
n.factorial / (k.factorial * (n-k).factorial)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the number of steps it takes to get from integer n to 1 using the
|
64
|
+
# Collatz conjecture.
|
65
|
+
#
|
66
|
+
# @see http://en.wikipedia.org/wiki/Collatz_conjecture
|
67
|
+
#
|
68
|
+
# @param [Integer] n the number to put into the Collatz conjecture initially
|
69
|
+
# @param [Integer] depth the number of steps that have passed so far (should
|
70
|
+
# not be modified unless this is being cached carefully)
|
71
|
+
#
|
72
|
+
# @return [Integer] the number of steps it takes to get from integer n to 1
|
73
|
+
# using the Collatz conjecture (the depth)
|
74
|
+
#
|
75
|
+
# @raise [ArgumentError] if n < 1
|
76
|
+
def collatz(n, depth=0)
|
77
|
+
raise ArgumentError, 'n must be at least 1' if n < 1
|
78
|
+
|
79
|
+
if n == 1
|
80
|
+
depth
|
81
|
+
elsif n % 2 == 0
|
82
|
+
depth += 1
|
83
|
+
collatz(n/2, depth)
|
84
|
+
else
|
85
|
+
depth += 1
|
86
|
+
collatz(3*n + 1, depth)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Using the Pythagorean theorem, gets c (the length of the hypotenuse) when a
|
91
|
+
# and b (the lengths of the other sides of a triangle) are given.
|
92
|
+
#
|
93
|
+
# @param [Numeric] a the length of the first side of the triangle
|
94
|
+
# @param [Numeric] b the length of the second side of the triangle
|
95
|
+
#
|
96
|
+
# @return [Float] the length of the hypotenuse of the triangle
|
97
|
+
#
|
98
|
+
# @raise [ArgumentError] if either argument is negative
|
99
|
+
def hypotenuse(a, b)
|
100
|
+
raise ArgumentError, 'a cannot be negative' if a < 0
|
101
|
+
raise ArgumentError, 'b cannot be negative' if b < 0
|
102
|
+
|
103
|
+
Math.sqrt(a**2 + b**2)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns true if the three given numbers are positive integers that form a
|
107
|
+
# Pythagorean triplet (that is, if a^2+b^2=c^2). C must be the last parameter.
|
108
|
+
#
|
109
|
+
# @param [Integer] a the length of the first side of the triangle
|
110
|
+
# @param [Integer] b the length of the second side of the triangle
|
111
|
+
# @param [Integer] c the length of the hypotenuse of the triangle
|
112
|
+
#
|
113
|
+
# @return [Boolean] true if the three numbers form a Pythagorean triplet
|
114
|
+
def triplet?(a, b, c)
|
115
|
+
return false if [a, b, c].any? { |n| n < 1 or not n.class <= Integer }
|
116
|
+
|
117
|
+
a**2 + b**2 == c**2
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculates a series of Fibonacci numbers of a specified length. Note that
|
121
|
+
# if terms are not passed to this method, it will start generating numbers
|
122
|
+
# with the terms [1, 1].
|
123
|
+
#
|
124
|
+
# @param [Integer] length the length of the Fibonacci series that should be
|
125
|
+
# returned
|
126
|
+
#
|
127
|
+
# @return [Array] a Fibonacci series of Integers with the specified length
|
128
|
+
# (ordered)
|
129
|
+
#
|
130
|
+
# @raise [ArgumentError] if a negative length is given, or if less than two
|
131
|
+
# terms are given
|
132
|
+
def fibs length, terms = [1, 1]
|
133
|
+
raise ArgumentError, 'Length must be at least 0' if length < 0
|
134
|
+
raise ArgumentError, 'At least two terms must be given' if terms.length < 2
|
135
|
+
|
136
|
+
terms << (terms[-2] + terms[-1]) while terms.length < length
|
137
|
+
|
138
|
+
terms.first length
|
139
|
+
end
|
140
|
+
|
141
|
+
# Finds all prime numbers from 1 to a given number n (inclusive) using the
|
142
|
+
# Sieve of Eratosthenes.
|
143
|
+
#
|
144
|
+
# @see http://www.algorithmist.com/index.php/Prime_Sieve_of_Eratosthenes
|
145
|
+
#
|
146
|
+
# @param [Integer] limit the upper limit of all primes to find (inclusive)
|
147
|
+
#
|
148
|
+
# @return [Array] an array of integers containing all discovered primes (in
|
149
|
+
# increasing order)
|
150
|
+
def primes limit
|
151
|
+
# Initialize the array of booleans
|
152
|
+
is_prime = [true] * (limit+1)
|
153
|
+
is_prime[0] = false
|
154
|
+
is_prime[1] = false
|
155
|
+
|
156
|
+
# Check for composite numbers and update the array with results
|
157
|
+
2.upto(Math.sqrt limit).each do |i|
|
158
|
+
if is_prime[i]
|
159
|
+
# Mark all multiples of i as composite
|
160
|
+
2.upto(limit).each do |factor|
|
161
|
+
multiple = i * factor
|
162
|
+
break if multiple > limit
|
163
|
+
is_prime[multiple] = false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Create an array of prime integers by iterating over the array of booleans
|
169
|
+
1.upto(limit).reduce [] { |primes, i| is_prime[i] ? primes << i : primes }
|
170
|
+
end
|
120
171
|
|
121
172
|
end
|
data/lib/magician/numeric.rb
CHANGED
@@ -1,31 +1,43 @@
|
|
1
1
|
# Magician's extensions to the Numeric class (affects Integers and Floats).
|
2
2
|
class Numeric
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
4
|
+
# Returns true if the number is evenly divisible by n. If n is equal to 0, it
|
5
|
+
# returns false, since numbers cannot be divided by 0 in real number
|
6
|
+
# arithmetic.
|
7
|
+
#
|
8
|
+
# @param [Numeric] n the number that this number (self) should be divided by
|
9
|
+
#
|
10
|
+
# @return [Boolean] true if the number is evenly divisible by n
|
11
|
+
def divisible? n
|
12
|
+
n.zero? ? false : modulo(n).zero?
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
15
|
+
# Performs to_s[selection].to_i on the number. Note that for floats, the
|
16
|
+
# decimal counts as a digit within the string.
|
17
|
+
#
|
18
|
+
# @param [Range] selection the selection/range to get from the number (you can
|
19
|
+
# use anything that works with the [] syntax)
|
20
|
+
#
|
21
|
+
# @return [Integer] substring of the number (using []), converted to an
|
22
|
+
# Integer
|
23
|
+
#
|
24
|
+
# @deprecated Avoid using this any more. It's inaccurate and not very useful.
|
25
|
+
def digits selection
|
26
|
+
to_s[selection].to_i
|
27
|
+
end
|
30
28
|
|
31
|
-
|
29
|
+
# Converts the number from degrees to radians and returns the result.
|
30
|
+
#
|
31
|
+
# @return [Numeric] the number in radians
|
32
|
+
def to_radians
|
33
|
+
self * Math::PI / 180
|
34
|
+
end
|
35
|
+
|
36
|
+
# Converts the number from radians to degrees and returns the result.
|
37
|
+
#
|
38
|
+
# @return [Numeric] the number in degrees
|
39
|
+
def to_degrees
|
40
|
+
self * 180 / Math::PI
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Magician's extensions to the Random class.
|
2
|
+
class Random
|
3
|
+
|
4
|
+
# For all the instance methods defined below, add a class method which invokes
|
5
|
+
# the instance method on DEFAULT.
|
6
|
+
[:boolean, :coin, :die].each do |name|
|
7
|
+
define_singleton_method(name) { DEFAULT.send name }
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a random boolean (true or false).
|
11
|
+
#
|
12
|
+
# @return [Boolean] true or false
|
13
|
+
def boolean
|
14
|
+
[true, false].sample random: self
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a random coin toss (heads or tails).
|
18
|
+
#
|
19
|
+
# @return [String] 'heads' or 'tails'
|
20
|
+
def coin
|
21
|
+
['heads', 'tails'].sample random: self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a random die roll (from 1-6).
|
25
|
+
#
|
26
|
+
# @return [Fixnum] a Fixnum from 1-6
|
27
|
+
def die
|
28
|
+
rand 1..6
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/magician/shortcuts.rb
CHANGED
data/lib/magician/string.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Magician's extensions to the String class.
|
2
2
|
class String
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
# Returns true if the string is a palindrome (meaning it is the same forward
|
5
|
+
# and backward).
|
6
|
+
#
|
7
|
+
# @return [Boolean] true if the string is a palindrome
|
8
|
+
def palindrome?
|
9
|
+
eql? reverse
|
10
|
+
end
|
11
11
|
|
12
12
|
end
|
data/magician.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "magician"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nicolas McCurdy"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2013-03-18"
|
13
13
|
s.description = "A suite of handy methods for doing calculations in irb."
|
14
14
|
s.email = "thenickperson@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/magician/integer.rb",
|
32
32
|
"lib/magician/math.rb",
|
33
33
|
"lib/magician/numeric.rb",
|
34
|
+
"lib/magician/random.rb",
|
34
35
|
"lib/magician/shortcuts.rb",
|
35
36
|
"lib/magician/string.rb",
|
36
37
|
"magician.gemspec",
|
@@ -38,6 +39,7 @@ Gem::Specification.new do |s|
|
|
38
39
|
"spec/integer_spec.rb",
|
39
40
|
"spec/math_spec.rb",
|
40
41
|
"spec/numeric_spec.rb",
|
42
|
+
"spec/random_spec.rb",
|
41
43
|
"spec/shortcuts_spec.rb",
|
42
44
|
"spec/spec_helper.rb",
|
43
45
|
"spec/string_spec.rb"
|
@@ -45,34 +47,34 @@ Gem::Specification.new do |s|
|
|
45
47
|
s.homepage = "http://github.com/thenickperson/magician"
|
46
48
|
s.licenses = ["MIT"]
|
47
49
|
s.require_paths = ["lib"]
|
48
|
-
s.rubygems_version = "1.8.
|
50
|
+
s.rubygems_version = "1.8.23"
|
49
51
|
s.summary = "A suite of handy methods for doing calculations in irb."
|
50
52
|
|
51
53
|
if s.respond_to? :specification_version then
|
52
54
|
s.specification_version = 3
|
53
55
|
|
54
56
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
55
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.
|
57
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.13"])
|
56
58
|
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
57
|
-
s.add_development_dependency(%q<rdoc>, ["~>
|
58
|
-
s.add_development_dependency(%q<bundler>, ["
|
59
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.8
|
60
|
-
s.add_development_dependency(%q<simplecov>, ["
|
59
|
+
s.add_development_dependency(%q<rdoc>, ["~> 4.0"])
|
60
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.2"])
|
61
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8"])
|
62
|
+
s.add_development_dependency(%q<simplecov>, ["~> 0.7"])
|
61
63
|
else
|
62
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
64
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
63
65
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
64
|
-
s.add_dependency(%q<rdoc>, ["~>
|
65
|
-
s.add_dependency(%q<bundler>, ["
|
66
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8
|
67
|
-
s.add_dependency(%q<simplecov>, ["
|
66
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
67
|
+
s.add_dependency(%q<bundler>, ["~> 1.2"])
|
68
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
69
|
+
s.add_dependency(%q<simplecov>, ["~> 0.7"])
|
68
70
|
end
|
69
71
|
else
|
70
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
72
|
+
s.add_dependency(%q<rspec>, ["~> 2.13"])
|
71
73
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
72
|
-
s.add_dependency(%q<rdoc>, ["~>
|
73
|
-
s.add_dependency(%q<bundler>, ["
|
74
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8
|
75
|
-
s.add_dependency(%q<simplecov>, ["
|
74
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
75
|
+
s.add_dependency(%q<bundler>, ["~> 1.2"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
77
|
+
s.add_dependency(%q<simplecov>, ["~> 0.7"])
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|